Blob Blame History Raw
From ceca74ccc397795db68ca6ffbe49d65af2178a50 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.de>
Date: Sat, 11 Jul 2020 00:15:34 +0200
Subject: [PATCH] dracut-functions: add ip_params_for_remote_addr() helper

This helper function takes a remote IP address, and tries to
determine the dracut command line arguments ip= and ifname= that
will make this remote address reachable during boot.

Functionality was taken from the module-setup.sh scripts of 95iscsi and 95nfs,
cleaned up and fixed some issues in particular with statically configured
networks, where the old code would print the unsupported string
"$ifname:static".
---
 dracut-functions.sh | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)

diff --git a/dracut-functions.sh b/dracut-functions.sh
index b5c28248..07ae88c0 100755
--- a/dracut-functions.sh
+++ b/dracut-functions.sh
@@ -728,3 +728,117 @@ btrfs_devs() {
         printf -- "%s\n" "$_dev"
         done
 }
+
+iface_for_remote_addr() {
+    set -- $(ip -o route get to "$1")
+    echo $3
+}
+
+local_addr_for_remote_addr() {
+    set -- $(ip -o route get to "$1")
+    echo $5
+}
+
+peer_for_addr() {
+    local addr=$1
+    local qtd
+
+    # quote periods in IPv4 address
+    qtd=${addr//./\\.}
+    ip -o addr show | \
+        sed -n 's%^.* '"$qtd"' peer \([0-9a-f.:]\{1,\}\(/[0-9]*\)\?\).*$%\1%p'
+}
+
+netmask_for_addr() {
+    local addr=$1
+    local qtd
+
+    # quote periods in IPv4 address
+    qtd=${addr//./\\.}
+    ip -o addr show | sed -n 's,^.* '"$qtd"'/\([0-9]*\) .*$,\1,p'
+}
+
+gateway_for_iface() {
+    local ifname=$1 addr=$2
+
+    case $addr in
+        *.*) proto=4;;
+        *:*) proto=6;;
+        *)   return;;
+    esac
+    ip -o -$proto route show | \
+        sed -n "s/^default via \([0-9a-z.:]\{1,\}\) dev $ifname .*\$/\1/p"
+}
+
+# This works only for ifcfg-style network configuration!
+bootproto_for_iface() {
+    local ifname=$1
+    local dir
+
+    # follow ifcfg settings for boot protocol
+    for dir in network-scripts network; do
+        [ -f "/etc/sysconfig/$dir/ifcfg-$ifname" ] && {
+            sed -n "s/BOOTPROTO=[\"']\?\([[:alnum:]]\{1,\}\)[\"']\?.*\$/\1/p" \
+                "/etc/sysconfig/$dir/ifcfg-$ifname"
+            return
+        }
+    done
+}
+
+is_unbracketed_ipv6_address() {
+    strglob "$1" '*:*' && ! strglob "$1" '\[*:*\]'
+}
+
+# Create an ip= string to set up networking such that the given
+# remote address can be reached
+ip_params_for_remote_addr() {
+    local remote_addr=$1
+    local ifname local_addr peer netmask= gateway ifmac
+
+    [[ $remote_addr ]] || return 1
+    ifname=$(iface_for_remote_addr "$remote_addr")
+    [[ $ifname ]] || {
+        berror "failed to determine interface to connect to $remote_addr"
+        return 1
+    }
+
+    # ifname clause to bind the interface name to a MAC address
+    if [ -d "/sys/class/net/$ifname/bonding" ]; then
+        dinfo "Found bonded interface '${ifname}'. Make sure to provide an appropriate 'bond=' cmdline."
+    elif [ -e "/sys/class/net/$ifname/address" ] ; then
+        ifmac=$(cat "/sys/class/net/$ifname/address")
+        [[ $ifmac ]] && printf 'ifname=%s:%s ' "${ifname}" "${ifmac}"
+    fi
+
+    bootproto=$(bootproto_for_iface "$ifname")
+    case $bootproto in
+        dhcp|dhcp6|auto6) ;;
+        dhcp4)
+            bootproto=dhcp;;
+        static*|"")
+            bootproto=;;
+        *)
+            derror "bootproto \"$bootproto\" is unsupported by dracut, trying static configuration"
+            bootproto=;;
+    esac
+    if [[ $bootproto ]]; then
+        printf 'ip=%s:%s ' "${ifname}" "${bootproto}"
+    else
+        local_addr=$(local_addr_for_remote_addr "$remote_addr")
+        [[ $local_addr ]] || {
+            berror "failed to determine local address to connect to $remote_addr"
+            return 1
+        }
+        peer=$(peer_for_addr "$local_addr")
+        # Set peer or netmask, but not both
+        [[ $peer ]] || netmask=$(netmask_for_addr "$local_addr")
+        gateway=$(gateway_for_iface "$ifname" "$local_addr")
+        # Quote IPv6 addresses with brackets
+        is_unbracketed_ipv6_address "$local_addr" && local_addr="[$local_addr]"
+        is_unbracketed_ipv6_address "$peer" && peer="[$peer]"
+        is_unbracketed_ipv6_address "$gateway" && gateway="[$gateway]"
+        printf 'ip=%s:%s:%s:%s::%s:none ' \
+               "${local_addr}" "${peer}" "${gateway}" "${netmask}" "${ifname}"
+    fi
+
+}