From 8dee039673865b234d14d567163a206bc93cd5a4 Mon Sep 17 00:00:00 2001 From: vivek Date: Mon, 29 Feb 2016 18:04:29 +0000 Subject: [PATCH] BGP: Unnumbered peering in a VRF Code changes to make unnumbered peering work in a VRF. The changes have to do with locating the interface in the correct VRF (in order to look for neighbor address) in the case of outgoing connections and when specifying source address as well as fetching the correct instance for an incoming connection based on reading the device the socket is bound to (the multi-vrf socket option in the kernel). Additionally, for IPv4 unnumbered peering in a VRF (based on /30 or /31 addresses), bind to the VRF rather than the interface. Signed-off-by: Vivek Venkatraman Reviewed-by: Donald Sharp Ticket: CM-9311 Reviewed By: CCR-4192 Testing Done: Manual, bgpsmoke --- bgpd/bgp_network.c | 120 +++++++++++++++++++++++++++++++-------------- bgpd/bgpd.c | 2 +- 2 files changed, 85 insertions(+), 37 deletions(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index d5ac479876..debb87a1fe 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -217,6 +217,65 @@ bgp_set_socket_ttl (struct peer *peer, int bgp_sock) return ret; } +/* + * Obtain the BGP instance that the incoming connection should be processed + * against. This is important because more than one VRF could be using the + * same IP address space. The instance is got by obtaining the device to + * which the incoming connection is bound to. This could either be a VRF + * or it could be an interface, which in turn determines the VRF. + */ +static int +bgp_get_instance_for_inc_conn (int sock, struct bgp **bgp_inst) +{ + char name[VRF_NAMSIZ + 1]; + socklen_t name_len = VRF_NAMSIZ; + struct bgp *bgp; + int rc; + struct listnode *node, *nnode; + + *bgp_inst = NULL; + name[0] = '\0'; + rc = getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, name, &name_len); + if (rc != 0) + { + zlog_err ("[Error] BGP SO_BINDTODEVICE get failed (%s), sock %d", + safe_strerror (errno), sock); + return -1; + } + + if (!strlen(name)) + return 0; /* default instance. */ + + /* First try match to instance; if that fails, check for interfaces. */ + bgp = bgp_lookup_by_name (name); + if (bgp) + { + if (!bgp->vrf_id) // unexpected + return -1; + *bgp_inst = bgp; + return 0; + } + + /* TODO - This will be optimized once interfaces move into the NS */ + for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) + { + struct interface *ifp; + + if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) + continue; + + ifp = if_lookup_by_name_vrf (name, bgp->vrf_id); + if (ifp) + { + *bgp_inst = bgp; + return 0; + } + } + + /* We didn't match to either an instance or an interface. */ + return -1; +} + /* Accept bgp connection. */ static int bgp_accept (struct thread *thread) @@ -228,10 +287,7 @@ bgp_accept (struct thread *thread) struct peer *peer; struct peer *peer1; char buf[SU_ADDRSTRLEN]; - struct bgp *bgp; - char name[VRF_NAMSIZ + 1]; - int rc; - socklen_t name_len = VRF_NAMSIZ; + struct bgp *bgp = NULL; sockunion_init (&su); @@ -253,38 +309,14 @@ bgp_accept (struct thread *thread) } set_nonblocking (bgp_sock); - name[0] = '\0'; - rc = getsockopt(bgp_sock, SOL_SOCKET, SO_BINDTODEVICE, name, &name_len); - if (rc != 0) + /* Obtain BGP instance this connection is meant for. */ + if (bgp_get_instance_for_inc_conn (bgp_sock, &bgp)) { - zlog_err ("[Error] BGP SO_BINDTODEVICE get failed (%s)", safe_strerror (errno)); + zlog_err ("[Error] Could not get instance for incoming conn from %s", + inet_sutop (&su, buf)); + close (bgp_sock); return -1; } - else if (name[0] != '\0') - { - /* Pending: - - Cleanup/add proper debugs in this area. - - Test/find a way to implement interface config within a VRF. - */ - zlog_debug ("BGP accept vrf/interface %s, %u", name, name_len); - bgp = bgp_lookup_by_name (name); - if (bgp) - { - if (bgp->vrf_id) - zlog_debug ("BGP SO_BINDTODEVICE vrf-id in BGP %u", bgp->vrf_id); - else - { - zlog_debug ("BGP vrf not active!"); - return -1; - } - } - /* if socket is interface bound, control may reach here, because name - may be equal to the interface name */ - } - else - { - bgp = NULL; - } /* Set socket send buffer size */ bgp_update_sock_send_buffer_size(bgp_sock); @@ -417,12 +449,28 @@ bgp_bind (struct peer *peer) { #ifdef SO_BINDTODEVICE int ret; - char *name; + char *name = NULL; + /* If not bound to an interface or part of a VRF, we don't care. */ if (!peer->bgp->vrf_id && ! peer->ifname && !peer->conf_if) return 0; - name = (peer->conf_if ? peer->conf_if : (peer->ifname ? peer->ifname : peer->bgp->name)); + if (peer->su.sa.sa_family != AF_INET && + peer->su.sa.sa_family != AF_INET6) + return 0; // unexpected + + /* For IPv6 peering, interface (unnumbered or link-local with interface) + * takes precedence over VRF. For IPv4 peering, explicit interface or + * VRF are the situations to bind. + */ + if (peer->su.sa.sa_family == AF_INET6) + name = (peer->conf_if ? peer->conf_if : \ + (peer->ifname ? peer->ifname : peer->bgp->name)); + else + name = peer->ifname ? peer->ifname : peer->bgp->name; + + if (!name) + return 0; if (bgp_debug_neighbor_events(peer)) zlog_debug ("%s Binding to interface %s", peer->host, name); @@ -491,7 +539,7 @@ bgp_update_source (struct peer *peer) /* Source is specified with interface name. */ if (peer->update_if) { - ifp = if_lookup_by_name (peer->update_if); + ifp = if_lookup_by_name_vrf (peer->update_if, peer->bgp->vrf_id); if (! ifp) return -1; diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index ab5de2f83b..868f8c0cb6 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1316,7 +1316,7 @@ bgp_peer_conf_if_to_su_update (struct peer *peer) return; prev_family = peer->su.sa.sa_family; - if ((ifp = if_lookup_by_name(peer->conf_if))) + if ((ifp = if_lookup_by_name_vrf (peer->conf_if, peer->bgp->vrf_id))) { /* If BGP unnumbered is not "v6only", we first see if we can derive the * peer's IPv4 address. -- 2.39.5