summaryrefslogblamecommitdiffstats
path: root/modules.d/systemd-networkd-ext/hooks/parse-kcl-for-networkd.sh
blob: afe4ab7ed5e61f0e638c9de0698df7b87dd56ead (plain) (tree)
1
2
3
4
5
6
7
8
9

           


                                                                         


                                                    
                                                    

                               
 








                                                                                   
 

                                                                          
                                                            



                              
                                                                                                                   
                                                       
 
 

                                                                           





                                                                                                         
                     
 


                                                                          
 

                                        
 

                                              


                                          
                               



                                                                                             
                                                                    
 
 
                                                                

                                                                 
                                                           

                                                              










                                                     


           

                                            








                                                                                      
                                

                                                                                                          
                                                                                                                                                                    

 
















                                       



                                                 


                                                                               
                                           
                                
                       






                                                                             
                            






                                                                                       

                                             
                  
                                    





                                                                                        
                                            










                                                                                             
                                 





                                            
                   




                                           

          
                      














                                                                                       
                                    



                                                                                               

                                                     


                                            
                
 
 




                                                    









                                                                                         

 



                         


                              
                                                                               

                                                                                 
 
                                    

                                                                    
 





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