From d7fc0e677e8c6804e00e770b4bd2c080c7ba028a Mon Sep 17 00:00:00 2001 From: Don Slice Date: Tue, 3 Dec 2019 14:02:20 +0000 Subject: [PATCH] zebra: send RA lifetime of 0 before ceasing to advertise RAs Problem reported by testing agency that RFC4861 section 6.2.5 states that a router should send an RA with a lifetime of 0 before ceasing to send RAs on the interface, or when the interace is shutdown, or the router is shutdown. This fix adds that capability. Ticket: CM-27061 Signed-off-by: Don Slice --- zebra/interface.c | 2 ++ zebra/main.c | 3 +++ zebra/rtadv.c | 48 ++++++++++++++++++++++++++++++++++++++++++----- zebra/rtadv.h | 2 ++ 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/zebra/interface.c b/zebra/interface.c index 20b05dfb3..bcb833b8d 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1977,6 +1977,8 @@ DEFUN (shutdown_if, struct zebra_if *if_data; if (ifp->ifindex != IFINDEX_INTERNAL) { + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv_stop_ra(ifp); ret = if_unset_flags(ifp, IFF_UP); if (ret < 0) { vty_out(vty, "Can't shutdown interface\n"); diff --git a/zebra/main.c b/zebra/main.c index 731c4e161..e0408c0bc 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -145,6 +145,9 @@ static void sigint(void) atomic_store_explicit(&zrouter.in_shutdown, true, memory_order_relaxed); + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv_stop_ra_all(); + frr_early_fini(); zebra_dplane_pre_finish(); diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 0adf654aa..5dd6012f6 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -166,7 +166,8 @@ static int rtadv_recv_packet(struct zebra_vrf *zvrf, int sock, uint8_t *buf, #define RTADV_MSG_SIZE 4096 /* Send router advertisement packet. */ -static void rtadv_send_packet(int sock, struct interface *ifp) +static void rtadv_send_packet(int sock, struct interface *ifp, + ipv6_nd_suppress_ra_status stop) { struct msghdr msg; struct iovec iov; @@ -252,7 +253,10 @@ static void rtadv_send_packet(int sock, struct interface *ifp) zif->rtadv.AdvDefaultLifetime != -1 ? zif->rtadv.AdvDefaultLifetime : MAX(1, 0.003 * zif->rtadv.MaxRtrAdvInterval); - rtadv->nd_ra_router_lifetime = htons(pkt_RouterLifetime); + + /* send RA lifetime of 0 before stopping. rfc4861/6.2.5 */ + rtadv->nd_ra_router_lifetime = + (stop == RA_SUPPRESS) ? htons(0) : htons(pkt_RouterLifetime); rtadv->nd_ra_reachable = htonl(zif->rtadv.AdvReachableTime); rtadv->nd_ra_retransmit = htonl(0); @@ -512,7 +516,7 @@ static int rtadv_timer(struct thread *thread) ifp->name); rtadv_send_packet(rtadv_get_socket(zvrf), - ifp); + ifp, RA_ENABLE); } else { zif->rtadv.AdvIntervalTimer -= period; if (zif->rtadv.AdvIntervalTimer <= 0) { @@ -526,7 +530,7 @@ static int rtadv_timer(struct thread *thread) .MaxRtrAdvInterval; rtadv_send_packet( rtadv_get_socket(zvrf), - ifp); + ifp, RA_ENABLE); } } } @@ -556,7 +560,7 @@ static void rtadv_process_solicit(struct interface *ifp) if ((zif->rtadv.UseFastRexmit) || (zif->rtadv.AdvIntervalTimer <= (zif->rtadv.MaxRtrAdvInterval - MIN_DELAY_BETWEEN_RAS))) { - rtadv_send_packet(rtadv_get_socket(zvrf), ifp); + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_ENABLE); zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; } else zif->rtadv.AdvIntervalTimer = MIN_DELAY_BETWEEN_RAS; @@ -911,6 +915,8 @@ static void ipv6_nd_suppress_ra_set(struct interface *ifp, if (status == RA_SUPPRESS) { /* RA is currently enabled */ if (zif->rtadv.AdvSendAdvertisements) { + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, + RA_SUPPRESS); zif->rtadv.AdvSendAdvertisements = 0; zif->rtadv.AdvIntervalTimer = 0; zvrf->rtadv.adv_if_count--; @@ -1012,6 +1018,38 @@ stream_failure: return; } +/* + * send router lifetime value of zero in RAs on this interface since we're + * ceasing to advertise and want to let our neighbors know. + * RFC 4861 secion 6.2.5 + */ +void rtadv_stop_ra(struct interface *ifp) +{ + struct zebra_if *zif; + struct zebra_vrf *zvrf; + + zif = ifp->info; + zvrf = vrf_info_lookup(ifp->vrf_id); + + if (zif->rtadv.AdvSendAdvertisements) + rtadv_send_packet(rtadv_get_socket(zvrf), ifp, RA_SUPPRESS); +} + +/* + * send router lifetime value of zero in RAs on all interfaces since we're + * ceasing to advertise globally and want to let all of our neighbors know + * RFC 4861 secion 6.2.5 + */ +void rtadv_stop_ra_all(void) +{ + struct vrf *vrf; + struct interface *ifp; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) + FOR_ALL_INTERFACES (vrf, ifp) + rtadv_stop_ra(ifp); +} + void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS) { zebra_interface_radv_set(client, hdr, msg, zvrf, 0); diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 409959d08..63cec9443 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -140,6 +140,8 @@ typedef enum { extern void rtadv_init(struct zebra_vrf *zvrf); extern void rtadv_terminate(struct zebra_vrf *zvrf); +extern void rtadv_stop_ra(struct interface *ifp); +extern void rtadv_stop_ra_all(void); extern void rtadv_cmd_init(void); extern void zebra_interface_radv_disable(ZAPI_HANDLER_ARGS); extern void zebra_interface_radv_enable(ZAPI_HANDLER_ARGS); -- 2.39.5