summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_network.c90
-rw-r--r--bgpd/bgp_network.h2
-rw-r--r--bgpd/bgpd.c27
-rw-r--r--doc/user/bgp.rst24
-rw-r--r--lib/sockopt.c50
-rw-r--r--lib/sockopt.h34
6 files changed, 209 insertions, 18 deletions
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 4153da5a64..6a5c2c4b38 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -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) {
diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h
index f18484e000..59b18f9376 100644
--- a/bgpd/bgp_network.h
+++ b/bgpd/bgp_network.h
@@ -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);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 225f119908..b2925cd512 100644
--- a/bgpd/bgpd.c
+++ b/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;
}
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index be331ffb99..35e42d95cb 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -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
diff --git a/lib/sockopt.c b/lib/sockopt.c
index ea04f2a43e..89f3d5b594 100644
--- a/lib/sockopt.c
+++ b/lib/sockopt.c
@@ -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);
}
diff --git a/lib/sockopt.h b/lib/sockopt.h
index 8fa5987cff..732fec92aa 100644
--- a/lib/sockopt.h
+++ b/lib/sockopt.h
@@ -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