mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-13 19:02:58 +00:00
bgpd: RFC 5082 Generalized TTL Security Mechanism support
* bgpd: Add support for RFC 5082 GTSM, which allows the TTL field to be used to verify that incoming packets have been sent from neighbours no more than X IP hops away. In other words, this allows packets that were sent from further away (i.e. not by the neighbour with known distance, and so possibly a miscreant) to be filtered out. * lib/sockunion.{c,h}: (sockopt_minttl) new function, to set a minimum TTL using the IP_MINTTL socket opt. * bgpd.h: (BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK) define for command error for minttl. (struct peer) add a config variable, to store the configured minttl. (peer_ttl_security_hops_{set,unset}) configuration handlers * bgpd.c: (peer_group_get) init gtsm_hops (peer_ebgp_multihop_{un,}set) check for conflicts with GTSM. Multihop and GTSM can't both be active for a peer at the same time. (peer_ttl_security_hops_set) set minttl, taking care to avoid conflicts with ebgp_multihop. (bgp_config_write_peer) write out minttl as "neighbor .. ttl-security hops X". * bgp_vty.c: (bgp_vty_return) message for BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK (peer_ebgp_multihop_{un,}set_vty) * bgp_network.c: (bgp_accept) set minttl on accepted sockets if appropriate. (bgp_connect) ditto for outbound.
This commit is contained in:
parent
db07ad7358
commit
fa411a212b
@ -173,8 +173,11 @@ bgp_accept (struct thread *thread)
|
||||
}
|
||||
|
||||
/* In case of peer is EBGP, we should set TTL for this connection. */
|
||||
if (peer_sort (peer1) == BGP_PEER_EBGP)
|
||||
if (peer_sort (peer1) == BGP_PEER_EBGP) {
|
||||
sockopt_ttl (peer1->su.sa.sa_family, bgp_sock, peer1->ttl);
|
||||
if (peer1->gtsm_hops)
|
||||
sockopt_minttl (peer1->su.sa.sa_family, bgp_sock, MAXTTL + 1 - peer1->gtsm_hops);
|
||||
}
|
||||
|
||||
/* Make dummy peer until read Open packet. */
|
||||
if (BGP_DEBUG (events, EVENTS))
|
||||
@ -314,8 +317,11 @@ bgp_connect (struct peer *peer)
|
||||
return -1;
|
||||
|
||||
/* If we can get socket for the peer, adjest TTL and make connection. */
|
||||
if (peer_sort (peer) == BGP_PEER_EBGP)
|
||||
if (peer_sort (peer) == BGP_PEER_EBGP) {
|
||||
sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl);
|
||||
if (peer->gtsm_hops)
|
||||
sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - peer->gtsm_hops);
|
||||
}
|
||||
|
||||
sockopt_reuseaddr (peer->fd);
|
||||
sockopt_reuseport (peer->fd);
|
||||
@ -462,7 +468,10 @@ bgp_socket (unsigned short port, const char *address)
|
||||
zlog_err ("socket: %s", safe_strerror (errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* if we intend to implement ttl-security, this socket needs ttl=255 */
|
||||
sockopt_ttl (ainfo->ai_family, sock, MAXTTL);
|
||||
|
||||
ret = bgp_listener (sock, ainfo->ai_addr, ainfo->ai_addrlen);
|
||||
if (ret == 0)
|
||||
++count;
|
||||
@ -495,6 +504,9 @@ bgp_socket (unsigned short port, const char *address)
|
||||
return sock;
|
||||
}
|
||||
|
||||
/* if we intend to implement ttl-security, this socket needs ttl=255 */
|
||||
sockopt_ttl (AF_INET, sock, MAXTTL);
|
||||
|
||||
memset (&sin, 0, sizeof (struct sockaddr_in));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons (port);
|
||||
|
@ -213,6 +213,9 @@ bgp_vty_return (struct vty *vty, int ret)
|
||||
case BGP_ERR_TCPSIG_FAILED:
|
||||
str = "Error while applying TCP-Sig to session(s)";
|
||||
break;
|
||||
case BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK:
|
||||
str = "ebgp-multihop and ttl-security cannot be configured together";
|
||||
break;
|
||||
}
|
||||
if (str)
|
||||
{
|
||||
@ -2626,6 +2629,7 @@ peer_ebgp_multihop_set_vty (struct vty *vty, const char *ip_str,
|
||||
{
|
||||
struct peer *peer;
|
||||
unsigned int ttl;
|
||||
int ret;
|
||||
|
||||
peer = peer_and_group_lookup_vty (vty, ip_str);
|
||||
if (! peer)
|
||||
@ -2636,23 +2640,24 @@ peer_ebgp_multihop_set_vty (struct vty *vty, const char *ip_str,
|
||||
else
|
||||
VTY_GET_INTEGER_RANGE ("TTL", ttl, ttl_str, 1, 255);
|
||||
|
||||
peer_ebgp_multihop_set (peer, ttl);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
ret = peer_ebgp_multihop_set (peer, ttl);
|
||||
|
||||
return bgp_vty_return (vty, ret);
|
||||
}
|
||||
|
||||
static int
|
||||
peer_ebgp_multihop_unset_vty (struct vty *vty, const char *ip_str)
|
||||
{
|
||||
struct peer *peer;
|
||||
int ret;
|
||||
|
||||
peer = peer_and_group_lookup_vty (vty, ip_str);
|
||||
if (! peer)
|
||||
return CMD_WARNING;
|
||||
|
||||
peer_ebgp_multihop_unset (peer);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
ret = peer_ebgp_multihop_unset (peer);
|
||||
|
||||
return bgp_vty_return (vty, ret);
|
||||
}
|
||||
|
||||
/* neighbor ebgp-multihop. */
|
||||
@ -3954,6 +3959,47 @@ DEFUN (no_neighbor_allowas_in,
|
||||
return bgp_vty_return (vty, ret);
|
||||
}
|
||||
|
||||
DEFUN (neighbor_ttl_security,
|
||||
neighbor_ttl_security_cmd,
|
||||
NEIGHBOR_CMD2 "ttl-security hops <1-254>",
|
||||
NEIGHBOR_STR
|
||||
NEIGHBOR_ADDR_STR2
|
||||
"Specify the maximum number of hops to the BGP peer\n")
|
||||
{
|
||||
struct peer *peer;
|
||||
int ret, gtsm_hops;
|
||||
|
||||
peer = peer_and_group_lookup_vty (vty, argv[0]);
|
||||
if (! peer)
|
||||
return CMD_WARNING;
|
||||
|
||||
VTY_GET_INTEGER_RANGE ("", gtsm_hops, argv[1], 1, 254);
|
||||
|
||||
ret = peer_ttl_security_hops_set (peer, gtsm_hops);
|
||||
|
||||
return bgp_vty_return (vty, ret);
|
||||
}
|
||||
|
||||
DEFUN (no_neighbor_ttl_security,
|
||||
no_neighbor_ttl_security_cmd,
|
||||
NO_NEIGHBOR_CMD2 "ttl-security hops <1-254>",
|
||||
NO_STR
|
||||
NEIGHBOR_STR
|
||||
NEIGHBOR_ADDR_STR2
|
||||
"Specify the maximum number of hops to the BGP peer\n")
|
||||
{
|
||||
struct peer *peer;
|
||||
int ret;
|
||||
|
||||
peer = peer_and_group_lookup_vty (vty, argv[0]);
|
||||
if (! peer)
|
||||
return CMD_WARNING;
|
||||
|
||||
ret = peer_ttl_security_hops_unset (peer);
|
||||
|
||||
return bgp_vty_return (vty, ret);
|
||||
}
|
||||
|
||||
/* Address family configuration. */
|
||||
DEFUN (address_family_ipv4,
|
||||
address_family_ipv4_cmd,
|
||||
@ -10060,6 +10106,10 @@ bgp_vty_init (void)
|
||||
install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_metric_rmap_cmd);
|
||||
#endif /* HAVE_IPV6 */
|
||||
|
||||
/* ttl_security commands */
|
||||
install_element (BGP_NODE, &neighbor_ttl_security_cmd);
|
||||
install_element (BGP_NODE, &no_neighbor_ttl_security_cmd);
|
||||
|
||||
/* "show bgp memory" commands. */
|
||||
install_element (VIEW_NODE, &show_bgp_memory_cmd);
|
||||
install_element (RESTRICTED_NODE, &show_bgp_memory_cmd);
|
||||
|
157
bgpd/bgpd.c
157
bgpd/bgpd.c
@ -1379,6 +1379,7 @@ peer_group_get (struct bgp *bgp, const char *name)
|
||||
group->conf->group = group;
|
||||
group->conf->as = 0;
|
||||
group->conf->ttl = 1;
|
||||
group->conf->gtsm_hops = 0;
|
||||
group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
|
||||
UNSET_FLAG (group->conf->config, PEER_CONFIG_TIMER);
|
||||
UNSET_FLAG (group->conf->config, PEER_CONFIG_CONNECT);
|
||||
@ -1416,6 +1417,9 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer,
|
||||
/* TTL */
|
||||
peer->ttl = conf->ttl;
|
||||
|
||||
/* GTSM hops */
|
||||
peer->gtsm_hops = conf->gtsm_hops;
|
||||
|
||||
/* Weight */
|
||||
peer->weight = conf->weight;
|
||||
|
||||
@ -2663,10 +2667,36 @@ peer_ebgp_multihop_set (struct peer *peer, int ttl)
|
||||
{
|
||||
struct peer_group *group;
|
||||
struct listnode *node, *nnode;
|
||||
struct peer *peer1;
|
||||
|
||||
if (peer_sort (peer) == BGP_PEER_IBGP)
|
||||
return 0;
|
||||
|
||||
/* see comment in peer_ttl_security_hops_set() */
|
||||
if (ttl != MAXTTL)
|
||||
{
|
||||
if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
|
||||
{
|
||||
group = peer->group;
|
||||
if (group->conf->gtsm_hops != 0)
|
||||
return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
|
||||
|
||||
for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
|
||||
{
|
||||
if (peer_sort (peer1) == BGP_PEER_IBGP)
|
||||
continue;
|
||||
|
||||
if (peer1->gtsm_hops != 0)
|
||||
return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (peer->gtsm_hops != 0)
|
||||
return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
|
||||
}
|
||||
}
|
||||
|
||||
peer->ttl = ttl;
|
||||
|
||||
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
|
||||
@ -2700,6 +2730,9 @@ peer_ebgp_multihop_unset (struct peer *peer)
|
||||
if (peer_sort (peer) == BGP_PEER_IBGP)
|
||||
return 0;
|
||||
|
||||
if (peer->gtsm_hops != 0 && peer->ttl != MAXTTL)
|
||||
return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
|
||||
|
||||
if (peer_group_active (peer))
|
||||
peer->ttl = peer->group->conf->ttl;
|
||||
else
|
||||
@ -4331,6 +4364,121 @@ peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set # of hops between us and BGP peer. */
|
||||
int
|
||||
peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops)
|
||||
{
|
||||
struct peer_group *group;
|
||||
struct listnode *node, *nnode;
|
||||
struct peer *peer1;
|
||||
int ret;
|
||||
|
||||
zlog_debug ("peer_ttl_security_hops_set: set gtsm_hops to %d for %s", gtsm_hops, peer->host);
|
||||
|
||||
if (peer_sort (peer) == BGP_PEER_IBGP)
|
||||
return 0;
|
||||
|
||||
/* We cannot configure ttl-security hops when ebgp-multihop is already
|
||||
set. For non peer-groups, the check is simple. For peer-groups, it's
|
||||
slightly messy, because we need to check both the peer-group structure
|
||||
and all peer-group members for any trace of ebgp-multihop configuration
|
||||
before actually applying the ttl-security rules. Cisco really made a
|
||||
mess of this configuration parameter, and OpenBGPD got it right.
|
||||
*/
|
||||
|
||||
if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
|
||||
{
|
||||
group = peer->group;
|
||||
if (group->conf->ttl != 1)
|
||||
return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
|
||||
|
||||
for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1))
|
||||
{
|
||||
if (peer_sort (peer1) == BGP_PEER_IBGP)
|
||||
continue;
|
||||
|
||||
if (peer1->ttl != 1)
|
||||
return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (peer->ttl != 1)
|
||||
return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
|
||||
}
|
||||
|
||||
peer->gtsm_hops = gtsm_hops;
|
||||
|
||||
/* specify MAXTTL on outgoing packets */
|
||||
ret = peer_ebgp_multihop_set (peer, MAXTTL);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
|
||||
{
|
||||
if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP)
|
||||
sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - gtsm_hops);
|
||||
}
|
||||
else
|
||||
{
|
||||
group = peer->group;
|
||||
for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
|
||||
{
|
||||
if (peer_sort (peer) == BGP_PEER_IBGP)
|
||||
continue;
|
||||
|
||||
peer->gtsm_hops = group->conf->gtsm_hops;
|
||||
|
||||
if (peer->fd >= 0 && peer->gtsm_hops != 0)
|
||||
sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - peer->gtsm_hops);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
peer_ttl_security_hops_unset (struct peer *peer)
|
||||
{
|
||||
struct peer_group *group;
|
||||
struct listnode *node, *nnode;
|
||||
struct peer *opeer;
|
||||
|
||||
zlog_debug ("peer_ttl_security_hops_unset: set gtsm_hops to zero for %s", peer->host);
|
||||
|
||||
if (peer_sort (peer) == BGP_PEER_IBGP)
|
||||
return 0;
|
||||
|
||||
/* if a peer-group member, then reset to peer-group default rather than 0 */
|
||||
if (peer_group_active (peer))
|
||||
peer->gtsm_hops = peer->group->conf->gtsm_hops;
|
||||
else
|
||||
peer->gtsm_hops = 0;
|
||||
|
||||
opeer = peer;
|
||||
if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP))
|
||||
{
|
||||
if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP)
|
||||
sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
group = peer->group;
|
||||
for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer))
|
||||
{
|
||||
if (peer_sort (peer) == BGP_PEER_IBGP)
|
||||
continue;
|
||||
|
||||
peer->gtsm_hops = 0;
|
||||
|
||||
if (peer->fd >= 0)
|
||||
sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return peer_ebgp_multihop_unset (opeer);
|
||||
}
|
||||
|
||||
int
|
||||
peer_clear (struct peer *peer)
|
||||
{
|
||||
@ -4635,12 +4783,19 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp,
|
||||
vty_out (vty, " neighbor %s passive%s", addr, VTY_NEWLINE);
|
||||
|
||||
/* EBGP multihop. */
|
||||
if (peer_sort (peer) != BGP_PEER_IBGP && peer->ttl != 1)
|
||||
if (peer_sort (peer) != BGP_PEER_IBGP && peer->ttl != 1 &&
|
||||
!(peer->gtsm_hops != 0 && peer->ttl == MAXTTL))
|
||||
if (! peer_group_active (peer) ||
|
||||
g_peer->ttl != peer->ttl)
|
||||
vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, peer->ttl,
|
||||
VTY_NEWLINE);
|
||||
|
||||
/* ttl-security hops */
|
||||
if (peer_sort (peer) != BGP_PEER_IBGP && peer->gtsm_hops != 0)
|
||||
if (! peer_group_active (peer) || g_peer->gtsm_hops != peer->gtsm_hops)
|
||||
vty_out (vty, " neighbor %s ttl-security hops %d%s", addr,
|
||||
peer->gtsm_hops, VTY_NEWLINE);
|
||||
|
||||
/* disable-connected-check. */
|
||||
if (CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))
|
||||
if (! peer_group_active (peer) ||
|
||||
|
@ -303,6 +303,7 @@ struct peer
|
||||
/* Peer information */
|
||||
int fd; /* File descriptor */
|
||||
int ttl; /* TTL of TCP connection to the peer. */
|
||||
int gtsm_hops; /* minimum hopcount to peer */
|
||||
char *desc; /* Description of the peer. */
|
||||
unsigned short port; /* Destination port for peer */
|
||||
char *host; /* Printable address of the peer. */
|
||||
@ -800,7 +801,8 @@ enum bgp_clear_type
|
||||
#define BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP -27
|
||||
#define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS -28
|
||||
#define BGP_ERR_TCPSIG_FAILED -29
|
||||
#define BGP_ERR_MAX -30
|
||||
#define BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK -30
|
||||
#define BGP_ERR_MAX -31
|
||||
|
||||
extern struct bgp_master *bm;
|
||||
|
||||
@ -953,4 +955,7 @@ extern int peer_maximum_prefix_unset (struct peer *, afi_t, safi_t);
|
||||
extern int peer_clear (struct peer *);
|
||||
extern int peer_clear_soft (struct peer *, afi_t, safi_t, enum bgp_clear_type);
|
||||
|
||||
extern int peer_ttl_security_hops_set (struct peer *, int);
|
||||
extern int peer_ttl_security_hops_unset (struct peer *);
|
||||
|
||||
#endif /* _QUAGGA_BGPD_H */
|
||||
|
@ -537,6 +537,28 @@ sockopt_cork (int sock, int onoff)
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
sockopt_minttl (int family, int sock, int minttl)
|
||||
{
|
||||
int ret;
|
||||
|
||||
zlog_debug ("sockopt_minttl: set minttl to %d", minttl);
|
||||
|
||||
#ifdef IP_MINTTL
|
||||
ret = setsockopt (sock, IPPROTO_IP, IP_MINTTL, &minttl, sizeof(minttl));
|
||||
#else
|
||||
ret = -1;
|
||||
errno = EOPNOTSUPP;
|
||||
#endif /* IP_MINTTL */
|
||||
if (ret < 0)
|
||||
{
|
||||
zlog (NULL, LOG_WARNING, "can't set sockopt IP_MINTTL to %d on socket %d: %s", minttl, sock, safe_strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If same family and same prefix return 1. */
|
||||
int
|
||||
sockunion_same (union sockunion *su1, union sockunion *su2)
|
||||
|
@ -102,6 +102,7 @@ extern int sockopt_reuseport (int);
|
||||
extern int sockunion_bind (int sock, union sockunion *,
|
||||
unsigned short, union sockunion *);
|
||||
extern int sockopt_ttl (int family, int sock, int ttl);
|
||||
extern int sockopt_minttl (int family, int sock, int minttl);
|
||||
extern int sockopt_cork (int sock, int onoff);
|
||||
extern int sockunion_socket (union sockunion *su);
|
||||
extern const char *inet_sutop (union sockunion *su, char *str);
|
||||
|
Loading…
Reference in New Issue
Block a user