#!/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. # A list of functions which get executed for each netlink event received. # These are configured by the files included below. GBMC_IP_MONITOR_HOOKS=() # Load configurations from a known location in the filesystem to populate # hooks that are executed after each event. shopt -s nullglob for conf in /usr/share/gbmc-ip-monitor/*.sh; do source "$conf" done gbmc_ip_monitor_run_hooks() { local hook for hook in "${GBMC_IP_MONITOR_HOOKS[@]}"; do "$hook" || continue done } gbmc_ip_monitor_generate_init() { ip link | sed 's,^[^ ],[LINK]\0,' local intf= local line while read line; do [[ "$line" =~ ^([0-9]+:[[:space:]][^:]+) ]] && intf="${BASH_REMATCH[1]}" [[ "$line" =~ ^[[:space:]]*inet ]] && echo "[ADDR]$intf $line" done < <(ip addr) ip -4 route | sed 's,^,[ROUTE],' ip -6 route | sed 's,^,[ROUTE],' echo '[INIT]' } GBMC_IP_MONITOR_DEFER_OUTSTANDING= gbmc_ip_monitor_defer_() { sleep 1 printf '[DEFER]\n' >&$GBMC_IP_MONITOR_DEFER } gbmc_ip_monitor_defer() { [ -z "$GBMC_IP_MONITOR_DEFER_OUTSTANDING" ] || return 0 gbmc_ip_monitor_defer_ & GBMC_IP_MONITOR_DEFER_OUTSTANDING=1 } gbmc_ip_monitor_parse_line() { local line="$1" if [[ "$line" == '[INIT]'* ]]; then change=init echo "Initialized" >&2 elif [[ "$line" == '[ADDR]'* ]]; then change=addr action=add pfx_re='^\[ADDR\](Deleted )?[0-9]+:[[:space:]]*' intf_re='([^ ]+)[[:space:]]+' fam_re='([^ ]+)[[:space:]]+' addr_re='([^/]+)/[0-9]+[[:space:]]+(brd[[:space:]]+[^ ]+[[:space:]]+)?' scope_re='scope[[:space:]]+([^ ]+)[[:space:]]*(.*)' combined_re="${pfx_re}${intf_re}${fam_re}${addr_re}${scope_re}" if ! [[ "$line" =~ ${combined_re} ]]; then echo "Failed to parse addr: $line" >&2 return 1 fi if [ -n "${BASH_REMATCH[1]}" ]; then action=del fi intf="${BASH_REMATCH[2]}" fam="${BASH_REMATCH[3]}" ip="${BASH_REMATCH[4]}" scope="${BASH_REMATCH[6]}" flags="${BASH_REMATCH[7]}" elif [[ "$line" == '[ROUTE]'* ]]; then line="${line#[ROUTE]}" change=route action=add if ! [[ "$line" =~ ^\[ROUTE\](Deleted )?(.*)$ ]]; then echo "Failed to parse link: $line" >&2 return 1 fi if [ -n "${BASH_REMATCH[1]}" ]; then action=del fi route="${BASH_REMATCH[2]}" elif [[ "$line" == '[LINK]'* ]]; then change=link action=add pfx_re='^\[LINK\](Deleted )?[0-9]+:[[:space:]]*' intf_re='([^:]+):[[:space:]]+' if ! [[ "$line" =~ ${pfx_re}${intf_re} ]]; then echo "Failed to parse link: $line" >&2 return 1 fi if [ -n "${BASH_REMATCH[1]}" ]; then action=del fi intf="${BASH_REMATCH[2]}" read line || break data=($line) mac="${data[1]}" elif [[ "$line" == '[DEFER]'* ]]; then GBMC_IP_MONITOR_DEFER_OUTSTANDING= change=defer else return 2 fi } return 0 2>/dev/null cleanup() { local st="$?" trap - HUP INT QUIT ABRT TERM EXIT jobs -l -p | xargs -r kill || true exit $st } trap cleanup HUP INT QUIT ABRT TERM EXIT FIFODIR="$(mktemp -d)" mkfifo "$FIFODIR"/fifo exec {GBMC_IP_MONITOR_DEFER}<>"$FIFODIR"/fifo rm -rf "$FIFODIR" while read line; do gbmc_ip_monitor_parse_line "$line" || continue gbmc_ip_monitor_run_hooks || continue if [ "$change" = 'init' ]; then systemd-notify --ready fi done < <(gbmc_ip_monitor_generate_init; ip monitor link addr route label & cat <&$GBMC_IP_MONITOR_DEFER)