diff options
-rw-r--r-- | bgpd/bgp_network.c | 18 | ||||
-rw-r--r-- | bgpd/bgp_vty.c | 62 | ||||
-rw-r--r-- | bgpd/bgpd.c | 157 | ||||
-rw-r--r-- | bgpd/bgpd.h | 7 | ||||
-rw-r--r-- | lib/sockunion.c | 22 | ||||
-rw-r--r-- | lib/sockunion.h | 1 |
6 files changed, 256 insertions, 11 deletions
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 4c79aa63..502f5679 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -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); diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 1f93eea7..e1c47f4e 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -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); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 882fe37c..cc0ea8d4 100644 --- a/bgpd/bgpd.c +++ b/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) || diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index a5afaedc..39cdf8eb 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -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 */ diff --git a/lib/sockunion.c b/lib/sockunion.c index f6c060f5..a32809c1 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -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) diff --git a/lib/sockunion.h b/lib/sockunion.h index 91bfbc7f..0ee2d63b 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -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); |