From 33f70c2d192413b4d483796d42acec15897fd485 Mon Sep 17 00:00:00 2001 From: "William A. Kennington III" Date: Wed, 4 Sep 2024 15:00:25 -0700 Subject: meta-google: gbmc-net-common: Add common RA script This consolidates all of the discrete code for different RA handling logic into one place. Change-Id: I8a697c198bd49e7c3d2aabcea78e1d0a8d073753 Signed-off-by: William A. Kennington III --- .../recipes-google/networking/gbmc-net-common.bb | 25 +++ .../networking/gbmc-net-common/gbmc-ra.sh | 186 +++++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 meta-google/recipes-google/networking/gbmc-net-common.bb create mode 100644 meta-google/recipes-google/networking/gbmc-net-common/gbmc-ra.sh (limited to 'meta-google') diff --git a/meta-google/recipes-google/networking/gbmc-net-common.bb b/meta-google/recipes-google/networking/gbmc-net-common.bb new file mode 100644 index 0000000000..1f709b4eef --- /dev/null +++ b/meta-google/recipes-google/networking/gbmc-net-common.bb @@ -0,0 +1,25 @@ +SUMMARY = "gBMC common networking components" +PR = "r1" +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/files/common-licenses/Apache-2.0;md5=89aea4e17d99a7cacdbeed46a0096b10" + +SRC_URI += " \ + file://gbmc-ra.sh \ + " +S = "${WORKDIR}" + +FILES:${PN} += " \ + ${datadir}/ \ + " + +RDEPENDS:${PN}:append = " \ + bash \ + network-sh \ + ndisc6-rdisc6 \ + " + +do_install() { + install -d -m0755 ${D}${datadir} + install -m0644 ${S}/gbmc-ra.sh ${D}${datadir}/ +} + diff --git a/meta-google/recipes-google/networking/gbmc-net-common/gbmc-ra.sh b/meta-google/recipes-google/networking/gbmc-net-common/gbmc-ra.sh new file mode 100644 index 0000000000..d2e92a57b4 --- /dev/null +++ b/meta-google/recipes-google/networking/gbmc-net-common/gbmc-ra.sh @@ -0,0 +1,186 @@ +#!/bin/bash +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# shellcheck source=meta-google/recipes-google/networking/network-sh/lib.sh +source /usr/share/network/lib.sh || exit + +: "${RA_IF:?No RA interface set}" +: "${IP_OFFSET=?1}" +: "${ROUTE_METRIC:?No Metric set}" + +# We would prefer empty string but it's easier for associative array handling +# to use invalid +old_rtr=invalid +old_mac=invalid +old_pfx=invalid +old_fqdn=invalid + +default_update_rtr() { + local rtr="$1" + local mac="$2" + + if ip addr show | grep -q "^[ ]*inet6 $rtr/"; then + echo "Router is ourself, ignoring" >&2 + return 0 + fi + + # In case we don't have a base network file, make one + # this is intentionally 00- as it will not preceed /etc/systemd/network/00-* + # or /lib/systemd/network/-* files. + local file=/run/systemd/network/00-bmc-$RA_IF.network + printf '[Match]\nName=%s\n[Network]\nDHCP=false\nIPv6AcceptRA=false\nLinkLocalAddressing=yes' \ + "$RA_IF" >"$file" + + # Override any existing gateway information within files + # Make sure we cover `00-*` and `-*` files + for file in /run/systemd/network/{00,}-bmc-$RA_IF.network; do + mkdir -p "$file.d" + printf '[Route]\nGateway=%s\nGatewayOnLink=true\nMetric=%d\n[Neighbor]\nMACAddress=%s\nAddress=%s' \ + "$rtr" "$ROUTE_METRIC" "$mac" "$rtr" >"$file.d"/10-gateway.conf + done + + # Don't force networkd to reload as this can break phosphor-networkd + # Fall back to reload only if ip link commands fail + (ip -6 route replace default via "$rtr" onlink dev "$RA_IF" metric "$ROUTE_METRIC" && \ + ip -6 neigh replace "$rtr" dev "$RA_IF" lladdr "$mac") || \ + (networkctl reload && networkctl reconfigure "$RA_IF") || true + + echo "Set router $rtr on $RA_IF" >&2 +} + +default_update_fqdn() { + local fqdn="$1" + [ -z "$fqdn" ] && return + hostnamectl set-hostname "$fqdn" || true + echo "Set hostname $fqdn on $RA_IF" >&2 +} + +retries=1 +min_w=10 +declare -A rtrs +rtrs=() +while true; do + # shellcheck disable=SC2206 + data=(${rtrs["${old_rtr}"]-}) + curr_dl="${data[1]-$(( min_w + SECONDS ))}" + args=(-m "$RA_IF" -w $(( (curr_dl - SECONDS) * 1000 ))) + if (( retries > 0 )); then + args+=(-r "$retries") + else + args+=(-d) + fi + while read -r line; do + # `script` terminates all lines with a CRLF, remove it + line="${line:0:-1}" + # shellcheck disable=SC2026 + if [ -z "$line" ]; then + lifetime=-1 + mac= + hextet= + pfx= + host= + domain= + elif [[ "$line" =~ ^Router' 'lifetime' '*:' '*([0-9]*) ]]; then + lifetime="${BASH_REMATCH[1]}" + elif [[ "$line" =~ ^Source' 'link-layer' 'address' '*:' '*([a-fA-F0-9:]*)$ ]]; then + mac="${BASH_REMATCH[1]}" + elif [[ "$line" =~ ^Prefix' '*:' '*(.*)/([0-9]+)$ ]]; then + t_pfx="${BASH_REMATCH[1]}" + t_pfx_len="${BASH_REMATCH[2]}" + ip_to_bytes t_pfx_b "$t_pfx" || continue + (( (t_pfx_len == 76 || t_pfx_len == 80) && (t_pfx_b[8] & 0xfd) == 0xfd )) || continue + (( t_pfx_b[9] &= 0xf0 )) + (( t_pfx_b[9] |= IP_OFFSET )) + hextet="fd$(printf '%02x' "${t_pfx_b[9]}")" + pfx="$(ip_bytes_to_str t_pfx_b)" + elif [[ "$line" =~ ^'DNS search list'' '*:' '*([^.]+)(.*[.]google[.]com)' '*$ ]]; then + # Ideally, we use PCRE and with lookahead and can do this in a single regex + # ^([a-zA-Z0-9-]+(?=-n[a-fA-F0-9]{1,4})|[a-zA-Z0-9-]+(?!-n[a-fA-F0-9]{1,4}))[^.]*[.]((?:[a-zA-Z0-9]*[.])*google[.]com)$ + # Instead we do multiple steps to extract the needed info + host="${BASH_REMATCH[1]}" + domain="${BASH_REMATCH[2]#.}" + if [[ "$host" =~ (-n[a-fA-F0-9]{1,4})$ ]]; then + host="${host%"${BASH_REMATCH[1]}"}" + fi + elif [[ "$line" =~ ^from' '(.*)$ ]]; then + rtr="${BASH_REMATCH[1]}" + # Only valid default routers can be considered, 0 lifetime implies + # a non-default router + (( lifetime > 0 )) || continue + + dl=$((lifetime + SECONDS)) + fqdn= + if [[ -n $host && -n $hextet && -n $domain ]]; then + fqdn="$host-n$hextet.$domain" + fi + rtrs["$rtr"]="$mac $dl $pfx $fqdn" + # We have some notoriously noisy lab environments with many routers being broadcast + # We always prefer "fe80::1" in prod and labs for routing, so prefer that gateway. + # We also want to take the first router we find to speed up acquisition on boot. + if [[ "$rtr" = "fe80::1" || "$old_rtr" = "invalid" ]]; then + if [[ "$rtr" != "$old_rtr" && "$mac" != "$old_mac" ]]; then + echo "Got defgw $rtr at $mac on $RA_IF" >&2 + update_rtr "$rtr" "$mac" || true + retries=-1 + old_mac="$mac" + old_rtr="$rtr" + fi + fi + # Only update router properties if we use this router + [[ "$rtr" == "$old_rtr" ]] || continue + if [[ $pfx != "$old_pfx" ]]; then + echo "Got PFX $pfx from $rtr on $RA_IF" >&2 + old_pfx="$pfx" + update_pfx "$pfx" || true + fi + if [[ $fqdn != "$old_fqdn" ]]; then + echo "Got FQDN $fqdn from $rtr on $RA_IF" >&2 + old_fqdn="$fqdn" + update_fqdn "$fqdn" || true + fi + fi + done < <(exec script -q -c "rdisc6 ${args[*]}" /dev/null 2>/dev/null) + # Purge any expired routers + for rtr in "${!rtrs[@]}"; do + # shellcheck disable=SC2206 + data=(${rtrs["$rtr"]}) + dl=${data[1]} + if (( dl <= SECONDS )); then + unset "rtrs[$rtr]" + fi + done + # Consider changing the gateway if the old one doesn't send RAs for the entire period + # This ensures we don't flip flop between multiple defaults if they exist. + if [[ "$old_rtr" != "invalid" && -z "${rtrs["$old_rtr"]-}" ]]; then + echo "Old router $old_rtr disappeared" >&2 + old_rtr=invalid + for rtr in "${!rtrs[@]}"; do + # shellcheck disable=SC2206 + data=(${rtrs["$rtr"]}) + mac=${data[0]} + dl=${data[1]} + pfx=${data[2]} + fqdn=${data[3]} + update_rtr "$rtr" "$mac" || true + update_pfx "$pfx" || true + update_fqdn "$fqdn" || true + break + done + fi + + # If rdisc6 exits early we still want to wait for the deadline before retrying + (( timeout = curr_dl - SECONDS )) + sleep $(( timeout < 0 ? 0 : timeout )) +done -- cgit v1.2.3