From adee8f216498b1e7e8798bf96189786328095010 Mon Sep 17 00:00:00 2001 From: Don Slice Date: Fri, 22 Nov 2019 17:31:29 +0000 Subject: [PATCH] zebra: knob to make ra retransmit interval rfc compliant Problem reported by testing facility that our sending of Router Advertisements more frequently than once very three seconds is not compliant with rfc4861. Added a knob to turn off fast retransmits in order to meet the requirement of the RFC. Ticket: CM-27063 Signed-off-by: Don Slice --- doc/user/ipv6.rst | 14 ++++++++ zebra/interface.c | 4 ++- zebra/interface.h | 7 ++++ zebra/rtadv.c | 87 ++++++++++++++++++++++++++++++++++++++++++++--- zebra/rtadv.h | 5 +++ 5 files changed, 111 insertions(+), 6 deletions(-) diff --git a/doc/user/ipv6.rst b/doc/user/ipv6.rst index cc8fd18fee..f3f064b850 100644 --- a/doc/user/ipv6.rst +++ b/doc/user/ipv6.rst @@ -76,6 +76,20 @@ Router Advertisement advertisements from the interface, in milliseconds. Default: ``600000`` +.. index:: + single: ipv6 nd ra-fast-retrans + single: no ipv6 nd ra-fast-retrans +.. clicmd:: [no] ipv6 nd ra-fast-retrans + + RFC4861 states that consecutive RA packets should be sent no more + frequently than three seconds apart. FRR by default allows faster + transmissions of RA packets in order to speed convergence and + neighbor establishment, particularly for unnumbered peering. By + turning off ipv6 nd ra-fast-retrans, the implementation is + compliant with the RFC at the cost of slower convergence + and neighbor establishment. + Default: enabled + .. index:: single: ipv6 nd ra-lifetime (0-9000) single: no ipv6 nd ra-lifetime [(0-9000)] diff --git a/zebra/interface.c b/zebra/interface.c index eea80652e5..64a7e9abc0 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -161,6 +161,7 @@ static int if_zebra_new_hook(struct interface *ifp) rtadv->HomeAgentLifetime = -1; /* derive from AdvDefaultLifetime */ rtadv->AdvIntervalOption = 0; + rtadv->UseFastRexmit = true; rtadv->DefaultPreference = RTADV_PREF_MEDIUM; rtadv->AdvPrefixList = list_new(); @@ -1037,7 +1038,8 @@ void if_up(struct interface *ifp) #if defined(HAVE_RTADV) /* Enable fast tx of RA if enabled && RA interval is not in msecs */ if (zif->rtadv.AdvSendAdvertisements - && (zif->rtadv.MaxRtrAdvInterval >= 1000)) { + && (zif->rtadv.MaxRtrAdvInterval >= 1000) + && zif->rtadv.UseFastRexmit) { zif->rtadv.inFastRexmit = 1; zif->rtadv.NumFastReXmitsRemain = RTADV_NUM_FAST_REXMITS; } diff --git a/zebra/interface.h b/zebra/interface.h index 78ccbae623..b7e90a0c31 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -189,6 +189,13 @@ struct rtadvconf { */ struct list *AdvDNSSLList; + /* + * rfc4861 states RAs must be sent at least 3 seconds apart. + * We allow faster retransmits to speed up convergence but can + * turn that capability off to meet the rfc if needed. + */ + bool UseFastRexmit; /* True if fast rexmits are enabled */ + uint8_t inFastRexmit; /* True if we're rexmits faster than usual */ /* Track if RA was configured by BGP or by the Operator or both */ diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 4903455a2b..f51c199f6b 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -495,7 +495,8 @@ static int rtadv_timer(struct thread *thread) zif = ifp->info; if (zif->rtadv.AdvSendAdvertisements) { - if (zif->rtadv.inFastRexmit) { + if (zif->rtadv.inFastRexmit + && zif->rtadv.UseFastRexmit) { /* We assume we fast rexmit every sec so * no * additional vars */ @@ -535,9 +536,28 @@ static int rtadv_timer(struct thread *thread) static void rtadv_process_solicit(struct interface *ifp) { struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id); + struct zebra_if *zif; assert(zvrf); - rtadv_send_packet(rtadv_get_socket(zvrf), ifp); + zif = ifp->info; + + /* + * If FastRetransmit is enabled, send the RA immediately. + * If not enabled but it has been more than MIN_DELAY_BETWEEN_RAS + * (3 seconds) since the last RA was sent, send it now and reset + * the timer to start at the max (configured) again. + * If not enabled and it is less than 3 seconds since the last + * RA packet was sent, set the timer for 3 seconds so the next + * one will be sent with a minimum of 3 seconds between RAs. + * RFC4861 sec 6.2.6 + */ + if ((zif->rtadv.UseFastRexmit) + || (zif->rtadv.AdvIntervalTimer <= + (zif->rtadv.MaxRtrAdvInterval - MIN_DELAY_BETWEEN_RAS))) { + rtadv_send_packet(rtadv_get_socket(zvrf), ifp); + zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; + } else + zif->rtadv.AdvIntervalTimer = MIN_DELAY_BETWEEN_RAS; } /* @@ -904,9 +924,12 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, zif->rtadv.AdvIntervalTimer = 0; zvrf->rtadv.adv_if_count++; - if (zif->rtadv.MaxRtrAdvInterval >= 1000) { - /* Enable Fast RA only when RA interval is in - * secs */ + if ((zif->rtadv.MaxRtrAdvInterval >= 1000) + && zif->rtadv.UseFastRexmit) { + /* + * Enable Fast RA only when RA interval is in + * secs and Fast RA retransmit is enabled + */ zif->rtadv.inFastRexmit = 1; zif->rtadv.NumFastReXmitsRemain = RTADV_NUM_FAST_REXMITS; @@ -996,6 +1019,51 @@ void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS) zebra_interface_radv_set(client, hdr, msg, zvrf, 1); } +DEFUN (ipv6_nd_ra_fast_retrans, + ipv6_nd_ra_fast_retrans_cmd, + "ipv6 nd ra-fast-retrans", + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Fast retransmit of RA packets\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif = ifp->info; + + if (if_is_loopback(ifp) + || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) { + vty_out(vty, + "Cannot configure IPv6 Router Advertisements on this interface\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + zif->rtadv.UseFastRexmit = true; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_nd_ra_fast_retrans, + no_ipv6_nd_ra_fast_retrans_cmd, + "no ipv6 nd ra-fast-retrans", + NO_STR + "Interface IPv6 config commands\n" + "Neighbor discovery\n" + "Fast retransmit of RA packets\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif = ifp->info; + + if (if_is_loopback(ifp) + || CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) { + vty_out(vty, + "Cannot configure IPv6 Router Advertisements on this interface\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + zif->rtadv.UseFastRexmit = false; + + return CMD_SUCCESS; +} + DEFUN (ipv6_nd_suppress_ra, ipv6_nd_suppress_ra_cmd, "ipv6 nd suppress-ra", @@ -1954,6 +2022,10 @@ static int nd_dump_vty(struct vty *vty, struct interface *ifp) " ND router advertisements are sent every " "%d seconds\n", interval / 1000); + if (!rtadv->UseFastRexmit) + vty_out(vty, + " ND router advertisements do not use fast retransmit\n"); + if (rtadv->AdvDefaultLifetime != -1) vty_out(vty, " ND router advertisements live for %d seconds\n", @@ -2025,6 +2097,9 @@ static int rtadv_config_write(struct vty *vty, struct interface *ifp) if (zif->rtadv.AdvIntervalOption) vty_out(vty, " ipv6 nd adv-interval-option\n"); + if (!zif->rtadv.UseFastRexmit) + vty_out(vty, " no ipv6 nd ra-fast-retrans\n"); + if (zif->rtadv.AdvDefaultLifetime != -1) vty_out(vty, " ipv6 nd ra-lifetime %d\n", zif->rtadv.AdvDefaultLifetime); @@ -2173,6 +2248,8 @@ void rtadv_cmd_init(void) hook_register(zebra_if_extra_info, nd_dump_vty); hook_register(zebra_if_config_wr, rtadv_config_write); + install_element(INTERFACE_NODE, &ipv6_nd_ra_fast_retrans_cmd); + install_element(INTERFACE_NODE, &no_ipv6_nd_ra_fast_retrans_cmd); install_element(INTERFACE_NODE, &ipv6_nd_suppress_ra_cmd); install_element(INTERFACE_NODE, &no_ipv6_nd_suppress_ra_cmd); install_element(INTERFACE_NODE, &ipv6_nd_ra_interval_cmd); diff --git a/zebra/rtadv.h b/zebra/rtadv.h index d692ef2417..f9bd2b1d39 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -59,6 +59,11 @@ struct rtadv_prefix { #endif }; +/* RFC4861 minimum delay between RAs */ +#ifndef MIN_DELAY_BETWEEN_RAS +#define MIN_DELAY_BETWEEN_RAS 3000 +#endif + /* RFC4584 Extension to Sockets API for Mobile IPv6 */ #ifndef ND_OPT_ADV_INTERVAL -- 2.39.5