1#!/bin/sh2# Copyright (c) 2012, Piotr Karbowski <piotr.karbowski@gmail.com>3# All rights reserved.4#5# Redistribution and use in source and binary forms, with or without modification, are6# permitted provided that the following conditions are met:7#8# * Redistributions of source code must retain the above copyright notice, this list9# of conditions and the following disclaimer.10# * Redistributions in binary form must reproduce the above copyright notice, this list11# of conditions and the following disclaimer in the documentation and/or other12# materials provided with the distribution.13# * Neither the name of the Piotr Karbowski nor the names of its contributors may be14# used to endorse or promote products derived from this software without specific15# prior written permission.16#17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY18# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF19# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL20# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT22# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS23# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE US25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2627# This script suppose to preserve interfaces names.28# First it will run nameif to rename interfaces if configured in /etc/mactab29# Then it will dump known interfaces to new mactab file.30# Next it will parse old /etc/mactab and copy all interfaces which wasnt added in step two.31# On the end, it will replace /etc/mactab with the new one.3233# Info: Step two will only care about eth*, wlan*, ath*, wifi* and ra* interfaces.3435umask 0773637if [ -n "${USER}" ] && [ "${USER}" != 'root' ]; then38 echo "You need root to run it." >&239 exit 140fi4142command_exists() {43 command -v "${1}" >/dev/null 2>&144}4546in_comma_list() {47 # Check whatever $1 is in the comma separated list $2.48 local x49 local check_for="$1"50 case ",$2," in51 *,"${check_for}",*)52 return 053 ;;54 esac55 return 156}5758get_nic_macaddr() {59 # Try to get real mac address from nic $1.60 local nic="$1"61 local macaddr62 if command_exists ethtool; then63 macaddr="$(ethtool -P ${nic} 2>/dev/null)"64 macaddr="${macaddr##*: }"65 if [ -n "${macaddr}" ] && [ "${macaddr}" != 'not set' ]; then66 echo "${macaddr}"67 return 068 fi69 fi70 if command_exists ip; then71 macaddr="$(ip link show dev ${nic} |grep permaddr)"72 macaddr="${macaddr##*permaddr }"73 if [ -n "${macaddr}" ]; then74 echo "${macaddr}"75 return 076 fi77 fi78 # Fallback to get mac address from sysfs.79 cat "/sys/class/net/${nic}/address"80}8182lockfile='/etc/mactab.settle-nics_lockfile'83while : ; do84 if ! test -f "${lockfile}" && ( set -o noclobber; echo "$$" > "${lockfile}") 2> /dev/null; then85 break86 else87 sleep 188 fi89done9091case "$1" in92 --write-mactab)93 write_mactab='true'94 tmpfile="/etc/mactab.settle-nics_tmpfile.$$"95 rm -f "${tmpfile}"96 ;;97 '')98 tmpfile='/dev/null'99 ;;100 *)101 echo "Wrong argument!" >&2102 exit 1103 ;;104105esac106107trap 'status="$?"; rm -f "${lockfile}"; test -f "${tmpfile}" && rm -f "${tmpfile}"; exit "${status}"' INT TERM EXIT108109# If we do have configured nics but the configured names are already used by something else, rename it to temp name.110# I wish nameif or ifrename could be smart enough to do it itself...111inconf_nics=""112inconf_macs=""113if [ -f '/etc/mactab' ]; then114 while read nic macaddr _; do115 case "${nic}" in116 '#'*)117 ;;118 *)119 inconf_nics="${inconf_nics},${nic}"120 inconf_macs="${inconf_macs},${macaddr}"121 if [ -e "/sys/class/net/${nic}" ]; then122 curr_macaddr="$(get_nic_macaddr ${nic})"123 if [ "${curr_macaddr}" != "${macaddr}" ]; then124 # Okey, so kernel added another NIC with name which we preserved for other NIC.125 echo "Looks like '${nic}' is preserved. renaming current '${nic}' to '${nic}_tmp' ..." >&2126 ip link set dev "${nic}" name "${nic}_tmp"127 fi128 fi129 ;;130 esac131 done < '/etc/mactab'132fi133134# Run nameif so all interfaces will be renamed to specified names.135if command_exists nameif && test -f /etc/mactab; then136 nameif137fi138139# Arrr rite. by now nemaif should put nics in right order, if there is still any *_tmp nic, we shall assign proper, free device name to it.140for nic in /sys/class/net/*_tmp; do141 nic="${nic##*/}"142 if [ "${nic}" = '*_tmp' ]; then break; fi143 nic_basename="${nic%%_tmp}"144 nic_basename="${nic_basename%%[0-9]*}"145 i=0146 while [ "${i}" -le "128" ]; do147 if ! [ -e "/sys/class/net/${nic_basename}${i}" ] && ! in_comma_list "${nic_basename}${i}" "${inconf_nics}"; then148 echo "Renaming '${nic}' to '${nic_basename}${i}' ..." >&2149 ip link set dev "${nic}" name "${nic_basename}${i}"150 break151 fi152 i="$(($i+1))"153 done154done155156if [ "${write_mactab}" != 'true' ]; then157 # We don't want write mactab thus there is no reason to check the network interfaces' mac addresses and so on...158 exit 0159fi160161printf '%s\n' "# Generated by settle-nics from mdev-like-a-boss." >> "${tmpfile}"162163# First get all the macs of current accessable nics164detected_nics=""165detected_macs=""166for i in /sys/class/net/*; do167 unset device macaddr168 device="${i##*/}"169 case "${device}" in170 *_tmp)171 ;;172 eth[0-9]*|wlan[0-9]*|ath[0-9]*|wifi[0-9]*|ra[0-9]*)173 macaddr="$(get_nic_macaddr ${device} 2>/dev/null)"174 if [ -n "${macaddr}" ] && ! in_comma_list "${macaddr}" "${inconf_macs}" && ! in_comma_list "${device}" "${inconf_nics}"; then175 detected_nics="${detected_nics},${device}"176 detected_macs="${detected_macs},${macaddr}"177 printf '%-15s %s\n' "${device}" "${macaddr}" >> "${tmpfile}"178 fi179 ;;180 esac181done182183# Now lets parse current /etc/mactab so we no loose any configured but not available at this moment interface.184if [ -f '/etc/mactab' ]; then185 unset device macaddr186 while read device macaddr; do187 case "${device}" in188 '#'*)189 ;;190 *)191 if [ -n "${macaddr}" ] && ! in_comma_list "${macaddr}" "${detected_macs}" && ! in_comma_list "${device}" "${detected_nics}"; then192 printf '%-15s %s\n' "${device}" "${macaddr}" >> "${tmpfile}"193 fi194 ;;195 esac196 done < '/etc/mactab'197fi198199mv "${tmpfile}" '/etc/mactab'