summaryrefslogtreecommitdiffstats
path: root/modules.d/40network/net-lib.sh
diff options
context:
space:
mode:
Diffstat (limited to 'modules.d/40network/net-lib.sh')
-rwxr-xr-xmodules.d/40network/net-lib.sh906
1 files changed, 906 insertions, 0 deletions
diff --git a/modules.d/40network/net-lib.sh b/modules.d/40network/net-lib.sh
new file mode 100755
index 0000000..9d88e0d
--- /dev/null
+++ b/modules.d/40network/net-lib.sh
@@ -0,0 +1,906 @@
+#!/bin/sh
+
+# shellcheck disable=SC2034
+IFNETFILE="/tmp/bootnetif"
+
+is_ip() {
+ echo "$1" | {
+ IFS=. read -r a b c d
+ test "$a" -ge 0 -a "$a" -le 255 \
+ -a "$b" -ge 0 -a "$b" -le 255 \
+ -a "$c" -ge 0 -a "$c" -le 255 \
+ -a "$d" -ge 0 -a "$d" -le 255 \
+ 2> /dev/null
+ } && return 0
+ return 1
+}
+
+get_ip() {
+ local iface="$1" ip=""
+ ip=$(ip -f inet addr show "$iface")
+ ip=${ip%%/*}
+ ip=${ip##* }
+ echo "$ip"
+}
+
+iface_for_remote_addr() {
+ # shellcheck disable=SC2046
+ set -- $(ip route get to "$@" | sed 's/.*\bdev\b//p;q')
+ echo "$1"
+}
+
+iface_for_ip() {
+ # shellcheck disable=SC2046
+ set -- $(ip addr show to "$@")
+ echo "${2%:}"
+}
+
+iface_for_mac() {
+ local interface=""
+ local mac
+ mac="$(echo "$@" | sed 'y/ABCDEF/abcdef/')"
+ for interface in /sys/class/net/*; do
+ if [ "$(cat "$interface"/address)" = "$mac" ]; then
+ echo "${interface##*/}"
+ fi
+ done
+}
+
+# get the iface name for the given identifier - either a MAC, IP, or iface name
+iface_name() {
+ case "$1" in
+ ??:??:??:??:??:?? | ??-??-??-??-??-??) iface_for_mac "$1" ;;
+ *:*:* | *.*.*.*) iface_for_ip "$1" ;;
+ *) echo "$1" ;;
+ esac
+}
+
+# list the configured interfaces
+configured_ifaces() {
+ local IFACES="" iface_id="" rv=1
+ [ -e "/tmp/net.ifaces" ] && read -r IFACES < /tmp/net.ifaces
+ if { pidof udevd || pidof systemd-udevd; } > /dev/null; then
+ for iface_id in $IFACES; do
+ printf "%s\n" "$(iface_name "$iface_id")"
+ rv=0
+ done
+ else
+ warn "configured_ifaces called before udev is running"
+ echo "$IFACES"
+ [ -n "$IFACES" ] && rv=0
+ fi
+ return $rv
+}
+
+all_ifaces_up() {
+ local iface="" IFACES=""
+ [ -e "/tmp/net.ifaces" ] && read -r IFACES < /tmp/net.ifaces
+ for iface in $IFACES; do
+ [ -e /tmp/net."$iface".up ] || return 1
+ done
+}
+
+all_ifaces_setup() {
+ local iface="" IFACES=""
+ [ -e "/tmp/net.ifaces" ] && read -r IFACES < /tmp/net.ifaces
+ for iface in $IFACES; do
+ [ -e /tmp/net."$iface".did-setup ] || return 1
+ done
+}
+
+get_netroot_ip() {
+ local prefix="" server="" rest=""
+ splitsep ":" "$1" prefix server rest
+ case $server in
+ [0-9]*\.[0-9]*\.[0-9]*\.[0-9]*)
+ echo "$server"
+ return 0
+ ;;
+ esac
+ return 1
+}
+
+ip_is_local() {
+ li=$(ip route get "$@" 2> /dev/null)
+ if [ -n "$li" ]; then
+ strstr "$li" " via " || return 0
+ fi
+ return 1
+}
+
+ifdown() {
+ local netif="$1"
+ # ip down/flush ensures that routing info goes away as well
+ ip link set "$netif" down
+ ip addr flush dev "$netif"
+ echo "#empty" > /etc/resolv.conf
+ rm -f -- /tmp/net."$netif".did-setup
+ [ -z "$DO_VLAN" ] \
+ && [ -e /sys/class/net/"$netif"/address ] \
+ && rm -f -- "/tmp/net.$(cat /sys/class/net/"$netif"/address).did-setup"
+ # TODO: send "offline" uevent?
+}
+
+setup_net() {
+ local netif="$1" f="" gw_ip="" netroot_ip="" iface="" IFACES=""
+ local _p
+ [ -e /tmp/net."$netif".did-setup ] && return
+ [ -z "$DO_VLAN" ] \
+ && [ -e /sys/class/net/"$netif"/address ] \
+ && [ -e "/tmp/net.$(cat /sys/class/net/"$netif"/address).did-setup" ] && return
+ [ -e "/tmp/net.ifaces" ] && read -r IFACES < /tmp/net.ifaces
+ [ -z "$IFACES" ] && IFACES="$netif"
+ # run the scripts written by ifup
+ # shellcheck disable=SC1090
+ [ -e /tmp/net."$netif".hostname ] && . /tmp/net."$netif".hostname
+ # shellcheck disable=SC1090
+ [ -e /tmp/net."$netif".override ] && . /tmp/net."$netif".override
+ # shellcheck disable=SC1090
+ [ -e /tmp/dhclient."$netif".dhcpopts ] && . /tmp/dhclient."$netif".dhcpopts
+ # set up resolv.conf
+ [ -e /tmp/net."$netif".resolv.conf ] \
+ && awk '!array[$0]++' /tmp/net."$netif".resolv.conf > /etc/resolv.conf
+ # shellcheck disable=SC1090
+ [ -e /tmp/net."$netif".gw ] && . /tmp/net."$netif".gw
+
+ # add static route
+ for _p in $(getargs rd.route); do
+ route_to_var "$_p" || continue
+ [ -n "$route_dev" ] && [ "$route_dev" != "$netif" ] && continue
+ ip route add "$route_mask" ${route_gw:+via $route_gw} ${route_dev:+dev $route_dev}
+ if strstr "$route_mask" ":"; then
+ printf -- "%s\n" "$route_mask ${route_gw:+via $route_gw} ${route_dev:+dev $route_dev}" \
+ > /tmp/net.route6."$netif"
+ else
+ printf -- "%s\n" "$route_mask ${route_gw:+via $route_gw} ${route_dev:+dev $route_dev}" \
+ > /tmp/net.route."$netif"
+ fi
+ done
+
+ # If a static route was necessary to reach the gateway, the
+ # first gateway setup call will have failed with
+ # RTNETLINK answers: Network is unreachable
+ # Replace the default route again after static routes to cover
+ # this scenario.
+ # shellcheck disable=SC1090
+ [ -e /tmp/net."$netif".gw ] && . /tmp/net."$netif".gw
+
+ # Handle STP Timeout: arping the default gateway.
+ # (or the root server, if a) it's local or b) there's no gateway.)
+ # Note: This assumes that if no router is present the
+ # root server is on the same subnet.
+
+ # Get DHCP-provided router IP, or the cmdline-provided "gw=" argument
+ [ -n "$new_routers" ] && gw_ip=${new_routers%%,*}
+ [ -n "$gw" ] && gw_ip=$gw
+
+ # Get the "netroot" IP (if there's an IP address in there)
+ netroot_ip=$(get_netroot_ip "$netroot")
+
+ # try netroot if it's local (or there's no gateway)
+ if ip_is_local "$netroot_ip" || [ -z "$gw_ip" ]; then
+ dest="$netroot_ip"
+ else
+ dest="$gw_ip"
+ fi
+
+ unset layer2
+ if [ -f /sys/class/net/"$netif"/device/layer2 ]; then
+ read -r layer2 < /sys/class/net/"$netif"/device/layer2
+ fi
+
+ if [ "$layer2" != "0" ] && [ -n "$dest" ] && ! strstr "$dest" ":"; then
+ if command -v arping2 > /dev/null; then
+ arping2 -q -C 1 -c 60 -I "$netif" "$dest" || info "Resolving $dest via ARP on $netif failed"
+ else
+ arping -q -f -w 60 -I "$netif" "$dest" || info "Resolving $dest via ARP on $netif failed"
+ fi
+ fi
+ unset layer2
+
+ : > /tmp/net."$netif".did-setup
+ [ -z "$DO_VLAN" ] \
+ && [ -e /sys/class/net/"$netif"/address ] \
+ && : > "/tmp/net.$(cat /sys/class/net/"$netif"/address).did-setup"
+}
+
+save_netinfo() {
+ local netif="$1" IFACES="" f="" i=""
+ [ -e /tmp/net.ifaces ] && read -r IFACES < /tmp/net.ifaces
+ # Add $netif to the front of IFACES (if it's not there already).
+ set -- "$netif"
+ for i in $IFACES; do [ "$i" != "$netif" ] && set -- "$@" "$i"; done
+ IFACES="$*"
+ for i in $IFACES; do
+ for f in "/tmp/dhclient.$i."*; do
+ [ -f "$f" ] && cp -f "$f" /tmp/net."${f#/tmp/dhclient.}"
+ done
+ done
+ echo "$IFACES" > /tmp/.net.ifaces.new
+ mv /tmp/.net.ifaces.new /tmp/net.ifaces
+}
+
+set_ifname() {
+ local name="$1" mac="$2" num=-1 n=""
+ # if it's already set, return the existing name
+ for n in $(getargs ifname=); do
+ strstr "$n" "$mac" && echo "${n%%:*}" && return
+ done
+ [ ! -f "/tmp/set_ifname_$name" ] || read -r num < "/tmp/set_ifname_$name"
+ # otherwise, pick a new name and use that
+ while :; do
+ num=$((num + 1))
+ [ -e /sys/class/net/"$name"$num ] && continue
+ for n in $(getargs ifname=); do
+ [ "$name$num" = "${n%%:*}" ] && continue 2
+ done
+ break
+ done
+ echo "ifname=$name$num:$mac" >> /etc/cmdline.d/45-ifname.conf
+ echo "$num" > "/tmp/set_ifname_$name"
+ echo "$name$num"
+}
+
+# pxelinux provides macaddr '-' separated, but we need ':'
+fix_bootif() {
+ local macaddr="${1}"
+ local IFS='-'
+ # shellcheck disable=SC2086
+ macaddr=$(printf '%s:' ${macaddr})
+ macaddr=${macaddr%:}
+ # strip hardware type field from pxelinux
+ [ -n "${macaddr%??:??:??:??:??:??}" ] && macaddr=${macaddr#??:}
+ # return macaddr with lowercase alpha characters expected by udev
+ echo "$macaddr" | sed 'y/ABCDEF/abcdef/'
+}
+
+ibft_to_cmdline() {
+ local iface=""
+ modprobe -q iscsi_ibft
+ (
+ for iface in /sys/firmware/ibft/ethernet*; do
+ local mac="" dev=""
+ local dhcp="" ip="" gw="" mask="" hostname=""
+ local dns1 dns2
+
+ [ -e "${iface}"/mac ] || continue
+ read -r mac < "${iface}"/mac
+ [ -z "$mac" ] && continue
+ dev=$(set_ifname ibft "$mac")
+
+ [ -e /tmp/net."${dev}".has_ibft_config ] && continue
+
+ [ -e "${iface}"/flags ] && read -r flags < "${iface}"/flags
+ # Skip invalid interfaces
+ awk -- 'BEGIN { exit (!and('"$flags"',1)) }' || continue
+ # Skip interfaces not used for booting unless using multipath
+ if ! getargbool 0 rd.iscsi.mp; then
+ awk -- 'BEGIN { exit (!and('"$flags"',2)) }' || continue
+ fi
+ [ -e "${iface}"/dhcp ] && read -r dhcp < "${iface}"/dhcp
+ [ -e "${iface}"/origin ] && read -r origin < "${iface}"/origin
+ [ -e "${iface}"/ip-addr ] && read -r ip < "${iface}"/ip-addr
+
+ if [ -n "$ip" ]; then
+ case "$ip" in
+ *.*.*.*)
+ family=ipv4
+ ;;
+ *:*)
+ family=ipv6
+ ;;
+ esac
+ fi
+ if [ -n "$dhcp" ] || [ "$origin" -eq 3 ]; then
+ if [ "$family" = "ipv6" ]; then
+ echo "ip=$dev:dhcp6"
+ else
+ echo "ip=$dev:dhcp"
+ fi
+ elif [ -e "${iface}"/ip-addr ]; then
+ # skip not assigned ip addresses
+ [ "$ip" = "0.0.0.0" ] && continue
+ [ -e "${iface}"/gateway ] && read -r gw < "${iface}"/gateway
+ [ "$gw" = "0.0.0.0" ] && unset gw
+ [ -e "${iface}"/subnet-mask ] && read -r mask < "${iface}"/subnet-mask
+ [ -e "${iface}"/prefix-len ] && read -r prefix < "${iface}"/prefix-len
+ [ -e "${iface}"/primary-dns ] && read -r dns1 < "${iface}"/primary-dns
+ [ "$dns1" = "0.0.0.0" ] && unset dns1
+ [ -e "${iface}"/secondary-dns ] && read -r dns2 < "${iface}"/secondary-dns
+ [ "$dns2" = "0.0.0.0" ] && unset dns
+ [ -e "${iface}"/hostname ] && read -r hostname < "${iface}"/hostname
+ if [ "$family" = "ipv6" ]; then
+ if [ -n "$ip" ]; then
+ [ -n "$prefix" ] || prefix=128
+ ip="[${ip}]"
+ mask=$prefix
+ fi
+ if [ -n "$gw" ]; then
+ gw="[${gw}]"
+ fi
+ fi
+ if [ -n "$ip" ] && [ -n "$mask" -o -n "$prefix" ]; then
+ echo "ip=$ip::$gw:$mask:$hostname:$dev:none${dns1:+:$dns1}${dns2:+:$dns2}"
+ else
+ warn "${iface} does not contain a valid iBFT configuration"
+ warn "ip-addr=$ip"
+ warn "gateway=$gw"
+ warn "subnet-mask=$mask"
+ warn "hostname=$hostname"
+ fi
+ else
+ info "${iface} does not contain a valid iBFT configuration"
+ # shellcheck disable=SC2012
+ ls -l "${iface}" | vinfo
+ fi
+
+ if [ -e "${iface}"/vlan ]; then
+ read -r vlan < "${iface}"/vlan
+ if [ "$vlan" -ne "0" ]; then
+ case "$vlan" in
+ [0-9]*)
+ echo "vlan=$dev.$vlan:$dev"
+ echo "$mac" > /tmp/net."${dev}"."${vlan}".has_ibft_config
+ ;;
+ *)
+ echo "vlan=$vlan:$dev"
+ echo "$mac" > /tmp/net."${vlan}".has_ibft_config
+ ;;
+ esac
+ else
+ echo "$mac" > /tmp/net."${dev}".has_ibft_config
+ fi
+ else
+ echo "$mac" > /tmp/net."${dev}".has_ibft_config
+ fi
+
+ done
+ ) >> /etc/cmdline.d/40-ibft.conf
+}
+
+parse_iscsi_root() {
+ local v
+ v=${1#iscsi:}
+
+ # extract authentication info
+ case "$v" in
+ *@*:*:*:*:*)
+ authinfo=${v%%@*}
+ v=${v#*@}
+ # allow empty authinfo to allow having an @ in iscsi_target_name like this:
+ # netroot=iscsi:@192.168.1.100::3260::iqn.2009-01.com.example:testdi@sk
+ if [ -n "$authinfo" ]; then
+ OLDIFS="$IFS"
+ IFS=:
+ # shellcheck disable=SC2086
+ set $authinfo
+ IFS="$OLDIFS"
+ if [ $# -gt 4 ]; then
+ warn "Wrong authentication info in iscsi: parameter!"
+ return 1
+ fi
+ iscsi_username=$1
+ iscsi_password=$2
+ if [ $# -gt 2 ]; then
+ iscsi_in_username=$3
+ iscsi_in_password=$4
+ fi
+ fi
+ ;;
+ esac
+
+ # extract target ip
+ case "$v" in
+ [[]*[]]:*)
+ iscsi_target_ip=${v#[[]}
+ iscsi_target_ip=${iscsi_target_ip%%[]]*}
+ v=${v#[[]"$iscsi_target_ip"[]]:}
+ ;;
+ *)
+ iscsi_target_ip=${v%%[:]*}
+ v=${v#"$iscsi_target_ip":}
+ ;;
+ esac
+
+ unset iscsi_target_name
+ # extract target name
+ case "$v" in
+ *:iqn.*)
+ iscsi_target_name=iqn.${v##*:iqn.}
+ v=${v%:iqn.*}:
+ ;;
+ *:eui.*)
+ iscsi_target_name=eui.${v##*:eui.}
+ v=${v%:eui.*}:
+ ;;
+ *:naa.*)
+ iscsi_target_name=naa.${v##*:naa.}
+ v=${v%:naa.*}:
+ ;;
+ esac
+
+ # parse the rest
+ OLDIFS="$IFS"
+ IFS=:
+ # shellcheck disable=SC2086
+ set $v
+ IFS="$OLDIFS"
+
+ iscsi_protocol=$1
+ shift # ignored
+ iscsi_target_port=$1
+ shift
+
+ if [ -n "$iscsi_target_name" ]; then
+ if [ $# -eq 3 ]; then
+ iscsi_iface_name=$1
+ shift
+ fi
+ if [ $# -eq 2 ]; then
+ iscsi_netdev_name=$1
+ shift
+ fi
+ iscsi_lun=$1
+ shift
+ if [ $# -ne 0 ]; then
+ warn "Invalid parameter in iscsi: parameter!"
+ return 1
+ fi
+ return 0
+ fi
+
+ if [ $# -gt 3 ] && [ -n "$1$2" ]; then
+ if [ -z "$3" ] || [ "$3" -ge 0 ] 2> /dev/null; then
+ iscsi_iface_name=$1
+ shift
+ iscsi_netdev_name=$1
+ shift
+ fi
+ fi
+
+ iscsi_lun=$1
+ shift
+
+ iscsi_target_name=$(printf "%s:" "$@")
+ iscsi_target_name=${iscsi_target_name%:}
+}
+
+ip_to_var() {
+ local v="${1}":
+ local i
+ set --
+ while [ -n "$v" ]; do
+ if [ "${v#\[*:*:*\]:}" != "$v" ]; then
+ # handle IPv6 address
+ i="${v%%\]:*}"
+ i="${i##\[}"
+ set -- "$@" "$i"
+ v=${v#\["$i"\]:}
+ else
+ set -- "$@" "${v%%:*}"
+ v=${v#*:}
+ fi
+ done
+
+ unset ip srv gw mask hostname dev autoconf macaddr mtu dns1 dns2
+
+ if [ $# -eq 0 ]; then
+ autoconf="error"
+ return 0
+ fi
+
+ if [ $# -eq 1 ]; then
+ # format: ip={dhcp|on|any|dhcp6|auto6|either6|single-dhcp}
+ # or
+ # ip=<ipv4-address> means anaconda-style static config argument cluster
+ autoconf="$1"
+
+ if strglob "$autoconf" "*.*.*.*"; then
+ # ip=<ipv4-address> means anaconda-style static config argument cluster:
+ # ip=<ip> gateway=<gw> netmask=<nm> hostname=<host> mtu=<mtu>
+ # ksdevice={link|bootif|ibft|<MAC>|<ifname>}
+ ip="$autoconf"
+ gw=$(getarg gateway=)
+ mask=$(getarg netmask=)
+ hostname=$(getarg hostname=)
+ dev=$(getarg ksdevice=)
+ autoconf="none"
+ mtu=$(getarg mtu=)
+
+ # handle special values for ksdevice
+ case "$dev" in
+ bootif | BOOTIF) dev=$(fix_bootif "$(getarg BOOTIF=)") ;;
+ link) dev="" ;; # FIXME: do something useful with this
+ ibft) dev="" ;; # ignore - ibft is handled elsewhere
+ esac
+ fi
+ return 0
+ fi
+
+ if [ "$2" = "dhcp" -o "$2" = "on" -o "$2" = "any" -o "$2" = "dhcp6" -o "$2" = "auto6" -o "$2" = "either6" ]; then
+ # format: ip=<interface>:{dhcp|on|any|dhcp6|auto6}[:[<mtu>][:<macaddr>]]
+ [ -n "$1" ] && dev="$1"
+ [ -n "$2" ] && autoconf="$2"
+ [ -n "$3" ] && mtu=$3
+ if [ -z "$5" ]; then
+ macaddr="$4"
+ else
+ macaddr="${4}:${5}:${6}:${7}:${8}:${9}"
+ fi
+ return 0
+ fi
+
+ # format: ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}:[:[<mtu>][:<macaddr>]]
+
+ [ -n "$1" ] && ip=$1
+ [ -n "$2" ] && srv=$2
+ [ -n "$3" ] && gw=$3
+ [ -n "$4" ] && mask=$4
+ [ -n "$5" ] && hostname=$5
+ [ -n "$6" ] && dev=$6
+ [ -n "$7" ] && autoconf=$7
+ case "$8" in
+ [0-9a-fA-F]*:* | [0-9]*.[0-9]*.[0-9]*.[0-9]*)
+ dns1="$8"
+ [ -n "$9" ] && dns2="$9"
+ ;;
+ [0-9]*)
+ mtu="$8"
+ if [ -n "${9}" -a -z "${10}" ]; then
+ macaddr="${9}"
+ elif [ -n "${9}" -a -n "${10}" -a -n "${11}" -a -n "${12}" -a -n "${13}" -a -n "${14}" ]; then
+ macaddr="${9}:${10}:${11}:${12}:${13}:${14}"
+ fi
+ ;;
+ *)
+ if [ -n "${9}" -a -z "${10}" ]; then
+ macaddr="${9}"
+ elif [ -n "${9}" -a -n "${10}" -a -n "${11}" -a -n "${12}" -a -n "${13}" -a -n "${14}" ]; then
+ macaddr="${9}:${10}:${11}:${12}:${13}:${14}"
+ fi
+ ;;
+ esac
+ return 0
+}
+
+route_to_var() {
+ local v="${1}":
+ local i
+ set --
+ while [ -n "$v" ]; do
+ if [ "${v#\[*:*:*\]:}" != "$v" ]; then
+ # handle IPv6 address
+ i="${v%%\]:*}"
+ i="${i##\[}"
+ set -- "$@" "$i"
+ v=${v#\["$i"\]:}
+ else
+ set -- "$@" "${v%%:*}"
+ v=${v#*:}
+ fi
+ done
+
+ unset route_mask route_gw route_dev
+ case $# in
+ 2)
+ [ -n "$1" ] && route_mask="$1"
+ [ -n "$2" ] && route_gw="$2"
+ return 0
+ ;;
+ 3)
+ [ -n "$1" ] && route_mask="$1"
+ [ -n "$2" ] && route_gw="$2"
+ [ -n "$3" ] && route_dev="$3"
+ return 0
+ ;;
+ *) return 1 ;;
+ esac
+}
+
+parse_ifname_opts() {
+ local IFS=:
+ # shellcheck disable=SC2086
+ set -- $1
+
+ case $# in
+ 7)
+ ifname_if=$1
+ # udev requires MAC addresses to be lower case
+ ifname_mac=$(echo "$2:$3:$4:$5:$6:$7" | sed 'y/ABCDEF/abcdef/')
+ ;;
+ 21)
+ ifname_if=$1
+ # udev requires MAC addresses to be lower case
+ ifname_mac=$(echo "$2:$3:$4:$5:$6:$7:$8:$9:${10}:${11}:${12}:${13}:${14}:${15}:${16}:${17}:${18}:${19}:${20}:${21}" | sed 'y/ABCDEF/abcdef/')
+ ;;
+ *)
+ die "Invalid arguments for ifname="
+ ;;
+ esac
+
+ case $ifname_if in
+ eth[0-9] | eth[0-9][0-9] | eth[0-9][0-9][0-9] | eth[0-9][0-9][0-9][0-9])
+ warn "ifname=$ifname_if uses the kernel name space for interfaces"
+ warn "This can fail for multiple network interfaces and is discouraged!"
+ warn 'Please use a custom name like "netboot" or "bluesocket"'
+ warn "or use biosdevname and no ifname= at all."
+ ;;
+ esac
+
+}
+
+# some network driver need long time to initialize, wait before it's ready.
+wait_for_if_link() {
+ local cnt=0
+ local li
+ local timeout
+ timeout=$(getargs rd.net.timeout.iflink=)
+ timeout=${timeout:-60}
+ timeout=$((timeout * 10))
+
+ while [ $cnt -lt $timeout ]; do
+ li=$(ip link show dev "$@" 2> /dev/null)
+ [ -n "$li" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_if_up() {
+ local cnt=0
+ local li
+ local timeout
+ timeout=$(getargs rd.net.timeout.ifup=)
+ timeout=${timeout:-20}
+ timeout=$((timeout * 10))
+
+ while [ $cnt -lt $timeout ]; do
+ li=$(ip link show up dev "$@")
+ if [ -n "$li" ]; then
+ case "$li" in
+ *\<UP*)
+ return 0
+ ;;
+ *\<*,UP\>*)
+ return 0
+ ;;
+ *\<*,UP,*\>*)
+ return 0
+ ;;
+ esac
+ fi
+ if strstr "$li" "LOWER_UP" \
+ && strstr "$li" "state UNKNOWN" \
+ && ! strstr "$li" "DORMANT"; then
+ return 0
+ fi
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_route_ok() {
+ local cnt=0
+ local timeout
+ timeout=$(getargs rd.net.timeout.route=)
+ timeout=${timeout:-20}
+ timeout=$((timeout * 10))
+
+ while [ $cnt -lt $timeout ]; do
+ li=$(ip route show)
+ [ -n "$li" ] && [ -z "${li##*"$1"*}" ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_ipv6_dad_link() {
+ local cnt=0
+ local timeout
+ timeout=$(getargs rd.net.timeout.ipv6dad=)
+ timeout=${timeout:-50}
+ timeout=$((timeout * 10))
+
+ while [ $cnt -lt $timeout ]; do
+ [ -n "$(ip -6 addr show dev "$@" scope link)" ] \
+ && [ -z "$(ip -6 addr show dev "$@" scope link tentative)" ] \
+ && return 0
+ [ -n "$(ip -6 addr show dev "$@" scope link dadfailed)" ] \
+ && return 1
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_ipv6_dad() {
+ local cnt=0
+ local timeout
+ timeout=$(getargs rd.net.timeout.ipv6dad=)
+ timeout=${timeout:-50}
+ timeout=$((timeout * 10))
+
+ while [ $cnt -lt $timeout ]; do
+ [ -n "$(ip -6 addr show dev "$@")" ] \
+ && [ -z "$(ip -6 addr show dev "$@" tentative)" ] \
+ && return 0
+ [ -n "$(ip -6 addr show dev "$@" dadfailed)" ] \
+ && return 1
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+wait_for_ipv6_auto() {
+ local cnt=0
+ local timeout
+ timeout=$(getargs rd.net.timeout.ipv6auto=)
+ timeout=${timeout:-40}
+ timeout=$((timeout * 10))
+
+ while [ $cnt -lt $timeout ]; do
+ [ -z "$(ip -6 addr show dev "$@" tentative)" ] \
+ && { ip -6 route list proto ra dev "$@" | grep -q ^default; } \
+ && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+linkup() {
+ wait_for_if_link "$@" 2> /dev/null && ip link set "$@" up 2> /dev/null && wait_for_if_up "$@" 2> /dev/null
+}
+
+type hostname > /dev/null 2>&1 \
+ || hostname() {
+ cat /proc/sys/kernel/hostname
+ }
+
+iface_has_carrier() {
+ local cnt=0
+ local iface="$1" flags=""
+ local timeout
+ local iface_sys_path
+ [ -n "$iface" ] || return 2
+ iface_sys_path="/sys/class/net/$iface"
+ [ -d "$iface_sys_path" ] || return 2
+ timeout=$(getargs rd.net.timeout.carrier=)
+ timeout=${timeout:-10}
+ timeout=$((timeout * 10))
+
+ linkup "$1"
+
+ li=$(ip link show up dev "$iface")
+ strstr "$li" "NO-CARRIER" && _no_carrier_flag=1
+
+ while [ $cnt -lt $timeout ]; do
+ if [ -n "$_no_carrier_flag" ]; then
+ li=$(ip link show up dev "$iface")
+ # NO-CARRIER flag was cleared
+ strstr "$li" "NO-CARRIER" || return 0
+ elif ! [ -e "$iface_sys_path/carrier" ]; then
+ # sysfs not available and "NO-CARRIER" not displayed
+ return 0
+ fi
+ # double check the syscfs carrier flag
+ [ -e "$iface_sys_path/carrier" ] && [ "$(cat "$iface_sys_path"/carrier)" = 1 ] && return 0
+ sleep 0.1
+ cnt=$((cnt + 1))
+ done
+ return 1
+}
+
+iface_has_link() {
+ iface_has_carrier "$@"
+}
+
+iface_is_enslaved() {
+ local _li
+ _li=$(ip link show dev "$@")
+ strstr "$_li" " master " || return 1
+ return 0
+}
+
+find_iface_with_link() {
+ local iface_path="" iface=""
+ for iface_path in /sys/class/net/*; do
+ iface=${iface_path##*/}
+ str_starts "$iface" "lo" && continue
+ if iface_has_link "$iface"; then
+ echo "$iface"
+ return 0
+ fi
+ done
+ return 1
+}
+
+is_persistent_ethernet_name() {
+ local _netif="$1"
+ local _name_assign_type="0"
+
+ [ -f "/sys/class/net/$_netif/name_assign_type" ] \
+ && read -r _name_assign_type < "/sys/class/net/$_netif/name_assign_type" 2> /dev/null
+
+ # NET_NAME_ENUM 1
+ [ "$_name_assign_type" = "1" ] && return 1
+
+ # NET_NAME_PREDICTABLE 2
+ [ "$_name_assign_type" = "2" ] && return 0
+
+ case "$_netif" in
+ # udev persistent interface names
+ eno[0-9] | eno[0-9][0-9] | eno[0-9][0-9][0-9]*) ;;
+
+ ens[0-9] | ens[0-9][0-9] | ens[0-9][0-9][0-9]*) ;;
+
+ enp[0-9]s[0-9]* | enp[0-9][0-9]s[0-9]* | enp[0-9][0-9][0-9]*s[0-9]*) ;;
+
+ 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]*) ;;
+
+ # biosdevname
+ em[0-9] | em[0-9][0-9] | em[0-9][0-9][0-9]*) ;;
+
+ p[0-9]p[0-9]* | p[0-9][0-9]p[0-9]* | p[0-9][0-9][0-9]*p[0-9]*) ;;
+
+ *)
+ return 1
+ ;;
+ esac
+ return 0
+}
+
+is_kernel_ethernet_name() {
+ local _netif="$1"
+ local _name_assign_type="1"
+
+ if [ -e "/sys/class/net/$_netif/name_assign_type" ]; then
+ read -r _name_assign_type < "/sys/class/net/$_netif/name_assign_type"
+
+ case "$_name_assign_type" in
+ 2 | 3 | 4)
+ # NET_NAME_PREDICTABLE 2
+ # NET_NAME_USER 3
+ # NET_NAME_RENAMED 4
+ return 1
+ ;;
+ 1 | *)
+ # NET_NAME_ENUM 1
+ return 0
+ ;;
+ esac
+ fi
+
+ # fallback to error prone manual name check
+ case "$_netif" in
+ eth[0-9] | eth[0-9][0-9] | eth[0-9][0-9][0-9]*)
+ return 0
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+
+}
+
+iface_get_subchannels() {
+ local _netif
+ local _subchannels
+
+ _netif="$1"
+
+ _subchannels=$({
+ for i in /sys/class/net/"$_netif"/device/cdev[0-9]*; do
+ [ -e "$i" ] || continue
+ channel=$(readlink -f "$i")
+ printf -- "%s" "${channel##*/},"
+ done
+ })
+ [ -n "$_subchannels" ] || return 1
+
+ printf -- "%s" "${_subchannels%,}"
+}