mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-14 18:01:54 +00:00
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:
parent
57e9ee0a94
commit
89ca90fad9
@ -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)
|
||||
|
@ -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 */
|
||||
|
192
bgpd/bgpd.c
192
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;
|
||||
|
Loading…
Reference in New Issue
Block a user