diff options
Diffstat (limited to 'modules.d/systemd-networkd-ext')
4 files changed, 425 insertions, 0 deletions
diff --git a/modules.d/systemd-networkd-ext/hooks/configure-dhcp-for-newroot.sh b/modules.d/systemd-networkd-ext/hooks/configure-dhcp-for-newroot.sh new file mode 100644 index 00000000..b9803729 --- /dev/null +++ b/modules.d/systemd-networkd-ext/hooks/configure-dhcp-for-newroot.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# +# This hook creates a networkd configuration for the NEWROOT +# * DHCP configuration for the boot interface +# * Set static hostname via /etc/hostname +# This uses the configuration file generated by the parse-kcl-for-networkd.sh +# hook Note: on systemd v219, UseDomains is bugged and does not seem to do +# anything thus that option is complemented with the KCL's domain to hopefully +# garantee one of these will take effect. + +. "/run/openslx/network.conf" + +NETWORKD_DIR="/etc/systemd/network" +mkdir -p "${NEWROOT}/${NETWORKD_DIR}" + +# First, copy over every configuration made so far, +# excluding the physical interface if bridged +cp "${NETWORKD_DIR}/${SLX_BRIDGE}"* "${NEWROOT}/${NETWORKD_DIR}" + +# Now determine which network configuration we are running +# VLANs are already configured to DHCP, so nothing to do +if [ -z "${SLX_VLAN_ID}" ]; then + # In both bridged and non-bridged mode, we need to adjust + # the configuration to DHCP for stage4 + MATCH_LINE= + MATCH_LINE_EXTRA= + MAIN_NET_CONF= + if [ -n "${SLX_BRIDGE}" ]; then + MATCH_LINE="Name=${SLX_BRIDGE}" + MAIN_NETIF_CONF="${NETWORKD_DIR}/${SLX_BRIDGE}.network" + elif [ -n "${SLX_PXE_MAC}" ]; then + MATCH_LINE="MACAddress=${SLX_PXE_MAC}" + MATCH_LINE_EXTRA="Path=pci*" + MAIN_NETIF_CONF="${NETWORKD_DIR}/${SLX_PXE_NETIF}.network" + else + emergency_shell "Could not determine network configuration mode!" + fi + # backup current network config for later reference before overwritting it + if [ -e "${MAIN_NETIF_CONF}" ]; then + mv "${NEWROOT}/${MAIN_NETIF_CONF}" "${NEWROOT}/${MAIN_NETIF_CONF}.stage3" + else + emergency_shell "Failed to find main networkd configuration: ${MAIN_NETIF_CONF}" + fi + # Now generate a DHCP configuration for the main interface + cat <<-EOF > "${NEWROOT}/${MAIN_NETIF_CONF}" + [Match] + ${MATCH_LINE} + ${MATCH_LINE_EXTRA} + + [Network] + DHCP=ipv4 + LinkLocalAddressing=no + IPv6AcceptRA=no + + [DHCP] + UseDNS=true + UseDomains=true + UseHostname=true + CriticalConnection=true + EOF +fi + +# Workaround: UseHostname seems broken, so manually set the hostname for now... +[ -n "${SLX_HOSTNAME}" ] && echo "${SLX_HOSTNAME}" > $NEWROOT/etc/hostname diff --git a/modules.d/systemd-networkd-ext/hooks/copy-networkd-files-to-newroot.sh b/modules.d/systemd-networkd-ext/hooks/copy-networkd-files-to-newroot.sh new file mode 100644 index 00000000..ec61616c --- /dev/null +++ b/modules.d/systemd-networkd-ext/hooks/copy-networkd-files-to-newroot.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# +# This hook copies the core systemd-networkd related service +# files to the NEWROOT and activate them in sysinit.target +# to prevent systemd from killing these while pivot_root +NEWROOT_SYSTEMD_DIR="${NEWROOT}/usr/lib/systemd/system" +NEWROOT_SYSINIT_TARGET_DIR="${NEWROOT_SYSTEMD_DIR}/sysinit.target.wants" +mkdir -p "${NEWROOT_SYSINIT_TARGET_DIR}" + +for UNIT in \ + systemd-networkd.socket \ + systemd-networkd.service \ + systemd-resolved.service +do + UNIT_PATH="$(systemctl show -p FragmentPath $UNIT | cut -c 14-)" + [ -z "${UNIT_PATH}" ] && continue + mkdir -p "${NEWROOT}/${UNIT_PATH%/*}" + cp "${UNIT_PATH}" "${NEWROOT}/${UNIT_PATH}" + ln -sf "${UNIT_PATH}" "${NEWROOT_SYSINIT_TARGET_DIR}/${UNIT}" +done + +if [ -e "${NEWROOT_SYSTEMD_DIR}/systemd-resolved.service" ]; then + # this drop-in is needed to prevent a pull-in loop sysinit.target <-> basic.target + mkdir -p "${NEWROOT_SYSTEMD_DIR}/systemd-resolved.service.d/" + cat <<-EOF > "${NEWROOT_SYSTEMD_DIR}/systemd-resolved.service.d/00-no-default-deps.conf" + [Unit] + DefaultDependencies=no + EOF + + # finally make sure the NEWROOT resolv.conf links to the one managed by resolved. + rm "${NEWROOT}"/etc/resolv.conf + ln -s /run/systemd/resolve/resolv.conf "${NEWROOT}"/etc/resolv.conf +fi 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} diff --git a/modules.d/systemd-networkd-ext/module-setup.sh b/modules.d/systemd-networkd-ext/module-setup.sh new file mode 100755 index 00000000..be50973d --- /dev/null +++ b/modules.d/systemd-networkd-ext/module-setup.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# shameless hacked version of dracut's systemd-networkd module +# called by dracut +check() { + [[ $mount_needs ]] && return 1 + + if dracut_module_included "network"; then + derror "The module 'systemd-networkd' conflicts with 'network'. Use either one not both!" + return 1 + fi + return 255 +} + +# called by dracut +depends() { + echo "systemd kernel-network-modules" +} + +installkernel() { + return 0 +} + +# called by dracut +install() { + # need initqueue to get a clean network start + dracut_need_initqueue + + # install required systemd units + inst_multiple -o \ + $systemdutildir/systemd-networkd \ + $systemdutildir/systemd-networkd-wait-online \ + $systemdutildir/systemd-resolved \ + $systemdsystemunitdir/systemd-networkd-wait-online.service \ + $systemdsystemunitdir/systemd-networkd.service \ + $systemdsystemunitdir/systemd-networkd.socket \ + $systemdsystemunitdir/systemd-resolved.service \ + networkctl ip + + # active them + for i in \ + systemd-networkd-wait-online.service \ + systemd-networkd.service \ + systemd-networkd.socket \ + systemd-resolved.service + do + systemctl --root "$initdir" enable "$i" + done + mkdir $initdir/$systemdsystemunitdir/network.target.wants + ln -s $systemdsystemunitdir/systemd-resolved.service \ + $initdir/$systemdsystemunitdir/network.target.wants/systemd-resolved.service + ln -sf /run/systemd/resolve/resolv.conf $initdir/etc/resolv.conf + + # create system users needed + for user in \ + systemd-network \ + systemd-resolve + do + grep "^${user}:" /etc/passwd 2>/dev/null >> "$initdir/etc/passwd" + grep "^${user}:" /etc/group >> "$initdir/etc/group" + done + + # install basic dns libs + _arch=$(uname -m) + inst_libdir_file {"tls/$_arch/",tls/,"$_arch/",}"libnss_dns.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libnss_mdns4_minimal.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libnss_myhostname.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libnss_resolve.so.*" \ + {"tls/$_arch/",tls/,"$_arch/",}"libresolve.so.*" + + # the hook script that will parse the "ip=" kernel command line + # parameter and generate the .link and .network files needed + # by systemd-networkd. + inst_hook cmdline 10 "$moddir/hooks/parse-kcl-for-networkd.sh" + inst_hook pre-pivot 10 "$moddir/hooks/copy-networkd-files-to-newroot.sh" + inst_hook pre-pivot 20 "$moddir/hooks/configure-dhcp-for-newroot.sh" +} + |