#!/bin/bash # **** License **** # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # This code was originally developed by Vyatta, Inc. # Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. # All Rights Reserved. # # Authors: Tom Grennan # Bob Gilligan # Description: search Vyatta config for interface name given address # # **** End License **** progname=${0##*/} debug= match= attr_address=0:0:0:0:0:0 declare -i ethn=0 udev_persistent_net_rules_file="/etc/udev/rules.d/70-persistent-net.rules" # Set log_file to "/dev/null" to turn off debugging log_file="/tmp/vnn_log" test -r /etc/default/vyatta && source /etc/default/vyatta # process command line variable overrides for arg ; do case "$arg" in --debug ) debug=echo ;; --*=* ) arg=${arg#--} eval ${arg%=*}=\"${arg#*=}\" ;; *=* ) eval ${arg%=*}=\"${arg#*=}\" ;; *:*:*:*:*:* ) attr_address=$arg ;; * ) kname=$arg ;; esac done : ${vyatta_prefix:=/opt/vyatta} : ${vyatta_sbindir:=${vyatta_prefix}/sbin} : ${vyatta_sysconfdir:=${vyatta_prefix}/etc} : ${BOOTFILE:=${vyatta_sysconfdir:-/opt/vyatta/etc}/config/config.boot} : ${DEFAULT_BOOTFILE:=${vyatta_sysconfdir:-/opt/vyatta/etc}/config.boot.default} shopt -s extglob nullglob # load cfg_eth_hwid array from config file as follows # interface { # ... # ethernet eth# { # ... # hw-id XX:XX:XX:XX:XX:XX # ... # } # } # # cfg_eth_hwid=( "eth#=xx:xx:xx:xx:xx:xx" ... ) declare -a cfg_net_hwid=( $( sed -ne ' /^interfaces {/,/^}/ { /^ *ethernet eth[0-9]* {/,/^ $/ { /^ *ethernet/ { s/\r// s/.* eth\([0-9]\+\) {$/ eth\1=/ # hold interface name h } /^.*hw-id:\?/ { # translate field name s/\r// s/.*hw-id:\? *// # tolower hex mac address y/ABCDEF/abcdef/ # exchange hold and pattern space x # concatenate hold and pattern G s/\n//p } } }' $BOOTFILE )) finish () { local cmd=$1 name=$2 address=$3 # The output from this program tells udev what name to give this device echo $name # This file tells rl_system startup script how to update the Vyatta # config file. touch /tmp/${progname}_${cmd}_${name}_${address} &> /dev/null # Remove entry for this MAC addr from the standard udev generated # config file, if it exists, so it doesn't rename the interface # out from under us. Remove the subject line plus the comment # line above it if [ -e $udev_persistent_net_rules_file ]; then sed -i -e "/^#/N;/${address}/d" $udev_persistent_net_rules_file fi exit $? } # Determine whether variable "ethn" conflicts with an ethernet unit # number that was assigned in previous runs of this script ethn_conflicts() { # Return value 1 (failure) means no conflicts found. # Return value 0 (success) means conflicts were found. conflicts=1 echo "`date`: ethn_conflicts is checking if $ethn has conflicts" >> $log_file # Generate list of ethernet unit numbers assigned previously by this script used_ethn="" for filename in /tmp/vyatta_net_name* ; do if [ -e $filename ]; then # strip off everything before the unit number unit=${filename##*vyatta_net_name_*_eth} # strip off everything after the unit number unit=${unit%%_*} # add unit number from this file to the list used_ethn="$used_ethn $unit" fi done echo "`date`: ethn_conflicts: about to run check" >> $log_file for this_ethn in $used_ethn ; do if [ $ethn -eq $this_ethn ]; then echo "`date`: ethn $ethn conflicts with previously configured $this_ethn" >> $log_file conflicts=0 break fi done echo "`date`: ethn_conflicts for ethn $ethn returns $conflicts" >> $log_file # return value (exit status) is true, i.e. 0, if there is a conflict return $conflicts } # # Find an ethernet unit number that is neither listed in the config # file nor assigned by this script in earlier runs. get_free_ethn() { # list of ethernet unit numbers assigned previously by this script used_ethn="" for filename in /tmp/vyatta_net_name* ; do if [ -e $filename ]; then # strip off everything before the unit number unit=${filename##*vyatta_net_name_*_eth} # strip off everything after the unit number unit=${unit%%_*} # add unit number from this file to the list used_ethn="$used_ethn $unit" fi done # Counting up from 0, try to find a free ethernet unit number found=0 for ((ethn_to_use=0 ; ; ethn_to_use+=1)) ; do found=1 # Check to see if this one is in the config file echo "`date`: get_free_ethn: cfg_net_hwid is ${cfg_net_hwid[@]}" >> $log_file for name_hwid in ${cfg_net_hwid[@]} ; do name=${name_hwid%=*} this_ethn=${name/eth/} echo "`date`: get_free_ethn 1 comparing $ethn_to_use vs $this_ethn" >> $log_file if [ $ethn_to_use -eq $this_ethn ]; then found=0 break fi done if [ $found -eq 0 ]; then continue fi echo "`date`: get_free_ethn: used_ethn is $used_ethn" >> $log_file # Check to see if this script has assigned this unit number already for this_ethn in $used_ethn ; do echo "`date`: get_free_ethn 2 comparing $ethn_to_use vs $this_ethn" >> $log_file if [ $ethn_to_use -eq $this_ethn ]; then found=0 break fi done if [ $found -eq 1 ]; then break fi done # The return value ethn=$ethn_to_use echo "`date`: get_free_ethn found $ethn_to_use" >> $log_file } # Run with lock held to protect atomicity of access to assigned ethn file ( flock 200 touch $log_file echo "`date`: vyatta_net_name $kname $attr_address" >> $log_file if [ ! -f $BOOTFILE ] ; then cp $DEFAULT_BOOTFILE $BOOTFILE chgrp vyattacfg $BOOTFILE chmod 660 $BOOTFILE fi for name_hwid in ${cfg_net_hwid[@]} ; do name=${name_hwid%=*} hwid=${name_hwid#*=} ethn=${name/eth/} echo "`date`: Checking $name_hwid against $kname $attr_address" >> $log_file if [ "$hwid" == "$attr_address" ] ; then # The MAC addr of this interface matches an entry in the config # file. We mod the config file interface sub-block in case it # is missing. echo "`date`: finish 1: mod $name $attr_address" >> $log_file finish mod $name $attr_address fi if [ "$name" = "$kname" ]; then # The kernel name matches an entry in the config file. Save the # config file entry for later examination. match=$name_hwid fi done if [ -z "$kname" ]; then exit 1 fi # We have not found a matching hwid in the config file. See if we can use # the kernel name. if [ -z "$match" ] ; then # The kernel interface name is not listed in the config file. # If the kernel's name is in the standard "ethN" format, and doesn't # conflict with any other name we've used, then # we can just go ahead and use the kernel's name. If not, then # we will generate a name in the standard format that does not # conflict with any names in the config file, or any other names # that we have seen. non_std_kname=${kname##eth+([0-9])} if [ -z "$non_std_kname" ]; then # kname is in standard format, so we get the unit number from it. ethn=${kname/eth/} # We can use this unit number unless it happens to conflict # with one we have already assigned. if ethn_conflicts ; then echo "`date`: kname $kname conflicts with already assigned unit" >> $log_file get_free_ethn fi else # kname is not in standard format, so we have to generate # a unit number echo "`date`: kname $kname is non-standard format" >> $log_file get_free_ethn fi echo "`date`: finish 2: add eth$ethn $attr_address" >> $log_file finish add eth$ethn $attr_address elif [ -z "${match#*=}" ] ; then # The config file has this interface but the sub-block is missing the hwid # field, so we use the kernel name. In this case, we know that the # kernel name is in the standard format because it matched an entry # in the config file, and all entries in the config file are in standard # format. This will cause the hwid for this NIC to be added to the # entry in the config file. echo "`date`: finish 3: mod $kname $attr_address" >> $log_file finish mod $kname $attr_address else # The config file has this interface name, but the mac address # that of this NIC. This indicates that the device is either a # replacement or new NIC that is being detected earlier than the device # configured with this name. Since we don't know which case it is, # we must generate a new unit number. get_free_ethn echo "`date`: finish 4: add eth$ethn $attr_address" >> $log_file finish add eth$ethn $attr_address fi # Should never get here. If this shows up in the log file, something # is wrong! echo "`date`: no finish: kname = $kname, attr_attr = $attr_address, match = $match" >> $log_file ) 200> /tmp/vnn_lock # Local Variables: # mode: shell-script # sh-indentation: 4 # End: