diff options
Diffstat (limited to '')
11 files changed, 396 insertions, 0 deletions
diff --git a/test/test-network-generator-conversion.sh b/test/test-network-generator-conversion.sh new file mode 100755 index 0000000..c0e74c1 --- /dev/null +++ b/test/test-network-generator-conversion.sh @@ -0,0 +1,300 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# shellcheck disable=SC2235 +set -eux +set -o pipefail + +# TODO/FIXME: +# - we should probably have something like "udevadm verify" but for .network files +# (networkctl verify?) so we can check that all directives are in correct sections +# - according to dracut.cmdline(7) <peer> address can also be followed by /CIDR, +# but this doesn't seem to work with sd-network-generator + +if [[ -n "${1:-}" ]]; then + GENERATOR_BIN=$1 +elif [[ -x /usr/lib/systemd/systemd-network-generator ]]; then + GENERATOR_BIN=/usr/lib/systemd/systemd-network-generator +elif [[ -x /lib/systemd/systemd-network-generator ]]; then + GENERATOR_BIN=/lib/systemd/systemd-network-generator +else + exit 1 +fi + +# See: https://github.com/systemd/systemd/pull/29888#issuecomment-1796187440 +unset NOTIFY_SOCKET + +WORK_DIR="$(mktemp --directory --tmpdir "test-network-generator-conversion.XXXXXX")" +# shellcheck disable=SC2064 +trap "rm -rf '$WORK_DIR'" EXIT + +# Convert octal netmask to CIDR notation (e.g. 255.255.255.0 => 24) +netmask_to_cidr() ( + set +x + + local netmask="${1:?}" + local x bits=0 + + # shellcheck disable=SC2086 + x="0$(printf "%o" ${netmask//./ })" + while [[ "$x" -gt 0 ]]; do + ((bits += x % 2)) + ((x >>= 1)) + done + + echo "$bits" +) + +run_network_generator() { + local stderr + + rm -rf "${WORK_DIR:?}"/* + stderr="$WORK_DIR/stderr" + if ! "$GENERATOR_BIN" --root "$WORK_DIR" 2>"$stderr"; then + echo >&2 "Generator failed when parsing $SYSTEMD_PROC_CMDLINE" + cat "$stderr" + return 1 + fi + + if [[ -s "$stderr" ]]; then + echo >&2 "Generator generated unexpected messages on stderr" + cat "$stderr" + return 1 + fi + + ls -l "$WORK_DIR/run/systemd/network/" + + rm -f "$stderr" + return 0 +} + +check_dhcp() { + local dhcp="${1:?}" + local network_file="${2:?}" + + case "$dhcp" in + dhcp) + grep -q "^DHCP=ipv4$" "$network_file" + ;; + dhcp6) + grep -q "^DHCP=ipv6$" "$network_file" + ;; + on|any) + grep -q "^DHCP=yes$" "$network_file" + ;; + none|off) + grep -q "^DHCP=no$" "$network_file" + ;; + auto6|ibft) + grep -q "^DHCP=no$" "$network_file" + ;; + either6) + grep -q "^DHCP=ipv6$" "$network_file" + ;; + link6) + grep -q "^DHCP=no$" "$network_file" + ;; + *) + echo >&2 "Invalid assignment $cmdline" + return 1 + esac + + return 0 +} + +# Check the shortest ip= variant, i.e.: +# ip={dhcp|on|any|dhcp6|auto6|either6|link6} +# +# Note: +# - dracut also supports single-dhcp +check_one_dhcp() { + local cmdline="${1:?}" + local dhcp="${cmdline#ip=}" + local network_file + + SYSTEMD_LOG_LEVEL=debug SYSTEMD_PROC_CMDLINE="$cmdline" run_network_generator + network_file="${WORK_DIR:?}/run/systemd/network/91-default.network" + cat "$network_file" + + check_dhcp "$dhcp" "$network_file" + + return 0 +} + +# Similar to the previous one, but with slightly more fields: +# ip=<interface>:{dhcp|on|any|dhcp6|auto6|link6}[:[<mtu>][:<macaddr>]] +# +# Same notes apply as well. +check_one_interface_dhcp() { + local cmdline="${1:?}" + local ifname dhcp mtu mac network_file + + IFS=":" read -r ifname dhcp mtu mac <<< "${cmdline#ip=}" + + SYSTEMD_LOG_LEVEL=debug SYSTEMD_PROC_CMDLINE="$cmdline" run_network_generator + network_file="${WORK_DIR:?}/run/systemd/network/90-$ifname.network" + cat "$network_file" + + grep -q "^Name=$ifname$" "$network_file" + check_dhcp "$dhcp" "$network_file" + [[ -n "$mtu" ]] && grep -q "^MTUBytes=$mtu$" "$network_file" + [[ -n "$mac" ]] && grep -q "^MACAddress=$mac$" "$network_file" + + return 0 +} + +# Check the "long" ip= formats, i.e: +# ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<mtu>][:<macaddr>] +# ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<dns1>][:<dns2>]] +check_one_long() { + local cmdline="${1:?}" + local ip peer gateway netmask hostname ifname dhcp arg1 arg2 network_file cidr stderr tmp + + # To make parsing a bit easier when IPv6 is involved, replace all colons between [] with #, ... + tmp="$(echo "${cmdline#ip=}" | sed -re ':l; s/(\[[^]:]*):/\1#/; tl')" + # ... drop the now unnecessary [] and split the string into colon separated fields as usual, ... + IFS=":" read -r ip peer gateway netmask hostname ifname dhcp arg1 arg2 <<<"${tmp//[\[\]]}" + # ... and then replace # back to colons for fields that might contain an IPv6 address. + ip="${ip//#/:}" + peer="${peer//#/:}" + gateway="${gateway//#/:}" + arg1="${arg1//#/:}" + arg2="${arg2//#/:}" + + SYSTEMD_LOG_LEVEL=debug SYSTEMD_PROC_CMDLINE="$cmdline" run_network_generator + + if [[ -n "$ifname" ]]; then + network_file="${WORK_DIR:?}/run/systemd/network/90-$ifname.network" + grep -q "^Name=$ifname$" "$network_file" + else + network_file="${WORK_DIR:?}/run/systemd/network/91-default.network" + grep -q "^Kind=!\*$" "$network_file" + fi + + cat "$network_file" + + if [[ -n "$ip" && -n "$netmask" ]]; then + # The "ip" and "netmask" fields are merged together into an IP/CIDR value + if [[ "$netmask" =~ ^[0-9]+$ ]]; then + cidr="$netmask" + else + cidr="$(netmask_to_cidr "$netmask")" + fi + + grep -q "^Address=$ip/$cidr$" "$network_file" + else + (! grep -q "^Address=" "$network_file") + fi + # If the "dhcp" field is empty, it defaults to "off" + [[ -z "$dhcp" ]] && dhcp="off" + [[ -n "$peer" ]] && grep -q "^Peer=$peer$" "$network_file" + [[ -n "$gateway" ]] && grep -q "^Gateway=$gateway$" "$network_file" + [[ -n "$hostname" ]] && grep -q "^Hostname=$hostname$" "$network_file" + check_dhcp "$dhcp" "$network_file" + + # If the first optional argument is empty, assume the first variant + # See: https://github.com/dracutdevs/dracut/blob/4d594210d6ef4f04a9dbadacea73e9461ded352d/modules.d/40network/net-lib.sh#L533 + if [[ -z "$arg1" || "$arg1" =~ ^[0-9]+$ ]]; then + # => [:[<mtu>][:<macaddr>] + [[ -n "$arg1" ]] && grep -q "^MTUBytes=$arg1$" "$network_file" + [[ -n "$arg2" ]] && grep -q "^MACAddress=$arg2$" "$network_file" + else + # => [:[<dns1>][:<dns2>]] + grep -q "^DNS=$arg1$" "$network_file" + [[ -n "$arg2" ]] && grep -q "^DNS=$arg2$" "$network_file" + fi + + return 0 +} + +# Check if the generated .network files match the expected stored ones +TEST_DATA="$(dirname "$0")/testdata/test-network-generator-conversion" +for f in "$TEST_DATA"/test-*.input; do + fname="${f##*/}" + out="$(mktemp --directory "${WORK_DIR:?}/${fname%%.input}.XXX")" + + # shellcheck disable=SC2046 + "$GENERATOR_BIN" --root "$out" -- $(cat "$f") + + if ! diff -u "$out/run/systemd/network" "${f%.input}.expected"; then + echo "**** Unexpected output for $f" + exit 1 + fi + + rm -rf "${out:?}" +done + +# Now generate bunch of .network units on the fly and check if they contain expected +# directives & values + +# ip={dhcp|on|any|dhcp6|auto6|either6|link6} +for dhcp in dhcp on any dhcp6 auto6 either6 link6 off none ibft; do + check_one_dhcp "ip=$dhcp" +done + +# ip=<interface>:{dhcp|on|any|dhcp6|auto6|link6}[:[<mtu>][:<macaddr>]] +COMMAND_LINES=( + "ip=foo:dhcp" + "ip=bar:dhcp6" + "ip=baz1:any:666" + "ip=baz1:any:128:52:54:00:a7:8f:ac" +) +for cmdline in "${COMMAND_LINES[@]}"; do + check_one_interface_dhcp "$cmdline" +done + +# ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<mtu>][:<macaddr>] +# ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<dns1>][:<dns2>]] +COMMAND_LINES=( + "ip=1.2.3.4:2.3.4.5:1.2.3.1:255.255.255.0:hello-world.local:dummy99:off" + "ip=1.2.3.4:2.3.4.5:1.2.3.1:24:hello-world.local:dummy99:off" + "ip=1.2.3.4:2.3.4.5:1.2.3.1:255.255.255.0:hello-world.local:dummy99:off:123" + "ip=1.2.3.4:2.3.4.5:1.2.3.1:255.255.255.0:hello-world.local:dummy99:off:123:52:54:00:a7:8f:ac" + "ip=1.2.3.4:2.3.4.5:1.2.3.1:255.255.255.0:hello-world.local:dummy99:off::52:54:00:a7:8f:ac" + "ip=1.2.3.4:2.3.4.5:1.2.3.1:255.255.255.0:hello-world.local:dummy99:off::" + "ip=1.2.3.4:2.3.4.5:1.2.3.1:255.255.255.0:hello-world.local:dummy99:off:1.2.3.2" + "ip=1.2.3.4:2.3.4.5:1.2.3.1:255.255.255.0:hello-world.local:dummy99:off:1.2.3.2:1.2.3.3" + "ip=192.168.0.2::192.168.0.1:255.255.128.0::foo1:off" + "ip=192.168.0.2::192.168.0.1:17::foo1:off" + "ip=10.0.0.1:::255.255.255.0::foo99:off" + "ip=[fdef:c400:bd01:1096::2]::[fdef:c400:bd01:1096::1]:64::ipv6:off" + "ip=[fdef:c400:bd01:1096::2]:[fdef:c400:bd01:1096::99]::64::ipv6:off" + "ip=[fdef:c400:bd01:1096::2]::[fdef:c400:bd01:1096::1]:64::ipv6:off:666" + "ip=[fdef:c400:bd01:1096::2]::[fdef:c400:bd01:1096::1]:64::ipv6:off:666:52:54:00:a7:8f:ac" + "ip=[fdef:c400:bd01:1096::2]::[fdef:c400:bd01:1096::1]:64::ipv6:off::52:54:00:a7:8f:ac" + "ip=[fdef:c400:bd01:1096::2]::[fdef:c400:bd01:1096::1]:64::ipv6:off::" + "ip=[fdef:c400:bd01:1096::2]::[fdef:c400:bd01:1096::1]:64::ipv6:off:[fdef:c400:bd01:1096::aaaa]" + "ip=[fdef:c400:bd01:1096::2]::[fdef:c400:bd01:1096::1]:64::ipv6:off:[fdef:c400:bd01:1096::aaaa]:[fdef:c400:bd01:1096::bbbb]" + "ip=:::::dhcp99:any" + "ip=:::::dhcp99:dhcp6:666" + "ip=:::::dhcp99:dhcp6:666:52:54:00:a7:8f:ac" + "ip=:::::dhcp99:dhcp6:10.0.0.128" + "ip=:::::dhcp99:dhcp6:10.0.0.128:10.0.0.129" + "ip=:::::dhcp99:dhcp6:10.0.0.128:[fdef:c400:bd01:1096::bbbb]" + "ip=::::::any" + "ip=::::::ibft" +) +for cmdline in "${COMMAND_LINES[@]}"; do + check_one_long "$cmdline" +done + +INVALID_COMMAND_LINES=( + "ip=foo" + "ip=:::::::" + "ip=:::::::foo" + "ip=10.0.0:::255.255.255.0::foo99:off" + "ip=10.0.0.1:::255.255.255::foo99:off" + "ip=10.0.0.1:::255.255.255.0:invalid_hostname:foo99:off" + "ip=10.0.0.1:::255.255.255.0::verylonginterfacename:off" + "ip=:::::dhcp99:dhcp6:4294967296" + "ip=:::::dhcp99:dhcp6:-1" + "ip=:::::dhcp99:dhcp6:666:52:54:00" + "ip=fdef:c400:bd01:1096::2::[fdef:c400:bd01:1096::1]:64::ipv6:off:[fdef:c400:bd01:1096::aaaa]" + "ip=[fdef:c400:bd01:1096::2]::[fdef:c400:bd01:1096::1]:64::ipv6:off:foo" + "ip=[fdef:c400:bd01:1096::2]::[fdef:c400:bd01:1096::1]:64::ipv6:off:[fdef:c400:bd01:1096::aaaa]:foo" + "ip=[fdef:c400:bd01:1096::2]::[fdef:c400:bd01:1096::1]:64::ipv6:off:[fdef:c400:bd01:1096::aaaa]:[fdef:c400:bd01:1096::bbbb]:" + "ip=:::::dhcp99:dhcp6:10.0.0.128:10.0.0.129:" + "ip=:::::dhcp99:dhcp6:10.0.0.128:[fdef:c400:bd01:1096::bbbb]:" +) +for cmdline in "${INVALID_COMMAND_LINES[@]}"; do + (! SYSTEMD_LOG_LEVEL=debug SYSTEMD_PROC_CMDLINE="$cmdline" "$GENERATOR_BIN" --root "$WORK_DIR") +done diff --git a/test/test-network-generator-conversion/.gitattributes b/test/test-network-generator-conversion/.gitattributes new file mode 100644 index 0000000..6df434f --- /dev/null +++ b/test/test-network-generator-conversion/.gitattributes @@ -0,0 +1 @@ +* generated diff --git a/test/test-network-generator-conversion/test-01-dhcp.expected/91-default.network b/test/test-network-generator-conversion/test-01-dhcp.expected/91-default.network new file mode 100644 index 0000000..657cde7 --- /dev/null +++ b/test/test-network-generator-conversion/test-01-dhcp.expected/91-default.network @@ -0,0 +1,12 @@ +# Automatically generated by systemd-network-generator + +[Match] +Kind=!* +Type=!loopback + +[Link] + +[Network] +DHCP=ipv4 + +[DHCP] diff --git a/test/test-network-generator-conversion/test-01-dhcp.input b/test/test-network-generator-conversion/test-01-dhcp.input new file mode 100644 index 0000000..e55893e --- /dev/null +++ b/test/test-network-generator-conversion/test-01-dhcp.input @@ -0,0 +1 @@ +ip=dhcp diff --git a/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.netdev b/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.netdev new file mode 100644 index 0000000..97c2248 --- /dev/null +++ b/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.netdev @@ -0,0 +1,5 @@ +# Automatically generated by systemd-network-generator + +[NetDev] +Kind=bridge +Name=bridge99 diff --git a/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.network b/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.network new file mode 100644 index 0000000..f8d19ba --- /dev/null +++ b/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.network @@ -0,0 +1,13 @@ +# Automatically generated by systemd-network-generator + +[Match] +Name=bridge99 + +[Link] +MACAddress=00:11:22:33:44:55 +MTUBytes=1530 + +[Network] +DHCP=ipv4 + +[DHCP] diff --git a/test/test-network-generator-conversion/test-02-bridge.expected/90-eth0.network b/test/test-network-generator-conversion/test-02-bridge.expected/90-eth0.network new file mode 100644 index 0000000..8842b57 --- /dev/null +++ b/test/test-network-generator-conversion/test-02-bridge.expected/90-eth0.network @@ -0,0 +1,21 @@ +# Automatically generated by systemd-network-generator + +[Match] +Name=eth0 + +[Link] + +[Network] +DHCP=no +DNS=10.10.10.10 +DNS=10.10.10.11 +Bridge=bridge99 + +[DHCP] +Hostname=hogehoge + +[Address] +Address=192.168.0.10/24 + +[Route] +Gateway=192.168.0.1 diff --git a/test/test-network-generator-conversion/test-02-bridge.expected/90-eth1.network b/test/test-network-generator-conversion/test-02-bridge.expected/90-eth1.network new file mode 100644 index 0000000..feff4f5 --- /dev/null +++ b/test/test-network-generator-conversion/test-02-bridge.expected/90-eth1.network @@ -0,0 +1,21 @@ +# Automatically generated by systemd-network-generator + +[Match] +Name=eth1 + +[Link] + +[Network] +DHCP=no +DNS=10.10.10.10 +DNS=10.10.10.11 +Bridge=bridge99 + +[DHCP] +Hostname=hogehoge + +[Address] +Address=192.168.0.11/24 + +[Route] +Gateway=192.168.0.1 diff --git a/test/test-network-generator-conversion/test-02-bridge.input b/test/test-network-generator-conversion/test-02-bridge.input new file mode 100644 index 0000000..0c863fc --- /dev/null +++ b/test/test-network-generator-conversion/test-02-bridge.input @@ -0,0 +1,4 @@ +ip=192.168.0.10::192.168.0.1:255.255.255.0:hogehoge:eth0:off:10.10.10.10:10.10.10.11 +ip=192.168.0.11::192.168.0.1:255.255.255.0:hogehoge:eth1:off:10.10.10.10:10.10.10.11 +ip=bridge99:dhcp:1530:00:11:22:33:44:55 +bridge=bridge99:eth0,eth1 diff --git a/test/test-network-generator-conversion/test-03-issue-14319.expected/90-enp3s0.network b/test/test-network-generator-conversion/test-03-issue-14319.expected/90-enp3s0.network new file mode 100644 index 0000000..28ccfdd --- /dev/null +++ b/test/test-network-generator-conversion/test-03-issue-14319.expected/90-enp3s0.network @@ -0,0 +1,17 @@ +# Automatically generated by systemd-network-generator + +[Match] +Name=enp3s0 + +[Link] + +[Network] +DHCP=no + +[DHCP] + +[Address] +Address=10.99.37.44/16 + +[Route] +Gateway=10.99.10.1 diff --git a/test/test-network-generator-conversion/test-03-issue-14319.input b/test/test-network-generator-conversion/test-03-issue-14319.input new file mode 100644 index 0000000..3be7520 --- /dev/null +++ b/test/test-network-generator-conversion/test-03-issue-14319.input @@ -0,0 +1 @@ +root=/dev/nfs nfsroot=10.99.37.240:/srv/netroot,v3,tcp ip=10.99.37.44::10.99.10.1:255.255.0.0::enp3s0:off |