#!/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=)" # ::: 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: . 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 60" /sbin/initqueue --finished ${BB} /lib/systemd/systemd-networkd-wait-online -i ${NETIF}