From 092ec5bd8ea2f1913543e85287e143fe5b139103 Mon Sep 17 00:00:00 2001 From: Jonathan Bauer Date: Tue, 28 Jul 2020 14:57:16 +0200 Subject: [slx-network] new style KCL --- .../slx-network/hooks/parse-ipxe-network-kcl.sh | 146 ++++++++++++--------- .../scripts/setup-bootif-network.stage3 | 26 +++- .../slx-network/scripts/udhcpc-trigger.stage3 | 34 +++-- 3 files changed, 125 insertions(+), 81 deletions(-) diff --git a/modules.d/slx-network/hooks/parse-ipxe-network-kcl.sh b/modules.d/slx-network/hooks/parse-ipxe-network-kcl.sh index 56abb680..3dfd7edd 100644 --- a/modules.d/slx-network/hooks/parse-ipxe-network-kcl.sh +++ b/modules.d/slx-network/hooks/parse-ipxe-network-kcl.sh @@ -1,89 +1,106 @@ #!/bin/bash # -# TODO VLAN support +# Script parsing the kernel command line to get the IP configuration from +# (i)PXE. Supports either the older +# ip=::: format or our new format +# consisting of 'ipv4.' key/value pairs. Valid attributes are: ip, router, +# dns, hostname, domain, search, if, ntpsrv, subnet. command -v getarg >/dev/null || . /lib/dracut-lib.sh # static names for the boot interface and its bridge -declare -rg BOOTIF_NAME="boot0" -declare -rg BRIDGE_NAME="br0" -declare -rg RUNTIME_CONF="/run/openslx/network.conf" -mkdir -p "${RUNTIME_CONF%/*}" -# Get all the ip-related arguments from the KCL -parse_kernel_command_line() { - ## KCL "BOOTIF": MAC address of the interface that DHCP'ed during PXE - declare -g BOOTIF="$(getarg BOOTIF=)" - # Remove the hardware type prefix of BOOTIF if it has 20 chars to get - # the plain MAC address. The hardware type prefix has length 3, e.g. "01-". - if [ -n "${BOOTIF}" ] && [ ${#BOOTIF} -eq 20 ]; then - BOOTIF="${BOOTIF#???}" - BOOTIF="${BOOTIF//-/:}" +declare -rg bootif_name="boot0" +declare -rg bridge_name="br0" + +# parse old syslinux style 'ip=...' and 'BOOTIF=...' parameters +parse_kcl_ip() { + declare -g if="$(getarg BOOTIF=)" + if [ -n "$if" ] && [ ${#if} -eq 20 ]; then + if="${if#???}" + if="${if//-/:}" fi - readonly BOOTIF - - ## KCL "ip": is expected in following format (syslinux IPAPPEND3): - declare -rg IPCONF="$(getarg ip=)" - # ::: - declare -g CLIENT_IP= - declare -g SERVER_IP= - declare -g GATEWAY_IP= - declare -g NETMASK= - read -r CLIENT_IP SERVER_IP GATEWAY_IP NETMASK \ - <<< $( awk -F: '{print $1" "$2" "$3" "$4}' <<< "${IPCONF}" ) - readonly CLIENT_IP SERVER_IP GATEWAY_IP NETMASK - - # Calculate routing prefix from netmask - declare -rg ROUTING_PREFIX="$(ipcalc -s -p "$CLIENT_IP" "$NETMASK")" - - # KCL "hostname" - declare -rg HOSTNAME="$(getarg hostname=)" - [ -n "$HOSTNAME" ] && echo "$HOSTNAME" > /proc/sys/kernel/hostname - - # KCL "dns" - declare -rg DNS="$(getarg dns=)" - - # KCL "domain" - declare -rg DOMAIN="$(getarg domain=)" - - # VLAN tag - declare -rg VLAN="$(getarg vlan=)" - if [ -n "$VLAN" ]; then - modprobe 8021q || warn "VLAN mode detected but failed to load 8021q!" + local ip_line="$(getarg ip=)" + [ -z "$ip_line" ] && return 0 + read -r ip bootsrv router subnet \ + <<< $( awk -F: '{print $1" "$2" "$3" "$4}' <<< "${ip_line}" ) + declare -g ip bootsrv router subnet +} + +# parse new style 'ipv4.*=...' parameters +parse_kcl_ipv4() { + for param in ip router dns hostname domain search if ntpsrv subnet; do + echo "Getting $param..." + local current="$(getarg ipv4.${param}=)" + [ -z "$current" ] && continue + declare -g "${param}=${current}" + done +} + +parse_kcl() { + # we assume (and we should) that both variants contain the + # same information if they are present simultaneously. + parse_kcl_ip + parse_kcl_ipv4 + + # if not boot server was given, use slxsrv + if [ -z "$bootsrv" ]; then + kclsrv="$(getarg slxsrv=)" + [ -n "$kclsrc" ] && bootsrv="$kclsrv" fi - # Bridged mode? - grep -wqE 'bridged' /proc/cmdline && declare -rg BRIDGED="y" + # calculate network mask + declare -g mask="$(ipcalc -s -p "$ip" "$subnet" | sed 's/.*=//')" + + # vlan specified? + declare -g vlan="$(getarg vlan=)" + + # Bridged the boot interface? + grep -wqE 'bridged' /proc/cmdline && declare -g bridged="y" + + # backwards compat for old style hostname/dns/domain + for conf in hostname dns domain; do + conf_value="$(getarg ${conf}=)" + if [ -n "$conf_value" ]; then + declare -g "${conf}=${conf_value}" + fi + done } save_network_config() { - cat <<-EOF > "${RUNTIME_CONF}" - SLX_PXE_CLIENT_IP='${CLIENT_IP}' - SLX_PXE_SERVER_IP='${SERVER_IP}' - SLX_PXE_GATEWAY='${GATEWAY_IP}' - SLX_PXE_NETMASK='${NETMASK}' - SLX_PXE_MAC='${BOOTIF}' - SLX_PXE_NETIF='${BOOTIF_NAME}' - SLX_PXE_DNS='${DNS}' - SLX_PXE_HOSTNAME='${HOSTNAME}' - SLX_PXE_DOMAIN='${DOMAIN}' - SLX_BRIDGE='${BRIDGED:+${BRIDGE_NAME}}' - SLX_VLAN_ID='${VLAN}' + declare -rg network_conf="/run/openslx/network.conf" + mkdir -p "${network_conf%/*}" + cat <<-EOF > "$network_conf" + SLX_PXE_CLIENT_IP='$ip' + SLX_PXE_SERVER_IP='$bootsrv' + SLX_PXE_GATEWAY='$router' + SLX_PXE_NETMASK='$mask' + SLX_PXE_SUBNET='$subnet' + SLX_PXE_MAC='$if' + SLX_PXE_NETIF='$bootif_name' + SLX_PXE_DNS='${dns//,/ }' + SLX_PXE_HOSTNAME='$hostname' + SLX_PXE_DOMAIN='${search//,/ }' + SLX_PXE_NTP='${ntpsrv//,/ }' + SLX_BRIDGE='${bridged:+${bridge_name}}' + SLX_VLAN_ID='$vlan' EOF } # Create udev rule to rename the PXE boot interface to BOOTIF_NAME create_udev_bootif_name_rule() { - if [ -z "${BOOTIF}" ]; then - echo "No BOOTIF set, was it present in the kernel command line?" + if [ -z "$if" ]; then + echo "MAC address of boot interface not set!" return 1 fi # priority 70 < 80-net-name-slot.rules. - echo 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="'${BOOTIF}'", NAME="'${BOOTIF_NAME}'"' > /etc/udev/rules.d/70-pxe-boot-interface.rules + echo 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="'$if'", NAME="'$bootif_name'"' > /etc/udev/rules.d/70-pxe-boot-interface.rules } ## MAIN ## -# Get IP config from KCL -parse_kernel_command_line +parse_kcl + +# set hostname asap +[ -n "$hostname" ] && echo "$hostname" > /proc/sys/kernel/hostname # Save network config for later use save_network_config & @@ -91,5 +108,8 @@ save_network_config & # Create the udev rule to rename the boot interface to the declared BOOTIF_NAME create_udev_bootif_name_rule & +wait + +# TODO handle case where the MAC address of the boot interface was not found /sbin/initqueue --settled /usr/local/bin/setup-bootif-network /sbin/initqueue --finished [ -e "/.network" ] diff --git a/modules.d/slx-network/scripts/setup-bootif-network.stage3 b/modules.d/slx-network/scripts/setup-bootif-network.stage3 index 50718894..74d1c2b5 100755 --- a/modules.d/slx-network/scripts/setup-bootif-network.stage3 +++ b/modules.d/slx-network/scripts/setup-bootif-network.stage3 @@ -80,14 +80,26 @@ if [ -n "$SLX_BRIDGE" ]; then done fi -# finally add the IP address on the main NIC -if [ -n "$SLX_PXE_CLIENT_IP" ]; then - ip addr add \ - "${SLX_PXE_CLIENT_IP}/$(ipcalc -s -p "$SLX_PXE_CLIENT_IP" "$SLX_PXE_NETMASK" | sed "s/.*=//")" \ - broadcast "$(ipcalc -s -b "$SLX_PXE_CLIENT_IP" "$SLX_PXE_NETMASK" | sed "s/.*=//")" \ - dev "$MAIN_NETIF" +# Finally add the IP address on the main NIC. +# Note: we should never have ip & netmask without the other. +if [ -n "$SLX_PXE_CLIENT_IP" ] && [ -n "$SLX_PXE_NETMASK" ]; then + ip addr add "${SLX_PXE_CLIENT_IP}/${SLX_PXE_NETMASK}" dev "$MAIN_NETIF" + + # now if we got everything from the KCL and skip dhcp if so + if [ -n "$SLX_PXE_GATEWAY" ] && [ -n "$SLX_PXE_DNS" ]; then + info "Got network configuration from KCL, skipping DHCP." + if ! interface="$MAIN_NETIF" \ + ip="$SLX_PXE_CLIENT_IP" \ + router="$SLX_PXE_GATEWAY" \ + dns="$SLX_PXE_DNS" \ + /usr/local/bin/udhcpc-trigger bound; then + warn "Failed to launch DHCP trigger with KCL configuration - will DHCP." + fi + exit 0 + fi fi +## DHCP additional_opts=() # we need to send the same UID (without '-') as the PXE firmware did, so use the plain @@ -106,6 +118,7 @@ fi # DHCP options to request request_opts=("-O" "hostname") +request_opts+=("-O" "dns") request_opts+=("-O" "ntpsrv") request_opts+=("-O" "domain") request_opts+=("-O" "wpad") @@ -131,3 +144,4 @@ set +x } &>> "/run/openslx/initramfs-network.log.$$" + diff --git a/modules.d/slx-network/scripts/udhcpc-trigger.stage3 b/modules.d/slx-network/scripts/udhcpc-trigger.stage3 index d54939a8..27c0f22a 100755 --- a/modules.d/slx-network/scripts/udhcpc-trigger.stage3 +++ b/modules.d/slx-network/scripts/udhcpc-trigger.stage3 @@ -32,26 +32,25 @@ if [ -n "$SLX_PXE_CLIENT_IP" ]; then #...some address is already configured... if [ "x${SLX_PXE_CLIENT_IP}" != "x${ip}" ]; then #...it's a different one, reconfigure... - echo "..reconfiguring ${SLX_PXE_CLIENT_IP} to ${ip}.." + echo "..reconfiguring '${SLX_PXE_CLIENT_IP}' to '${ip}'." # remove default route and let it be added again below, as it might get lost when changing the primary address on the interface ip route del default 2>/dev/null ip addr del "$SLX_PXE_CLIENT_IP" dev "${interface}" 2>/dev/null ip addr add "${ip}/$(ipcalc -s -p "${ip}" "${subnet}" | sed s/.*=//)" dev "${interface}" + echo "SLX_DHCP_CLIENT_IP='$ip'" >> "$NETWORK_CONF" fi else #...no address configured yet, just add... echo "..adding ${ip}.." ip addr add "${ip}/$(ipcalc -s -p "${ip}" "${subnet}" | sed s/.*=//)" dev "${interface}" fi -echo "SLX_DHCP_CLIENT_IP='$ip'" >> "$NETWORK_CONF" # Same procedure for default gateway -if [ -n "${router}" ]; then - if [ -s "$SLX_PXE_GATEWAY" ]; then - if [ "x${SLX_PXE_GATEWAY}" != "x${router}" ]; then - echo "..reconfiguring default gw from ${SLX_PXE_GATEWAY} to ${router}.." - ip route replace default via "$router" - fi +if [ -n "$router" ]; then + if [ -n "$SLX_PXE_GATEWAY" ] && [ "$SLX_PXE_GATEWAY" != "$router" ]; then + echo "..reconfiguring default gw from '$SLX_PXE_GATEWAY' to '$router'." + ip route del default 2>/dev/null + ip route add default via "$router" else ip route add default via "$router" fi @@ -65,21 +64,30 @@ fi rm -f -- "/etc/resolv.conf" +reverse_lookup() { + [ -z "$dns" ] && return + [ -z "$1" ] && return + timeout -t 3 nslookup "$1" | grep -E "^Address +[0-9]+: +$1 " | head -n 1 | awk '{print $4}' +} + +echo "# From DHCP in stage 3.1" >> "$NETWORK_CONF" + # DNS/domain? if [ -n "$dns" ]; then echo "..got DNS.." - echo "# From DHCP in stage 3.1" >> "$NETWORK_CONF" echo "SLX_DNS='$dns'" >> "$NETWORK_CONF" fi for srv in $dns; do echo "nameserver $srv" >> "/etc/resolv.conf" done + if [ -z "$domain" ]; then # try to get domain via reverse lookup if empty echo "..trying to get domain via DNS, as DHCP didn't supply one.." - fqdn=$(timeout -t 3 nslookup "$ip" | grep -E "^Address +[0-9]+: +$ip " | head -n 1 | awk '{print $4}') + fqdn="$(reverse_lookup "$ip")" domain="${fqdn#*.}" fi + # Add domain to list of search domains if not in there yet if [ -n "$domain" ] && [ -n "$search" ]; then FOUND=no @@ -90,11 +98,13 @@ if [ -n "$domain" ] && [ -n "$search" ]; then elif [ -n "$domain" ]; then search="$domain" fi + # Write out if [ -n "$domain" ]; then echo "domain $domain" >> "/etc/resolv.conf" echo "SLX_NET_DOMAIN='$domain'" >> "/run/openslx/network.conf" fi + if [ -n "$search" ]; then echo "search $search" >> "/etc/resolv.conf" echo "SLX_NET_SEARCH='$search'" >> "/run/openslx/network.conf" @@ -113,7 +123,7 @@ else if [ -z "$hostname" ]; then # as with domain, if there's no hostname, try to get via DNS echo "..trying to get hostname via DNS, as DHCP didn't supply one.." - [ -z "$fqdn" ] && fqdn=$(timeout -t 3 nslookup "$ip" | grep -E "^Address +[0-9]+: +$ip " | head -n 1 | awk '{print $4}') + [ -z "$fqdn" ] && fqdn="$(reverse_lookup "$ip")" hostname="${fqdn%%.*}" elif [ -n "$domain" ]; then fqdn="${hostname}.${domain%% *}" # in case domain is a list @@ -132,7 +142,7 @@ if [ -n "$hostname" ]; then echo "$hostname" > "/etc/hostname" echo "127.0.0.1 localhost" > "/etc/hosts" echo "127.0.1.1 $fqdn $hostname" >> "/etc/hosts" - echo "SLX_HOSTNAME='$hostname'" >> "/run/openslx/network.conf" + echo "SLX_HOSTNAME='$hostname'" >> "$NETWORK_CONF" fi touch /.network -- cgit v1.2.3-55-g7522