Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(node): setup-networking component #3135

Draft
wants to merge 66 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
3d3b6a0
Create setup-networking component
andrewbattat Dec 12, 2024
eba4e33
Merge branch 'master' into andrew/setup-networking-component
andrewbattat Dec 12, 2024
432a4e7
Merge branch 'master' into andrew/setup-networking-component
andrewbattat Dec 12, 2024
8443f5f
Refactor setup-networking.sh
andrewbattat Dec 12, 2024
ca3fa21
Remove /opt/ic/share setupos dockerfile creation
andrewbattat Dec 12, 2024
95b65c7
SetupOS GenerateMacAddress command
andrewbattat Dec 12, 2024
99bfd5d
Fix setup-networking logging
andrewbattat Dec 12, 2024
d90bd3f
Add Mac and IPv6 address logging
andrewbattat Dec 12, 2024
d77d660
Fix NETPLAN_OUTPUT
andrewbattat Dec 12, 2024
dd8d73b
Remove reference to generate-network-config.service
andrewbattat Dec 12, 2024
82a5255
Remove unnecessary network-online.target dependency
andrewbattat Dec 12, 2024
0fb5d11
Fix config dependency
andrewbattat Dec 12, 2024
115fabe
Add more logging to setup-network.sh
andrewbattat Dec 12, 2024
a1b6173
Fix setup-networking syntax error
andrewbattat Dec 12, 2024
e2d6808
Add netplan package
andrewbattat Dec 12, 2024
ef4a048
Check ipv6 connectivity for each interface
andrewbattat Dec 12, 2024
898ce0d
Create bond for all interfaces
andrewbattat Dec 13, 2024
831e99b
Fix logging and dynamic interface creation
andrewbattat Dec 13, 2024
b8ad47c
Fix ipv6_gateway reference
andrewbattat Dec 13, 2024
8c203fa
Add MAC_ADDR / IPV6_ADDR check
andrewbattat Dec 13, 2024
3ca531f
Simplify speed_str parsing
andrewbattat Dec 13, 2024
fece2af
Refactor gather_interfaces_by_speed
andrewbattat Dec 13, 2024
ebd1e5e
Fix reference to SORTED_INTERFACES
andrewbattat Dec 13, 2024
350eaed
Fix interfaces logging
andrewbattat Dec 13, 2024
4195e62
Fix netplan config file
andrewbattat Dec 13, 2024
c42bed8
Add bonding parameters
andrewbattat Dec 13, 2024
6ceba72
Replace gateway6 with routes
andrewbattat Dec 13, 2024
ed8495b
Fix HostOS service dependency
andrewbattat Dec 13, 2024
0cb91eb
Fix setup-networking to run in HostOS
andrewbattat Dec 13, 2024
3b7b488
Revert local-base-dev changes
andrewbattat Dec 16, 2024
19793c9
Rename 99-setup-netplan.yaml.template
andrewbattat Dec 16, 2024
e026a40
Minor refactoring
andrewbattat Dec 16, 2024
c8c8aca
Properly quote variables
andrewbattat Dec 16, 2024
645a3c4
Remove old networking logic
andrewbattat Dec 17, 2024
4e35fbc
Fix check file references check
andrewbattat Dec 17, 2024
ba2c395
Fix netplan configuration file permission warnings
andrewbattat Dec 17, 2024
34e4883
Create test-setup-networking.sh
andrewbattat Dec 17, 2024
9472c10
Fix pre-commit
andrewbattat Dec 17, 2024
063f597
Fix netplan warnings
andrewbattat Dec 17, 2024
a211a54
Fix pre-commit
andrewbattat Dec 17, 2024
987b39a
Merge branch 'master' into andrew/setup-networking-component
andrewbattat Dec 17, 2024
745f894
creat test-setup-networking-netplan.sh
andrewbattat Dec 17, 2024
8a80f53
Remove netplan template mock and just test setupOS
andrewbattat Dec 17, 2024
ca73c3f
Fix test-setup-networking-netplan.sh
andrewbattat Dec 17, 2024
20db6a4
Consolidate test_gather_interfaces_by_speed and test_netplan_config
andrewbattat Dec 17, 2024
0cc75f6
Merge mock_tools
andrewbattat Dec 17, 2024
5d3f44b
Clean up test-setup-networking.sh
andrewbattat Dec 17, 2024
857e8cd
Fix pre-commit
andrewbattat Dec 17, 2024
b23a4ff
Revert ALLOWED_UNDECLARED_DEPENDENCIES update
andrewbattat Dec 17, 2024
ddc4b2b
Add test_setup_networking
andrewbattat Dec 17, 2024
e5969eb
Ignore test-setup-networking.sh in check_unused_components_test
andrewbattat Dec 17, 2024
7a5c5a6
Fix Interfaces call
andrewbattat Dec 18, 2024
d062670
Fix buildifier
andrewbattat Dec 18, 2024
349f0b1
Fix setup-networking service files
andrewbattat Dec 18, 2024
f114e34
Read interface speed from /sys/class/net rather than using ethtool
andrewbattat Dec 19, 2024
2b24eb5
Fix setup-networking-setupos issues
andrewbattat Dec 19, 2024
55c561e
Fix SORTED_INTERFACES
andrewbattat Dec 19, 2024
fc24cc5
Fix pre-commit
andrewbattat Dec 19, 2024
fdb1098
Merge branch 'master' into andrew/setup-networking-component
andrewbattat Dec 19, 2024
979073a
Reduce dependency redundancy
andrewbattat Dec 19, 2024
06d8a30
Fix up/down delay values and mii monitor interval
andrewbattat Dec 26, 2024
4ae6d06
Merge branch 'master' into andrew/setup-networking-component
andrewbattat Jan 31, 2025
1c20ea5
Remove manual tag on test_setup_networking
andrewbattat Jan 31, 2025
870e581
Standardize RemainAfterExit=yes
andrewbattat Jan 31, 2025
fd626e4
Fix merge errors
andrewbattat Jan 31, 2025
e5e956a
Merge branch 'master' into andrew/setup-networking-component
andrewbattat Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ic-os/components/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ ignored_repo_components = [
"hostos-scripts/generate-guestos-config/dev-generate-guestos-config.sh",
"networking/dev-certs/canister_http_test_ca.key",
"networking/dev-certs/root_cert_gen.sh",
"networking/setup-networking/test-setup-networking.sh",
]

