diff options
author | Jonathan Bauer | 2017-09-22 11:00:34 +0200 |
---|---|---|
committer | Jonathan Bauer | 2017-09-22 11:00:34 +0200 |
commit | d7d92a6fbf097fb34075b3e1d57ef547a209e362 (patch) | |
tree | f4c26d6a9584fb110792588f9aebedd22658c83e | |
parent | [conf-tgz] add gzip (diff) | |
download | systemd-init-d7d92a6fbf097fb34075b3e1d57ef547a209e362.tar.gz systemd-init-d7d92a6fbf097fb34075b3e1d57ef547a209e362.tar.xz systemd-init-d7d92a6fbf097fb34075b3e1d57ef547a209e362.zip |
reworked systemd-networkd module
* support new KCL arguments: domain, dns, hostname
* fallback to DHCP configuration during stage3, if IP config is missing
* copies the unit files to NEWROOT to ensure they present
* configures boot interface to do DHCP within stage4 (this is to get
proper nameservers and domain from the DHCP - currently bugged due to
systemd version afaik)
4 files changed, 233 insertions, 101 deletions
diff --git a/builder/modules.d/systemd-networkd-ext/hooks/configure-dhcp-for-newroot.sh b/builder/modules.d/systemd-networkd-ext/hooks/configure-dhcp-for-newroot.sh new file mode 100644 index 00000000..a0e876a4 --- /dev/null +++ b/builder/modules.d/systemd-networkd-ext/hooks/configure-dhcp-for-newroot.sh @@ -0,0 +1,27 @@ +#!/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. +mkdir -p "$NEWROOT/etc/systemd/network/" +new_network_conf="$NEWROOT/etc/systemd/network/10-pxe-interface.network" +echo '[Match]' > $new_network_conf +grep "MACAddress=" /etc/systemd/network/*boot*.network >> $new_network_conf +echo '' >> $new_network_conf +echo '[Network]' >> $new_network_conf +echo 'DHCP=ipv4' >> $new_network_conf +grep "Domains=" /etc/systemd/network/*boot*.network >> $new_network_conf +echo '' >> $new_network_conf +echo '[DHCP]' >> $new_network_conf +echo 'UseDNS=true' >> $new_network_conf +echo 'UseDomains=true' >> $new_network_conf +echo 'UseHostname=true' >> $new_network_conf +echo 'CriticalConnection=true' >> $new_network_conf + +# Similarly, UseHostname seems to have no effect, so work around for now... +cat /proc/sys/kernel/hostname > $NEWROOT/etc/hostname diff --git a/builder/modules.d/systemd-networkd-ext/hooks/copy-networkd-files-to-newroot.sh b/builder/modules.d/systemd-networkd-ext/hooks/copy-networkd-files-to-newroot.sh new file mode 100644 index 00000000..9510c8d3 --- /dev/null +++ b/builder/modules.d/systemd-networkd-ext/hooks/copy-networkd-files-to-newroot.sh @@ -0,0 +1,28 @@ + +#!/bin/bash +# +# This hook copies the core systemd-networkd related service +# to the NEWROOT and activated them in sysinit.target +# to prevent systemd from killing these while pivot_root +new_sysinit_wants="${NEWROOT}/usr/lib/systemd/system/sysinit.target.wants/" +mkdir -p "${new_sysinit_wants}" + +for unit in \ + systemd-networkd.socket \ + systemd-networkd.service \ + systemd-networkd-wait-online.service \ + systemd-resolved.service +do + service_path="$(systemctl show -p FragmentPath $unit | cut -c 14-)" + cp "${service_path}" "${NEWROOT}/${service_path}" + ln -sf "../${unit}" "${new_sysinit_wants}/${unit}" +done + +# this drop-in is needed to prevent a pull-in loop sysinit.target <-> basic.target +mkdir -p "${NEWROOT}/usr/lib/systemd/system/systemd-resolved.service.d/" +echo '[Unit]' > "${NEWROOT}/usr/lib/systemd/system/systemd-resolved.service.d/00-no-default-deps.conf" +echo 'DefaultDependencies=no' >> "${NEWROOT}/usr/lib/systemd/system/systemd-resolved.service.d/00-no-default-deps.conf" + +# 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 diff --git a/builder/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh b/builder/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh index cdf080a3..0f9c698f 100644 --- a/builder/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh +++ b/builder/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh @@ -1,98 +1,148 @@ #!/bin/bash # -# This script was mostly stolen from 40network/parse-ip-opts.sh. Its -# actions are adapted to write .network files to /etc/systemd/network -# in the initramfs instead o +# 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 -main_work() { - local IPCONF="$(getarg ip=)" - local BOOTIF="$(getarg BOOTIF=)" +# static names for the boot interface and its bridge +# TODO could be configurable from the KCL +declare -rg BOOTIF_NAME="boot0" +declare -rg BRIDGE_NAME="br0" +#declare -rg BRIDGED="bridged" - if [ -z "${IPCONF}" ] && [ -z "${BOOTIF}" ]; then - # No ip= argument(s) given by PXE, TODO try dhcp - return; - fi +# 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 - # ip= is expected in following format (syslinux IPAPPEND3): + ## KCL "ip": is expected in following format (syslinux IPAPPEND3): + declare -rg IPCONF="$(getarg ip=)" # <CLIENT_IP>:<PXE_SERVER_IP>:<GATEWAY_IP>:<NETMASK> - local CLIENT_IP= - local SERVER_IP= - local GATEWAY_IP= - local 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 - # BOOTIF= contains the MAC address of the boot interface - # and a hardware type prefix of length 2, e.g. "01-" - # So if BOOTIF has 20 chars, then we got the prefix. - # This needs to be stripped to get the MAC address only - [ -n "${BOOTIF}" ] && [ ${#BOOTIF} -eq 20 ] && BOOTIF="${BOOTIF#???}" - BOOTIF="$(tr '-' ':' <<< $BOOTIF)" - - # TODO sanity checks - - # Taken from parse-ip-opts.sh - # Convert the netmask to CIDR notation + # 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 - # Generate network and link file for networkd - NETWORK_FILE=/etc/systemd/network/01-boot-lan.network - mkdir -p $(dirname $NETWORK_FILE) - echo '[Match]' > $NETWORK_FILE - echo 'Name=bootnet' >> $NETWORK_FILE - # Hardcode bootnet as our boot interface - [ -n "$BOOTIF" ] && echo "MACAddress=$BOOTIF" >> $NETWORK_FILE - echo '[Network]' >> $NETWORK_FILE - echo 'Bridge=br0' >> $NETWORK_FILE - - ## Boot interface has 'bootnet' as dedicated name - LINK_FILE=/etc/systemd/network/00-boot-lan.link - echo '[Match]' > $LINK_FILE - [ -n "$BOOTIF" ] && echo "MACAddress=$BOOTIF" >> $LINK_FILE - echo '[Link]' >> $LINK_FILE - echo "Name=bootnet" >> $LINK_FILE - - # HACK pro bridge setup - BRIDGE_DEV_CONF=/etc/systemd/network/10-bridge-lan.netdev - echo '[NetDev]' > $BRIDGE_DEV_CONF - echo 'Name=br0' >> $BRIDGE_DEV_CONF - echo 'Kind=bridge' >> $BRIDGE_DEV_CONF - - BRIDGE_IF_CONF=/etc/systemd/network/11-bridge-lan.network - echo '[Match]' > $BRIDGE_IF_CONF - echo "Name=br0" >> $BRIDGE_IF_CONF - echo '[Network]' >> $BRIDGE_IF_CONF - [ -n "$CLIENT_IP" ] && echo "Address=${CLIENT_IP}/${CIDR:-24}" >> $BRIDGE_IF_CONF - [ -n "$GATEWAY_IP" ] && echo "Gateway=${GATEWAY_IP}" >> $BRIDGE_IF_CONF + # KCL "dns" + declare -rg DNS="$(getarg dns=)" + # KCL "domain" + declare -rg DOMAIN="$(getarg domain=)" +} +# Helper to echo a mac address matching [Match] section +print_match_mac() { + [ $# -ne 1 ] && echo "No MAC given!" && return 1 + echo '[Match]' + echo "MACAddress=${1}" +} +# 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_bootif_udev_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", ATTR{address}=="'${BOOTIF}'\ + ", NAME="'${BOOTIF_NAME}'"' > /etc/udev/rules.d/70-pxe-boot-interface.rules + +} + +## 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/01-boot-lan.network" + print_match_mac "${BOOTIF}" > "${BOOTIF_NETWORK_CONF}" + echo '[Network]' >> "${BOOTIF_NETWORK_CONF}" + if [ -n "${BRIDGED}" ]; then + echo "Bridge=${BRIDGE_NAME}" >> "${BOOTIF_NETWORK_CONF}" + elif [ -n "${CLIENT_IP}" -a -n "${GATEWAY_IP}" ]; then + print_static_ip_conf "${CLIENT_IP}/${CIDR:-24}" "${GATEWAY_IP}" >> "${BOOTIF_NETWORK_CONF}" + else + echo "DHCP=ipv4" >> "${BRIDGE_IF_CONF}" + fi + # we're done unless running in bridged mode + [ -n "${BRIDGED}" ] || return 0 + + # bridge setup + local BRIDGE_DEV_CONF="/etc/systemd/network/10-bridge-lan.netdev" + echo '[NetDev]' > "${BRIDGE_DEV_CONF}" + echo "Name=${BRIDGE_NAME}" >> "${BRIDGE_DEV_CONF}" + echo 'Kind=bridge' >> "${BRIDGE_DEV_CONF}" + + local BRIDGE_NETWORK_CONF="/etc/systemd/network/11-bridge-lan.network" + echo '[Match]' > "${BRIDGE_NETWORK_CONF}" + echo "Name=${BRIDGE_NAME}" >> "${BRIDGE_NETWORK_CONF}" + echo '[Network]' >> "${BRIDGE_NETWORK_CONF}" + if [ -n "${CLIENT_IP}" -a -n "${GATEWAY_IP}" ]; then + print_static_ip_conf "${CLIENT_IP}/${CIDR:-24}" "${GATEWAY_IP}" >> "${BRIDGE_NETWORK_CONF}" + else + # bad/no IP info, fallback to DHCP + echo "DHCP=ipv4" >> "${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 -function 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 +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_work +## MAIN ## +# Get IP config from KCL +parse_kernel_command_line + +# Create the udev rule to rename the boot interface to the declared BOOTIF_NAME +rename_boot_if -# HACK to wait for network during udev loop... -touch /lib/dracut/need-initqueue -/sbin/initqueue --finished /bin/busybox timeout -t 10 /lib/systemd/systemd-networkd-wait-online -i br0 +# Generate config files for networkd +generate_networkd_config +# 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}" +/sbin/initqueue --finished /bin/busybox timeout -t 10 /lib/systemd/systemd-networkd-wait-online -i "${NETIF}" diff --git a/builder/modules.d/systemd-networkd-ext/module-setup.sh b/builder/modules.d/systemd-networkd-ext/module-setup.sh index 3b3aabd6..fd7dc537 100755 --- a/builder/modules.d/systemd-networkd-ext/module-setup.sh +++ b/builder/modules.d/systemd-networkd-ext/module-setup.sh @@ -1,49 +1,76 @@ #!/bin/bash - +# shameless hacked version of dracut's systemd-networkd module # called by dracut check() { - if ! dracut_module_included "systemd-networkd"; then - derror "systemd-networkd-ext needs systemd-networkd in the initramfs" - return 1 - fi + [[ $mount_needs ]] && return 1 - return 255 + 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-networkd" + echo "systemd kernel-network-modules" } installkernel() { - return 0 + return 0 } # called by dracut install() { - # need initqueue to get a clean network start - dracut_need_initqueue - - inst_multiple -o \ - $systemdutildir/systemd-networkd \ - $systemdutildir/systemd-networkd-wait-online \ - $systemdsystemunitdir/systemd-networkd-wait-online.service \ - $systemdsystemunitdir/systemd-networkd.service \ - $systemdsystemunitdir/systemd-networkd.socket \ - networkctl ip - - # 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" - for i in \ - systemd-networkd-wait-online.service \ - systemd-networkd.service \ - systemd-networkd.socket - do - systemctl --root "$initdir" enable "$i" - done - # HACK: disables udev new persitent naming scheme - rm $systemdutildir/network/99-default.link + # 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.*" + + # 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" } |