From 4a04e5f7968d46e446bdb530724da18b90b7f5f8 Mon Sep 17 00:00:00 2001 From: vivek Date: Mon, 2 May 2016 13:53:38 -0700 Subject: [PATCH] BGP: Trigger IPv6 router advertisements upon config of unnumbered neighbor Instead of turning on IPv6 RA on every interface as soon as it has an IPv6 address, only enable it upon configuration of BGP neighbor. When the BGP neighbor is deleted, signal that RAs can be turned off. To support this, introduce new message interaction between BGP and Zebra. Also, take appropriate actions in BGP upon interface add/del since the unnumbered neighbor could exist prior to interface creation etc. Only unnumbered IPv6 neighbors require RA, the /30 or /31 based neighbors don't. However, to keep the interaction simple and not have to deal with too many dynamic conditions (e.g., address deletes or neighbor change to/from 'v6only'), RAs on the interface are triggered upon any unnumbered neighbor configuration. BGP-triggered RAs will cause RAs to be initiated on the interface; however, if BGP asks that RAs be stopped (upon delete of unnumbered neighbor), RAs will continue to be exchanged if the operator has explicitly enabled. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-10640 Reviewed By: CCR-4589 Testing Done: Various manual and automated (refer to defect) --- bgpd/bgp_vty.c | 14 ++++++++++ bgpd/bgp_zebra.c | 67 +++++++++++++++++++++++++++++++++++++++++++++- bgpd/bgp_zebra.h | 3 +++ bgpd/bgpd.c | 1 + bgpd/bgpd.h | 1 + lib/log.c | 4 ++- lib/zclient.c | 31 +++++++++++++++++++++ lib/zclient.h | 3 +++ lib/zebra.h | 4 ++- zebra/connected.c | 13 --------- zebra/interface.c | 18 ------------- zebra/interface.h | 1 + zebra/rt_netlink.c | 7 ----- zebra/rtadv.c | 61 ++++++++++++++++++++++++++++++++++++++++- zebra/rtadv.h | 22 ++------------- zebra/rtadv_null.c | 3 ++- zebra/zserv.c | 7 +++++ 17 files changed, 197 insertions(+), 63 deletions(-) diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 768abcdf25..bdc57769eb 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -2712,6 +2712,14 @@ peer_conf_interface_get (struct vty *vty, const char *conf_if, afi_t afi, if (peer && v6only) SET_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY); + + /* Request zebra to initiate IPv6 RAs on this interface. We do this + * any unnumbered peer in order to not worry about run-time transitions + * (e.g., peering is initially IPv4, but the IPv4 /30 or /31 address + * gets deleted later etc.) + */ + if (peer->ifp) + bgp_zebra_initiate_radv (bgp, peer); } else if ((v6only && !CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY)) || (!v6only && CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))) @@ -2842,6 +2850,9 @@ DEFUN (no_neighbor, peer = peer_lookup_by_conf_if (vty->index, argv[0]); if (peer) { + /* Request zebra to terminate IPv6 RAs on this interface. */ + if (peer->ifp) + bgp_zebra_terminate_radv (peer->bgp, peer); peer_delete (peer); return CMD_SUCCESS; } @@ -2900,6 +2911,9 @@ DEFUN (no_neighbor_interface_config, peer = peer_lookup_by_conf_if (vty->index, argv[0]); if (peer) { + /* Request zebra to terminate IPv6 RAs on this interface. */ + if (peer->ifp) + bgp_zebra_terminate_radv (peer->bgp, peer); peer_delete (peer); } else diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 43653a36f8..7f5f47e062 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -167,6 +167,31 @@ bgp_read_import_check_update(int command, struct zclient *zclient, return 0; } +/* Set or clear interface on which unnumbered neighbor is configured. This + * would in turn cause BGP to initiate or turn off IPv6 RAs on this + * interface. + */ +static void +bgp_update_interface_nbrs (struct bgp *bgp, struct interface *ifp, + struct interface *upd_ifp) +{ + struct listnode *node, *nnode; + struct peer *peer; + + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->conf_if && + (strcmp (peer->conf_if, ifp->name) == 0)) + { + peer->ifp = upd_ifp; + if (upd_ifp) + bgp_zebra_initiate_radv (bgp, peer); + else + bgp_zebra_terminate_radv (bgp, peer); + } + } +} + static void bgp_start_interface_nbrs (struct bgp *bgp, struct interface *ifp) { @@ -241,12 +266,20 @@ bgp_interface_add (int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; + struct bgp *bgp; ifp = zebra_interface_add_read (zclient->ibuf, vrf_id); + if (!ifp) // unexpected + return 0; if (BGP_DEBUG (zebra, ZEBRA) && ifp) zlog_debug("Rx Intf add VRF %u IF %s", vrf_id, ifp->name); + bgp = bgp_lookup_by_vrf_id (vrf_id); + if (!bgp) + return 0; + + bgp_update_interface_nbrs (bgp, ifp, ifp); return 0; } @@ -256,10 +289,11 @@ bgp_interface_delete (int command, struct zclient *zclient, { struct stream *s; struct interface *ifp; + struct bgp *bgp; s = zclient->ibuf; ifp = zebra_interface_state_read (s, vrf_id); - if (! ifp) /* This may happen if we've just unregistered for a VRF. */ + if (!ifp) /* This may happen if we've just unregistered for a VRF. */ return 0; ifp->ifindex = IFINDEX_DELETED; @@ -267,6 +301,11 @@ bgp_interface_delete (int command, struct zclient *zclient, if (BGP_DEBUG (zebra, ZEBRA)) zlog_debug("Rx Intf del VRF %u IF %s", vrf_id, ifp->name); + bgp = bgp_lookup_by_vrf_id (vrf_id); + if (!bgp) + return 0; + + bgp_update_interface_nbrs (bgp, ifp, NULL); return 0; } @@ -1965,6 +2004,32 @@ bgp_zebra_instance_deregister (struct bgp *bgp) zclient_send_dereg_requests (zclient, bgp->vrf_id); } +void +bgp_zebra_initiate_radv (struct bgp *bgp, struct peer *peer) +{ + /* Don't try to initiate if we're not connected to Zebra */ + if (zclient->sock < 0) + return; + + if (BGP_DEBUG (zebra, ZEBRA)) + zlog_debug("%u: Initiating RA for peer %s", bgp->vrf_id, peer->host); + + zclient_send_interface_radv_req (zclient, bgp->vrf_id, peer->ifp, 1); +} + +void +bgp_zebra_terminate_radv (struct bgp *bgp, struct peer *peer) +{ + /* Don't try to terminate if we're not connected to Zebra */ + if (zclient->sock < 0) + return; + + if (BGP_DEBUG (zebra, ZEBRA)) + zlog_debug("%u: Terminating RA for peer %s", bgp->vrf_id, peer->host); + + zclient_send_interface_radv_req (zclient, bgp->vrf_id, peer->ifp, 0); +} + /* BGP has established connection with Zebra. */ static void bgp_zebra_connected (struct zclient *zclient) diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 308834ce2b..1770a2c0d5 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -38,6 +38,9 @@ extern void bgp_zebra_announce (struct prefix *, struct bgp_info *, struct bgp * extern void bgp_zebra_announce_table (struct bgp *, afi_t, safi_t); extern void bgp_zebra_withdraw (struct prefix *, struct bgp_info *, safi_t); +extern void bgp_zebra_initiate_radv (struct bgp *bgp, struct peer *peer); +extern void bgp_zebra_terminate_radv (struct bgp *bgp, struct peer *peer); + extern void bgp_zebra_instance_register (struct bgp *); extern void bgp_zebra_instance_deregister (struct bgp *); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 26c8f48c42..1a2c874c24 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1339,6 +1339,7 @@ bgp_peer_conf_if_to_su_update (struct peer *peer) prev_family = peer->su.sa.sa_family; if ((ifp = if_lookup_by_name_vrf (peer->conf_if, peer->bgp->vrf_id))) { + peer->ifp = ifp; /* If BGP unnumbered is not "v6only", we first see if we can derive the * peer's IPv4 address. */ diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index a07bd389f5..d15a58ecd4 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -578,6 +578,7 @@ struct peer unsigned int ifindex; /* ifindex of the BGP connection. */ char *conf_if; /* neighbor interface config name. */ + struct interface *ifp; /* corresponding interface */ char *ifname; /* bind interface name. */ char *update_if; union sockunion *update_source; diff --git a/lib/log.c b/lib/log.c index b031206a21..a8577bf9a4 100644 --- a/lib/log.c +++ b/lib/log.c @@ -876,7 +876,9 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY (ZEBRA_REDISTRIBUTE_IPV6_ADD), DESC_ENTRY (ZEBRA_REDISTRIBUTE_IPV6_DEL), DESC_ENTRY (ZEBRA_INTERFACE_VRF_UPDATE), - DESC_ENTRY (ZEBRA_BFD_CLIENT_REGISTER) + DESC_ENTRY (ZEBRA_BFD_CLIENT_REGISTER), + DESC_ENTRY (ZEBRA_INTERFACE_ENABLE_RADV), + DESC_ENTRY (ZEBRA_INTERFACE_DISABLE_RADV) }; #undef DESC_ENTRY diff --git a/lib/zclient.c b/lib/zclient.c index 1aa1251bf5..8cc6bd92a4 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -481,6 +481,37 @@ zclient_send_dereg_requests (struct zclient *zclient, vrf_id_t vrf_id) zebra_message_send (zclient, ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, vrf_id); } +/* Send request to zebra daemon to start or stop RA. */ +void +zclient_send_interface_radv_req (struct zclient *zclient, vrf_id_t vrf_id, + struct interface *ifp, int enable) +{ + struct stream *s; + + /* zclient is disabled. */ + if (!zclient->enable) + return; + + /* If not connected to the zebra yet. */ + if (zclient->sock < 0) + return; + + /* Form and send message. */ + s = zclient->obuf; + stream_reset (s); + + if (enable) + zclient_create_header (s, ZEBRA_INTERFACE_ENABLE_RADV, vrf_id); + else + zclient_create_header (s, ZEBRA_INTERFACE_DISABLE_RADV, vrf_id); + + stream_putl (s, ifp->ifindex); + + stream_putw_at (s, 0, stream_get_endp (s)); + + zclient_send_message(zclient); +} + /* Make connection to zebra daemon. */ int zclient_start (struct zclient *zclient) diff --git a/lib/zclient.h b/lib/zclient.h index d411965217..726f254a2a 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -175,6 +175,9 @@ extern void redist_del_instance (struct redist_proto *, u_short); extern void zclient_send_reg_requests (struct zclient *, vrf_id_t); extern void zclient_send_dereg_requests (struct zclient *, vrf_id_t); +extern void zclient_send_interface_radv_req (struct zclient *zclient, vrf_id_t vrf_id, + struct interface *ifp, int enable); + /* Send redistribute command to zebra daemon. Do not update zclient state. */ extern int zebra_redistribute_send (int command, struct zclient *, afi_t, int type, u_short instance, vrf_id_t vrf_id); diff --git a/lib/zebra.h b/lib/zebra.h index 751e8ce3df..38a1889e72 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -453,7 +453,9 @@ struct in_pktinfo #define ZEBRA_VRF_DELETE 44 #define ZEBRA_INTERFACE_VRF_UPDATE 45 #define ZEBRA_BFD_CLIENT_REGISTER 46 -#define ZEBRA_MESSAGE_MAX 47 +#define ZEBRA_INTERFACE_ENABLE_RADV 47 +#define ZEBRA_INTERFACE_DISABLE_RADV 48 +#define ZEBRA_MESSAGE_MAX 49 /* Marker value used in new Zserv, in the byte location corresponding * the command value in the old zserv header. To allow old and new diff --git a/zebra/connected.c b/zebra/connected.c index 272689b594..7da40ce03a 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -66,13 +66,6 @@ connected_withdraw (struct connected *ifc) /* The address is not in the kernel anymore, so clear the flag */ UNSET_FLAG(ifc->conf, ZEBRA_IFC_QUEUED); - /* Enable RA suppression if there are no IPv6 addresses on this interface */ - if (interface_ipv6_auto_ra_allowed (ifc->ifp)) - { - if (! ipv6_address_configured(ifc->ifp)) - ipv6_nd_suppress_ra_set (ifc->ifp, RA_SUPPRESS); - } - if (!CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) { listnode_delete (ifc->ifp->connected, ifc); @@ -100,12 +93,6 @@ connected_announce (struct interface *ifp, struct connected *ifc) if (ifc->address->family == AF_INET) if_subnet_add (ifp, ifc); - else if (ifc->address->family == AF_INET6) - { - if (interface_ipv6_auto_ra_allowed (ifp)) - ipv6_nd_suppress_ra_set (ifp, RA_ENABLE); - } - zebra_interface_address_add_update (ifp, ifc); if (if_is_operative(ifp)) diff --git a/zebra/interface.c b/zebra/interface.c index 66ab536cf6..91eb5c49ce 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -684,9 +684,6 @@ if_handle_vrf_change (struct interface *ifp, vrf_id_t vrf_id) /* Delete all neighbor addresses learnt through IPv6 RA */ if_down_del_nbr_connected (ifp); - /* Suppress RAs on this interface, if enabled. */ - ipv6_nd_suppress_ra_set (ifp, RA_SUPPRESS); - /* Send out notification on interface VRF change. */ /* This is to issue an UPDATE or a DELETE, as appropriate. */ zebra_interface_vrf_update_del (ifp, vrf_id); @@ -701,10 +698,6 @@ if_handle_vrf_change (struct interface *ifp, vrf_id_t vrf_id) /* Install connected routes (in new VRF). */ if_install_connected (ifp); - /* Enable RAs on this interface, if IPv6 addresses are present. */ - if (ipv6_address_configured(ifp)) - ipv6_nd_suppress_ra_set (ifp, RA_ENABLE); - /* Due to connected route change, schedule RIB processing for both old * and new VRF. */ @@ -1929,10 +1922,6 @@ ipv6_address_install (struct vty *vty, struct interface *ifp, /* Add to linked list. */ listnode_add (ifp->connected, ifc); - - /* Enable RA on this interface */ - if (interface_ipv6_auto_ra_allowed (ifp)) - ipv6_nd_suppress_ra_set (ifp, RA_ENABLE); } /* This address is configured from zebra. */ @@ -2031,13 +2020,6 @@ ipv6_address_uninstall (struct vty *vty, struct interface *ifp, return CMD_WARNING; } - /* Enable RA suppression if there are no IPv6 addresses on this interface */ - if (interface_ipv6_auto_ra_allowed (ifp)) - { - if (! ipv6_address_configured(ifp)) - ipv6_nd_suppress_ra_set (ifp, RA_SUPPRESS); - } - UNSET_FLAG (ifc->conf, ZEBRA_IFC_QUEUED); /* This information will be propagated to the zclients when the * kernel notification is received. */ diff --git a/zebra/interface.h b/zebra/interface.h index 2e88566e1a..3784253e6d 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -172,6 +172,7 @@ struct rtadvconf #define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */ u_char inFastRexmit; /* True if we're rexmits faster than usual */ + u_char configured; /* Has operator configured RA? */ int NumFastReXmitsRemain; /* Loaded first with number of fast rexmits to do */ #define RTADV_FAST_REXMIT_PERIOD 1 /* 1 sec */ diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 4102cc375d..0c7d72130a 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1350,13 +1350,6 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h, /* pre-configured interface, learnt now */ if (ifp->vrf_id != vrf_id) if_update_vrf (ifp, name, strlen(name), vrf_id); - - /* Start IPv6 RA, if any IPv6 addresses on interface. */ - if (interface_ipv6_auto_ra_allowed (ifp)) - { - if (ipv6_address_configured (ifp)) - ipv6_nd_suppress_ra_set (ifp, RA_ENABLE); - } } /* Update interface information. */ diff --git a/zebra/rtadv.c b/zebra/rtadv.c index e3e390d565..9179701302 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -26,6 +26,7 @@ #include "sockopt.h" #include "thread.h" #include "if.h" +#include "stream.h" #include "log.h" #include "prefix.h" #include "linklist.h" @@ -734,7 +735,7 @@ rtadv_prefix_reset (struct zebra_if *zif, struct rtadv_prefix *rp) return 0; } -void +static void ipv6_nd_suppress_ra_set (struct interface *ifp, ipv6_nd_suppress_ra_status status) { struct zebra_if *zif; @@ -783,6 +784,58 @@ ipv6_nd_suppress_ra_set (struct interface *ifp, ipv6_nd_suppress_ra_status statu } } +/* + * Handle client (BGP) message to enable or disable IPv6 RA on an interface. + * Note that while the client could request RA on an interface on which the + * operator has not enabled RA, RA won't be disabled upon client request + * if the operator has explicitly enabled RA. + */ +void +zebra_interface_radv_set (struct zserv *client, int sock, u_short length, + struct zebra_vrf *zvrf, int enable) +{ + struct stream *s; + unsigned int ifindex; + struct interface *ifp; + struct zebra_if *zif; + + s = client->ibuf; + + /* Get interface index. */ + ifindex = stream_getl (s); + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%u: IF %u RA %s from client %s", + zvrf->vrf_id, ifindex, enable ? "enable" : "disable", + zebra_route_string(client->proto)); + + /* Locate interface and check VRF match. */ + ifp = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT), ifindex); + if (!ifp) + { + zlog_warn("%u: IF %u RA %s client %s - interface unknown", + zvrf->vrf_id, ifindex, enable ? "enable" : "disable", + zebra_route_string(client->proto)); + return; + } + if (ifp->vrf_id != zvrf->vrf_id) + { + zlog_warn("%u: IF %u RA %s client %s - VRF mismatch, IF VRF %u", + zvrf->vrf_id, ifindex, enable ? "enable" : "disable", + zebra_route_string(client->proto), ifp->vrf_id); + return; + } + + zif = ifp->info; + if (enable) + ipv6_nd_suppress_ra_set (ifp, RA_ENABLE); + else + { + if (!zif->rtadv.configured) + ipv6_nd_suppress_ra_set (ifp, RA_SUPPRESS); + } +} + DEFUN (ipv6_nd_suppress_ra, ipv6_nd_suppress_ra_cmd, "ipv6 nd suppress-ra", @@ -791,6 +844,7 @@ DEFUN (ipv6_nd_suppress_ra, "Suppress Router Advertisement\n") { struct interface *ifp; + struct zebra_if *zif; ifp = vty->index; @@ -802,6 +856,8 @@ DEFUN (ipv6_nd_suppress_ra, } ipv6_nd_suppress_ra_set (ifp, RA_SUPPRESS); + zif = ifp->info; + zif->rtadv.configured = 0; return CMD_SUCCESS; } @@ -814,6 +870,7 @@ DEFUN (no_ipv6_nd_suppress_ra, "Suppress Router Advertisement\n") { struct interface *ifp; + struct zebra_if *zif; ifp = vty->index; @@ -825,6 +882,8 @@ DEFUN (no_ipv6_nd_suppress_ra, } ipv6_nd_suppress_ra_set (ifp, RA_ENABLE); + zif = ifp->info; + zif->rtadv.configured = 1; return CMD_SUCCESS; } diff --git a/zebra/rtadv.h b/zebra/rtadv.h index a3ea425931..3011505dbc 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -108,26 +108,8 @@ typedef enum { extern void rtadv_init (struct zebra_ns *); extern void rtadv_terminate (struct zebra_ns *); extern void rtadv_cmd_init (void); -extern void ipv6_nd_suppress_ra_set (struct interface *ifp, ipv6_nd_suppress_ra_status status); +extern void zebra_interface_radv_set (struct zserv *client, int sock, u_short length, + struct zebra_vrf *zvrf, int enable); -/* Can we turn on IPv6 RAs automatically on this interface? */ -static inline int -interface_ipv6_auto_ra_allowed (struct interface *ifp) -{ -#if defined (HAVE_RTADV) - if (if_is_loopback (ifp) || - CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK)) - return 0; -#if defined (HAVE_CUMULUS) - if ((strncmp (ifp->name, "eth", strlen("eth")) == 0) || - (strncmp (ifp->name, "switch", strlen("switch")) == 0)) - return 0; -#endif - return 1; -#else - return 0; -#endif -} - #endif /* _ZEBRA_RTADV_H */ diff --git a/zebra/rtadv_null.c b/zebra/rtadv_null.c index 851c8e0a64..ee6eda6bdd 100644 --- a/zebra/rtadv_null.c +++ b/zebra/rtadv_null.c @@ -24,7 +24,8 @@ #include #include -void ipv6_nd_suppress_ra_set (struct interface *ifp, ipv6_nd_suppress_ra_status status) +void zebra_interface_radv_set (struct zserv *client, int sock, u_short length, + struct zebra_vrf *zvrf, int enable) { return; } void rtadv_init (struct zebra_ns *zns) diff --git a/zebra/zserv.c b/zebra/zserv.c index 3872d17409..9491b39a91 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -50,6 +50,7 @@ #include "zebra/rt_netlink.h" #include "zebra/interface.h" #include "zebra/zebra_ptm.h" +#include "zebra/rtadv.h" /* Event list of zebra. */ enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE }; @@ -2026,6 +2027,12 @@ zebra_client_read (struct thread *thread) case ZEBRA_BFD_CLIENT_REGISTER: zebra_ptm_bfd_client_register(client, sock, length); break; + case ZEBRA_INTERFACE_ENABLE_RADV: + zebra_interface_radv_set (client, sock, length, zvrf, 1); + break; + case ZEBRA_INTERFACE_DISABLE_RADV: + zebra_interface_radv_set (client, sock, length, zvrf, 0); + break; default: zlog_info ("Zebra received unknown command %d", command); break; -- 2.39.5