1#!/bin/bash
2# Copyright 2021 Google LLC
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16[[ -n ${gbmc_br_ula_lib-} ]] && return
17
18# shellcheck source=meta-google/recipes-google/networking/network-sh/lib.sh
19source /usr/share/network/lib.sh || exit
20
21declare -A gbmc_br_ulas=()
22
23# BITs set for address suffixes
24GBMC_BR_ULA_SFX_HAS_LL=1
25GBMC_BR_ULA_SFX_HAS_ULA=2
26
27gbmc_br_ula_cleanup() {
28  local addr
29  for addr in "${!gbmc_br_ulas[@]}"; do
30    local val="${gbmc_br_ulas["$addr"]}"
31    if (( val & GBMC_BR_ULA_SFX_HAS_LL == 0 )); then
32      echo "Removing Stale ULA: $addr" >&2
33      ip addr del "$addr"/64 dev gbmcbr || true
34    fi
35  done
36}
37
38gbmc_br_ula_is_ll() {
39  # shellcheck disable=SC2178
40  local -n bytes="$1"
41  (( bytes[0] == 0xfe && bytes[1] == 0x80 && bytes[2] == 0x00 &&
42     bytes[3] == 0x00 && bytes[4] == 0x00 && bytes[5] == 0x00 &&
43     bytes[6] == 0x00 && bytes[7] == 0x00 ))
44}
45
46gbmc_br_ula_is_ula() {
47  # shellcheck disable=SC2178
48  local -n bytes="$1"
49  (( bytes[0] == 0xfd && bytes[1] == 0xb5 && bytes[2] == 0x04 &&
50     bytes[3] == 0x81 && bytes[4] == 0x10 && bytes[5] == 0xce &&
51     bytes[6] == 0x00 && bytes[7] == 0x00 ))
52}
53
54gbmc_br_ula_hook() {
55  # shellcheck disable=SC2154
56  if [[ $change == init ]]; then
57    gbmc_br_ula_cleanup
58  elif [[ $change == addr && $intf == gbmcbr && $fam == inet6 ]]; then
59    local pfx_bytes=()
60    ip_to_bytes pfx_bytes "$ip" || return
61    local val=0
62    if gbmc_br_ula_is_ll pfx_bytes; then
63      val="$GBMC_BR_ULA_SFX_HAS_LL"
64    elif gbmc_br_ula_is_ula pfx_bytes; then
65      val="$GBMC_BR_ULA_SFX_HAS_ULA"
66    else
67      return 0
68    fi
69    # Force all addresses into what they would be as a ULA so that we can
70    # store bits about the assigned addresses on the interface
71    pfx_bytes[0]=0xfd
72    pfx_bytes[1]=0xb5
73    pfx_bytes[2]=0x04
74    pfx_bytes[3]=0x81
75    pfx_bytes[4]=0x10
76    pfx_bytes[5]=0xce
77    addr="$(ip_bytes_to_str pfx_bytes)"
78    local old=${gbmc_br_ulas["$addr"]-0}
79    if [[ $action == add ]]; then
80      val=$((old | val))
81    elif [[ $action == del ]]; then
82      val=$((old & ~val))
83    fi
84    gbmc_br_ulas["$addr"]=$val
85    if (( val == GBMC_BR_ULA_SFX_HAS_LL )); then
86      # We have a link local address but no ULA, so we need to add the ULA
87      echo "Adding ULA: $addr" >&2
88      ip addr replace "$addr"/64 dev gbmcbr
89    elif (( val == GBMC_BR_ULA_SFX_HAS_ULA )); then
90      # We have a ULA without a link local, so we should not longer have this ULA
91      echo "Removing ULA: $addr" >&2
92      ip addr del "$addr"/64 dev gbmcbr || true
93    elif (( val == 0 )); then
94      # Cleanup the map if we no longer have any addresses for the suffix
95      unset 'gbmc_br_ulas[$addr]'
96    fi
97  fi
98}
99
100GBMC_IP_MONITOR_HOOKS+=(gbmc_br_ula_hook)
101
102gbmc_br_ula_lib=1
103