From 9e77c3d3bbd47863c0f60e4499396429bb46582c Mon Sep 17 00:00:00 2001 From: Sean Mooney Date: Thu, 13 Dec 2018 00:56:44 +0000 Subject: [PATCH] Libvirt: do not set MAC when unplugging macvtap VF - This change updates unplug_hw_veb to reset the VF MAC when unplugging macvtap SR-IOV vifs. Doing this will prevent a network partition to be formed as the VM's MAC is no longer retained on the VF after the vif has been unplugged, allowing traffic with the VFs former MAC to be transmitted on the PF in case it is reused in another host. - This change corrects an outdated comment to reflect that network drivers allow setting a VF's MAC to zero via ip tool. It should be noted that setting the VF's netdev MAC to zero is not allowed and thus ip tool return code in this scenario is ignored in linux_utils.set_vf_interface_vlan(). We also leave a TODO to futher clean up this code later in Stein. Change-Id: I4f78f3cc412d7ec59456aea915a1ebfc8b27681f Closes-Bug: #1808252 --- nova/tests/unit/virt/libvirt/test_vif.py | 13 +++++-------- nova/virt/libvirt/vif.py | 17 +++++++++++------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/nova/tests/unit/virt/libvirt/test_vif.py b/nova/tests/unit/virt/libvirt/test_vif.py index 260148188ce3..1955dfb33143 100644 --- a/nova/tests/unit/virt/libvirt/test_vif.py +++ b/nova/tests/unit/virt/libvirt/test_vif.py @@ -975,6 +975,8 @@ class LibvirtVifTestCase(test.NoDBTestCase): mock_get_ifname.side_effect = ['eth1', 'eth13'] exit_code = [0, 2, 254] port_state = 'up' if vlan > 0 else 'down' + mac = ('00:00:00:00:00:00' if op.__name__ == 'unplug' + else self.vif_hw_veb_macvtap['address']) calls = { 'get_ifname': [mock.call(self.vif_hw_veb_macvtap['profile']['pci_slot'], @@ -982,15 +984,10 @@ class LibvirtVifTestCase(test.NoDBTestCase): mock.call(self.vif_hw_veb_macvtap['profile']['pci_slot'])], 'get_vf_num': [mock.call(self.vif_hw_veb_macvtap['profile']['pci_slot'])], - 'execute': [mock.call('ip', 'link', 'set', 'eth1', - 'vf', 1, 'mac', - self.vif_hw_veb_macvtap['address'], - 'vlan', vlan, - run_as_root=True, + 'execute': [mock.call('ip', 'link', 'set', 'eth1', 'vf', 1, + 'mac', mac, 'vlan', vlan, run_as_root=True, check_exit_code=exit_code)], - 'set_macaddr': [mock.call('eth13', - self.vif_hw_veb_macvtap['address'], - port_state=port_state)] + 'set_macaddr': [mock.call('eth13', mac, port_state=port_state)] } op(self.instance, self.vif_hw_veb_macvtap) mock_get_ifname.assert_has_calls(calls['get_ifname']) diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index 4b846b6c2bb9..08039ce9fad0 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -765,14 +765,19 @@ class LibvirtGenericVIFDriver(object): pass def unplug_hw_veb(self, instance, vif): - # TODO(vladikr): This code can be removed once the minimum version of - # Libvirt is incleased above 1.3.5, as vlan will be set by libvirt + # TODO(sean-k-mooney): remove in Train after backporting 0 mac + # change as this should no longer be needed with libvirt >= 3.2.0. if vif['vnic_type'] == network_model.VNIC_TYPE_MACVTAP: - # The ip utility doesn't accept the MAC 00:00:00:00:00:00. - # Therefore, keep the MAC unchanged. Later operations on - # the same VF will not be affected by the existing MAC. + # NOTE(sean-k-mooney): Retaining the vm mac on the vf + # after unplugging the vif prevents the PF from transmitting + # a packet with that destination address. This would create a + # a network partition in the event a vm is migrated or the neuton + # port is reused for another vm before the VF is reused. + # The ip utility accepts the MAC 00:00:00:00:00:00 which can + # be used to reset the VF mac when no longer in use by a vm. + # As such we hardcode the 00:00:00:00:00:00 mac. linux_net_utils.set_vf_interface_vlan(vif['profile']['pci_slot'], - mac_addr=vif['address']) + mac_addr='00:00:00:00:00:00') elif vif['vnic_type'] == network_model.VNIC_TYPE_DIRECT: if "trusted" in vif['profile']: linux_net.set_vf_trusted(vif['profile']['pci_slot'], False)