3

Following this guide to setup a macvtap works great.
An excerpt from the guide loosk like this:

# ip link add link eth1 name macvtap0 type macvtap
# ip link set macvtap0 address 1a:46:0b:ca:bc:7b up
# ip link show macvtap0

The guide then goes on to explain that you can do:

#qemu-system-x86_64 -net nic,model=virtio,addr=1a:46:0b:ca:bc:7b -net tap,fd=3 3<>/dev/tap11

And that works great, if your macvtap ends up on /dev/tap11.
But how do you check which /dev/tapX interface actually got assigned to the macvtap0 above?

I have quite a lot of services setting up tap devices on my machine at random times. And I can't guess which one my specific one ended up on.

I was hoping that booting the qemu machine with:

    -netdev tap,ifname="macvtap0",id=network0,script=no,downscript=no \
    -device i82559b,netdev=network0,mac=${MAC}

Would work, where macvtap0 is the device I've just created.
However, that gives me:

qemu-system-x86_64: could not configure /dev/net/tun (macvtap0): Invalid argument

Outlined in this article: How to find the connection between tap interface and its file descriptor? - they describe a way to do it by accessing the PID and check associated file descriptors, problem here for me is that qemu isn't setting up the device, I am. And there's no PID associated with it.

Sow how can I get the file-handle under /dev associated with the newly created macvtap interface?

Edit: I'm already a few days in on this problem, but I just realised that I forgot to check /sys/class/net/macvtap0/, and there is a /tap2 device. Obivously I need to iterate through all the contents still to find a name matching regex(tap[0-9]+). This will work, but not sure it's the best/correct way to do this.

2
  • So, did you ever check my answer to your question?
    – A.B
    Commented Oct 18, 2020 at 17:27
  • 1
    @A.B I'm getting around to it. Been a very hectic year. But I'm still highly interested in this, just haven't been around setting up virtual interfaces for a while.
    – Torxed
    Commented Oct 18, 2020 at 19:13

1 Answer 1

6

The device name is created based upon the interface index:

snprintf(tap_name, IFNAMSIZ, "tap%d", dev->ifindex);

[...]

      devt = MKDEV(MAJOR(macvtap_major), vlantap->tap.minor);
      classdev = device_create(&macvtap_class, &dev->dev, devt,
                   dev, tap_name);

If OP's ip link show macvtap0's answer was present it would most likely begin with:

11: macvtap0@eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 [...]

If you don't want to rely on this specific property which doesn't appear to be officially documented (the word macvtap doesn't exist in the whole linux/Documentation/ tree), you can rely on the entries in /sys (which don't appear either to be officially documented for macvtap):

The informations are available in the macvtap/* subdir. Note that tap11 is a symlink to the single entry macvtap/tap11 and since it's impractical where it is, it should be ignored (but not the directory it points to).

  • From network interface name find device node entry name:

    1. /sys/class/net/macvtap0/macvtap/* has a single entry, the tap device name, which is a directory:

      $ printf '/dev/%s\n' "$(basename /sys/class/net/macvtap0/macvtap/*)"
      /dev/tap11
      

      A function called intf_to_devtap could be used:

      intf_to_devtap () {
          [ -e "/sys/class/net/$1/macvtap" ] || return 1
          printf '/dev/%s\n' "$(basename /sys/class/net/$1/macvtap/*)"
      }
      

      For OP's case:

      $ intf_to_devtap macvtap0
      /dev/tap11
      
    2. dev entry for major:minor:

      $ cat /sys/class/net/macvtap0/macvtap/*/dev
      242:1
      

      This property (and the equivalent below in uevent) is the only guaranteed to be unique in case there are macvtap created in multiple network namespaces, each with the same interface index, thus making a device node name collision. Example of collision (when no macvtap device were created before):

      # ip netns add collision1; ip netns add collision2
      # ip -n collision1 link add type dummy; ip -n collision2 link add type dummy
      # ip -n collision1 link add link dummy0 type macvtap
      # ls -l /dev/tap*
      crw-------. 1 root root 242, 1 Apr 19 21:24 /dev/tap3
      # ip -n collision2 link add link dummy0 type macvtap
      # ls -l /dev/tap*
      crw-------. 1 root root 242, 1 Apr 19 21:24 /dev/tap3
      

      A second tap entry didn't appear. That's because there was a collision when the second network namespace also created an interface with index 3 (lo=1, dummy0=2, macvtap0=3), and the second device entry wasn't created in /dev because a file of the same name already existed. As ip netns also remounts /sys for accurate handling of network devices, one can verify that actually two tap devices exist, even if the second one wasn't "mapped":

      # ip netns exec collision1 sh -c 'cat /sys/class/net/macvtap0/macvtap/*/dev'
      242:1
      # ip netns exec collision2 sh -c 'cat /sys/class/net/macvtap0/macvtap/*/dev'
      242:2
      

      So one could still use the 2nd macvtap interface backend by "mapping" and then using the device, even if half of /sys informations won't match this, for example:

      # mknod /dev/tap3b c 242 2
      

      or with a more verbose name like /dev/tap-VM1-intfA.

    3. uevent entry, giving both major:minor and name:

      $ cat /sys/class/net/macvtap0/macvtap/*/uevent
      MAJOR=242
      MINOR=1
      DEVNAME=tap11
      
  • From dev entry to interface name:

    With /dev/tap11:

    /dev/tap11 -> /sys/class/macvtap/tap11 -> /sys/devices/virtual/net/macvtap0/macvtap/tap11. Note that /sys/class/net/macvtap0 before is actually a symlink to /sys/devices/virtual/net/macvtap0.

    Still in the last directory, the device entry is a symlink to the interface directory, so we have in the end /sys/class/macvtap/tap11/device -> /sys/devices/virtual/net/macvtap0

    A function called devtap_to_intf could be used:

    devtap_to_intf () {
        [ -c "$1" ] || [ -c "/dev/$1" ] || return 1
        local path="$(readlink -e "/sys/class/macvtap/${1#/dev/}/device")"
        printf '%s\n' "${path#/sys/devices/virtual/net/}"
    }
    

    With OP's case:

    $ devtap_to_intf /dev/tap11
    macvtap0
    
1
  • Apologies it took so long time. Great work, and really appreciated all the descriptions of the steps. I'm still a bit puzzled, but at least I get the concept and where to look : )
    – Torxed
    Commented Feb 3, 2022 at 12:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .