Skip to main content

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.