Constantly changing MAC addresses can be quite frustrating. The following two solutions are what we use when working with eMMC based devices such as the NanoPi R5C that we modify for OutagesIO.
Method number one
The code will try to discover the path and name of the eMMC device and if it cannot auto discover it, it will prompt you for the device name.
In the case of the R5C NanoPi, the path is /sys/class/block/mmcblk2/device/cid so the name of the device is mmcblk2.
After knowing the name of the eMMC, the code will prompt you for how many Ethernet ports your device has and it will consistently generate the same MAC addresses for your device based on the eMMC’s unique identifier.
Once you have your MAC addresses, you can enter them in your network file. In the case of the R5C, this is;
/etc/config/network
config interface ‘loopback’
option device ‘lo’
option proto ‘static’
option ipaddr ‘127.0.0.1’
option netmask ‘255.0.0.0’
config globals ‘globals’
option ula_prefix ‘fd00:ab:cd::/48’
config device
option name ‘eth0’
option macaddr ’20:3b:87:07:1a:83′
config interface ‘wan’
option device ‘eth0’
option proto ‘dhcp’
config interface ‘wan6’
option device ‘eth0’
option proto ‘dhcpv6’
config device
option name ‘br-lan’
option type ‘bridge’
list ports ‘eth1’
config device
option name ‘eth1’
option macaddr ’20:3b:87:07:1a:84′
config interface ‘lan’
option device ‘br-lan’
option proto ‘dhcp’
option ip6assign ’60’
This should work with most devices as long as you know what the eMMC device name is. The code can be found below but please read the manual as we do not support this code, we are simply sharing it to help others. The code can of course be improved but it works for our needs.
Using gen-mac.sh:
1. Introduction:
The gen-mac.sh script is designed to generate MAC addresses for Ethernet ports based on the eMMC block device name provided by the user.
2. Prerequisites:
Linux environment.
The eMMC block device name (e.g., mmcblk0) should be known or detectable.
3. Usage:
Copy the code to a script named gen-mac.sh on your device.
use ‘chmod +x gen-mac.sh’ to make it executable.
Execute the script by running ./gen-mac.sh in the terminal.
4. Instructions:
The script will prompt you to enter the eMMC block device name. You can manually enter the name or press Enter to attempt automatic detection.
If automatic detection fails, you’ll be prompted to manually enter the device name.
After entering the device name, the script will prompt you to enter the number of Ethernet ports for which you want to generate MAC addresses.
Once the number of ports is specified, the script will generate and display MAC addresses for each Ethernet port based on the provided eMMC block device name.
5. Example:
$ ./gen-mac.sh
Please enter your eMMC block device name (e.g., mmcblk0).
This is usually found under /sys/class/block/ and has a ‘cid’ file in its directory.
If you do not know your device name or wish to attempt automatic detection, just press Enter.
Enter device name or press Enter to attempt automatic detection:
Automatic detection failed or device not found. Please enter the device name manually.
Enter device name: mmcblk2
Using eMMC device: mmcblk2
Enter the number of Ethernet ports: 2
Generated MAC Address for Ethernet 0: 20:3b:87:07:1a:83
Generated MAC Address for Ethernet 1: 20:3b:87:07:1a:84
6. Exiting:
The script will exit after displaying the generated MAC addresses.
7. Note:
Ensure that the eMMC block device name provided exists and has a ‘cid’ file in its directory.
The MAC addresses are generated based on the device name, so different device names will produce different MAC addresses.
If you rebuild your firmware and lose your MAC addresses, simply use this script again and it will re-generate the same MAC addresses.
The code
#!/bin/bash
# Function to increment the MAC address
increment_mac() {
local mac=$1
IFS=’:’ read -r -a octets <<< “$mac” # Split MAC address into octets
local last_octet_decimal=$((16#${octets[-1]})) # Convert last octet from hex to decimal
last_octet_decimal=$((last_octet_decimal + 1)) # Increment the last octet
# Check if last octet is 256, then wrap around and increment the next octet
if [ $last_octet_decimal -eq 256 ]; then
last_octet_decimal=0
# Increment the second last octet (this is a simple case and might not cover every scenario)
local second_last_octet_decimal=$((16#${octets[-2]} + 1))
octets[-2]=$(printf “%02X” $second_last_octet_decimal)
fi
octets[-1]=$(printf “%02X” $last_octet_decimal) # Convert back to hex and pad with zero if necessary
IFS=’:’; echo “${octets[*]}” # Return the incremented MAC address, correctly formatted
}
# Prompt user to enter eMMC block device name
echo “Please enter your eMMC block device name (e.g., mmcblk0).”
echo “This is usually found under /sys/class/block/ and has a ‘cid’ file in its directory.”
echo “If you do not know your device name or wish to attempt automatic detection, just press Enter.”
# Attempt automatic detection if the device name is not provided
read -rp “Enter device name or press Enter to attempt automatic detection: ” mmc_dev_input
if [ -z “$mmc_dev_input” ]; then
# Attempt automatic detection
mmc_dev=$(find /sys/class/block/ -name cid -exec dirname {} \; | sed ‘s#.*/##’ | head -n 1)
if [ -z “$mmc_dev” ] || [ ! -e “/sys/class/block/$mmc_dev/device/cid” ]; then
echo “Automatic detection failed or device not found. Please enter the device name manually.”
read -rp “Enter device name: ” mmc_dev
if [ -z “$mmc_dev” ] || [ ! -e “/sys/class/block/$mmc_dev/device/cid” ]; then
echo “Invalid device name or device does not have a CID. Exiting.”
exit 1
fi
fi
else
mmc_dev=$mmc_dev_input
if [ ! -e “/sys/class/block/$mmc_dev/device/cid” ]; then
echo “Invalid device name or device does not have a CID. Exiting.”
exit 1
fi
fi
echo “Using eMMC device: $mmc_dev”
# Prompt user to enter the number of Ethernet ports
read -rp “Enter the number of Ethernet ports: ” port_count
# Generate MAC addresses
cid_hash=$(sha256sum “/sys/class/block/$mmc_dev/device/cid” | cut -d ‘ ‘ -f 1)
base_mac=$(echo “${cid_hash:0:12}” | sed ‘s/\(..\)/\1:/g;s/:$//’)
# Display generated MAC addresses for each Ethernet port
for ((i = 0; i < port_count; i++)); do
if [ $i -gt 0 ]; then
base_mac=$(increment_mac “$base_mac”) # Increment MAC address for subsequent ports
fi
echo “Generated MAC Address for Ethernet $i: $base_mac”
done
Method number two
OS Compatibility
This method works on Linux distributions that use the Ubuntu-like systemd networking stack (most modern Linux distributions such as Ubuntu, Debian, etc.).
For non-systemd distributions (or older distros using init or other networking methods), the approach may need to be adjusted.
For example
For older systems using /etc/network/interfaces, you would modify the configuration file directly instead of using a script.
For non-systemd distributions, custom startup scripts would be placed in other boot-time directories (like /etc/rc.local or /etc/init.d).
Non-Linux OSes (like FreeBSD or macOS) would require a completely different approach based on their networking methods.
Summary
Extract CID
You obtained the cid from the eMMC with cat /sys/class/block/mmcblk2/device/cid and used portions of it to create unique MAC addresses.
Set MAC Addresses Manually
You manually set the MAC addresses for eth0 and eth1 using:
sudo ip link set dev eth0 address d6:01:03:19:00:00
sudo ip link set dev eth1 address d6:01:03:19:00:01
Make Changes Persistent
To ensure the changes persisted after a reboot, you created a script in /etc/network/if-pre-up.d/set-mac-address to automatically set the MAC addresses at boot.
Verification
After rebooting, the MAC addresses were correctly set and persisted across restarts.
Hopefully, one of these methods can work for you and no more ramdonly changing mAC addresses.