#!/bin/sh # This script expect a system with resolvconf (openresolv) and iproute2 [ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1 ACTION="$1" IP_CACHE="/var/lib/misc/${interface}.cache" RESOLV_CONF="/run/resolvconf/interfaces/${interface}.conf" NTPFILE="/run/chrony/dhcp-sources.d/${interface}.sources" NAME="/etc/frr/static.d/${interface}-dhcp.conf" NEXT="${NAME}+" [ -n "$broadcast" ] && BROADCAST="broadcast $broadcast" [ -n "$subnet" ] || subnet=32 [ -n "$metric" ] || metric=5 # Handle stateful DHCPv6 like DHCPv4 [ -n "$ipv6" ] && ip="$ipv6/128" if [ -z "${IF_WAIT_DELAY}" ]; then IF_WAIT_DELAY=10 fi log() { logger -I $$ -t udhcpc -p user.notice "${interface}: $*" } dbg() { logger -I $$ -t udhcpc -p user.debug "${interface}: $*" } err() { logger -I $$ -t udhcpc -p user.err "${interface}: $*" } wait_for_ipv6_default_route() { dbg "waiting for IPv6 default route to be installed." while [ $IF_WAIT_DELAY -gt 0 ]; do if ip -6 route list proto dhcp dev $interface | grep -q default; then return fi sleep 1 printf "." : $((IF_WAIT_DELAY -= 1)) done err "Timed out waiting for IPv6 default route!" } # Check if a DHCP option was requested in the parameter request list # Returns: 0 if requested, 1 if not requested or config unavailable was_option_requested() { local opt_num="$1" local config="/etc/finit.d/available/dhcp-client-${interface}.conf" if [ ! -f "$config" ]; then dbg "config file not found: $config" return 1 fi # Extract udhcpc command line and check for -O if grep -q -- "-O ${opt_num}\b" "$config"; then return 0 fi return 1 } # RFC3442: If the DHCP server returns both a Classless # Static Routes option and a Router option, the DHCP # client MUST ignore the Router option. set_dhcp_routes() { echo "! Generated by udhcpc" > "$NEXT" if [ -n "$staticroutes" ]; then if was_option_requested 121; then # format: dest1/mask gw1 ... destn/mask gwn set -- $staticroutes while [ -n "$1" -a -n "$2" ]; do dbg "adding route $1 via $2 metric $metric tag 100" echo "ip route $1 $2 $metric tag 100" >> "$NEXT" shift 2 done else log "ignoring unrequested staticroutes (option 121)" fi elif [ -n "$router" ] ; then if was_option_requested 3; then for i in $router ; do echo "ip route 0.0.0.0/0 $i $metric tag 100" >> "$NEXT" done else log "ignoring unrequested router (option 3)" fi fi # Reduce changes needed by comparing with previous route(s) cmp -s "$NAME" "$NEXT" && return mv "$NEXT" "$NAME" initctl -nbq restart staticd } clr_dhcp_routes() { log "deleting DHCP routes" [ -f "$NAME" ] || return rm "$NAME" initctl -nbq restart staticd } clr_dhcp_addresses() { addrs=$(ip -j addr show dev $interface \ | jq -c '.[0].addr_info[] | select(.family == "inet") | select(.protocol == "dhcp")') for addr in $addrs; do ip="$(echo "$addr" | jq -r '."local"')" prefix="$(echo "$addr" | jq -r '."prefixlen"')" log "removing $ip/$prefix" ip addr del "$ip/$prefix" dev "$interface" done } log "action $ACTION" case "$ACTION" in deconfig) clr_dhcp_addresses clr_dhcp_routes # drop info from this interface rm -f "$RESOLV_CONF" rm -f "$NTPFILE" if [ -f "/etc/hostname.d/90-dhcp-${interface}" ]; then log "removing /etc/hostname.d/90-dhcp-${interface}" rm -f "/etc/hostname.d/90-dhcp-${interface}" /usr/libexec/infix/hostname dhcp fi if [ -x /usr/sbin/avahi-autoipd ]; then /usr/sbin/avahi-autoipd -c $interface && /usr/sbin/avahi-autoipd -k $interface fi ;; leasefail|nak) if [ -x /usr/sbin/avahi-autoipd ]; then /usr/sbin/avahi-autoipd -c $interface || /usr/sbin/avahi-autoipd -wD $interface --no-chroot fi ;; renew|bound) if [ -x /usr/sbin/avahi-autoipd ]; then /usr/sbin/avahi-autoipd -c $interface && /usr/sbin/avahi-autoipd -k $interface fi if /bin/ip addr add dev $interface $ip/$subnet $BROADCAST proto dhcp; then echo "$ip" > "$IP_CACHE" fi if [ -n "$ipv6" ] ; then wait_for_ipv6_default_route fi set_dhcp_routes # set hostname if given and requested if [ -n "$hostname" ]; then if was_option_requested 12; then log "received DHCP hostname: $hostname" mkdir -p /etc/hostname.d echo "$hostname" > "/etc/hostname.d/90-dhcp-${interface}" /usr/libexec/infix/hostname dhcp else log "ignoring unrequested hostname (option 12): $hostname" fi fi # drop info from this interface truncate -s 0 "$RESOLV_CONF" # prefer rfc3397 domain search list (option 119) if available search_list="" if [ -n "$search" ]; then if was_option_requested 119; then search_list=$search else log "ignoring unrequested search (option 119): $search" fi elif [ -n "$domain" ]; then if was_option_requested 15; then search_list=$domain else log "ignoring unrequested domain (option 15): $domain" fi fi if [ -n "$search_list" ]; then dbg "adding search $search_list" echo "search $search_list # $interface" >> $RESOLV_CONF fi if [ -n "$dns" ]; then if was_option_requested 6; then for i in $dns ; do dbg "adding dns $i" echo "nameserver $i # $interface" >> $RESOLV_CONF resolvconf -u done else log "ignoring unrequested dns (option 6): $dns" fi fi if [ -n "$ntpsrv" ]; then if was_option_requested 42; then truncate -s 0 "$NTPFILE" for srv in $ntpsrv; do dbg "got NTP server $srv" echo "server $srv iburst" >> "$NTPFILE" done chronyc reload sources >/dev/null else log "ignoring unrequested ntpsrv (option 42): $ntpsrv" fi fi esac HOOK_DIR="$0.d" for hook in "${HOOK_DIR}/"*; do [ -f "${hook}" -a -x "${hook}" ] || continue "${hook}" "$ACTION" done exit 0