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:
Nick Hilliard 2011-03-23 15:33:17 +00:00 committed by Paul Jakma
parent db07ad7358
commit fa411a212b
6 changed files with 256 additions and 11 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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) ||

View File

@ -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 */

View File

@ -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)

View File

@ -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);