]> git.ipfire.org Git - thirdparty/dracut.git/blob - modules.d/40network/net-lib.sh
Specify strstr tightly, add strglob/strglobin.
[thirdparty/dracut.git] / modules.d / 40network / net-lib.sh
1 #!/bin/sh
2 # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
3 # ex: ts=8 sw=4 sts=4 et filetype=sh
4
5 get_ip() {
6 local iface="$1" ip=""
7 ip=$(ip -o -f inet addr show $iface)
8 ip=${ip%%/*}
9 ip=${ip##* }
10 }
11
12 iface_for_remote_addr() {
13 set -- $(ip -o route get to $1)
14 echo $5
15 }
16
17 iface_for_ip() {
18 set -- $(ip -o addr show to $1)
19 echo $2
20 }
21
22 iface_for_mac() {
23 local interface="" mac="$(echo $1 | sed 'y/ABCDEF/abcdef/')"
24 for interface in /sys/class/net/*; do
25 if [ $(cat $interface/address) = "$mac" ]; then
26 echo ${interface##*/}
27 fi
28 done
29 }
30
31 # get the iface name for the given identifier - either a MAC, IP, or iface name
32 iface_name() {
33 case $1 in
34 ??:??:??:??:??:??|??-??-??-??-??-??) iface_for_mac $1 ;;
35 *:*:*|*.*.*.*) iface_for_ip $1 ;;
36 *) echo $1 ;;
37 esac
38 }
39
40 # list the configured interfaces
41 configured_ifaces() {
42 local IFACES="" iface_id="" rv=1
43 [ -e "/tmp/net.ifaces" ] && read IFACES < /tmp/net.ifaces
44 if { pidof udevd || pidof systemd-udevd; } > /dev/null; then
45 for iface_id in $IFACES; do
46 echo $(iface_name $iface_id)
47 rv=0
48 done
49 else
50 warn "configured_ifaces called before udev is running"
51 echo $IFACES
52 [ -n "$IFACES" ] && rv=0
53 fi
54 return $rv
55 }
56
57 all_ifaces_up() {
58 local iface="" IFACES=""
59 [ -e "/tmp/net.ifaces" ] && read IFACES < /tmp/net.ifaces
60 for iface in $IFACES; do
61 [ -e /tmp/net.$iface.up ] || return 1
62 done
63 }
64
65 get_netroot_ip() {
66 local prefix="" server="" rest=""
67 splitsep "$1" ":" prefix server rest
68 case $server in
69 [0-9]*\.[0-9]*\.[0-9]*\.[0-9]*) echo "$server"; return 0 ;;
70 esac
71 return 1
72 }
73
74 ip_is_local() {
75 strstr "$(ip route get $1 2>/dev/null)" " via "
76 }
77
78 ifdown() {
79 local netif="$1"
80 # ip down/flush ensures that routing info goes away as well
81 ip link set $netif down
82 ip addr flush dev $netif
83 echo "#empty" > /etc/resolv.conf
84 rm -f -- /tmp/net.$netif.did-setup
85 [ -e /sys/class/net/$netif/address ] && \
86 rm -f -- /tmp/net.$(cat /sys/class/net/$netif/address).did-setup
87 # TODO: send "offline" uevent?
88 }
89
90 setup_net() {
91 local netif="$1" f="" gw_ip="" netroot_ip="" iface="" IFACES=""
92 [ -e /tmp/net.$netif.did-setup ] && return
93 [ -e /sys/class/net/$netif/address ] && \
94 [ -e /tmp/net.$(cat /sys/class/net/$netif/address).did-setup ] && return
95 [ -e "/tmp/net.ifaces" ] && read IFACES < /tmp/net.ifaces
96 [ -z "$IFACES" ] && IFACES="$netif"
97 # run the scripts written by ifup
98 [ -e /tmp/net.$netif.gw ] && . /tmp/net.$netif.gw
99 [ -e /tmp/net.$netif.hostname ] && . /tmp/net.$netif.hostname
100 [ -e /tmp/net.$netif.override ] && . /tmp/net.$netif.override
101 [ -e /tmp/dhclient.$netif.dhcpopts ] && . /tmp/dhclient.$netif.dhcpopts
102 # set up resolv.conf
103 [ -e /tmp/net.$netif.resolv.conf ] && \
104 cp -f /tmp/net.$netif.resolv.conf /etc/resolv.conf
105
106 # Handle STP Timeout: arping the default gateway.
107 # (or the root server, if a) it's local or b) there's no gateway.)
108 # Note: This assumes that if no router is present the
109 # root server is on the same subnet.
110
111 # Get DHCP-provided router IP, or the cmdline-provided "gw=" argument
112 [ -n "$new_routers" ] && gw_ip=${new_routers%%,*}
113 [ -n "$gw" ] && gw_ip=$gw
114
115 # Get the "netroot" IP (if there's an IP address in there)
116 netroot_ip=$(get_netroot_ip $netroot)
117
118 # try netroot if it's local (or there's no gateway)
119 if ip_is_local $netroot_ip || [ -z "$gw_ip" ]; then
120 dest="$netroot_ip"
121 else
122 dest="$gw_ip"
123 fi
124
125 unset layer2
126 if [ -f /sys/class/net/$netif/device/layer2 ]; then
127 read layer2 < /sys/class/net/$netif/device/layer2
128 fi
129
130 if [ "$layer2" != "0" ] && [ -n "$dest" ] && ! strstr "$dest" ":"; then
131 arping -q -f -w 60 -I $netif $dest || info "Resolving $dest via ARP on $netif failed"
132 fi
133 unset layer2
134
135 > /tmp/net.$netif.did-setup
136 [ -e /sys/class/net/$netif/address ] && \
137 > /tmp/net.$(cat /sys/class/net/$netif/address).did-setup
138 }
139
140 save_netinfo() {
141 local netif="$1" IFACES="" f="" i=""
142 [ -e /tmp/net.ifaces ] && read IFACES < /tmp/net.ifaces
143 # Add $netif to the front of IFACES (if it's not there already).
144 set -- "$netif"
145 for i in $IFACES; do [ "$i" != "$netif" ] && set -- "$@" "$i"; done
146 IFACES="$*"
147 for i in $IFACES; do
148 for f in /tmp/dhclient.$i.*; do
149 [ -f $f ] && cp -f $f /tmp/net.${f#/tmp/dhclient.}
150 done
151 done
152 echo $IFACES > /tmp/.net.ifaces.new
153 mv /tmp/.net.ifaces.new /tmp/net.ifaces
154 }
155
156 set_ifname() {
157 local name="$1" mac="$2" num=-1 n=""
158 # if it's already set, return the existing name
159 for n in $(getargs ifname=); do
160 strstr "$n" "$mac" && echo ${n%%:*} && return
161 done
162 # otherwise, pick a new name and use that
163 while :; do
164 num=$(($num+1));
165 [ -e /sys/class/net/$name$num ] && continue
166 for n in $(getargs ifname=); do
167 [ "$name$num" = "${n%%:*}" ] && continue 2
168 done
169 break
170 done
171 echo "ifname=$name$num:$mac" >> /etc/cmdline.d/45-ifname.conf
172 echo "$name$num"
173 }
174
175 # pxelinux provides macaddr '-' separated, but we need ':'
176 fix_bootif() {
177 local macaddr=${1}
178 local IFS='-'
179 macaddr=$(for i in ${macaddr} ; do echo -n $i:; done)
180 macaddr=${macaddr%:}
181 # strip hardware type field from pxelinux
182 [ -n "${macaddr%??:??:??:??:??:??}" ] && macaddr=${macaddr#??:}
183 # return macaddr with lowercase alpha characters expected by udev
184 echo $macaddr | sed 'y/ABCDEF/abcdef/'
185 }
186
187 ibft_to_cmdline() {
188 local iface=""
189 modprobe -q iscsi_ibft
190 (
191 for iface in /sys/firmware/ibft/ethernet*; do
192 local mac="" dev=""
193 local dhcp="" ip="" gw="" mask="" hostname=""
194 local dns1 dns2
195
196 [ -e ${iface}/mac ] || continue
197 mac=$(read a < ${iface}/mac; echo $a)
198 [ -z "$mac" ] && continue
199 dev=$(set_ifname ibft $mac)
200
201 [ -e /tmp/net.${dev}.has_ibft_config ] && continue
202
203 [ -e ${iface}/dhcp ] && dhcp=$(read a < ${iface}/dhcp; echo $a)
204
205 if [ -n "$dhcp" ]; then
206 echo "ip=$dev:dhcp"
207 elif [ -e ${iface}/ip-addr ]; then
208 [ -e ${iface}/ip-addr ] && ip=$(read a < ${iface}/ip-addr; echo $a)
209 # skip not assigned ip adresses
210 [ "$ip" = "0.0.0.0" ] && continue
211 [ -e ${iface}/gateway ] && gw=$(read a < ${iface}/gateway; echo $a)
212 [ -e ${iface}/subnet-mask ] && mask=$(read a < ${iface}/subnet-mask; echo $a)
213 [ -e ${iface}/primary-dns ] && dns1=$(read a < ${iface}/primary-dns; echo $a)
214 [ -e ${iface}/secondary-dns ] && dns2=$(read a < ${iface}/secondary-dns; echo $a)
215 [ -e ${iface}/hostname ] && hostname=$(read a < ${iface}/hostname; echo $a)
216 if [ -n "$ip" ] && [ -n "$mask" ]; then
217 echo "ip=$ip::$gw:$mask:$hostname:$dev:none${dns1:+:$dns1}${dns2:+:$dns2}"
218 else
219 warn "${iface} does not contain a valid iBFT configuration"
220 warn "ip-addr=$ip"
221 warn "gateway=$gw"
222 warn "subnet-mask=$mask"
223 warn "hostname=$hostname"
224 fi
225 else
226 info "${iface} does not contain a valid iBFT configuration"
227 ls -l ${iface} | vinfo
228 fi
229
230 if [ -e ${iface}/vlan ]; then
231 vlan=$(read a < ${iface}/vlan; echo $a)
232 if [ "$vlan" -ne "0" ]; then
233 case "$vlan" in
234 [0-9]*)
235 echo "vlan=$dev.$vlan:$dev"
236 ;;
237 *)
238 echo "vlan=$vlan:$dev"
239 ;;
240 esac
241 fi
242 fi
243
244 echo $mac > /tmp/net.${dev}.has_ibft_config
245 done
246 ) >> /etc/cmdline.d/40-ibft.conf
247 }
248
249 parse_iscsi_root()
250 {
251 local v
252 v=${1#iscsi:}
253
254 # extract authentication info
255 case "$v" in
256 *@*:*:*:*:*)
257 authinfo=${v%%@*}
258 v=${v#*@}
259 # allow empty authinfo to allow having an @ in iscsi_target_name like this:
260 # netroot=iscsi:@192.168.1.100::3260::iqn.2009-01.com.example:testdi@sk
261 if [ -n "$authinfo" ]; then
262 OLDIFS="$IFS"
263 IFS=:
264 set $authinfo
265 IFS="$OLDIFS"
266 if [ $# -gt 4 ]; then
267 warn "Wrong authentication info in iscsi: parameter!"
268 return 1
269 fi
270 iscsi_username=$1
271 iscsi_password=$2
272 if [ $# -gt 2 ]; then
273 iscsi_in_username=$3
274 iscsi_in_password=$4
275 fi
276 fi
277 ;;
278 esac
279
280 # extract target ip
281 case "$v" in
282 [[]*[]]:*)
283 iscsi_target_ip=${v#[[]}
284 iscsi_target_ip=${iscsi_target_ip%%[]]*}
285 v=${v#[[]$iscsi_target_ip[]]:}
286 ;;
287 *)
288 iscsi_target_ip=${v%%[:]*}
289 v=${v#$iscsi_target_ip:}
290 ;;
291 esac
292
293 # extract target name
294 case "$v" in
295 *:iqn.*)
296 iscsi_target_name=iqn.${v##*:iqn.}
297 v=${v%:iqn.*}:
298 ;;
299 *:eui.*)
300 iscsi_target_name=iqn.${v##*:eui.}
301 v=${v%:iqn.*}:
302 ;;
303 *:naa.*)
304 iscsi_target_name=iqn.${v##*:naa.}
305 v=${v%:iqn.*}:
306 ;;
307 *)
308 warn "Invalid iscii target name, should begin with 'iqn.' or 'eui.' or 'naa.'"
309 return 1
310 ;;
311 esac
312
313 # parse the rest
314 OLDIFS="$IFS"
315 IFS=:
316 set $v
317 IFS="$OLDIFS"
318
319 iscsi_protocol=$1; shift # ignored
320 iscsi_target_port=$1; shift
321 if [ $# -eq 3 ]; then
322 iscsi_iface_name=$1; shift
323 fi
324 if [ $# -eq 2 ]; then
325 iscsi_netdev_name=$1; shift
326 fi
327 iscsi_lun=$1; shift
328 if [ $# -ne 0 ]; then
329 warn "Invalid parameter in iscsi: parameter!"
330 return 1
331 fi
332 }
333
334 ip_to_var() {
335 local v=${1}:
336 local i
337 set --
338 while [ -n "$v" ]; do
339 if [ "${v#\[*:*:*\]:}" != "$v" ]; then
340 # handle IPv6 address
341 i="${v%%\]:*}"
342 i="${i##\[}"
343 set -- "$@" "$i"
344 v=${v#\[$i\]:}
345 else
346 set -- "$@" "${v%%:*}"
347 v=${v#*:}
348 fi
349 done
350
351 unset ip srv gw mask hostname dev autoconf macaddr mtu dns1 dns2
352 case $# in
353 0) autoconf="error" ;;
354 1) autoconf=$1 ;;
355 2) [ -n "$1" ] && dev=$1; [ -n "$2" ] && autoconf=$2 ;;
356 3) [ -n "$1" ] && dev=$1; [ -n "$2" ] && autoconf=$2; [ -n "$3" ] && mtu=$3 ;;
357 4) [ -n "$1" ] && dev=$1; [ -n "$2" ] && autoconf=$2; [ -n "$3" ] && mtu=$3; [ -n "$4" ] && macaddr=$4 ;;
358 *) [ -n "$1" ] && ip=$1; [ -n "$2" ] && srv=$2; [ -n "$3" ] && gw=$3; [ -n "$4" ] && mask=$4;
359 [ -n "$5" ] && hostname=$5; [ -n "$6" ] && dev=$6; [ -n "$7" ] && autoconf=$7;
360 case "$8" in
361 [0-9]*:*|[0-9]*.[0-9]*.[0-9]*.[0-9]*)
362 dns1="$8"
363 [ -n "$9" ] && dns2="$9"
364 ;;
365 [0-9]*)
366 mtu="$8"
367 ;;
368 *)
369 if [ -n "${9}" -a -n "${10}" -a -n "${11}" -a -n "${12}" -a -n "${13}" -a -n "${14}" ]; then
370 macaddr="${9}:${10}:${11}:${12}:${13}:${14}"
371 fi
372 ;;
373 esac
374 ;;
375 esac
376
377 # ip=<ipv4-address> means anaconda-style static config argument cluster:
378 # ip=<ip> gateway=<gw> netmask=<nm> hostname=<host> mtu=<mtu>
379 # ksdevice={link|bootif|ibft|<MAC>|<ifname>}
380 if strglob "$autoconf" "*.*.*.*"; then
381 ip="$autoconf"
382 gw=$(getarg gateway=)
383 mask=$(getarg netmask=)
384 hostname=$(getarg hostname=)
385 dev=$(getarg ksdevice=)
386 autoconf="none"
387 mtu=$(getarg mtu=)
388
389 # handle special values for ksdevice
390 case "$dev" in
391 bootif|BOOTIF) dev=$(fix_bootif $(getarg BOOTIF=)) ;;
392 link) dev="" ;; # FIXME: do something useful with this
393 ibft) dev="" ;; # ignore - ibft is handled elsewhere
394 esac
395 fi
396 }
397
398 parse_ifname_opts() {
399 local IFS=:
400 set $1
401
402 case $# in
403 7)
404 ifname_if=$1
405 # udev requires MAC addresses to be lower case
406 ifname_mac=$(echo $2:$3:$4:$5:$6:$7 | sed 'y/ABCDEF/abcdef/')
407 ;;
408 *)
409 die "Invalid arguments for ifname="
410 ;;
411 esac
412
413 case $ifname_if in
414 eth[0-9]|eth[0-9][0-9]|eth[0-9][0-9][0-9]|eth[0-9][0-9][0-9][0-9])
415 warn "ifname=$ifname_if uses the kernel name space for interfaces"
416 warn "This can fail for multiple network interfaces and is discouraged!"
417 warn "Please use a custom name like \"netboot\" or \"bluesocket\""
418 warn "or use biosdevname and no ifname= at all."
419 ;;
420 esac
421
422 }
423
424 # some network driver need long time to initialize, wait before it's ready.
425 wait_for_if_link() {
426 local cnt=0
427 local li
428 while [ $cnt -lt 600 ]; do
429 li=$(ip -o link show dev $1 2>/dev/null)
430 [ -n "$li" ] && return 0
431 sleep 0.1
432 cnt=$(($cnt+1))
433 done
434 return 1
435 }
436
437 wait_for_if_up() {
438 local cnt=0
439 local li
440 while [ $cnt -lt 200 ]; do
441 li=$(ip -o link show up dev $1)
442 [ -n "$li" ] && [ -z "${li##*state UP*}" ] && return 0
443 sleep 0.1
444 cnt=$(($cnt+1))
445 done
446 return 1
447 }
448
449 wait_for_route_ok() {
450 local cnt=0
451 while [ $cnt -lt 200 ]; do
452 li=$(ip route show)
453 [ -n "$li" ] && [ -z "${li##*$1*}" ] && return 0
454 sleep 0.1
455 cnt=$(($cnt+1))
456 done
457 return 1
458 }
459
460 wait_for_ipv6_dad() {
461 local cnt=0
462 local li
463 while [ $cnt -lt 500 ]; do
464 li=$(ip -6 addr show dev $1)
465 strstr "$li" "tentative" || return 0
466 sleep 0.1
467 cnt=$(($cnt+1))
468 done
469 return 1
470 }
471
472 wait_for_ipv6_auto() {
473 local cnt=0
474 local li
475 while [ $cnt -lt 400 ]; do
476 li=$(ip -6 addr show dev $1)
477 if ! strstr "$li" "tentative"; then
478 strstr "$li" "dynamic" && return 0
479 fi
480 sleep 0.1
481 cnt=$(($cnt+1))
482 done
483 return 1
484 }
485
486 linkup() {
487 wait_for_if_link $1 2>/dev/null\
488 && ip link set $1 up 2>/dev/null\
489 && wait_for_if_up $1 2>/dev/null
490 }
491
492 type hostname >/dev/null 2>&1 || \
493 hostname() {
494 cat /proc/sys/kernel/hostname
495 }
496
497 iface_has_link() {
498 local interface="$1" flags=""
499 [ -n "$interface" ] || return 2
500 interface="/sys/class/net/$interface"
501 [ -d "$interface" ] || return 2
502 linkup "$1"
503 [ "$(cat $interface/carrier)" = 1 ] || return 1
504 # XXX Do we need to reset the flags here? anaconda never bothered..
505 }
506
507 find_iface_with_link() {
508 local iface_path="" iface=""
509 for iface_path in /sys/class/net/*; do
510 iface=${iface_path##*/}
511 str_starts "$iface" "lo" && continue
512 if iface_has_link $iface; then
513 echo "$iface"
514 return 0
515 fi
516 done
517 return 1
518 }
519
520 is_persistent_ethernet_name() {
521 case "$1" in
522 # udev persistent interface names
523 eth[0-9]|eth[0-9][0-9]|eth[0-9][0-9][0-9]*)
524 ;;
525 eno[0-9]|eno[0-9][0-9]|eno[0-9][0-9][0-9]*)
526 ;;
527 ens[0-9]|ens[0-9][0-9]|ens[0-9][0-9][0-9]*)
528 ;;
529 enp[0-9]s[0-9]*|enp[0-9][0-9]s[0-9]*|enp[0-9][0-9][0-9]*s[0-9]*)
530 ;;
531 enP*p[0-9]s[0-9]*|enP*p[0-9][0-9]s[0-9]*|enP*p[0-9][0-9][0-9]*s[0-9]*)
532 ;;
533 # biosdevname
534 em[0-9]|em[0-9][0-9]|em[0-9][0-9][0-9]*)
535 ;;
536 p[0-9]p[0-9]*|p[0-9][0-9]p[0-9]*|p[0-9][0-9][0-9]*p[0-9]*)
537 ;;
538 *)
539 return 1
540 esac
541 return 0
542 }