MAC spoofing using Linux kernel random generated address
I was struggling with a Microchip LAN 9500 with no EEPROM saving its MAC address. While using the smsc95xx driver provided by the Linux kernel, I found out the kernel was (reasonably) creating a random address at each reboot.
My client didn't like this, and he only needed the hw to be installed in a LAN, so I began to look for a way of spoofing the MAC address.
I found this interesting link, which gave me the first ideas about how to deal with the problem.
However, none of the automatic method proposed there was working in my Yocto
distribution. For instance, "Method 1", based on
/etc/systemd/network/00-default.link
, was giving the following error
message in journalctl:
Dec 12 09:22:26 colibri-imx6 systemd-udevd[273]: Could not set Alias, MACAddress or MTU on eth1: Device or resource busy Dec 12 09:22:26 colibri-imx6 systemd-udevd[273]: Could not apply link config to eth1: Device or resource busy
I suspect systemd was trying to apply the rule too early, when the eth1 interface was busy on something else.
Another problem with the standard spoofing method is related to the target MAC address, which must be manually changed for each machine, and this isn't that easy to manage. I thought the best would be let the kernel generate the random address at first boot, then use it on next reboots.
Here is how I could achieve the goal, and some suggestions in case you want to do the same.
First, choose, in your filesystem, where you're going to put the script for
spoofing. I decided to create a directory for that purpose,
/var/local/mac_spoof/
.
Then, I added the following udev rule, by creating a one-line file udev rule in
/etc/dev/rules.d/75-mac-spoof.rules
. This is its content:
ACTION=="add", SUBSYSTEM=="net", KERNEL=="eth1", RUN+="/var/local/mac_spoof/mac_spoof.sh %k"
Finally, this is the proper /var/local/mac_spoof/mac_spoof.sh
script:
#!/bin/sh ETH=$1 BATCH=/var/local/mac_spoof/${ETH}_spoof.batch ADDR=$(cat /sys/class/net/$ETH/address) # Create batch file if not exists (first boot) if [ ! -f $BATCH ] then echo link set dev $ETH down > $BATCH echo link set dev $ETH address $ADDR >> $BATCH echo link set dev $ETH up >> $BATCH fi # Apply ip batch, or delete it if anything failed ip -batch $BATCH || rm -f $BATCH
How does the script work
mac_spoof.sh
is called by udev each time eth1 interface is added to the
system. Its task is:
check whether
/var/local/mac_spoof/eth1_spoof.batch
file exists; if not, create it by writing the ip batch to perform mac spoofing from now on. The address (ADDR
variable) is read from sysfs, and we rely on it being randomly created by the kernel;apply the ip batch, or remove the
eth1_spoof.batch
file in case of failure.
Here is a how can you check that everything worked fine: the MAC address in eth1_spoof.batch must be the same as the one given by ip (or ifconfig) command. You can see it is 52:23:64:e2:fa:ad in my case:
root@colibri-imx6:~# cat /var/local/mac_spoof/eth1_spoof.batch link set dev eth1 down link set dev eth1 address 52:23:64:e2:fa:ad link set dev eth1 up root@colibri-imx6:~# ip addr show eth1 4: eth1: <BROADCAST,MULTICAST,DYNAMIC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:23:64:e2:fa:ad brd ff:ff:ff:ff:ff:ff inet 169.254.138.55/16 brd 169.254.255.255 scope global eth1 valid_lft forever preferred_lft forever inet6 fe80::5023:64ff:fee2:faad/64 scope link valid_lft forever preferred_lft forever
The above method worked for me, I can't assure it will work in any distribution or situation; in case you find anything to point out, just contact me.