133f70c2dSWilliam A. Kennington III#!/bin/bash 233f70c2dSWilliam A. Kennington III# Copyright 2021 Google LLC 333f70c2dSWilliam A. Kennington III# 433f70c2dSWilliam A. Kennington III# Licensed under the Apache License, Version 2.0 (the "License"); 533f70c2dSWilliam A. Kennington III# you may not use this file except in compliance with the License. 633f70c2dSWilliam A. Kennington III# You may obtain a copy of the License at 733f70c2dSWilliam A. Kennington III# 833f70c2dSWilliam A. Kennington III# http://www.apache.org/licenses/LICENSE-2.0 933f70c2dSWilliam A. Kennington III# 1033f70c2dSWilliam A. Kennington III# Unless required by applicable law or agreed to in writing, software 1133f70c2dSWilliam A. Kennington III# distributed under the License is distributed on an "AS IS" BASIS, 1233f70c2dSWilliam A. Kennington III# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1333f70c2dSWilliam A. Kennington III# See the License for the specific language governing permissions and 1433f70c2dSWilliam A. Kennington III# limitations under the License. 1533f70c2dSWilliam A. Kennington III 1633f70c2dSWilliam A. Kennington III# shellcheck source=meta-google/recipes-google/networking/network-sh/lib.sh 1733f70c2dSWilliam A. Kennington IIIsource /usr/share/network/lib.sh || exit 18*a108fcdfSWilliam A. Kennington III# shellcheck source=meta-google/recipes-google/networking/gbmc-net-common/gbmc-net-lib.sh 19*a108fcdfSWilliam A. Kennington IIIsource /usr/share/gbmc-net-lib.sh || exit 2033f70c2dSWilliam A. Kennington III 2133f70c2dSWilliam A. Kennington III: "${RA_IF:?No RA interface set}" 2233f70c2dSWilliam A. Kennington III: "${IP_OFFSET=?1}" 2333f70c2dSWilliam A. Kennington III: "${ROUTE_METRIC:?No Metric set}" 2433f70c2dSWilliam A. Kennington III 2533f70c2dSWilliam A. Kennington III# We would prefer empty string but it's easier for associative array handling 2633f70c2dSWilliam A. Kennington III# to use invalid 2733f70c2dSWilliam A. Kennington IIIold_rtr=invalid 2833f70c2dSWilliam A. Kennington IIIold_mac=invalid 2933f70c2dSWilliam A. Kennington IIIold_pfx=invalid 3033f70c2dSWilliam A. Kennington IIIold_fqdn=invalid 3133f70c2dSWilliam A. Kennington III 3233f70c2dSWilliam A. Kennington IIIdefault_update_rtr() { 3333f70c2dSWilliam A. Kennington III local rtr="$1" 3433f70c2dSWilliam A. Kennington III local mac="$2" 3533f70c2dSWilliam A. Kennington III 3633f70c2dSWilliam A. Kennington III if ip addr show | grep -q "^[ ]*inet6 $rtr/"; then 3733f70c2dSWilliam A. Kennington III echo "Router is ourself, ignoring" >&2 3833f70c2dSWilliam A. Kennington III return 0 3933f70c2dSWilliam A. Kennington III fi 4033f70c2dSWilliam A. Kennington III 4133f70c2dSWilliam A. Kennington III # Override any existing gateway information within files 4233f70c2dSWilliam A. Kennington III # Make sure we cover `00-*` and `-*` files 4333f70c2dSWilliam A. Kennington III for file in /run/systemd/network/{00,}-bmc-$RA_IF.network; do 4433f70c2dSWilliam A. Kennington III mkdir -p "$file.d" 4533f70c2dSWilliam A. Kennington III printf '[Route]\nGateway=%s\nGatewayOnLink=true\nMetric=%d\n[Neighbor]\nMACAddress=%s\nAddress=%s' \ 4633f70c2dSWilliam A. Kennington III "$rtr" "$ROUTE_METRIC" "$mac" "$rtr" >"$file.d"/10-gateway.conf 4733f70c2dSWilliam A. Kennington III done 4833f70c2dSWilliam A. Kennington III 4933f70c2dSWilliam A. Kennington III # Don't force networkd to reload as this can break phosphor-networkd 5033f70c2dSWilliam A. Kennington III # Fall back to reload only if ip link commands fail 5133f70c2dSWilliam A. Kennington III (ip -6 route replace default via "$rtr" onlink dev "$RA_IF" metric "$ROUTE_METRIC" && \ 5233f70c2dSWilliam A. Kennington III ip -6 neigh replace "$rtr" dev "$RA_IF" lladdr "$mac") || \ 53*a108fcdfSWilliam A. Kennington III gbmc_net_networkd_reload "$RA_IF" || true 5433f70c2dSWilliam A. Kennington III 5533f70c2dSWilliam A. Kennington III echo "Set router $rtr on $RA_IF" >&2 5633f70c2dSWilliam A. Kennington III} 5733f70c2dSWilliam A. Kennington III 5833f70c2dSWilliam A. Kennington IIIdefault_update_fqdn() { 5933f70c2dSWilliam A. Kennington III local fqdn="$1" 6033f70c2dSWilliam A. Kennington III [ -z "$fqdn" ] && return 6133f70c2dSWilliam A. Kennington III hostnamectl set-hostname "$fqdn" || true 6233f70c2dSWilliam A. Kennington III echo "Set hostname $fqdn on $RA_IF" >&2 6333f70c2dSWilliam A. Kennington III} 6433f70c2dSWilliam A. Kennington III 6533f70c2dSWilliam A. Kennington IIIretries=1 6633f70c2dSWilliam A. Kennington IIImin_w=10 6733f70c2dSWilliam A. Kennington IIIdeclare -A rtrs 6833f70c2dSWilliam A. Kennington IIIrtrs=() 6933f70c2dSWilliam A. Kennington IIIwhile true; do 7033f70c2dSWilliam A. Kennington III # shellcheck disable=SC2206 7133f70c2dSWilliam A. Kennington III data=(${rtrs["${old_rtr}"]-}) 7233f70c2dSWilliam A. Kennington III curr_dl="${data[1]-$(( min_w + SECONDS ))}" 7333f70c2dSWilliam A. Kennington III args=(-m "$RA_IF" -w $(( (curr_dl - SECONDS) * 1000 ))) 7433f70c2dSWilliam A. Kennington III if (( retries > 0 )); then 7533f70c2dSWilliam A. Kennington III args+=(-r "$retries") 7633f70c2dSWilliam A. Kennington III else 7733f70c2dSWilliam A. Kennington III args+=(-d) 7833f70c2dSWilliam A. Kennington III fi 7933f70c2dSWilliam A. Kennington III while read -r line; do 8033f70c2dSWilliam A. Kennington III # `script` terminates all lines with a CRLF, remove it 8133f70c2dSWilliam A. Kennington III line="${line:0:-1}" 8233f70c2dSWilliam A. Kennington III # shellcheck disable=SC2026 8333f70c2dSWilliam A. Kennington III if [ -z "$line" ]; then 8433f70c2dSWilliam A. Kennington III lifetime=-1 8533f70c2dSWilliam A. Kennington III mac= 8633f70c2dSWilliam A. Kennington III hextet= 8733f70c2dSWilliam A. Kennington III pfx= 8833f70c2dSWilliam A. Kennington III host= 8933f70c2dSWilliam A. Kennington III domain= 9033f70c2dSWilliam A. Kennington III elif [[ "$line" =~ ^Router' 'lifetime' '*:' '*([0-9]*) ]]; then 9133f70c2dSWilliam A. Kennington III lifetime="${BASH_REMATCH[1]}" 9233f70c2dSWilliam A. Kennington III elif [[ "$line" =~ ^Source' 'link-layer' 'address' '*:' '*([a-fA-F0-9:]*)$ ]]; then 9333f70c2dSWilliam A. Kennington III mac="${BASH_REMATCH[1]}" 9433f70c2dSWilliam A. Kennington III elif [[ "$line" =~ ^Prefix' '*:' '*(.*)/([0-9]+)$ ]]; then 9533f70c2dSWilliam A. Kennington III t_pfx="${BASH_REMATCH[1]}" 9633f70c2dSWilliam A. Kennington III t_pfx_len="${BASH_REMATCH[2]}" 9733f70c2dSWilliam A. Kennington III ip_to_bytes t_pfx_b "$t_pfx" || continue 9833f70c2dSWilliam A. Kennington III (( (t_pfx_len == 76 || t_pfx_len == 80) && (t_pfx_b[8] & 0xfd) == 0xfd )) || continue 9933f70c2dSWilliam A. Kennington III (( t_pfx_b[9] &= 0xf0 )) 10033f70c2dSWilliam A. Kennington III (( t_pfx_b[9] |= IP_OFFSET )) 10133f70c2dSWilliam A. Kennington III hextet="fd$(printf '%02x' "${t_pfx_b[9]}")" 10233f70c2dSWilliam A. Kennington III pfx="$(ip_bytes_to_str t_pfx_b)" 10333f70c2dSWilliam A. Kennington III elif [[ "$line" =~ ^'DNS search list'' '*:' '*([^.]+)(.*[.]google[.]com)' '*$ ]]; then 10433f70c2dSWilliam A. Kennington III # Ideally, we use PCRE and with lookahead and can do this in a single regex 10533f70c2dSWilliam A. Kennington III # ^([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)$ 10633f70c2dSWilliam A. Kennington III # Instead we do multiple steps to extract the needed info 10733f70c2dSWilliam A. Kennington III host="${BASH_REMATCH[1]}" 10833f70c2dSWilliam A. Kennington III domain="${BASH_REMATCH[2]#.}" 10933f70c2dSWilliam A. Kennington III if [[ "$host" =~ (-n[a-fA-F0-9]{1,4})$ ]]; then 11033f70c2dSWilliam A. Kennington III host="${host%"${BASH_REMATCH[1]}"}" 11133f70c2dSWilliam A. Kennington III fi 11233f70c2dSWilliam A. Kennington III elif [[ "$line" =~ ^from' '(.*)$ ]]; then 11333f70c2dSWilliam A. Kennington III rtr="${BASH_REMATCH[1]}" 11433f70c2dSWilliam A. Kennington III # Only valid default routers can be considered, 0 lifetime implies 11533f70c2dSWilliam A. Kennington III # a non-default router 11633f70c2dSWilliam A. Kennington III (( lifetime > 0 )) || continue 11733f70c2dSWilliam A. Kennington III 11833f70c2dSWilliam A. Kennington III dl=$((lifetime + SECONDS)) 11933f70c2dSWilliam A. Kennington III fqdn= 12033f70c2dSWilliam A. Kennington III if [[ -n $host && -n $hextet && -n $domain ]]; then 12133f70c2dSWilliam A. Kennington III fqdn="$host-n$hextet.$domain" 12233f70c2dSWilliam A. Kennington III fi 12333f70c2dSWilliam A. Kennington III rtrs["$rtr"]="$mac $dl $pfx $fqdn" 12433f70c2dSWilliam A. Kennington III # We have some notoriously noisy lab environments with many routers being broadcast 12533f70c2dSWilliam A. Kennington III # We always prefer "fe80::1" in prod and labs for routing, so prefer that gateway. 12633f70c2dSWilliam A. Kennington III # We also want to take the first router we find to speed up acquisition on boot. 12733f70c2dSWilliam A. Kennington III if [[ "$rtr" = "fe80::1" || "$old_rtr" = "invalid" ]]; then 12833f70c2dSWilliam A. Kennington III if [[ "$rtr" != "$old_rtr" && "$mac" != "$old_mac" ]]; then 12933f70c2dSWilliam A. Kennington III echo "Got defgw $rtr at $mac on $RA_IF" >&2 13033f70c2dSWilliam A. Kennington III update_rtr "$rtr" "$mac" || true 13133f70c2dSWilliam A. Kennington III retries=-1 13233f70c2dSWilliam A. Kennington III old_mac="$mac" 13333f70c2dSWilliam A. Kennington III old_rtr="$rtr" 13433f70c2dSWilliam A. Kennington III fi 13533f70c2dSWilliam A. Kennington III fi 13633f70c2dSWilliam A. Kennington III # Only update router properties if we use this router 13733f70c2dSWilliam A. Kennington III [[ "$rtr" == "$old_rtr" ]] || continue 13833f70c2dSWilliam A. Kennington III if [[ $pfx != "$old_pfx" ]]; then 13933f70c2dSWilliam A. Kennington III echo "Got PFX $pfx from $rtr on $RA_IF" >&2 14033f70c2dSWilliam A. Kennington III old_pfx="$pfx" 14133f70c2dSWilliam A. Kennington III update_pfx "$pfx" || true 14233f70c2dSWilliam A. Kennington III fi 14333f70c2dSWilliam A. Kennington III if [[ $fqdn != "$old_fqdn" ]]; then 14433f70c2dSWilliam A. Kennington III echo "Got FQDN $fqdn from $rtr on $RA_IF" >&2 14533f70c2dSWilliam A. Kennington III old_fqdn="$fqdn" 14633f70c2dSWilliam A. Kennington III update_fqdn "$fqdn" || true 14733f70c2dSWilliam A. Kennington III fi 14833f70c2dSWilliam A. Kennington III fi 14933f70c2dSWilliam A. Kennington III done < <(exec script -q -c "rdisc6 ${args[*]}" /dev/null 2>/dev/null) 15033f70c2dSWilliam A. Kennington III # Purge any expired routers 15133f70c2dSWilliam A. Kennington III for rtr in "${!rtrs[@]}"; do 15233f70c2dSWilliam A. Kennington III # shellcheck disable=SC2206 15333f70c2dSWilliam A. Kennington III data=(${rtrs["$rtr"]}) 15433f70c2dSWilliam A. Kennington III dl=${data[1]} 15533f70c2dSWilliam A. Kennington III if (( dl <= SECONDS )); then 15633f70c2dSWilliam A. Kennington III unset "rtrs[$rtr]" 15733f70c2dSWilliam A. Kennington III fi 15833f70c2dSWilliam A. Kennington III done 15933f70c2dSWilliam A. Kennington III # Consider changing the gateway if the old one doesn't send RAs for the entire period 16033f70c2dSWilliam A. Kennington III # This ensures we don't flip flop between multiple defaults if they exist. 16133f70c2dSWilliam A. Kennington III if [[ "$old_rtr" != "invalid" && -z "${rtrs["$old_rtr"]-}" ]]; then 16233f70c2dSWilliam A. Kennington III echo "Old router $old_rtr disappeared" >&2 16333f70c2dSWilliam A. Kennington III old_rtr=invalid 16433f70c2dSWilliam A. Kennington III for rtr in "${!rtrs[@]}"; do 16533f70c2dSWilliam A. Kennington III # shellcheck disable=SC2206 16633f70c2dSWilliam A. Kennington III data=(${rtrs["$rtr"]}) 16733f70c2dSWilliam A. Kennington III mac=${data[0]} 16833f70c2dSWilliam A. Kennington III dl=${data[1]} 16933f70c2dSWilliam A. Kennington III pfx=${data[2]} 17033f70c2dSWilliam A. Kennington III fqdn=${data[3]} 17133f70c2dSWilliam A. Kennington III update_rtr "$rtr" "$mac" || true 17233f70c2dSWilliam A. Kennington III update_pfx "$pfx" || true 17333f70c2dSWilliam A. Kennington III update_fqdn "$fqdn" || true 17433f70c2dSWilliam A. Kennington III break 17533f70c2dSWilliam A. Kennington III done 17633f70c2dSWilliam A. Kennington III fi 17733f70c2dSWilliam A. Kennington III 17833f70c2dSWilliam A. Kennington III # If rdisc6 exits early we still want to wait for the deadline before retrying 17933f70c2dSWilliam A. Kennington III (( timeout = curr_dl - SECONDS )) 18033f70c2dSWilliam A. Kennington III sleep $(( timeout < 0 ? 0 : timeout )) 18133f70c2dSWilliam A. Kennington IIIdone 182