When cloning a Linux virtual machine, reinitializing the MAC address
renames the network interfaces. For example, "eth0" stops
referencing a valid device and "eth1" now references the first ethernet
device. This doesn't work well with clones though; I like
reinitializing MAC
addresses for concurrency, but I dislike new names and particularly
dislike waiting 120+ seconds for "new" interfaces to "start".
The crux of the issue is easily shown with ifconfig -a and, in greater
detail, by 70-persistent-net.rules [automatically generated by udev].
Here's the original "parent" VM-
> test@original~$ ifconfig -a | grep "Link\|inet"
eth0 Link encap:Ethernet HWaddr 08:00:27:aa:af:eb
inet addr:192.168.0.2 Bcast:192.168.0.255 Mask:255.255.255.0
eth1 Link encap:Ethernet HWaddr 08:00:27:e9:c5:fd
inet addr:10.0.0.2 Bcast:10.255.255.255 Mask:255.0.0.0
> test@original~$ grep 'eth' /etc/udev/rules.d/70-persistent-net.rules
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:aa:af:eb", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:e9:c5:fd", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"
The clone, with a reinitialized MAC, looks like this after booting:
> test@clone:~$ ifconfig -a | grep "Link\inet"
eth2 Link encap:Ethernet HWaddr 08:00:27:e3:dc:0d
eth3 Link encap:Ethernet HWaddr 08:00:27:73:2d:87
> test@clone:~$ grep 'eth' /etc/udev/rules.d/70-persistent-net.rules
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:aa:af:eb", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:e9:c5:fd", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:e3:dc:0d", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth2"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:73:2d:87", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth3"
UDEV automatically assigned a new name to each interface because they
failed to match any previous rules (different ATTR{address}).
Unfortunately, eth2 and eth3 weren't setup at all because
/etc/network/interfaces had no mention of them. Even worse, for the
impatient, /etc/network/interfaces directed the system to DHCP eth0 and
eth1, but both failed (after 2 minutes) because the devices weren't
mapped.
Previously, I applied a 10-second manual fix using "vim
/etc/udev/rules.d/70-persistent-net.rules" and changing the offending
lines:
> sudo cp /etc/udev/rules.d/70-persistent-net.rules{,.bak} # make a backup
> cat /etc/udev/rules.d/70-persistent-net.rules
# This file was automatically generated by the /lib/udev/write_net_rules
# program, run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single
# line, and change only the value of the NAME= key.
# PCI device 0x8086:/sys/devices/pci0000:00/0000:00:08.0 (e1000)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:aa:af:eb", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
# PCI device 0x8086:/sys/devices/pci0000:00/0000:00:03.0 (e1000)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:e9:c5:fd", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"
# PCI device 0x8086:/sys/devices/pci0000:00/0000:00:03.0 (e1000)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:e3:dc:0d", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth2"
# PCI device 0x8086:/sys/devices/pci0000:00/0000:00:08.0 (e1000)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:73:2d:87", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth3"
# notice that eth0 (pci...08.0) mapped to eth3 (pci...08.0) this time;
it mapped to eth2 every other time, but concurrent driver startup
causes random name selections
> sudo vim /etc/udev/rules.d/70-persistent-net.rules # delete eth0/eth1, change eth3 -> eth0, change eth2 -> eth1
> cat /etc/udev/rules.d/70-persistent-net.rules
# This file was automatically generated by the /lib/udev/write_net_rules
# program, run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single
# line, and change only the value of the NAME= key.
# PCI device 0x8086:/sys/devices/pci0000:00/0000:00:03.0 (e1000)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:e3:dc:0d", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"
# PCI device 0x8086:/sys/devices/pci0000:00/0000:00:08.0 (e1000)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:73:2d:87", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
After a quick reboot, everything works and looks like the original:
> ifconfig -a | grep "Link\|inet\|\n"
eth0 Link encap:Ethernet HWaddr 08:00:27:e3:dc:0d
inet addr:192.168.0.3 Bcast:192.168.0.255 Mask:255.255.255.0
eth1 Link encap:Ethernet HWaddr 08:00:27:73:2d:87
inet addr:10.0.0.3 Bcast:10.255.255.255 Mask:255.0.0.0
> grep 'eth' /etc/udev/rules.d/70-persistent-net.rules
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:e3:dc:0d", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:73:2d:87", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"
This fix is quick and works great, but manually fixing every clone
every time their reinitialized is a bad long-term fix. And fortunately,
there's a very simple fix available. As we saw above, although the MAC
address changed the PCI device ID
(/sys/devices/pci0000:00/0000:00:03.0) stayed the same. We can fix the
problem by directly modifying
/etc/udev/rules.d/70-persistent-net.rules, but that's automatically
generated and will be overwritten whenever we connect a new network
device. The source of that file, /lib/udev/write_new_rules, is a far
better target - especially for such an easy fix. (btw, there's a good
tutorial on udev at http://www.reactivated.net/writing_udev_rules.html)
> sudo cp /lib/udev/write_new_rules{,.bak} # always make a backup
> sudo mv /etc/udev/rules.d/70-persistent-net.rules{,.old} # remove, but keep it handy
> sudo vi /lib/udev/write_new_rules # replace if [ "$MATCHADDR" ]; then +2 lines w/
if [ "$ID_MODEL_ID" ]; then
match="$match, DRIVERS==\"?*\", ATTR{device}==\"$ID_MODEL_ID\""
elif [ "$MATCHADDR" ]; then
match="$match, DRIVERS==\"?*\", ATTR{address}==\"$MATCHADDR\""
fi
After a quick reboot, everything works and looks like the original AND we can freely add/remove interfaces / reclone the VM.
> ifconfig -a | grep "Link\|inet\|\n"
eth0 Link encap:Ethernet HWaddr 08:00:27:e3:dc:0d
inet addr:192.168.0.3 Bcast:192.168.0.255 Mask:255.255.255.0
eth1 Link encap:Ethernet HWaddr 08:00:27:73:2d:87
inet addr:10.0.0.3 Bcast:10.255.255.255 Mask:255.0.0.0
> grep 'eth' /etc/udev/rules.d/70-persistent-net.rules
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{device}=="/sys/devices/pci0000:00/0000:00:03.0", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{device}=="/sys/devices/pci0000:00/0000:00:08.0", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"
- Kelson (kelson
@shysecurity.com)