/
ethname
280 lines (238 loc) · 7.76 KB
/
ethname
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
#!/bin/sh
#
# * Copyright (c) 2016-2019 Eric Borisch <eborisch@gmail.com>
# * All rights reserved.
#
# Self-contained rc.d script for re-naming devices based on their MAC address.
# Renaming is performed before interface bring-up -- netif -- so all
# configurations of the devices can be done with the new names.
#
# USAGE:
# 1) Add the following to rc.conf:
# ethname_enable="YES"
# ethname_external_mac="aa:bb:cc:dd:ee:00"
# ethname_private_mac="aa:bb:cc:dd:ee:01"
# 1a) You can optionally restrict handling to a set of defined names with:
# ethname_names="external private"
# otherwise all defined ethname_*_mac="" values are used
# 2) Make sure any interfaces you want to rename have their drivers loaded or
# compiled in. If ue0 is on axe0, for example, add 'if_load_axe="YES"' to
# /boot/loader.conf. See the man page for your device (eg 'man axe') for
# particulars.
# 3) That's it. Use ifconfig_<name>="" settings with the new names.
#
# All other devices are untouched.
#
# Optional rc.conf settings:
# ethname_timeout : Maximum wait time for devices to appear. [default=30]
#
# PROVIDE: ethname
# REQUIRE: FILESYSTEMS
# BEFORE: netif
# KEYWORD: nojail
# ethname version 2.0
. /etc/rc.subr
name=ethname
rcvar=ethname_enable
extra_commands="check"
check_cmd="en_check"
start_cmd="${name}_start"
stop_cmd=":"
load_rc_config ${name}
: ${ethname_names:=""}
: ${ethname_enable:=no}
: ${ethname_timeout:="30"}
en_str=""
# Will fill with mac interface [mac interface] ...]
en_map=""
# Will fill with original device names that match a managed mac address.
en_orig=""
# Total wait timeout; won't wait n*timeout for n devices, just timeout
en_waited=0
known_mac()
{
echo "${en_map}" | grep -qi "$1"
}
to_lower()
{
echo "$*" | tr "[:upper:]" "[:lower:]"
}
kv_lookup()
{
# Called with $1=K, the key we want to find the value for, and $2:$3
# $4:$5 ... forming pairs of key:value mappings
local _K _key _value
_K=$(to_lower "$1")
[ -z "${_K}" ] && err 1 "Called kv_lookup() with missing args."
shift
while [ $# -ge 2 ]; do
_key=$(to_lower "$1")
_value=$2
shift 2
# Only supports non-zero-length keys/values
[ -z "${_key}" -o -z "${_value}" ] && err 1 "Zero length values passed?"
[ "${_key}" == "${_K}" ] && echo "${_value}" && return 0
done
return 1
}
good_mac() {
echo "$1" | egrep -qi '^([0-9a-z]{2}:){5}[0-9a-z]{2}$' || \
err 1 "Invalid MAC address defined: [$1]"
return 0
}
good_devname() {
echo "$1" | egrep -qi '^[a-z][a-z0-9_]+$' || \
err 1 "Invalid device name defined: [$1]"
return 0
}
breakout_map () {
# This takes a single ethname_map variable (old interface) and breaks it
# into the new interface (ethname_names and ethname_NAME_mac vars.)
local _mac _name
while [ $# -gt 0 ]; do
_mac=$1
_name=$2
good_mac "${_mac}"
good_devname "${_name}"
shift 2
# Params checked for validity above
eval ethname_${_name}_mac="${_mac}"
ethname_names="${ethname_names} ${_name}"
done
}
en_prep()
{
local _mac _name _dev _found
local _compat=0
if [ -z "${ethname_names}" ]; then
# Compatibility code
if [ ! -z "${ethname_map}" -a ! -z "${ethname_devices}" ]; then
ethname_names=""
warn "ethname: Using old interface. Please see documentation."
breakout_map ${ethname_map}
_compat=1
else
# Detect set ethname_*_mac names
ethname_names=$(set | sed -En '/^ethname_([^=]+)_mac=.*/s//\1/p')
fi
fi
# Transforms set of ethname_NAME_mac="" values into en_map="MAC NAME ..."
# and en_orig="EXISTINGDEV ..."; a map of desired MAC:name mappings
# and the devices with those MACs, respectively.
for _name in ${ethname_names}; do
# Make sure ${_name} is good before eval call
good_devname "${_name}"
eval _mac=\$ethname_${_name}_mac
[ -z "${_mac}" -a ${_compat} -eq 0 ] && \
warn "ethname_${_name}_mac is not set in rc.conf!" && continue
good_mac "${_mac}"
# Enable ctrl-c for wait loop
trap break SIGINT
_found=0
while [ ${en_waited} -lt ${ethname_timeout} ]; do
for _dev in $(ifconfig -l ether); do
if ifconfig ${_dev} | grep -qi "${_mac}"; then
en_map="${en_map} ${_mac} ${_name}"
en_orig="${en_orig} ${_dev}"
_found=1
break
fi
done
[ ${_found} -eq 1 ] && break
sleep 1
warn "Waiting for a device with MAC [${_mac}] to appear..."
en_waited=$((en_waited + 1))
done
trap - SIGINT
[ ${_found} -eq 0 ] && \
warn "Unable to locate device to rename [${_name}]!"
done
}
en_check() {
local _mac _name _orig
local _n=1
en_prep
# Piping into a while loop, but we don't need any results from this loop to
# be visible in this shell, so it's not an issue.
echo "${en_map}" | xargs -n 2 echo | while read _mac _name; do
_orig=$(echo "${en_orig}" | awk "{print \$${_n}}")
if [ "${_orig}" = "${_name}" ]; then
printf "Device with MAC [%s] already named '%s'\n" \
"${_mac}" "${_name}"
else
printf "Will rename [%s] to [%s] with MAC [%s]\n" \
"${_orig}" "${_name}" "${_mac}"
fi
_n=$((_n + 1))
done
}
fix_name()
{
# Can be called with or without a second argument (which is used as the new
# name if provided.) If only one argument, lookup desired name in map.
dev=$1
name=$2
# Make sure the device exists as an ifconfig device
if ! ifconfig -l ether | grep -q "${dev}"; then
en_str="could not find device."
return 1
fi
# Grab MAC address
mac=$(ifconfig ${dev} | awk '/ether/{print tolower($2)}')
if [ ${#mac} -eq 0 ]; then
en_str="unable to get MAC address"
return 1
fi
# Make sure the MAC for this device is in our rename table.
if ! known_mac "${mac}"; then
en_str="no maching MAC in ethname_<NAME>_mac params."
return 1
fi
# Find name from MAC -> dev_name table in map
dname=$(kv_lookup ${mac} ${en_map})
if [ "${dname}" == "${dev}" ]; then
en_str="already has desired name."
return 1
fi
# Use name from MAC -> dev_name table in map if $2 was empty
: ${name:=${dname}}
# We have everything we need. Now actual rename of the device.
if ! ifconfig ${dev} name ${name} > /dev/null ; then
en_str="return code: $?"
return 2
fi
}
ethname_start()
{
local _n _m _prefix _x
# Build the map of "mac name [mac name] [...]"
en_prep
# Don't report any other errors if we haven't been asked to do anything.
if [ ${#en_orig} -eq 0 ]; then
warn "Unable to locate any of the specified ethname_\*_mac addresses."
exit 0
fi
# Rename interfaces; first into en_tmp_$_n with _n = 0, 1, ... to avoid any
# possible collision with the desired names. (ex. ue0 -> ue1; ue1 -> ue0
# renaming.)
_prefix=en_$$_
_n=0
for _x in ${en_orig}; do
if fix_name ${_x} ${_prefix}${_n}; then
_n=$((_n+1))
elif [ $? -eq 1 ]; then
info "Skipping rename of [${_x}]: ${en_str}"
else
warn "Error during rename of [${_x}]: ${en_str}"
fi
done
# Loop back over renamed devices and lookup their desired names.
_m=0
while [ ${_m} -lt ${_n} ]; do
fix_name ${_prefix}${_m} || \
warn "Error during renaming process. Stranded [${_prefix}${_m}]."
_m=$((_m+1))
done
}
run_rc_command "$1"
# vim: et:ts=4:sw=4