Currently, libvirt takes upon itself to create a macvtap device on top of a host interface. I'd like libvirt to be able to do less. I'd like to create a macvtap device and configure it to my liking (in a CNI), and make it available to libvirt which runs as a normal user in its own netns. I would like to specify the device it as a <source dev='macvtap0'> element, and have libvirt connect it to qemu.
Today I was finally able to put together a proper test for this, and successfully started a guest with session-mode (unprivileged/non-root) libvirtd that used a pre-created macvtap device. My test was this: (as root) # ip link add link p14p1 name mymacvtap0 address 52:54:00:12:12:12 type macvtap mode bridge # ip link set dev mymacvtap0 up # chown laine:laine /dev/tap$(ip link show mymacvtap0 | head -1 | cut -d: -f1) (as user laine) # virsh define [a guest with the following interface definition] <interface type='direct'> <mac address='52:54:00:12:12:12'/> <source dev='p14p1' mode='bridge'/> <target dev='mymacvtap0'/> </interface> # virsh start [that guest] If the macvtap device didn't exist, or the mac address of the macvtap device is different from what's in the definition, it would log an error. Conversely, the setting of source dev and mode are ignored - that's only used during device creation, and my patch bypasses device creation completely when the device already exists. This could maybe be made a bit friendlier in the error reporting department (e.g., right now it will complain that it can't create a non-existing device rather than saying that it can't create it due to insufficient privileges), but it would be at least good enough to try it out and see if this is what you want to do. I'll send a patch upstream tomorrow AM (US EST), and try to make either a RHEL8 or Fedora 30 rpm for you if it would be useful. (It's a very minor patch once I got enough time to actually think through it).
I thought about this more before sending a patch, and realized I didn't want the simple patch because it is a hack. In the meantime I've also found that a small modification to the device setup for conventional tap devices makes precreated standard taps usable from a non-privileged libvirt too - as with macvtap devices, it the tap device is created as owned by the uid of the process running libvirtd, and as long as we avoid attempting to set the MAC address or modify the interface flags (e.g. to set IFF_UP or MULTIQUEUE) then it can be opened by the unprivileged libvirtd and consumed successfully by qemu. I'm now thinking that this could all be exposed to libvirt's consumers with an extra option to <interface type='ethernet'>, e.g.: <interface type='ethernet'> <target dev='tapdevname' unmanaged='yes'/> ... </interface> Any network device declared like this would be opened, but no setup would be done (no mac address setting, no interface flags, no MTU). This could work for both macvtap and standard tap even though they each use a different method of opening the fd for the device - just check first whether or not it's a macvtap device. )There could be disagreement with this, since in the past type='ethernet' was used only for standard tap devices.)
I just posted patches upstream that support use of precreated tap and macvtap devices by an unprivileged libvirt. https://www.redhat.com/archives/libvir-list/2019-August/msg01256.html (I did switch the attribute name to "managed" :-) I don't know that this can be reviewed in time for the feature freeze for 5.7.0, since that will be happening within 12-16 hours.
Patches have been pushed upstream to support an unprivileged libvirtd using pre-created standard tap and macvtap devices: commit 1b46566eed0c9c23251d05c8fedb3522d711f5c0 Author: Laine Stump <laine> Date: Mon Aug 26 01:24:08 2019 -0400 util: new function virNetDevMacVLanIsMacvtap() commit 3d21ff72e0e37ebfb3305c93c003ab0ec36b98e4 Author: Laine Stump <laine> Date: Mon Aug 26 01:51:40 2019 -0400 util: make a couple virNetDevMacVlan*() functions public commit 3c049fadce477869af1830a0af8e25ec7c427fe8 Author: Laine Stump <laine> Date: Tue Aug 27 12:18:35 2019 -0400 qemu: reorganize qemuInterfaceEthernetConnect() commit 33d02dfca67bd8fd65a50631f7aaae07215ed7cd Author: Laine Stump <laine> Date: Tue Aug 20 22:53:11 2019 -0400 conf: use virXMLFormatElement for interface <target> commit 77f72a86159beccc6cfff3392a2aca78485729be Author: Laine Stump <laine> Date: Wed Aug 21 16:42:41 2019 -0400 conf: new "managed" attribute for target dev of <interface type='ethernet'> commit 7cd0911e1a6c1b1cfe57c6e375077c689424a009 Author: Laine Stump <laine> Date: Mon Aug 26 00:24:34 2019 -0400 qemu: support unmanaged target tap dev for <interface type='ethernet'> commit 51d66b92e6c4873016409f90fbe257fb23bd4bc0 Author: Laine Stump <laine> Date: Mon Aug 26 13:05:19 2019 -0400 qemu: support unmanaged macvtap devices with <interface type='ethernet'>
This patch isn't needed for building on RHEL, but will be needed by anyone who wants to apply it to a platform that doesn't support macvtap: commit c803e05870f459948efa7dce97682d0169a525f7 Author: Michal Privoznik <mprivozn> Date: Tue Sep 10 11:24:19 2019 +0200 virnetdevmacvlan: Provide stubs for macvlan related functions
Test 2 libvirt-5.6.0-7.el8.x86_64 with below 2 scenarios: 1. macvatp: Create the macvtap device and start vm with it under unprivileged libvirtd: # ip link add link ens1f1 name mymacvtap0 address 52:54:00:11:11:11 type macvtap mode bridge # ip link set mymacvtap0 up $ whoami yalzhang $ virsh dumpxml rhel | grep /interface -B5 <interface type='ethernet'> <mac address='52:54:00:11:11:11'/> <target dev='mymacvtap0' managed='no'/> <model type='virtio'/> <address type='pci' domain='0x0000' bus='0x07' slot='0x00' function='0x0'/> </interface> $ virsh start rhel error: Failed to start domain rhel error: cannot open macvtap tap device /dev/tap11: Permission denied # chown yalzhang:yalzhang /dev/tap11 or # chown yalzhang:yalzhang /dev/tap$(ip link show mymacvtap0 | head -1 | cut -d: -f1) $ virsh start rhel Domain rhel started login the guest, guest can get ip address in the same subnet of the host's. And check the network functions, the network works well. 2. bridge type: create linux bridge and connect it with the host physical interface, then start vm under unprivileged libvirtd: # tmux -c "ip link add name br0 type bridge; ip link set ens1f1 up; ip link set ens1f1 master br0; ip link set br0 up; pkill dhclient; dhclient br0" # ip tuntap add mode tap user yalzhang group yalzhang name mytap0 # ip link set mytap0 up # ip link set mytap0 master br0 $ whoami yalzhang $ virsh dumpxml rhel | grep /interface -B6 <interface type='ethernet'> <mac address='52:54:00:93:0f:bc'/> <target dev='mytap0' managed='no'/> <model type='virtio'/> <alias name='net0'/> <address type='pci' domain='0x0000' bus='0x07' slot='0x00' function='0x0'/> </interface> $ virsh start rhel Domain rhel started login the vm and check network function, it works as expected: [root@localhost ~]# ifconfig enp8s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.xx.xx.xx netmask 255.255.252.0 broadcast 10.xx.xx.xx inet6 fe80::78ee:d280:ee35:7ca8 prefixlen 64 scopeid 0x20<link> ether 52:54:00:93:0f:bc txqueuelen 1000 (Ethernet) ... $ ip a | grep mytap0 -A3 12: mytap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000 link/ether 16:7b:c1:cc:bb:4f brd ff:ff:ff:ff:ff:ff inet6 fe80::147b:c1ff:fecc:bb4f/64 scope link valid_lft forever preferred_lft forever Test the guest's network function, it works well.
Some negative scenarios: 1. managed='no' without specify the target dev: $ virsh dumpxml rhel | grep /interface -B5 <interface type='ethernet'> <mac address='52:54:00:11:11:12'/> <target managed='no'/> <model type='virtio'/> <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/> </interface> $ virsh start rhel error: Failed to start domain rhel error: internal error: target dev must be supplied when managed='no' 2. managed='no' + the specified target dev do not exists: $ ip l | grep vnet0 $ ===> no output $ virsh dumpxml rhel | grep /interface -B5 <interface type='ethernet'> <mac address='52:54:00:11:11:12'/> <target dev='vnet0' managed='no'/> <model type='virtio'/> <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/> </interface> $ virsh start rhel error: Failed to start domain rhel error: internal error: target managed='no' but specified dev doesn't exist 3. "managed=yes" $ virsh dumpxml rhel | grep /interface -B5 <interface type='ethernet'> <mac address='52:54:00:11:11:12'/> <target dev='mytap0' managed='yes'/> <model type='virtio'/> <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/> </interface> $ virsh start rhel error: Failed to start domain rhel error: Cannot set interface MAC to fe:54:00:11:11:12 on 'mytap0': Operation not permitted
Created attachment 1629743 [details] the coverage report for the patches
Since the problem described in this bug report should be resolved in a recent advisory, it has been closed with a resolution of ERRATA. For information on the advisory, and where to find the updated files, follow the link below. If the solution does not work for you, open a new bug report. https://access.redhat.com/errata/RHBA-2020:0404