network: IPv6 status, wait for tentative flag to be cleared
[dracut.git] / modules.d / 40network / net-lib.sh
bloba5867118ccd3dc4e7b96ac5e31a0eb9a6faa4b47
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
5 get_ip() {
6 local iface="$1" ip=""
7 ip=$(ip -o -f inet addr show $iface)
8 ip=${ip%%/*}
9 ip=${ip##* }
12 iface_for_remote_addr() {
13 set -- $(ip -o route get to $1)
14 echo $5
17 iface_for_ip() {
18 set -- $(ip -o addr show to $1)
19 echo $2
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##*/}
28 done
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
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
54 return $rv
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
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
74 ip_is_local() {
75 strstr "$(ip route get $1 2>/dev/null)" " via "
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 # TODO: send "offline" uevent?
88 setup_net() {
89 local netif="$1" f="" gw_ip="" netroot_ip="" iface="" IFACES=""
90 [ -e /tmp/net.$netif.did-setup ] && return
91 [ -e "/tmp/net.ifaces" ] && read IFACES < /tmp/net.ifaces
92 [ -z "$IFACES" ] && IFACES="$netif"
93 # run the scripts written by ifup
94 [ -e /tmp/net.$netif.gw ] && . /tmp/net.$netif.gw
95 [ -e /tmp/net.$netif.hostname ] && . /tmp/net.$netif.hostname
96 [ -e /tmp/net.$netif.override ] && . /tmp/net.$netif.override
97 [ -e /tmp/dhclient.$netif.dhcpopts ] && . /tmp/dhclient.$netif.dhcpopts
98 # set up resolv.conf
99 [ -e /tmp/net.$netif.resolv.conf ] && \
100 cp -f /tmp/net.$netif.resolv.conf /etc/resolv.conf
102 # Handle STP Timeout: arping the default gateway.
103 # (or the root server, if a) it's local or b) there's no gateway.)
104 # Note: This assumes that if no router is present the
105 # root server is on the same subnet.
107 # Get DHCP-provided router IP, or the cmdline-provided "gw=" argument
108 [ -n "$new_routers" ] && gw_ip=${new_routers%%,*}
109 [ -n "$gw" ] && gw_ip=$gw
111 # Get the "netroot" IP (if there's an IP address in there)
112 netroot_ip=$(get_netroot_ip $netroot)
114 # try netroot if it's local (or there's no gateway)
115 if ip_is_local $netroot_ip || [ -z "$gw_ip" ]; then
116 dest="$netroot_ip"
117 else
118 dest="$gw_ip"
121 unset layer2
122 if [ -f /sys/class/net/$netif/device/layer2 ]; then
123 read layer2 < /sys/class/net/$netif/device/layer2
126 if [ "$layer2" != "0" ] && [ -n "$dest" ] && ! strstr "$dest" ":"; then
127 arping -q -f -w 60 -I $netif $dest || info "Resolving $dest via ARP on $netif failed"
129 unset layer2
131 > /tmp/net.$netif.did-setup
134 save_netinfo() {
135 local netif="$1" IFACES="" f="" i=""
136 [ -e /tmp/net.ifaces ] && read IFACES < /tmp/net.ifaces
137 # Add $netif to the front of IFACES (if it's not there already).
138 set -- "$netif"
139 for i in $IFACES; do [ "$i" != "$netif" ] && set -- "$@" "$i"; done
140 IFACES="$*"
141 for i in $IFACES; do
142 for f in /tmp/dhclient.$i.*; do
143 [ -f $f ] && cp -f $f /tmp/net.${f#/tmp/dhclient.}
144 done
145 done
146 echo $IFACES > /tmp/.net.ifaces.new
147 mv /tmp/.net.ifaces.new /tmp/net.ifaces
150 set_ifname() {
151 local name="$1" mac="$2" num=-1 n=""
152 # if it's already set, return the existing name
153 for n in $(getargs ifname=); do
154 strstr "$n" "$mac" && echo ${n%%:*} && return
155 done
156 # otherwise, pick a new name and use that
157 while :; do
158 num=$(($num+1));
159 [ -e /sys/class/net/$name$num ] && continue
160 for n in $(getargs ifname=); do
161 [ "$name$num" = "${n%%:*}" ] && continue 2
162 done
163 break
164 done
165 echo "ifname=$name$num:$mac" >> /etc/cmdline.d/45-ifname.conf
166 echo "$name$num"
169 # pxelinux provides macaddr '-' separated, but we need ':'
170 fix_bootif() {
171 local macaddr=${1}
172 local IFS='-'
173 macaddr=$(for i in ${macaddr} ; do echo -n $i:; done)
174 macaddr=${macaddr%:}
175 # strip hardware type field from pxelinux
176 [ -n "${macaddr%??:??:??:??:??:??}" ] && macaddr=${macaddr#??:}
177 # return macaddr with lowercase alpha characters expected by udev
178 echo $macaddr | sed 'y/ABCDEF/abcdef/'
181 ibft_to_cmdline() {
182 local iface=""
183 modprobe -q iscsi_ibft
185 for iface in /sys/firmware/ibft/ethernet*; do
186 local mac="" dev=""
187 local dhcp="" ip="" gw="" mask="" hostname=""
188 local dns1 dns2
190 [ -e ${iface}/mac ] || continue
191 mac=$(read a < ${iface}/mac; echo $a)
192 [ -z "$mac" ] && continue
193 dev=$(set_ifname ibft $mac)
195 [ -e /tmp/net.${dev}.has_ibft_config ] && continue
197 [ -e ${iface}/dhcp ] && dhcp=$(read a < ${iface}/dhcp; echo $a)
199 if [ -n "$dhcp" ]; then
200 echo "ip=$dev:dhcp"
201 elif [ -e ${iface}/ip-addr ]; then
202 [ -e ${iface}/ip-addr ] && ip=$(read a < ${iface}/ip-addr; echo $a)
203 # skip not assigned ip adresses
204 [ "$ip" = "0.0.0.0" ] && continue
205 [ -e ${iface}/gateway ] && gw=$(read a < ${iface}/gateway; echo $a)
206 [ -e ${iface}/subnet-mask ] && mask=$(read a < ${iface}/subnet-mask; echo $a)
207 [ -e ${iface}/primary-dns ] && dns1=$(read a < ${iface}/primary-dns; echo $a)
208 [ -e ${iface}/secondary-dns ] && dns2=$(read a < ${iface}/secondary-dns; echo $a)
209 [ -e ${iface}/hostname ] && hostname=$(read a < ${iface}/hostname; echo $a)
210 if [ -n "$ip" ] && [ -n "$mask" ]; then
211 echo "ip=$ip::$gw:$mask:$hostname:$dev:none${dns1:+:$dns1}${dns2:+:$dns2}"
212 else
213 warn "${iface} does not contain a valid iBFT configuration"
214 warn "ip-addr=$ip"
215 warn "gateway=$gw"
216 warn "subnet-mask=$mask"
217 warn "hostname=$hostname"
219 else
220 info "${iface} does not contain a valid iBFT configuration"
221 ls -l ${iface} | vinfo
224 if [ -e ${iface}/vlan ]; then
225 vlan=$(read a < ${iface}/vlan; echo $a)
226 if [ "$vlan" -ne "0" ]; then
227 case "$vlan" in
228 [0-9]*)
229 echo "vlan=$dev.$vlan:$dev"
232 echo "vlan=$vlan:$dev"
234 esac
238 echo $mac > /tmp/net.${dev}.has_ibft_config
239 done
240 ) >> /etc/cmdline.d/40-ibft.conf
243 parse_iscsi_root()
245 local v
246 v=${1#iscsi:}
248 # extract authentication info
249 case "$v" in
250 *@*:*:*:*:*)
251 authinfo=${v%%@*}
252 v=${v#*@}
253 # allow empty authinfo to allow having an @ in iscsi_target_name like this:
254 # netroot=iscsi:@192.168.1.100::3260::iqn.2009-01.com.example:testdi@sk
255 if [ -n "$authinfo" ]; then
256 OLDIFS="$IFS"
257 IFS=:
258 set $authinfo
259 IFS="$OLDIFS"
260 if [ $# -gt 4 ]; then
261 warn "Wrong authentication info in iscsi: parameter!"
262 return 1
264 iscsi_username=$1
265 iscsi_password=$2
266 if [ $# -gt 2 ]; then
267 iscsi_in_username=$3
268 iscsi_in_password=$4
272 esac
274 # extract target ip
275 case "$v" in
276 [[]*[]]:*)
277 iscsi_target_ip=${v#[[]}
278 iscsi_target_ip=${iscsi_target_ip%%[]]*}
279 v=${v#[[]$iscsi_target_ip[]]:}
282 iscsi_target_ip=${v%%[:]*}
283 v=${v#$iscsi_target_ip:}
285 esac
287 # extract target name
288 case "$v" in
289 *:iqn.*)
290 iscsi_target_name=iqn.${v##*:iqn.}
291 v=${v%:iqn.*}:
293 *:eui.*)
294 iscsi_target_name=iqn.${v##*:eui.}
295 v=${v%:iqn.*}:
297 *:naa.*)
298 iscsi_target_name=iqn.${v##*:naa.}
299 v=${v%:iqn.*}:
302 warn "Invalid iscii target name, should begin with 'iqn.' or 'eui.' or 'naa.'"
303 return 1
305 esac
307 # parse the rest
308 OLDIFS="$IFS"
309 IFS=:
310 set $v
311 IFS="$OLDIFS"
313 iscsi_protocol=$1; shift # ignored
314 iscsi_target_port=$1; shift
315 if [ $# -eq 3 ]; then
316 iscsi_iface_name=$1; shift
318 if [ $# -eq 2 ]; then
319 iscsi_netdev_name=$1; shift
321 iscsi_lun=$1; shift
322 if [ $# -ne 0 ]; then
323 warn "Invalid parameter in iscsi: parameter!"
324 return 1
328 ip_to_var() {
329 local v=${1}:
330 local i
331 set --
332 while [ -n "$v" ]; do
333 if [ "${v#\[*:*:*\]:}" != "$v" ]; then
334 # handle IPv6 address
335 i="${v%%\]:*}"
336 i="${i##\[}"
337 set -- "$@" "$i"
338 v=${v#\[$i\]:}
339 else
340 set -- "$@" "${v%%:*}"
341 v=${v#*:}
343 done
345 unset ip srv gw mask hostname dev autoconf macaddr mtu dns1 dns2
346 case $# in
347 0) autoconf="error" ;;
348 1) autoconf=$1 ;;
349 2) [ -n "$1" ] && dev=$1; [ -n "$2" ] && autoconf=$2 ;;
350 3) [ -n "$1" ] && dev=$1; [ -n "$2" ] && autoconf=$2; [ -n "$3" ] && mtu=$3 ;;
351 4) [ -n "$1" ] && dev=$1; [ -n "$2" ] && autoconf=$2; [ -n "$3" ] && mtu=$3; [ -n "$4" ] && macaddr=$4 ;;
352 *) [ -n "$1" ] && ip=$1; [ -n "$2" ] && srv=$2; [ -n "$3" ] && gw=$3; [ -n "$4" ] && mask=$4;
353 [ -n "$5" ] && hostname=$5; [ -n "$6" ] && dev=$6; [ -n "$7" ] && autoconf=$7;
354 case "$8" in
355 [0-9]*:*|[0-9]*.[0-9]*.[0-9]*.[0-9]*)
356 dns1="$8"
357 [ -n "$9" ] && dns2="$9"
359 [0-9]*)
360 mtu="$8"
363 if [ -n "${9}" -a -n "${10}" -a -n "${11}" -a -n "${12}" -a -n "${13}" -a -n "${14}" ]; then
364 macaddr="${9}:${10}:${11}:${12}:${13}:${14}"
367 esac
369 esac
371 # ip=<ipv4-address> means anaconda-style static config argument cluster:
372 # ip=<ip> gateway=<gw> netmask=<nm> hostname=<host> mtu=<mtu>
373 # ksdevice={link|bootif|ibft|<MAC>|<ifname>}
374 if strstr "$autoconf" "*.*.*.*"; then
375 ip="$autoconf"
376 gw=$(getarg gateway=)
377 mask=$(getarg netmask=)
378 hostname=$(getarg hostname=)
379 dev=$(getarg ksdevice=)
380 autoconf="none"
381 mtu=$(getarg mtu=)
383 # handle special values for ksdevice
384 case "$dev" in
385 bootif|BOOTIF) dev=$(fix_bootif $(getarg BOOTIF=)) ;;
386 link) dev="" ;; # FIXME: do something useful with this
387 ibft) dev="" ;; # ignore - ibft is handled elsewhere
388 esac
392 parse_ifname_opts() {
393 local IFS=:
394 set $1
396 case $# in
398 ifname_if=$1
399 # udev requires MAC addresses to be lower case
400 ifname_mac=$(echo $2:$3:$4:$5:$6:$7 | sed 'y/ABCDEF/abcdef/')
403 die "Invalid arguments for ifname="
405 esac
407 case $ifname_if in
408 eth[0-9]|eth[0-9][0-9]|eth[0-9][0-9][0-9]|eth[0-9][0-9][0-9][0-9])
409 warn "ifname=$ifname_if uses the kernel name space for interfaces"
410 warn "This can fail for multiple network interfaces and is discouraged!"
411 warn "Please use a custom name like \"netboot\" or \"bluesocket\""
412 warn "or use biosdevname and no ifname= at all."
414 esac
418 # some network driver need long time to initialize, wait before it's ready.
419 wait_for_if_link() {
420 local cnt=0
421 local li
422 while [ $cnt -lt 600 ]; do
423 li=$(ip -o link show dev $1 2>/dev/null)
424 [ -n "$li" ] && return 0
425 sleep 0.1
426 cnt=$(($cnt+1))
427 done
428 return 1
431 wait_for_if_up() {
432 local cnt=0
433 local li
434 while [ $cnt -lt 200 ]; do
435 li=$(ip -o link show up dev $1)
436 [ -n "$li" ] && [ -z "${li##*state UP*}" ] && return 0
437 sleep 0.1
438 cnt=$(($cnt+1))
439 done
440 return 1
443 wait_for_route_ok() {
444 local cnt=0
445 while [ $cnt -lt 200 ]; do
446 li=$(ip route show)
447 [ -n "$li" ] && [ -z "${li##*$1*}" ] && return 0
448 sleep 0.1
449 cnt=$(($cnt+1))
450 done
451 return 1
454 wait_for_ipv6_dad() {
455 local cnt=0
456 local li
457 while [ $cnt -lt 500 ]; do
458 li=$(ip -6 addr show dev $1)
459 strstr "$li" "tentative" || return 0
460 sleep 0.1
461 cnt=$(($cnt+1))
462 done
463 return 1
466 wait_for_ipv6_auto() {
467 local cnt=0
468 local li
469 while [ $cnt -lt 400 ]; do
470 li=$(ip -6 addr show dev $1)
471 if ! strstr "$li" "tentative"; then
472 strstr "$li" "dynamic" && return 0
474 sleep 0.1
475 cnt=$(($cnt+1))
476 done
477 return 1
480 linkup() {
481 wait_for_if_link $1 2>/dev/null\
482 && ip link set $1 up 2>/dev/null\
483 && wait_for_if_up $1 2>/dev/null
486 type hostname >/dev/null 2>&1 || \
487 hostname() {
488 cat /proc/sys/kernel/hostname
491 iface_has_link() {
492 local interface="$1" flags=""
493 [ -n "$interface" ] || return 2
494 interface="/sys/class/net/$interface"
495 [ -d "$interface" ] || return 2
496 linkup "$1"
497 [ "$(cat $interface/carrier)" = 1 ] || return 1
498 # XXX Do we need to reset the flags here? anaconda never bothered..
501 find_iface_with_link() {
502 local iface_path="" iface=""
503 for iface_path in /sys/class/net/*; do
504 iface=${iface_path##*/}
505 str_starts "$iface" "lo" && continue
506 if iface_has_link $iface; then
507 echo "$iface"
508 return 0
510 done
511 return 1