summaryrefslogtreecommitdiffstats
path: root/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh
diff options
context:
space:
mode:
Diffstat (limited to 'modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh')
-rw-r--r--modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh251
1 files changed, 251 insertions, 0 deletions
diff --git a/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh b/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh
new file mode 100644
index 00000000..a64e6efe
--- /dev/null
+++ b/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh
@@ -0,0 +1,251 @@
+#!/bin/bash
+#
+# This script was mostly stolen from 40network/parse-ip-opts.sh. It was
+# adapted to generate .network files for systemd-networkd using the IP
+# configuration from PXE/Syslinux in addition to domain, dns and hostname
+
+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"
+
+# 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-".
+ [ -n "${BOOTIF}" ] && [ ${#BOOTIF} -eq 20 ] && BOOTIF="${BOOTIF#???}"
+ BOOTIF="$(tr '-' ':' <<< $BOOTIF)"
+ readonly BOOTIF
+
+ ## KCL "ip": is expected in following format (syslinux IPAPPEND3):
+ declare -rg IPCONF="$(getarg ip=)"
+ # <CLIENT_IP>:<PXE_SERVER_IP>:<GATEWAY_IP>:<NETMASK>
+ 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
+
+
+ # Taken from parse-ip-opts.sh: Convert the netmask to CIDR notation
+ declare -g CIDR
+ if [[ "x$NETMASK" =~ ^x[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
+ CIDR=$(mask2cidr "$NETMASK")
+ elif [ -n "$NETMASK" -a "x${NETMASK//[0-9]/}" = 'x' ]; then
+ # The mask is already a prefix length (uint), so validate it
+ [[ "x$CLIENT_IP" == x*:*:* && "$NETMASK" -le 128 || "$NETMASK" -le 32 ]] && CIDR=$NETMASK
+ fi
+ readonly CIDR
+
+ # 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 || emergency_shell "VLAN mode detected - failed to load 8021q"
+ fi
+
+ # Bridged mode?
+ grep -wqE 'bridged' /proc/cmdline && declare -rg BRIDGED="y"
+}
+
+# Dumps the parsed IP configuration to /run/openslx/network.conf
+# Creates a list of "KEY=value" type variables to source later on
+save_ip_config() {
+ local IP_RUNTIME_CONFIG="/run/openslx/network.conf"
+ mkdir -p "${IP_RUNTIME_CONFIG%/*}" # dirname a la bash
+ cat <<-EOF > "${IP_RUNTIME_CONFIG}"
+ SLX_PXE_CLIENT_IP=${CLIENT_IP}
+ SLX_PXE_SERVER_IP=${SERVER_IP}
+ SLX_PXE_GATEWAY=${GATEWAY_IP}
+ SLX_PXE_DNS=${DNS}
+ SLX_PXE_MAC=${BOOTIF}
+ SLX_PXE_NETIF=${BOOTIF_NAME}
+ SLX_BRIDGE=${BRIDGED:+${BRIDGE_NAME}}
+ SLX_VLAN_ID=${VLAN}
+ SLX_DNS=${DNS}
+ SLX_HOSTNAME=${HOSTNAME}
+ SLX_NET_DOMAIN=${DOMAIN}
+ EOF
+}
+
+# Helper to echo the static configuration,
+# including DNS and Domain, if given per KCL
+print_static_ip_conf() {
+ [ $# -ne 2 ] && echo "Need 2 args: <ip/cidr> <gateway>. Given: $@" && return 1
+ echo "Address=${1}"
+ echo "Gateway=${2}"
+ [ -n "${DNS}" ] && echo "DNS=${DNS}"
+ [ -n "${DOMAIN}" ] && echo "Domains=${DOMAIN}"
+}
+
+# Create udev rule to rename the PXE boot interface to BOOTIF_NAME
+create_udev_bootif_name_rule() {
+ [ -z "${BOOTIF}" ] && echo "No BOOTIF set, was it present in the kernel command line?" && return 1
+ # 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
+}
+
+disable_ipv6() {
+ cat <<- EOF
+ LinkLocalAddressing=no
+ IPv6AcceptRA=no
+ EOF
+}
+
+dhcp_options() {
+ cat <<- EOF
+ [DHCP]
+ UseDNS=true
+ UseDomains=true
+ UseHostname=true
+ CriticalConnection=true
+ EOF
+}
+
+## Generate network and link file(s) for networkd
+# Checks if an IP configuration was forwarded by
+generate_networkd_config() {
+ mkdir -p "/etc/systemd/network"
+ local BOOTIF_NETWORK_CONF="/etc/systemd/network/${BOOTIF_NAME}.network"
+ (
+ echo '[Match]'
+ echo "MACAddress=${BOOTIF}"
+ echo "Path=pci*"
+ echo ''
+ # Currently the boot interface is configured to either:
+ # * static ip as given by the initial DHCP during PXE
+ # * bridged
+ # * vlan-tagged (ethernet interface not configured)
+ # In particular, configuring both vlan tags and the physical
+ # interface is not supported (might be needed in some setups)
+ echo '[Network]'
+ disable_ipv6
+ if [ -n "${BRIDGED}" ]; then
+ echo "Bridge=${BRIDGE_NAME}"
+ elif [ -n "${CLIENT_IP}" -a -n "${GATEWAY_IP}" ]; then
+ print_static_ip_conf "${CLIENT_IP}/${CIDR:-24}" "${GATEWAY_IP}"
+ elif [ -n "${VLAN}" ]; then
+ echo "VLAN=$BOOTIF_NAME.$VLAN"
+ else
+ echo -e "DHCP=ipv4\n"
+ dhcp_options
+ fi
+ ) > "${BOOTIF_NETWORK_CONF}"
+
+ if [ -n "$VLAN" ]; then
+ local VLAN_DEV_CONF="/etc/systemd/network/${BOOTIF_NAME}.${VLAN}.netdev"
+ cat <<- EOF > "${VLAN_DEV_CONF}"
+ [NetDev]
+ Name=${BOOTIF_NAME}.${VLAN}
+ MACAddress=${BOOTIF}
+ Kind=vlan
+
+ [VLAN]
+ Id=${VLAN}
+ EOF
+
+ # Configure the vlan tagged interface for DHCPv4, TODO IPv6
+ local VLAN_NETWORK_CONF="/etc/systemd/network/${BOOTIF_NAME}.${VLAN}.network"
+ cat <<- EOF > "${VLAN_NETWORK_CONF}"
+ [Match]
+ Name=${BOOTIF_NAME}.${VLAN}
+ Type=vlan
+
+ [Link]
+ MACAddress=${BOOTIF}
+
+ [Network]
+ DHCP=ipv4
+ EOF
+ (
+ disable_ipv6
+ echo ""
+ dhcp_options
+ ) >> "${VLAN_NETWORK_CONF}"
+ fi
+
+ # bridge setup
+ if [ -n "$BRIDGED" ]; then
+ local BRIDGE_DEV_CONF="/etc/systemd/network/${BRIDGE_NAME}.netdev"
+ cat <<- EOF > "${BRIDGE_DEV_CONF}"
+ [NetDev]
+ Name=${BRIDGE_NAME}
+ Kind=bridge
+ MACAddress=${BOOTIF}
+ EOF
+
+ local BRIDGE_NETWORK_CONF="/etc/systemd/network/${BRIDGE_NAME}.network"
+ (
+ echo '[Match]'
+ echo "Name=${BRIDGE_NAME}"
+ echo ""
+ echo '[Network]'
+ disable_ipv6
+ if [ -n "${CLIENT_IP}" -a -n "${GATEWAY_IP}" ]; then
+ print_static_ip_conf "${CLIENT_IP}/${CIDR:-24}" "${GATEWAY_IP}"
+ else
+ # bad/no IP info, fallback to DHCP
+ echo -e "DHCP=ipv4\n"
+ dhcp_options
+ fi
+ ) > "${BRIDGE_NETWORK_CONF}"
+ fi
+ return 0
+}
+
+# from parse-ip-opts.sh
+# Takes a netmask and outputs the corresponding CIDR
+# e.g.
+# mask2cidr 255.255.255.0
+# returns: 24
+mask2cidr() {
+ local -i bits=0
+ for octet in ${1//./ }; do
+ for i in {0..8}; do
+ [ "$octet" -eq $(( 256 - (1 << i) )) ] && bits+=$((8-i)) && break
+ done
+ [ $i -eq 8 -a "$octet" -ne 0 ] && warn "Bad netmask $mask" && return
+ [ $i -gt 0 ] && break
+ done
+ echo $bits
+}
+
+## MAIN ##
+# Get IP config from KCL
+parse_kernel_command_line
+
+# Save IP config for later use
+save_ip_config
+
+# Create the udev rule to rename the boot interface to the declared BOOTIF_NAME
+create_udev_bootif_name_rule || \
+ emergency_shell "Failed to create udev rule for boot interface renaming."
+
+# Generate config files for networkd
+generate_networkd_config || \
+ emergency_shell "Failed to generate networkd configuration."
+
+# Make dracut wait for network during the udev loop (initqueue) to make
+# sure we have network access in the pre-mount hook as it is needed
+# to get configurations and the root filesystem
+NETIF=
+[ -n "${BOOTIF_NAME}" ] && NETIF="${BOOTIF_NAME}"
+[ -n "${BRIDGED}" ] && [ -n "${BRIDGE_NAME}" ] && NETIF="${BRIDGE_NAME}"
+[ -n "${VLAN}" ] && NETIF="${BOOTIF_NAME}.${VLAN}"
+
+BB=$(command -v busybox)
+[ -n "${BB}" ] && BB+=" timeout -t 60"
+/sbin/initqueue --finished ${BB} /lib/systemd/systemd-networkd-wait-online -i ${NETIF}