1#!/bin/bash 2# shellcheck disable=SC2034 3# shellcheck disable=SC2317 4# Copyright 2021 Google LLC 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17 18# A list of functions which get executed for each netlink event received. 19# These are configured by the files included below. 20GBMC_IP_MONITOR_HOOKS=() 21 22# Load configurations from a known location in the filesystem to populate 23# hooks that are executed after each event. 24shopt -s nullglob 25for conf in /usr/share/gbmc-ip-monitor/*.sh; do 26 # shellcheck source=/dev/null 27 source "$conf" 28done 29 30gbmc_ip_monitor_run_hooks() { 31 local hook 32 for hook in "${GBMC_IP_MONITOR_HOOKS[@]}"; do 33 "$hook" || continue 34 done 35} 36 37gbmc_ip_monitor_generate_init() { 38 ip link | sed 's,^[^ ],[LINK]\0,' 39 local intf= 40 local line 41 while read -r line; do 42 [[ "$line" =~ ^([0-9]+:[[:space:]][^:]+) ]] && intf="${BASH_REMATCH[1]}" 43 [[ "$line" =~ ^[[:space:]]*inet ]] && echo "[ADDR]$intf $line" 44 done < <(ip addr) 45 ip -4 route | sed 's,^,[ROUTE],' 46 ip -6 route | sed 's,^,[ROUTE],' 47 echo '[INIT]' 48} 49 50GBMC_IP_MONITOR_DEFER_OUTSTANDING= 51gbmc_ip_monitor_defer_() { 52 sleep 1 53 printf '[DEFER]\n' >&"$GBMC_IP_MONITOR_DEFER" 54} 55gbmc_ip_monitor_defer() { 56 [ -z "$GBMC_IP_MONITOR_DEFER_OUTSTANDING" ] || return 0 57 gbmc_ip_monitor_defer_ & 58 GBMC_IP_MONITOR_DEFER_OUTSTANDING=1 59} 60 61gbmc_ip_monitor_parse_line() { 62 local line="$1" 63 if [[ "$line" == '[INIT]'* ]]; then 64 change=init 65 echo "Initialized" >&2 66 elif [[ "$line" == '[ADDR]'* ]]; then 67 change=addr 68 action=add 69 pfx_re='^\[ADDR\](Deleted )?[0-9]+:[[:space:]]*' 70 intf_re='([^ ]+)[[:space:]]+' 71 fam_re='([^ ]+)[[:space:]]+' 72 addr_re='([^/]+)/[0-9]+[[:space:]]+' 73 metric_re='(metric[[:space:]]+[^ ]+[[:space:]]+)?' 74 brd_re='(brd[[:space:]]+[^ ]+[[:space:]]+)?' 75 scope_re='scope[[:space:]]+([^ ]+)[[:space:]]*(.*)' 76 combined_re="${pfx_re}${intf_re}${fam_re}${addr_re}${metric_re}${brd_re}${scope_re}" 77 if ! [[ "$line" =~ ${combined_re} ]]; then 78 echo "Failed to parse addr: $line" >&2 79 return 1 80 fi 81 if [ -n "${BASH_REMATCH[1]}" ]; then 82 action=del 83 fi 84 intf="${BASH_REMATCH[2]}" 85 fam="${BASH_REMATCH[3]}" 86 ip="${BASH_REMATCH[4]}" 87 scope="${BASH_REMATCH[7]}" 88 flags="${BASH_REMATCH[8]}" 89 elif [[ "$line" == '[ROUTE]'* ]]; then 90 line="${line#[ROUTE]}" 91 change=route 92 action=add 93 if ! [[ "$line" =~ ^\[ROUTE\](Deleted )?(.*)$ ]]; then 94 echo "Failed to parse link: $line" >&2 95 return 1 96 fi 97 if [ -n "${BASH_REMATCH[1]}" ]; then 98 action=del 99 fi 100 route="${BASH_REMATCH[2]}" 101 elif [[ "$line" == '[LINK]'* ]]; then 102 change='link' 103 action=add 104 pfx_re='^\[LINK\](Deleted )?[0-9]+:[[:space:]]*' 105 intf_re='([^:]+):[[:space:]]+' 106 if ! [[ "$line" =~ ${pfx_re}${intf_re} ]]; then 107 echo "Failed to parse link: $line" >&2 108 return 1 109 fi 110 if [ -n "${BASH_REMATCH[1]}" ]; then 111 action=del 112 fi 113 intf="${BASH_REMATCH[2]}" 114 read -ra data || return 115 mac="${data[1]}" 116 elif [[ "$line" == '[DEFER]'* ]]; then 117 GBMC_IP_MONITOR_DEFER_OUTSTANDING= 118 change=defer 119 else 120 return 2 121 fi 122} 123 124return 0 2>/dev/null 125 126cleanup() { 127 local st="$?" 128 trap - HUP INT QUIT ABRT TERM EXIT 129 jobs -l -p | xargs -r kill || true 130 exit "$st" 131} 132trap cleanup HUP INT QUIT ABRT TERM EXIT 133 134FIFODIR="$(mktemp -d)" 135mkfifo "$FIFODIR"/fifo 136exec {GBMC_IP_MONITOR_DEFER}<>"$FIFODIR"/fifo 137rm -rf "$FIFODIR" 138 139while read -r line; do 140 gbmc_ip_monitor_parse_line "$line" || continue 141 gbmc_ip_monitor_run_hooks || continue 142 if [ "$change" = 'init' ]; then 143 systemd-notify --ready 144 fi 145done < <(gbmc_ip_monitor_generate_init; ip monitor link addr route label & cat <&"$GBMC_IP_MONITOR_DEFER") 146