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.
This commit is contained in:
vivek 2015-08-29 16:10:12 -07:00
parent 57e9ee0a94
commit 89ca90fad9
3 changed files with 142 additions and 73 deletions

View File

@ -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)

View File

@ -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 */

View File

@ -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;