Merge pull request #4084 from qlyoung/bgp-prefix-md5-auth

Support TCP MD5 auth on prefixes & enable MD5 auth for BGP listen ranges
This commit is contained in:
Russ White 2019-04-16 19:25:43 -04:00 committed by GitHub
commit fe586c647b
6 changed files with 209 additions and 18 deletions

View File

@ -64,7 +64,7 @@ struct bgp_listener {
* If the password is NULL or zero-length, the option will be disabled.
*/
static int bgp_md5_set_socket(int socket, union sockunion *su,
const char *password)
uint16_t prefixlen, const char *password)
{
int ret = -1;
int en = ENOSYS;
@ -81,27 +81,49 @@ static int bgp_md5_set_socket(int socket, union sockunion *su,
su2.sin.sin_port = 0;
else
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;
#endif /* HAVE_TCP_MD5SIG */
if (ret < 0)
flog_warn(EC_BGP_NO_TCP_MD5,
"can't set TCP_MD5SIG option on socket %d: %s",
socket, safe_strerror(en));
if (ret < 0) {
char sabuf[SU_ADDRSTRLEN];
sockunion2str(su, sabuf, sizeof(sabuf));
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;
}
/* Helper for bgp_connect */
static int bgp_md5_set_connect(int socket, union sockunion *su,
const char *password)
uint16_t prefixlen, const char *password)
{
int ret = -1;
#if HAVE_DECL_TCP_MD5SIG
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 */
@ -114,21 +136,57 @@ static int bgp_md5_set_password(struct peer *peer, const char *password)
int ret = 0;
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.
*/
frr_elevate_privs(&bgpd_privs)
{
for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))
if (listener->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,
&peer->su, password);
&peer->su, prefixlen,
password);
break;
}
}
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)
{
/* Set the password from listen socket. */
@ -577,8 +635,14 @@ int bgp_connect(struct peer *peer)
}
#endif
if (peer->password)
bgp_md5_set_connect(peer->fd, &peer->su, peer->password);
if (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. */
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_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_unset(struct peer *);
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_copy(prefix, range);
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;
}
@ -2689,6 +2694,10 @@ int peer_group_listen_range_del(struct peer_group *group, struct prefix *range)
/* Get rid of the listen range */
listnode_delete(group->listen_range[afi], prefix);
/* Remove passwords for deleted ranges */
if (group->conf->password)
bgp_md5_unset_prefix(prefix);
return 0;
}
@ -5519,6 +5528,15 @@ int peer_password_set(struct peer *peer, const char *password)
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;
}
@ -5583,6 +5601,15 @@ int peer_password_unset(struct peer *peer)
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;
}

View File

@ -826,6 +826,30 @@ Defining Peers
peers ASN is the same as mine as specified under the :clicmd:`router bgp ASN`
command the connection will be denied.
.. index:: [no] bgp listen range <A.B.C.D/M|X:X::X:X/M> peer-group WORD
.. clicmd:: [no] bgp listen range <A.B.C.D/M|X:X::X:X/M> peer-group WORD
Accept connections from any peers in the specified prefix. Configuration
from the specified peer-group is used to configure these peers.
.. note::
When using BGP listen ranges, if the associated peer group has TCP MD5
authentication configured, your kernel must support this on prefixes. On
Linux, this support was added in kernel version 4.14. If your kernel does
not support this feature you will get a warning in the log file, and the
listen range will only accept connections from peers without MD5 configured.
Additionally, we have observed that when using this option at scale (several
hundred peers) the kernel may hit its option memory limit. In this situation
you will see error messages like:
``bgpd: sockopt_tcp_signature: setsockopt(23): Cannot allocate memory``
In this case you need to increase the value of the sysctl
``net.core.optmem_max`` to allow the kernel to allocate the necessary option
memory.
.. _bgp-configuring-peers:
Configuring Peers

View File

@ -587,10 +587,30 @@ int sockopt_tcp_rtt(int sock)
#endif
}
int sockopt_tcp_signature(int sock, union sockunion *su, const char *password)
int sockopt_tcp_signature_ext(int sock, union sockunion *su, uint16_t prefixlen,
const char *password)
{
#ifndef HAVE_DECL_TCP_MD5SIG
/*
* We have been asked to enable MD5 auth for an address, but our
* platform doesn't support that
*/
return -2;
#endif
#ifndef TCP_MD5SIG_EXT
/*
* We have been asked to enable MD5 auth for a prefix, but our platform
* doesn't support that
*/
if (prefixlen > 0)
return -2;
#endif
#if HAVE_DECL_TCP_MD5SIG
int ret;
int optname = TCP_MD5SIG;
#ifndef GNU_LINUX
/*
* XXX Need to do PF_KEY operation here to add/remove an SA entry,
@ -643,12 +663,29 @@ int sockopt_tcp_signature(int sock, union sockunion *su, const char *password)
memset(&md5sig, 0, sizeof(md5sig));
memcpy(&md5sig.tcpm_addr, su2, sizeof(*su2));
md5sig.tcpm_keylen = keylen;
if (keylen)
memcpy(md5sig.tcpm_key, password, keylen);
sockunion_free(susock);
/*
* Handle support for MD5 signatures on prefixes, if available and
* requested. Technically the #ifdef check below is not needed because
* if prefixlen > 0 and we don't have support for this feature we would
* have already returned by now, but leaving it there to be explicit.
*/
#ifdef TCP_MD5SIG_EXT
if (prefixlen > 0) {
md5sig.tcpm_prefixlen = prefixlen;
md5sig.tcpm_flags = TCP_MD5SIG_FLAG_PREFIX;
optname = TCP_MD5SIG_EXT;
}
#endif /* TCP_MD5SIG_EXT */
#endif /* GNU_LINUX */
if ((ret = setsockopt(sock, IPPROTO_TCP, TCP_MD5SIG, &md5sig,
if ((ret = setsockopt(sock, IPPROTO_TCP, optname, &md5sig,
sizeof md5sig))
< 0) {
/* ENOENT is harmless. It is returned when we clear a password
@ -663,7 +700,10 @@ int sockopt_tcp_signature(int sock, union sockunion *su, const char *password)
sock, safe_strerror(errno));
}
return ret;
#else /* HAVE_TCP_MD5SIG */
return -2;
#endif /* !HAVE_TCP_MD5SIG */
#endif /* HAVE_TCP_MD5SIG */
}
int sockopt_tcp_signature(int sock, union sockunion *su, const char *password)
{
return sockopt_tcp_signature_ext(sock, su, 0, password);
}

View File

@ -100,9 +100,43 @@ extern void sockopt_iphdrincl_swab_htosys(struct ip *iph);
extern void sockopt_iphdrincl_swab_systoh(struct ip *iph);
extern int sockopt_tcp_rtt(int);
/*
* TCP MD5 signature option. This option allows TCP MD5 to be enabled on
* addresses.
*
* sock
* Socket to enable option on.
*
* su
* Sockunion specifying address to enable option on.
*
* password
* MD5 auth password
*/
extern int sockopt_tcp_signature(int sock, union sockunion *su,
const char *password);
/*
* Extended TCP MD5 signature option. This option allows TCP MD5 to be enabled
* on prefixes.
*
* sock
* Socket to enable option on.
*
* su
* Sockunion specifying address (or prefix) to enable option on.
*
* prefixlen
* 0 - su is an address; fall back to non-extended mode
* Else - su is a prefix; prefixlen is the mask length
*
* password
* MD5 auth password
*/
extern int sockopt_tcp_signature_ext(int sock, union sockunion *su,
uint16_t prefixlen, const char *password);
#ifdef __cplusplus
}
#endif