From fa6851caa60df8ebb0ed9554d2b676e37155afc0 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sun, 22 Sep 2024 11:05:17 +0200 Subject: [PATCH] board/common: set DHCP and ZeroConf routes using Frr/staticd This patch changes the way Infix DHCP and ZerocConf clients set their routes in the system. Instead of setting them directly in the kernel we ask FRR staticd to set them for us. The reason for this change is to be able to override routes from these protocols with locally set static routes. The routes are now set with a distance of 5 and 254, respectively, while static routes by default have a distance of 1. In contrast, kernel routes are always treated by Frr as distance 0, i.e., they are preferred over static routes. Finally, this patch drops the use of Linux legacy interface aliases, or colon interfaces, in an effort to reduce confusion for end users. This may give some odd results if using older tools like ifconfig, so we recommend using 'show interfaces' or 'ipb a' instead. Fixes #640 Signed-off-by: Joachim Wiberg --- .../rootfs/etc/avahi/avahi-autoipd.action | 43 ++++++++++++------- board/common/rootfs/etc/frr/static.d/.empty | 0 .../rootfs/etc/frr/static.d/00default.conf | 9 ++++ board/common/rootfs/usr/sbin/staticd-helper | 15 +++++++ .../rootfs/usr/share/udhcpc/default.script | 35 +++++++-------- .../skeleton-init-finit.mk | 1 + .../etc/finit.d/available/frr/staticd.conf | 5 ++- src/confd/src/ietf-routing.c | 27 +++--------- 8 files changed, 80 insertions(+), 55 deletions(-) create mode 100644 board/common/rootfs/etc/frr/static.d/.empty create mode 100644 board/common/rootfs/etc/frr/static.d/00default.conf create mode 100755 board/common/rootfs/usr/sbin/staticd-helper diff --git a/board/common/rootfs/etc/avahi/avahi-autoipd.action b/board/common/rootfs/etc/avahi/avahi-autoipd.action index 239d7853..20912b82 100755 --- a/board/common/rootfs/etc/avahi/avahi-autoipd.action +++ b/board/common/rootfs/etc/avahi/avahi-autoipd.action @@ -7,38 +7,49 @@ # STOP: The daemon is terminating # $2 interface name # $3 IP adddress -# -# For details on the distance adjustment below, please see the following -# section in the Frr documentation on distance + prefence => to metric. -# https://docs.frrouting.org/en/stable-8.0/zebra.html#administrative-distance PATH="$PATH:/usr/bin:/usr/sbin:/bin:/sbin" +NAME="/etc/frr/static.d/$2-zeroconf.conf" +NEXT="${NAME}+" log() { logger -I $$ -t zeroconf -p user.notice "$*" } -# Use a different metric for each interface as tie breaker, to ensure we -# can set identical routes to multiple interfaces. -IFINDEX=$(cat "/sys/class/net/$2/ifindex" 2>/dev/null || echo 0) +# Reduce changes needed by comparing with previous route(s) +act() +{ + case $1 in + add) + echo "! Generated by avahi-autoipd" > "$NEXT" + echo "ip route 0.0.0.0/0 $2 254" >> "$NEXT" + cmp -s "$NAME" "$NEXT" && return + mv "$NEXT" "$NAME" + ;; + del) + [ -f "$NAME" ] || return + rm "$NAME" + ;; + *) + return + ;; + esac -# Distance adjustment to ensure that static routes set by Zebra and any -# routes learned from DHCP take precedence over these interface routes. -# A distance of 255 ensures these routes are never redistributed. -METRIC=$(((255 << 24) + 1000 + IFINDEX)) + initctl -nbq restart staticd +} case "$1" in BIND) - ip addr flush dev "$2" label "$2:avahi" - ip addr add "$3"/16 brd 169.254.255.255 label "$2:avahi" scope link dev "$2" proto random - ip route add default dev "$2" metric "$METRIC" scope link proto zeroconf + ip addr flush dev "$2" proto random + ip addr add "$3"/16 brd 169.254.255.255 scope link dev "$2" proto random + act add "$2" log "set ipv4ll $3 on iface $2" ;; CONFLICT|UNBIND|STOP) - ip route del default dev "$2" metric "$METRIC" scope link - ip addr flush dev "$2" label "$2:avahi" + act del "$2" + ip addr flush dev "$2" proto random log "clr ipv4ll on iface $2" ;; diff --git a/board/common/rootfs/etc/frr/static.d/.empty b/board/common/rootfs/etc/frr/static.d/.empty new file mode 100644 index 00000000..e69de29b diff --git a/board/common/rootfs/etc/frr/static.d/00default.conf b/board/common/rootfs/etc/frr/static.d/00default.conf new file mode 100644 index 00000000..c23631f5 --- /dev/null +++ b/board/common/rootfs/etc/frr/static.d/00default.conf @@ -0,0 +1,9 @@ +! Default settings for staticd, used for both +! confd generated routes, udhcpc and zeroconf +frr defaults traditional +hostname Router +password zebra +enable password zebra +no log unique-id +log syslog informational +log facility local2 diff --git a/board/common/rootfs/usr/sbin/staticd-helper b/board/common/rootfs/usr/sbin/staticd-helper new file mode 100755 index 00000000..744b2cd3 --- /dev/null +++ b/board/common/rootfs/usr/sbin/staticd-helper @@ -0,0 +1,15 @@ +#!/bin/sh +# Sort and collate all /etc/frr/static.d/*.conf files managed by confd, +# udhcpc, and avahi-autoipd before starting staticd. + +DIRD=/etc/frr/static.d +NAME=/etc/frr/staticd.conf +NEXT=${NAME}+ + +rm -f "$NEXT" +find "$DIRD" -type f -name '*.conf' ! -name '*~' | sort | while read -r file; do + cat "$file" >> "$NEXT" +done + +cmp -s "$NAME" "$NEXT" && exit 0 +mv "$NEXT" "$NAME" diff --git a/board/common/rootfs/usr/share/udhcpc/default.script b/board/common/rootfs/usr/share/udhcpc/default.script index 73e4c78b..1917e790 100755 --- a/board/common/rootfs/usr/share/udhcpc/default.script +++ b/board/common/rootfs/usr/share/udhcpc/default.script @@ -1,9 +1,5 @@ #!/bin/sh # This script expect a system with resolvconf (openresolv) and iproute2 -# -# For details on the distance adjustment below, please see the following -# section in the Frr documentation on distance + prefence => to metric. -# https://docs.frrouting.org/en/stable-8.0/zebra.html#administrative-distance [ -z "$1" ] && echo "Error: should be called from udhcpc" && exit 1 @@ -11,13 +7,12 @@ 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=100 - -# Distance adjustment to ensure that static routes set by Zebra -# can take precedence over routes learned from DHCP. -metric=$(((5 << 24) + metric)) +[ -n "$metric" ] || metric=5 # Handle stateful DHCPv6 like DHCPv4 [ -n "$ipv6" ] && ip="$ipv6/128" @@ -31,7 +26,7 @@ log() logger -I $$ -t udhcpc -p user.notice "$*" } -wwait_for_ipv6_default_route() +wait_for_ipv6_default_route() { log "waiting for IPv6 default route to be installed." while [ $IF_WAIT_DELAY -gt 0 ]; do @@ -50,28 +45,35 @@ wwait_for_ipv6_default_route() # client MUST ignore the Router option. set_dhcp_routes() { + echo "! Generated by udhcpc" > "$NEXT" if [ -n "$staticroutes" ]; then # format: dest1/mask gw1 ... destn/mask gwn set -- $staticroutes while [ -n "$1" -a -n "$2" ]; do log "adding route $1 via $2 dev $interface proto dhcp" - ip route add "$1" via "$2" dev $interface metric $metric proto dhcp + echo "ip route $1 $2 $metric tag 100" >> "$NEXT" shift 2 done elif [ -n "$router" ] ; then for i in $router ; do - ip route add default via $i dev $interface metric $((metric++)) proto dhcp + echo "ip route 0.0.0.0/0 $i $metric tag 100" >> "$NEXT" done 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 from $interface" - ip route show proto dhcp dev $interface | while read rt via nh dev dev; do - log "removing $rt nh $nh on $dev" - ip route del $rt via $nh dev $dev proto dhcp - done + [ -f "$NAME" ] || return + rm "$NAME" + + initctl -nbq restart staticd } clr_dhcp_addresses() @@ -120,7 +122,6 @@ case "$ACTION" in wait_for_ipv6_default_route fi - clr_dhcp_routes set_dhcp_routes # set hostname if given diff --git a/package/skeleton-init-finit/skeleton-init-finit.mk b/package/skeleton-init-finit/skeleton-init-finit.mk index c1339e22..30088ec0 100644 --- a/package/skeleton-init-finit/skeleton-init-finit.mk +++ b/package/skeleton-init-finit/skeleton-init-finit.mk @@ -89,6 +89,7 @@ define SKELETON_INIT_FINIT_SET_FRR for svc in babeld bfdd bgpd eigrpd isisd ldpd ospfd ospf6d pathd ripd ripng staticd vrrpd zebra; do \ cp $(SKELETON_INIT_FINIT_AVAILABLE)/frr/$$svc.conf $(FINIT_D)/available/$$svc.conf; \ done + ln -sf ../available/staticd.conf $(FINIT_D)/enabled/staticd.conf ln -sf ../available/zebra.conf $(FINIT_D)/enabled/zebra.conf endef SKELETON_INIT_FINIT_POST_INSTALL_TARGET_HOOKS += SKELETON_INIT_FINIT_SET_FRR diff --git a/package/skeleton-init-finit/skeleton/etc/finit.d/available/frr/staticd.conf b/package/skeleton-init-finit/skeleton/etc/finit.d/available/frr/staticd.conf index d6fae1ae..e3af6b5f 100644 --- a/package/skeleton-init-finit/skeleton/etc/finit.d/available/frr/staticd.conf +++ b/package/skeleton-init-finit/skeleton/etc/finit.d/available/frr/staticd.conf @@ -1 +1,4 @@ -service [2345] log:null staticd -A 127.0.0.1 -u frr -g frr -f /etc/frr/staticd.conf -- Static routing daemon +# Run staticd-helper to collate /etc/static.d/*.conf before starting staticd +service log:null pre:/usr/sbin/staticd-helper \ + [2345] staticd -A 127.0.0.1 -u frr -g frr -f /etc/frr/staticd.conf \ + -- Static routing daemon diff --git a/src/confd/src/ietf-routing.c b/src/confd/src/ietf-routing.c index de6aacfb..b3fc4dea 100644 --- a/src/confd/src/ietf-routing.c +++ b/src/confd/src/ietf-routing.c @@ -8,7 +8,7 @@ #define XPATH_BASE_ "/ietf-routing:routing/control-plane-protocols/control-plane-protocol" #define XPATH_OSPF_ XPATH_BASE_ "/ietf-ospf:ospf" -#define STATICD_CONF "/etc/frr/staticd.conf" +#define STATICD_CONF "/etc/frr/static.d/confd.conf" #define STATICD_CONF_NEXT STATICD_CONF "+" #define STATICD_CONF_PREV STATICD_CONF "-" #define OSPFD_CONF "/etc/frr/ospfd.conf" @@ -245,7 +245,7 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su const char *xpath, sr_event_t event, unsigned request_id, void *priv) { int staticd_enabled = 0, ospfd_enabled = 0, bfdd_enabled = 0; - bool ospfd_running, staticd_running, bfdd_running; + bool ospfd_running, bfdd_running; struct lyd_node *cplane, *tmp; bool restart_zebra = false; int rc = SR_ERR_OK; @@ -260,7 +260,7 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su ERROR("Failed to open %s", STATICD_CONF_NEXT); return SR_ERR_INTERNAL; } - fputs(FRR_STATIC_CONFIG, fp); + fputs("! Generated by Infix confd\n", fp); break; case SR_EV_ABORT: /* User abort, or other plugin failed */ @@ -272,18 +272,8 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su staticd_enabled = fexist(STATICD_CONF_NEXT); ospfd_enabled = fexist(OSPFD_CONF_NEXT); bfdd_enabled = fexist(BFDD_CONF_NEXT); - staticd_running = !systemf("initctl -bfq status staticd"); ospfd_running = !systemf("initctl -bfq status ospfd"); bfdd_running = !systemf("initctl -bfq status bfdd"); - if (staticd_running && !staticd_enabled) { - if (systemf("initctl -bfq disable staticd")) { - ERROR("Failed to disable static routing daemon"); - rc = SR_ERR_INTERNAL; - goto err_abandon; - } - /* Remove all generated files */ - (void)remove(STATICD_CONF); - } if (bfdd_running && !bfdd_enabled) { if (systemf("initctl -bfq disable bfdd")) { @@ -337,15 +327,10 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su (void)remove(STATICD_CONF_PREV); (void)rename(STATICD_CONF, STATICD_CONF_PREV); (void)rename(STATICD_CONF_NEXT, STATICD_CONF); - if (!staticd_running) { - if (systemf("initctl -bfq enable staticd")) { - ERROR("Failed to enable static routing daemon"); - rc = SR_ERR_INTERNAL; - goto err_abandon; - } - } else { + restart_zebra = true; + } else { + if (!remove(STATICD_CONF)) restart_zebra = true; - } } if (restart_zebra) {