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; }