e01e56bfa3
This is the first MR to replace !125, and contains everything except the new python stuff -- which is part two.
188 lines
4.8 KiB
Bash
Executable File
188 lines
4.8 KiB
Bash
Executable File
#!/bin/sh
|
|
# vim: set ts=4 et:
|
|
|
|
# This script should be installed as symlinks in
|
|
# /etc/udhcpc/<hook>/eth-eni-hook
|
|
# <hook> :-
|
|
# post-bound - after udhcpc binds an IP to the interface
|
|
# post-renew - after udhcpc renews the lease for the IP
|
|
#
|
|
# udhcpc provides via ENV...
|
|
# IFACE - eth0, etc.
|
|
# mask - bits in IPv4 subnet mask
|
|
|
|
set -e
|
|
|
|
HOOK="$(basename "$(dirname "$0")")"
|
|
|
|
DEBUG=
|
|
|
|
log() {
|
|
[ -z "$DEBUG" ] && [ "$1" = "debug" ] && return
|
|
FACILITY="daemon.$1"
|
|
shift
|
|
logger -s -p "$FACILITY" -t "udhcpc/${HOOK}[$$]" -- "$@"
|
|
}
|
|
|
|
if [ -z "$IFACE" ] || [ -z "$mask" ]; then
|
|
log err "Missing 'IFACE' or 'mask' ENV from udhcpc"
|
|
exit 1
|
|
fi
|
|
|
|
# route table number
|
|
RTABLE="${IFACE#eth}"
|
|
let RTABLE+=1000
|
|
|
|
IMDS=X-aws-ec2-metadata-token
|
|
IMDS_IP=169.254.169.254
|
|
IMDS_MAC="http://$IMDS_IP/latest/meta-data/network/interfaces/macs/$(
|
|
cat "/sys/class/net/$IFACE/address")"
|
|
|
|
get_imds_token() {
|
|
IMDS_TOKEN="$(echo -ne \
|
|
"PUT /latest/api/token HTTP/1.0\r\n$IMDS-ttl-seconds: 60\r\n\r\n" |
|
|
nc "$IMDS_IP" 80 | tail -n 1)"
|
|
}
|
|
|
|
mac_meta() {
|
|
wget -qO - --header "$IMDS: $IMDS_TOKEN" "$IMDS_MAC/$1" 2>/dev/null \
|
|
|| true # when no ipv6s attached (yet), IMDS returns 404 error
|
|
}
|
|
|
|
ip() {
|
|
FAIL_OK=
|
|
if [ "$1" = '+F' ]; then
|
|
FAIL_OK=1
|
|
shift
|
|
fi
|
|
v=-4
|
|
if [ "$1" = '-4' ] || [ "$1" = '-6' ]; then
|
|
v="$1"
|
|
shift
|
|
fi
|
|
OP="$2"
|
|
[ "$OP" = show ] && LEV=debug || LEV=info
|
|
if /sbin/ip "$v" "$@" || [ -n "$FAIL_OK" ]; then
|
|
log "$LEV" "OK: ip $v $*"
|
|
else
|
|
log err "FAIL: ip $v $*"
|
|
fi
|
|
}
|
|
|
|
iface_ip4s() {
|
|
ip -4 addr show "$IFACE" secondary |
|
|
sed -E -e '/inet /!d' -e 's/.*inet ([0-9.]+).*/\1/'
|
|
}
|
|
|
|
iface_ip6s() {
|
|
ip -6 addr show "$IFACE" scope global |
|
|
sed -E -e '/inet6/!d' -e 's/.*inet6 ([0-9a-f:]+).*/\1/'
|
|
}
|
|
|
|
ec2_ip4s() {
|
|
get_imds_token
|
|
# NOTE: metadata for ENI arrives later than hotplug events
|
|
TRIES=60
|
|
while true; do
|
|
IP4="$(mac_meta local-ipv4s)"
|
|
[ -n "$IP4" ] && break
|
|
let TRIES--
|
|
if [ "$TRIES" -eq 0 ]; then
|
|
log err "Unable to get IPv4 addresses for $IFACE after 30s"
|
|
exit 1
|
|
fi
|
|
sleep 0.5
|
|
done
|
|
IP4S="$(echo "$IP4" | tail +2)" # secondary IPs (udhcpc handles primary)
|
|
|
|
# non-eth0 interfaces need custom route tables
|
|
#
|
|
if [ "$IFACE" != eth0 ] && [ -n "$IP4S" ] &&
|
|
[ -z "$(ip +F -4 route show table "$RTABLE")" ]; then
|
|
IP4P="$(echo "$IP4" | head -1)" # primary IP
|
|
IP4_CIDR="$(mac_meta subnet-ipv4-cidr-block)"
|
|
IP4_GW="$(echo "$IP4_CIDR" | cut -d/ -f1 |
|
|
awk -F. '{ print $1"."$2"."$3"."$4+1 }')"
|
|
ip -4 route add default via "$IP4_GW" dev "$IFACE" table "$RTABLE"
|
|
ip -4 route add "$IP4_CIDR" dev "$IFACE" proto kernel scope link \
|
|
src "$IP4P" table "$RTABLE"
|
|
fi
|
|
echo "$IP4S"
|
|
}
|
|
|
|
ec2_ip6s() {
|
|
get_imds_token
|
|
# NOTE: IPv6 metadata (if any) may arrive later than IPv4 metadata
|
|
TRIES=60
|
|
while true; do
|
|
IP6S="$(mac_meta ipv6s)"
|
|
[ -n "$IP6S" ] && break
|
|
let TRIES--
|
|
if [ "$TRIES" -eq 0 ]; then
|
|
log warn "Unable to get IPv6 addresses for $IFACE after 30s"
|
|
break
|
|
fi
|
|
sleep 0.5
|
|
done
|
|
|
|
# non-eth0 interfaces need custom route tables
|
|
#
|
|
# NOTE: busybox iproute2 doesn't do 'route show table' properly for IPv6,
|
|
# so iproute2-minimal package is required!
|
|
#
|
|
if [ "$IFACE" != eth0 ] && [ -n "$IP6S" ] &&
|
|
[ -z "$(ip +F -6 route show table "$RTABLE")" ]; then
|
|
TRIES=20
|
|
while true; do
|
|
GW="$(ip -6 route show dev "$IFACE" default | awk '{ print $3 }')"
|
|
[ -n "$GW" ] && break
|
|
let TRIES--
|
|
if [ "$TRIES" -eq 0 ]; then
|
|
log warn "Unable to get IPv6 gateway RA after 10s"
|
|
break
|
|
fi
|
|
sleep 0.5
|
|
done
|
|
ip -6 route add default via "$GW" dev "$IFACE" table "$RTABLE"
|
|
fi
|
|
echo "$IP6S"
|
|
}
|
|
|
|
in_list() {
|
|
echo "$2" | grep -q "^$1$"
|
|
}
|
|
|
|
# ip_addr {4|6} {add|del} <ip>
|
|
ip_addr() {
|
|
[ "$1" -eq 6 ] && MASK=128 || MASK="$mask" # IP6s are always /128
|
|
ip -"$1" addr "$2" "$3/$MASK" dev "$IFACE"
|
|
[ "$IFACE" = eth0 ] && return
|
|
|
|
# non-eth0 interfaces get rules associating IPs with route tables
|
|
ip -"$1" rule "$2" from "$3" lookup "$RTABLE"
|
|
}
|
|
|
|
# sync_ips {4|6} "<ec2-ips>" "<iface-ips>"
|
|
sync_ips() {
|
|
# remove extra IPs
|
|
for i in $3; do
|
|
in_list "$i" "$2" || ip_addr "$1" del "$i"
|
|
done
|
|
# add missing IPs
|
|
for i in $2; do
|
|
in_list "$i" "$3" || ip_addr "$1" add "$i"
|
|
done
|
|
}
|
|
|
|
log info "STARTING: $IFACE"
|
|
|
|
if [ "$HOOK" != post-bound ] && [ "$HOOK" != post-renew ]; then
|
|
log err "Unhandled udhcpc hook '$HOOK'"
|
|
exit 1
|
|
fi
|
|
|
|
sync_ips 4 "$(ec2_ip4s)" "$(iface_ip4s)"
|
|
sync_ips 6 "$(ec2_ip6s)" "$(iface_ip6s)"
|
|
|
|
log info "FINISHED: $IFACE"
|