mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-05 22:07:57 +00:00
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:
commit
fe586c647b
@ -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) {
|
||||
|
@ -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);
|
||||
|
27
bgpd/bgpd.c
27
bgpd/bgpd.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user