From: vivek Date: Sat, 29 Aug 2015 23:10:12 +0000 (-0700) Subject: BGP: Fix MD5 authentication for unnumbered neighbors X-Git-Tag: frr-2.0-rc1~1271^2~2 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=89ca90fad972362d622c50883dc0d36314d002e5;p=matthieu%2Ffrr.git BGP: Fix MD5 authentication for unnumbered neighbors Ticket: CM-6369 Reviewed By: CCR-3318 Testing Done: Manual testing of various password scenarios. This is a port of patch bgpd-unnumbered-nbr-fix-password.patch from 2.5-br. In the case of BGP unnumbered, the peer IP address is derived and not explicitly configured. If there is a password configured for the peer, it can be set on the listen socket only after the IP address has been derived and needs to be cleared when the IP address goes away. --- diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index a3ad3cc9dc..6b0b792f4a 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -104,8 +104,8 @@ bgp_md5_set_connect (int socket, union sockunion *su, const char *password) return ret; } -int -bgp_md5_set (struct peer *peer) +static int +bgp_md5_set_password (struct peer *peer, const char *password) { struct listnode *node; int ret = 0; @@ -117,13 +117,13 @@ bgp_md5_set (struct peer *peer) return -1; } - /* Just set the password on the listen socket(s). Outbound connections + /* Set or unset the password on the listen socket(s). Outbound connections * are taken care of in bgp_connect() below. */ for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener)) if (listener->su.sa.sa_family == peer->su.sa.sa_family) { - ret = bgp_md5_set_socket (listener->fd, &peer->su, peer->password); + ret = bgp_md5_set_socket (listener->fd, &peer->su, password); break; } @@ -133,6 +133,20 @@ bgp_md5_set (struct peer *peer) return ret; } +int +bgp_md5_set (struct peer *peer) +{ + /* Set the password from listen socket. */ + return bgp_md5_set_password (peer, peer->password); +} + +int +bgp_md5_unset (struct peer *peer) +{ + /* Unset the password from listen socket. */ + return bgp_md5_set_password (peer, NULL); +} + /* Update BGP socket send buffer size */ static void bgp_update_sock_send_buffer_size (int fd) diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 4216967fbf..1148d1978b 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -29,6 +29,7 @@ extern int bgp_connect (struct peer *); extern int bgp_getsockname (struct peer *); extern int bgp_md5_set (struct peer *); +extern int bgp_md5_unset (struct peer *); extern int bgp_set_socket_ttl(struct peer *, int fd); #endif /* _QUAGGA_BGP_NETWORK_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index eb25fd9909..14066462b8 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1168,83 +1168,127 @@ peer_xfer_config (struct peer *peer_dst, struct peer *peer_src) } } -/* - * Set or reset the peer address socketunion structure based on the - * learnt peer address. Currently via the source address of the - * ipv6 ND router-advertisement. - */ -void -bgp_peer_conf_if_to_su_update (struct peer *peer) +static int +bgp_peer_conf_if_to_su_update_v4 (struct peer *peer, struct interface *ifp) { - struct interface *ifp; - struct nbr_connected *ifc_nbr; struct connected *ifc; struct prefix p; u_int32_t s_addr; struct listnode *node; - if (!peer->conf_if) - return; - - if ((ifp = if_lookup_by_name(peer->conf_if))) + /* If our IPv4 address on the interface is /30 or /31, we can derive the + * IPv4 address of the other end. + */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { - /* if multiple IP addresses assigned to link, we pick the first */ - if (!CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY)) - for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) - if (ifc->address && (ifc->address->family == AF_INET)) - { - /* Try IPv4 connection first, if present */ - PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc)); - /* We can determine peer's IP address if prefixlen is 30/31 */ - if (p.prefixlen == 30) - { - peer->su.sa.sa_family = AF_INET; - s_addr = ntohl(p.u.prefix4.s_addr); - if (s_addr % 4 == 1) - peer->su.sin.sin_addr.s_addr = htonl(s_addr+1); - else if (s_addr % 4 == 2) - peer->su.sin.sin_addr.s_addr = htonl(s_addr-1); + if (ifc->address && (ifc->address->family == AF_INET)) + { + PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc)); + if (p.prefixlen == 30) + { + peer->su.sa.sa_family = AF_INET; + s_addr = ntohl(p.u.prefix4.s_addr); + if (s_addr % 4 == 1) + peer->su.sin.sin_addr.s_addr = htonl(s_addr+1); + else if (s_addr % 4 == 2) + peer->su.sin.sin_addr.s_addr = htonl(s_addr-1); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - peer->su->sin.sin_len = sizeof(struct sockaddr_in); + peer->su->sin.sin_len = sizeof(struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - return; - } - else if (p.prefixlen == 31) - { - peer->su.sa.sa_family = AF_INET; - s_addr = ntohl(p.u.prefix4.s_addr); - if (s_addr % 2 == 0) - peer->su.sin.sin_addr.s_addr = htonl(s_addr+1); - else - peer->su.sin.sin_addr.s_addr = htonl(s_addr-1); + return 1; + } + else if (p.prefixlen == 31) + { + peer->su.sa.sa_family = AF_INET; + s_addr = ntohl(p.u.prefix4.s_addr); + if (s_addr % 2 == 0) + peer->su.sin.sin_addr.s_addr = htonl(s_addr+1); + else + peer->su.sin.sin_addr.s_addr = htonl(s_addr-1); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - peer->su->sin.sin_len = sizeof(struct sockaddr_in); + peer->su->sin.sin_len = sizeof(struct sockaddr_in); #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - return; - } - else - zlog_warn("%s: IPv4 interface address is not /30 or /31, v4 session not started", - peer->conf_if); - } + return 1; + } + else + zlog_warn("%s: IPv4 interface address is not /30 or /31, v4 session not started", + peer->conf_if); + } + } - if (ifp->nbr_connected && - (ifc_nbr = listnode_head(ifp->nbr_connected))) - { - peer->su.sa.sa_family = AF_INET6; - memcpy(&peer->su.sin6.sin6_addr, &ifc_nbr->address->u.prefix, - sizeof (struct in6_addr)); + return 0; +} + +static int +bgp_peer_conf_if_to_su_update_v6 (struct peer *peer, struct interface *ifp) +{ + struct nbr_connected *ifc_nbr; + + /* Have we learnt the peer's IPv6 link-local address? */ + if (ifp->nbr_connected && + (ifc_nbr = listnode_head(ifp->nbr_connected))) + { + peer->su.sa.sa_family = AF_INET6; + memcpy(&peer->su.sin6.sin6_addr, &ifc_nbr->address->u.prefix, + sizeof (struct in6_addr)); #ifdef SIN6_LEN - peer->su.sin6.sin6_len = sizeof (struct sockaddr_in6); + peer->su.sin6.sin6_len = sizeof (struct sockaddr_in6); #endif - peer->su.sin6.sin6_scope_id = ifp->ifindex; + peer->su.sin6.sin6_scope_id = ifp->ifindex; + return 1; + } - return; - } + return 0; +} + +/* + * Set or reset the peer address socketunion structure based on the + * learnt/derived peer address. If the address has changed, update the + * password on the listen socket, if needed. + */ +void +bgp_peer_conf_if_to_su_update (struct peer *peer) +{ + struct interface *ifp; + int prev_family; + int peer_addr_updated = 0; + + if (!peer->conf_if) + return; + + prev_family = peer->su.sa.sa_family; + if ((ifp = if_lookup_by_name(peer->conf_if))) + { + /* If BGP unnumbered is not "v6only", we first see if we can derive the + * peer's IPv4 address. + */ + if (!CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY)) + peer_addr_updated = bgp_peer_conf_if_to_su_update_v4 (peer, ifp); + + /* If "v6only" or we can't derive peer's IPv4 address, see if we've + * learnt the peer's IPv6 link-local address. This is from the source + * IPv6 address in router advertisement. + */ + if (!peer_addr_updated) + peer_addr_updated = bgp_peer_conf_if_to_su_update_v6 (peer, ifp); + } + /* If we could derive the peer address, we may need to install the password + * configured for the peer, if any, on the listen socket. Otherwise, mark + * that peer's address is not available and uninstall the password, if + * needed. + */ + if (peer_addr_updated) + { + if (peer->password && prev_family == AF_UNSPEC) + bgp_md5_set (peer); + } + else + { + if (peer->password && prev_family != AF_UNSPEC) + bgp_md5_unset (peer); + peer->su.sa.sa_family = AF_UNSPEC; + memset(&peer->su.sin6.sin6_addr, 0, sizeof (struct in6_addr)); } - /* This works as an indication of unresolved peer address - on a BGP interface*/ - peer->su.sa.sa_family = AF_UNSPEC; - memset(&peer->su.sin6.sin6_addr, 0, sizeof (struct in6_addr)); } /* Create new BGP peer. */ @@ -1745,8 +1789,9 @@ peer_delete (struct peer *peer) peer->password = NULL; if (!accept_peer && + ! BGP_PEER_SU_UNSPEC(peer) && ! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) - bgp_md5_set (peer); + bgp_md5_unset (peer); } bgp_timer_set (peer); /* stops all timers for Deleted */ @@ -1987,7 +2032,8 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer, if (conf->password && !peer->password) peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, conf->password); - bgp_md5_set (peer); + if (! BGP_PEER_SU_UNSPEC(peer)) + bgp_md5_set (peer); /* maximum-prefix */ peer->pmax[afi][safi] = conf->pmax[afi][safi]; @@ -4597,6 +4643,9 @@ peer_password_set (struct peer *peer, const char *password) else bgp_session_reset(peer); + if (BGP_PEER_SU_UNSPEC(peer)) + return BGP_SUCCESS; + return (bgp_md5_set (peer) >= 0) ? BGP_SUCCESS : BGP_ERR_TCPSIG_FAILED; } @@ -4615,8 +4664,11 @@ peer_password_set (struct peer *peer, const char *password) else bgp_session_reset(peer); - if (bgp_md5_set (peer) < 0) - ret = BGP_ERR_TCPSIG_FAILED; + if (! BGP_PEER_SU_UNSPEC(peer)) + { + if (bgp_md5_set (peer) < 0) + ret = BGP_ERR_TCPSIG_FAILED; + } } return ret; @@ -4648,7 +4700,8 @@ peer_password_unset (struct peer *peer) peer->password = NULL; - bgp_md5_set (peer); + if (! BGP_PEER_SU_UNSPEC(peer)) + bgp_md5_unset (peer); return 0; } @@ -4669,7 +4722,8 @@ peer_password_unset (struct peer *peer) XFREE (MTYPE_PEER_PASSWORD, peer->password); peer->password = NULL; - bgp_md5_set (peer); + if (! BGP_PEER_SU_UNSPEC(peer)) + bgp_md5_unset (peer); } return 0;