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 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 (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); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + 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); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + return 1; + } + else + zlog_warn("%s: IPv4 interface address is not /30 or /31, v4 session not started", + peer->conf_if); + } + } + + 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); +#endif + peer->su.sin6.sin6_scope_id = ifp->ifindex; + return 1; + } + + 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 multiple IP addresses assigned to link, we pick the first */ + /* 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)) - 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); -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - 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); -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - 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); - } + peer_addr_updated = bgp_peer_conf_if_to_su_update_v4 (peer, ifp); - 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); -#endif - peer->su.sin6.sin6_scope_id = ifp->ifindex; - - return; - } + /* 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;