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