bgpd: add support for MD5 auth on listen ranges

Co-authored-by: Donald Sharp <sharpd@cumulusnetworks.com>
Co-authored-by: Quentin Young <qlyoung@cumulusnetworks.com>
Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
This commit is contained in:
Quentin Young 2019-03-29 19:24:08 -04:00
parent b33e46666d
commit 9e7d9a61ac
3 changed files with 106 additions and 13 deletions

View File

@ -64,7 +64,7 @@ struct bgp_listener {
* If the password is NULL or zero-length, the option will be disabled. * If the password is NULL or zero-length, the option will be disabled.
*/ */
static int bgp_md5_set_socket(int socket, union sockunion *su, static int bgp_md5_set_socket(int socket, union sockunion *su,
const char *password) uint16_t prefixlen, const char *password)
{ {
int ret = -1; int ret = -1;
int en = ENOSYS; int en = ENOSYS;
@ -81,27 +81,49 @@ static int bgp_md5_set_socket(int socket, union sockunion *su,
su2.sin.sin_port = 0; su2.sin.sin_port = 0;
else else
su2.sin6.sin6_port = 0; su2.sin6.sin6_port = 0;
ret = sockopt_tcp_signature(socket, &su2, password);
/* For addresses, use the non-extended signature functionality */
if ((su2.sa.sa_family == AF_INET && prefixlen == IPV4_MAX_PREFIXLEN)
|| (su2.sa.sa_family == AF_INET6
&& prefixlen == IPV6_MAX_PREFIXLEN))
ret = sockopt_tcp_signature(socket, &su2, password);
else
ret = sockopt_tcp_signature_ext(socket, &su2, prefixlen,
password);
en = errno; en = errno;
#endif /* HAVE_TCP_MD5SIG */ #endif /* HAVE_TCP_MD5SIG */
if (ret < 0) if (ret < 0) {
flog_warn(EC_BGP_NO_TCP_MD5, char sabuf[SU_ADDRSTRLEN];
"can't set TCP_MD5SIG option on socket %d: %s", sockunion2str(su, sabuf, sizeof(sabuf));
socket, safe_strerror(en));
switch (ret) {
case -2:
flog_warn(
EC_BGP_NO_TCP_MD5,
"Unable to set TCP MD5 option on socket for peer %s (sock=%d): This platform does not support MD5 auth for prefixes",
sabuf, socket);
break;
default:
flog_warn(
EC_BGP_NO_TCP_MD5,
"Unable to set TCP MD5 option on socket for peer %s (sock=%d): %s",
sabuf, socket, safe_strerror(en));
}
}
return ret; return ret;
} }
/* Helper for bgp_connect */ /* Helper for bgp_connect */
static int bgp_md5_set_connect(int socket, union sockunion *su, static int bgp_md5_set_connect(int socket, union sockunion *su,
const char *password) uint16_t prefixlen, const char *password)
{ {
int ret = -1; int ret = -1;
#if HAVE_DECL_TCP_MD5SIG #if HAVE_DECL_TCP_MD5SIG
frr_elevate_privs(&bgpd_privs) { frr_elevate_privs(&bgpd_privs) {
ret = bgp_md5_set_socket(socket, su, password); ret = bgp_md5_set_socket(socket, su, prefixlen, password);
} }
#endif /* HAVE_TCP_MD5SIG */ #endif /* HAVE_TCP_MD5SIG */
@ -114,21 +136,57 @@ static int bgp_md5_set_password(struct peer *peer, const char *password)
int ret = 0; int ret = 0;
struct bgp_listener *listener; struct bgp_listener *listener;
frr_elevate_privs(&bgpd_privs) { /*
/* Set or unset the password on the listen socket(s). Outbound * Set or unset the password on the listen socket(s). Outbound
* connections are taken care of in bgp_connect() below. * connections are taken care of in bgp_connect() below.
*/ */
frr_elevate_privs(&bgpd_privs)
{
for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener)) for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))
if (listener->su.sa.sa_family if (listener->su.sa.sa_family
== peer->su.sa.sa_family) { == peer->su.sa.sa_family) {
uint16_t prefixlen =
peer->su.sa.sa_family == AF_INET
? IPV4_MAX_PREFIXLEN
: IPV6_MAX_PREFIXLEN;
ret = bgp_md5_set_socket(listener->fd, ret = bgp_md5_set_socket(listener->fd,
&peer->su, password); &peer->su, prefixlen,
password);
break; break;
} }
} }
return ret; return ret;
} }
int bgp_md5_set_prefix(struct prefix *p, const char *password)
{
int ret = 0;
union sockunion su;
struct listnode *node;
struct bgp_listener *listener;
/* Set or unset the password on the listen socket(s). */
frr_elevate_privs(&bgpd_privs)
{
for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))
if (listener->su.sa.sa_family == p->family) {
prefix2sockunion(p, &su);
ret = bgp_md5_set_socket(listener->fd, &su,
p->prefixlen,
password);
break;
}
}
return ret;
}
int bgp_md5_unset_prefix(struct prefix *p)
{
return bgp_md5_set_prefix(p, NULL);
}
int bgp_md5_set(struct peer *peer) int bgp_md5_set(struct peer *peer)
{ {
/* Set the password from listen socket. */ /* Set the password from listen socket. */
@ -577,8 +635,14 @@ int bgp_connect(struct peer *peer)
} }
#endif #endif
if (peer->password) if (peer->password) {
bgp_md5_set_connect(peer->fd, &peer->su, peer->password); uint16_t prefixlen = peer->su.sa.sa_family == AF_INET
? IPV4_MAX_PREFIXLEN
: IPV6_MAX_PREFIXLEN;
bgp_md5_set_connect(peer->fd, &peer->su, prefixlen,
peer->password);
}
/* Update source bind. */ /* Update source bind. */
if (bgp_update_source(peer) < 0) { if (bgp_update_source(peer) < 0) {

View File

@ -30,6 +30,8 @@ extern void bgp_close(void);
extern int bgp_connect(struct peer *); extern int bgp_connect(struct peer *);
extern int bgp_getsockname(struct peer *); extern int bgp_getsockname(struct peer *);
extern int bgp_md5_set_prefix(struct prefix *p, const char *password);
extern int bgp_md5_unset_prefix(struct prefix *p);
extern int bgp_md5_set(struct peer *); extern int bgp_md5_set(struct peer *);
extern int bgp_md5_unset(struct peer *); extern int bgp_md5_unset(struct peer *);
extern int bgp_set_socket_ttl(struct peer *, int fd); extern int bgp_set_socket_ttl(struct peer *, int fd);

View File

@ -2645,6 +2645,11 @@ int peer_group_listen_range_add(struct peer_group *group, struct prefix *range)
prefix = prefix_new(); prefix = prefix_new();
prefix_copy(prefix, range); prefix_copy(prefix, range);
listnode_add(group->listen_range[afi], prefix); listnode_add(group->listen_range[afi], prefix);
/* Update passwords for new ranges */
if (group->conf->password)
bgp_md5_set_prefix(prefix, group->conf->password);
return 0; return 0;
} }
@ -2689,6 +2694,10 @@ int peer_group_listen_range_del(struct peer_group *group, struct prefix *range)
/* Get rid of the listen range */ /* Get rid of the listen range */
listnode_delete(group->listen_range[afi], prefix); listnode_delete(group->listen_range[afi], prefix);
/* Remove passwords for deleted ranges */
if (group->conf->password)
bgp_md5_unset_prefix(prefix);
return 0; return 0;
} }
@ -5519,6 +5528,15 @@ int peer_password_set(struct peer *peer, const char *password)
ret = BGP_ERR_TCPSIG_FAILED; ret = BGP_ERR_TCPSIG_FAILED;
} }
/* Set flag and configuration on all peer-group listen ranges */
struct listnode *ln;
struct prefix *lr;
for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP], ln, lr))
bgp_md5_set_prefix(lr, password);
for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP6], ln, lr))
bgp_md5_set_prefix(lr, password);
return ret; return ret;
} }
@ -5583,6 +5601,15 @@ int peer_password_unset(struct peer *peer)
bgp_md5_unset(member); bgp_md5_unset(member);
} }
/* Set flag and configuration on all peer-group listen ranges */
struct listnode *ln;
struct prefix *lr;
for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP], ln, lr))
bgp_md5_unset_prefix(lr);
for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP6], ln, lr))
bgp_md5_unset_prefix(lr);
return 0; return 0;
} }