automatic renaming and numbering of nics using udev
-
10-07-2019 - |
Question
I'm writing on an udev-rule to automatically rename and number NICs with specific MAC addresses.
The resulting rule should do nearly the same 75-persistent-net-generator.rules
does (match first 3 bytes of the MAC address of card, name it 'mycard*' depending on how much cards of this vendor are installed, write the renaming-rule into 70-persistent-net.rules
).
This is how far I've come until now:
# udev rules to name rename cards to mycard
ACTION!="add", GOTO="persistent_mycard_generator_end"
SUBSYSTEM!="net", GOTO="persistent_mycard_generator_end"
# ignore the interface if a name has already been set
NAME=="mycard*", GOTO="persistent_mycard_generator_end"
# device name whitelist
KERNEL!="eth*", GOTO="persistent_mycard_generator_end"
# read MAC address
ENV{MATCHADDR}="$attr{address}"
# match interface type
ENV{MATCHIFTYPE}="$attr{type}"
# ignore non mycard MAC addresses
ENV{MATCHADDR}!="00:11:22:*", GOTO="persistent_mycard_generator_end"
# default comment
ENV{COMMENT}=="", ENV{COMMENT}="mycard connected through ($attr{driver})"
#### THIS IS THE PART I DON'T GET ####
# write rule
DRIVERS=="?*", IMPORT{program}="write_net_rules"
# rename interface if needed
ENV{INTERFACE_NEW}=="?*", NAME="mycard*"
#### THIS IS THE END OF THE PART I DON'T GET ####
LABEL="persistent_mycard_generator_end
The task "THE PART I DON'T GET" should do is to rename a card (lets say it's eth3) to mycard0 or if it's the second card in the system with a matching MAC address mycard1 and so on.
Thanks in advance, flokra
Solution
If you set ENV{INTERFACE} to "mycard0" before calling write_net_rules, it will find the first unused mycardN for you, write out the rule for it, and return the name in ENV{INTERFACE_NEW}.
OTHER TIPS
OK, here's my solution (I've tested it with Debian 5.0 and Ubuntu 9.04, so I'm not sure if it works with the udev-implementations of other distributions):
75-persistent-mycard-generator.rules
ACTION!="add", GOTO="persistent_mycard_generator_end" SUBSYSTEM!="net", GOTO="persistent_mycard_generator_end" # ignore the interface if a name has already been set NAME=="?*", GOTO="persistent_mycard_generator_end" # device name whitelist KERNEL!="eth*", GOTO="persistent_mycard_generator_end" # by default match on the MAC address and interface type ENV{MATCHADDR}="$attr{address}" ENV{MATCHIFTYPE}="$attr{type}" # match interface dev_id ATTR{dev_id}=="?*", ENV{MATCHDEVID}="$attr{dev_id}" # by default match on the MAC address and interface type ENV{MATCHADDR}="$attr{address}" ENV{MATCHIFTYPE}="$attr{type}" # match interface dev_id ATTR{dev_id}=="?*", ENV{MATCHDEVID}="$attr{dev_id}" # terminate processing if card is not a mycard ENV{MATCHADDR}!="AA:BB:CC:*", GOTO="persistent_mycard_generator_end" # provide nice comments for the generated rules SUBSYSTEMS=="pci", ENV{COMMENT}="PCI device $attr{vendor}:$attr{device}" SUBSYSTEMS=="pcmcia", ENV{COMMENT}="PCMCIA device $attr{card_id}:$attr{manf_id}" SUBSYSTEMS=="usb", ENV{COMMENT}="USB device 0x$attr{idVendor}:0x$attr{idProduct}" ENV{COMMENT}=="", ENV{COMMENT}="Unknown $env{SUBSYSTEM} device($env{DEVPATH})" ATTRS{driver}=="?*", ENV{COMMENT}="$env{COMMENT} ($attr{driver})" # add mycard to comment ENV{COMMENT}="$env{COMMENT} (mycard)" # set interface name to mycard0 (initially) ENV{INTERFACE}="mycard0" # generate and write the rule DRIVERS=="?*", IMPORT{program}="write_mycard_rules" # rename the interface if requested ENV{INTERFACE_NEW}=="?*",NAME="$env{INTERFACE_NEW}" LABEL="persistent_mycard_generator_end"
write_mycard_rules
RULES_FILE='/etc/udev/rules.d/70-persistent-net.rules' . /lib/udev/hotplug.functions interface_name_taken() { local value="$(find_all_rules 'NAME=' $INTERFACE)" if [ "$value" ]; then return 0 fi return 1 } find_next_available() { raw_find_next_available "$(find_all_rules 'NAME=' "$1")" } write_rule() { local match="$1" local name="$2" local comment="$3" { if [ "$PRINT_HEADER" ]; then PRINT_HEADER= echo "# This file was automatically generated by the $0" echo "# program run by the persistent-mycard-generator.rules rules file." echo "#" echo "# You can modify it, as long as you keep each rule on a single line." fi echo "" [ "$comment" ] && echo "# $comment" echo "SUBSYSTEM==\"net\", ACTION==\"add\"$match, NAME=\"$name\"" } >> $RULES_FILE } if [ -z "$INTERFACE" ]; then echo "missing \$INTERFACE" >&2 exit 1 fi # Prevent concurrent processes from modifying the file at the same time. lock_rules_file # Check if the rules file is writeable. choose_rules_file # the DRIVERS key is needed to not match bridges and VLAN sub-interfaces if [ "$MATCHADDR" ]; then match="$match, DRIVERS==\"?*\", ATTR{address}==\"$MATCHADDR\"" fi if [ "$MATCHDRV" ]; then match="$match, DRIVERS==\"$MATCHDRV\"" fi if [ "$MATCHDEVID" ]; then match="$match, ATTR{dev_id}==\"$MATCHDEVID\"" fi if [ "$MATCHID" ]; then match="$match, KERNELS==\"$MATCHID\"" fi if [ "$MATCHIFTYPE" ]; then match="$match, ATTR{type}==\"$MATCHIFTYPE\"" fi if [ -z "$match" ]; then echo "missing valid match" >&2 unlock_rules_file exit 1 fi basename=${INTERFACE%%[0-9]*} match="$match, KERNEL==\"eth*\"" if [ "$INTERFACE_NAME" ]; then # external tools may request a custom name COMMENT="$COMMENT (custom name provided by external tool)" if [ "$INTERFACE_NAME" != "$INTERFACE" ]; then INTERFACE=$INTERFACE_NAME; echo "INTERFACE_NEW=$INTERFACE" fi else # if a rule using the current name already exists, find a new name if interface_name_taken; then INTERFACE="$basename$(find_next_available "$basename[0-9]*")" echo "INTERFACE_NEW=$INTERFACE" fi fi write_rule "$match" "$INTERFACE" "$COMMENT" unlock_rules_file exit 0
The most important changes are ENV{INTERFACE}="mycard0"
in 75-persistent-mycard-generator.rules which sets the name the card should get and match="$match, KERNEL==\"eth*\""
in write_mycard_rules which forces udev to not override the used Kernel Subsystem with the new name.