check_unused_components_test(
Expand Down Expand Up @@ -97,3 +98,16 @@ sh_test(
data = ["ic/generate-ic-config/ic.json5.template"],
tags = ["manual"],
)

sh_test(
name = "test_setup_networking",
srcs = ["networking/setup-networking/test-setup-networking.sh"],
args = [
"$(execpath networking/setup-networking/setup-networking.sh)",
"$(execpath networking/setup-networking/99-setup-netplan.yaml.template)",
],
data = [
"networking/setup-networking/99-setup-netplan.yaml.template",
"networking/setup-networking/setup-networking.sh",
],
)
4 changes: 3 additions & 1 deletion ic-os/components/hostos.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ component_files = {
Label("misc/log-config/log-config.sh"): "/opt/ic/bin/log-config.sh",

# networking
Label("networking/generate-network-config/hostos/generate-network-config.service"): "/etc/systemd/system/generate-network-config.service",
Label("networking/setup-networking/setup-networking-hostos.service"): "/etc/systemd/system/setup-networking.service",
Label("networking/setup-networking/setup-networking.sh"): "/opt/ic/bin/setup-networking.sh",
Label("networking/setup-networking/99-setup-netplan.yaml.template"): "/opt/ic/share/99-setup-netplan.yaml.template",
Label("networking/fallback.conf"): "/etc/systemd/resolved.conf.d/fallback.conf",
Label("networking/resolv.conf"): "/etc/resolv.conf",
Label("networking/network-tweaks.conf"): "/etc/sysctl.d/network-tweaks.conf",
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
network:
version: 2
renderer: networkd
ethernets:
# Interfaces will be dynamically inserted here
bonds:
bond0:
interfaces: [{INTERFACES}]
parameters:
mode: active-backup
mii-monitor-interval: 5s
up-delay: 500ms
down-delay: 500ms
macaddress: {MAC_ADDRESS}
mtu: 1500
optional: true
bridges:
br6:
interfaces: [bond0]
addresses: [{IPV6_ADDR}]
routes:
- to: ::/0
via: {IPV6_GATEWAY}
nameservers:
addresses: [2606:4700:4700::1111, 2606:4700:4700::1001, 2001:4860:4860::8888, 2001:4860:4860::8844]
parameters:
forward-delay: 0
stp: false
link-local: ["ipv6"]
accept-ra: no
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[Unit]
Description=Setup networking configuration for HostOS
After=systemd-modules-load.service
After=systemd-udev-settle.service
Wants=systemd-udev-settle.service
Before=network.target network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/ic/bin/setup-networking.sh HostOS

[Install]
WantedBy=multi-user.target
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[Unit]
Description=Setup networking configuration for SetupOS
Before=setupos.service
Before=network.target network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/opt/ic/bin/setup-networking.sh SetupOS

[Install]
WantedBy=multi-user.target
138 changes: 138 additions & 0 deletions ic-os/components/networking/setup-networking/setup-networking.sh
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a high level, do we like moving forward with this idea of putting all the interfaces in a single bond and prioritizing interface usage by speed?

The only significant concern I have is if there are two interfaces connected, but the one selected first is just an internal connection. If that’s the case, I’m concerned that the bond won’t fall through to the next interface, and therefore, networking will fail. I understand that the internal networks are usually set up on the 1G ports, so hopefully this won’t be an issue as the 10G interfaces will be prioritized by speed, but this is still something I want to test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving this discussion to a thread (@tmshlvck)

I wonder what is the state when the script runs? Do we have any systemd-networkd config active at that point? Assuming that Netplan (that uses systemd-networkd is installed there can be some interference that would eventually lead to install time issues. Maybe using netns for the tests may avoid that by just adding a few commands to the script.

Is having the setup-networking.service files running Before=network.target network-online.target not sufficient?

Or is the "script" you're talking about the test-setup-networking.sh script? Sorry, I don't entirely follow.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving this discussion to a thread (@tmshlvck)

Another concern is the interface name - we use the interface name in bridge setup and then in ethernet we use it as an identifier but we do not rename based on MAC. This will work as long as kernel+ACPI+udev do not change the interface name (unlikely but possible - there is a BIOS setting on some SuperMicro servers that allows to enable/disable ACPI tree element that carry "predictable name" -> interface name may change based on BIOS settings). If there is a change it would bind the interface based on MAC but it would fail bridge setup. Consider adding set-name statement (see https://netplan.readthedocs.io/en/latest/netplan-yaml/).

I do want to keep the networking code as simple as possible. Is this change necessary? I'll follow you're judgement, I just want to be certain.

Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#!/usr/bin/env bash

set -o nounset
set -o pipefail

SHELL="/bin/bash"
PATH="${PATH}:/sbin:/bin:/usr/sbin:/usr/bin"

function parse_args() {
if [ $# -ne 1 ]; then
echo "Usage: $0 <SetupOS|HostOS>"
exit 1
fi

NODE_TYPE="$1"
if [ "$NODE_TYPE" != "SetupOS" ] && [ "$NODE_TYPE" != "HostOS" ]; then
echo "Invalid node type: $NODE_TYPE"
exit 1
fi

# Allow overriding config paths via environment variables
if [ "$NODE_TYPE" = "SetupOS" ]; then
CONFIG_BASE_PATH="${CONFIG_BASE_PATH:-/var/ic/config}"
else
CONFIG_BASE_PATH="${CONFIG_BASE_PATH:-/boot/config}"
fi

CONFIG="${CONFIG_BASE_PATH}/config.ini"
}

function read_variables() {
# Read limited set of keys. Be extra-careful quoting values as it could
# otherwise lead to executing arbitrary shell code!
while IFS="=" read -r key value; do
case "$key" in
"ipv6_gateway") IPV6_GATEWAY="${value}" ;;
esac
done <"${CONFIG}"

if [ -z "$IPV6_GATEWAY" ]; then
echo "No IPv6 gateway found in $CONFIG."
exit 1
fi
}

function generate_addresses() {
echo "Generating MAC and IPv6 addresses..."
IC_BIN_PATH="${IC_BIN_PATH:-/opt/ic/bin}"

if [ "$NODE_TYPE" = "SetupOS" ]; then
MAC_ADDR=$("${IC_BIN_PATH}/setupos_tool" generate-mac-address --node-type SetupOS)
IPV6_ADDR=$("${IC_BIN_PATH}/setupos_tool" generate-ipv6-address --node-type SetupOS)
else
MAC_ADDR=$("${IC_BIN_PATH}/hostos_tool" generate-mac-address --node-type HostOS)
IPV6_ADDR=$("${IC_BIN_PATH}/hostos_tool" generate-ipv6-address --node-type HostOS)
fi

if [ -z "$MAC_ADDR" ] || [ -z "$IPV6_ADDR" ]; then
echo "Failed to generate MAC or IPv6 address."
exit 1
fi

echo "Generated MAC address: $MAC_ADDR"
echo "Generated IPv6 address: $IPV6_ADDR"
}

function gather_interfaces_by_speed() {
echo "Gathering and sorting interfaces by speed..."

SYS_NET_PATH="${SYS_NET_PATH:-/sys/class/net}"
INTERFACES=($(find "$SYS_NET_PATH" -type l -not -lname '*virtual*' -exec basename '{}' ';'))
declare -A SPEED_MAP

for IFACE in "${INTERFACES[@]}"; do
if [ -f "$SYS_NET_PATH/$IFACE/speed" ]; then
SPEED=$(cat "$SYS_NET_PATH/$IFACE/speed" 2>/dev/null || echo 0)
Comment on lines +70 to +76
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to draw attention to the way we're gathering interface speeds. I'm curious what PFOPS thinks of this. Does this seem proper? In the old code, we were parsing the output of Ethtool to gather speeds (see interfaces.rs)

else
SPEED=0
echo "Warning: Unable to determine speed for interface $IFACE, defaulting to 0." >&2
fi
SPEED_MAP["$IFACE"]=$SPEED
done

# Sort interfaces by speed descending
SORTED_INTERFACES=$(for IFACE in "${!SPEED_MAP[@]}"; do
echo "${SPEED_MAP[$IFACE]} $IFACE"
done | sort -nrk1 | awk '{print $2}')

if [ -z "$SORTED_INTERFACES" ]; then
echo "No interfaces found."
exit 1
fi

INTERFACE_LIST=$(echo "$SORTED_INTERFACES" | paste -sd, -)
echo "Interfaces sorted by speed: $INTERFACE_LIST"
}

function configure_netplan() {
echo "Configuring netplan..."
NETPLAN_TEMPLATE_PATH="${NETPLAN_TEMPLATE_PATH:-/opt/ic/share}"
NETPLAN_TEMPLATE="${NETPLAN_TEMPLATE_PATH}/99-setup-netplan.yaml.template"
NETPLAN_RUN_PATH="${NETPLAN_RUN_PATH:-/run/netplan}"
NETPLAN_OUTPUT="${NETPLAN_RUN_PATH}/99-setup-netplan.yaml"

mkdir -p "$NETPLAN_RUN_PATH"
cp "$NETPLAN_TEMPLATE" "$NETPLAN_OUTPUT"

sed -i "s|{INTERFACES}|${INTERFACE_LIST}|g" "$NETPLAN_OUTPUT"
sed -i "s|{MAC_ADDRESS}|${MAC_ADDR}|g" "$NETPLAN_OUTPUT"
sed -i "s|{IPV6_ADDR}|${IPV6_ADDR}|g" "$NETPLAN_OUTPUT"
sed -i "s|{IPV6_GATEWAY}|${IPV6_GATEWAY}|g" "$NETPLAN_OUTPUT"

# Dynamically add ethernets for each interface in descending speed
for IFACE in $SORTED_INTERFACES; do
sed -i "/^ ethernets:/a \ \ \ \ $IFACE:\n mtu: 1500\n optional: true\n emit-lldp: true\n" "$NETPLAN_OUTPUT"
Copy link
Member Author

@andrewbattat andrewbattat Dec 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spawned from: #3135 (comment)

In our old code, we were writing the following values to systemd-networkd directly (see Systemd.rs):

LLDP=true
EmitLLDP=true

Netplan does have an LLDP property, and only an emit-lldp property. Is this sufficient?

From my basic understanding, it seems the default of routers-only is sufficient

done

# Fix netplan configuration file permissions to silence warnings
chmod 0600 "$NETPLAN_OUTPUT"

echo "Netplan configuration written to $NETPLAN_OUTPUT"
echo "Applying netplan..."
netplan generate
netplan apply
echo "Network configuration applied successfully for $NODE_TYPE."
}

function main() {
parse_args "$@"
read_variables
generate_addresses
gather_interfaces_by_speed
configure_netplan
}

if [ "$0" = "$BASH_SOURCE" ]; then
main "$@"
fi
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/usr/bin/env bash

set -euo pipefail

if [ $# -ne 2 ]; then
echo "Usage: $0 <path-to-setup-networking.sh> <path-to-99-setup-netplan.yaml.template>"
exit 1
fi

SETUP_SCRIPT="$1"
TEMPLATE_FILE="$2"

TEST_DIR="$(mktemp -d)"
trap 'rm -rf "$TEST_DIR"' EXIT

SHELL=/bin/bash

function mock_tools() {
export SYS_NET_PATH="${TEST_DIR}/sys/class/net"
mkdir -p "${SYS_NET_PATH}"

mkdir -p "${TEST_DIR}/sys/devices/mock_devices_eth0"
mkdir -p "${TEST_DIR}/sys/devices/mock_devices_eth1"
mkdir -p "${TEST_DIR}/sys/devices/mock_devices_eth2"

ln -sf "${TEST_DIR}/sys/devices/mock_devices_eth0" "${SYS_NET_PATH}/eth0"
ln -sf "${TEST_DIR}/sys/devices/mock_devices_eth1" "${SYS_NET_PATH}/eth1"
ln -sf "${TEST_DIR}/sys/devices/mock_devices_eth2" "${SYS_NET_PATH}/eth2"

# Intentionally absent eth0/speed:
# echo "0" >"$SYS_NET_PATH/eth0/speed"

echo "1000" >"$SYS_NET_PATH/eth1/speed"
echo "2500" >"$SYS_NET_PATH/eth2/speed"

mkdir -p "${TEST_DIR}/opt/ic/bin"
cat >"${TEST_DIR}/opt/ic/bin/setupos_tool" <<'EOF'
#!/bin/bash
if [ "$1" = "generate-mac-address" ]; then
echo "02:00:00:aa:bb:cc"
elif [ "$1" = "generate-ipv6-address" ]; then
echo "2001:db8::1234"
fi
EOF
chmod +x "${TEST_DIR}/opt/ic/bin/setupos_tool"

cat >"${TEST_DIR}/netplan" <<'EOF'
#!/bin/bash
if [ "$1" = "generate" ] || [ "$1" = "apply" ]; then
exit 0
fi
EOF
chmod +x "${TEST_DIR}/netplan"

mkdir -p "${TEST_DIR}/var/ic/config"
cat >"${TEST_DIR}/var/ic/config/config.ini" <<'EOF'
ipv6_gateway=fe80::2
EOF

mkdir -p "${TEST_DIR}/run/netplan"
}

function test_gather_interfaces_by_speed() {
export PATH="${TEST_DIR}:${PATH}"
hash -r

source "$SETUP_SCRIPT"
gather_interfaces_by_speed
local EXPECTED_INTERFACES="eth2,eth1,eth0"
if [ "${INTERFACE_LIST}" = "${EXPECTED_INTERFACES}" ]; then
echo "TEST PASSED: gather_interfaces_by_speed"
else
echo "TEST FAILED: gather_interfaces_by_speed"
exit 1
fi
}

function test_netplan_config() {
export PATH="${TEST_DIR}:${PATH}"
hash -r

CONFIG_BASE_PATH="${TEST_DIR}/var/ic/config" \
NETPLAN_TEMPLATE_PATH="$(dirname "$TEMPLATE_FILE")" \
NETPLAN_RUN_PATH="${TEST_DIR}/run/netplan" \
IC_BIN_PATH="${TEST_DIR}/opt/ic/bin" \
SHELL=/bin/bash \
/bin/bash "$SETUP_SCRIPT" SetupOS

local OUTPUT_FILE="${TEST_DIR}/run/netplan/99-setup-netplan.yaml"

[ -f "$OUTPUT_FILE" ] || {
echo "Test failed: netplan output file not created"
exit 1
}
grep -q "macaddress: 02:00:00:aa:bb:cc" "$OUTPUT_FILE" || {
echo "Test failed: MAC address substitution"
exit 1
}
grep -q "2001:db8::1234" "$OUTPUT_FILE" || {
echo "Test failed: IPv6 address substitution"
exit 1
}
grep -q "fe80::2" "$OUTPUT_FILE" || {
echo "Test failed: IPv6 gateway substitution"
exit 1
}
grep -Eq "interfaces:\s*\[eth2,eth1,eth0\]" "$OUTPUT_FILE" || {
echo "Test failed: Interfaces insertion"
exit 1
}

echo "TEST PASSED: netplan_config"
}

mock_tools
test_gather_interfaces_by_speed
test_netplan_config

echo "All tests passed."
exit 0
2 changes: 1 addition & 1 deletion ic-os/components/setupos-scripts/config.service
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[Unit]
Description=SetupOS config process
Before=generate-network-config.service
Before=setup-networking.service
Before=setupos.service

[Service]
Expand Down
Loading
Loading