diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 28cfb45af262..5683c1544429 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -951,6 +951,15 @@ bgp_stop (struct peer *peer) char orf_name[BUFSIZ]; int ret = 0; + if (peer_dynamic_neighbor(peer) && + !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) + { + if (bgp_debug_neighbor_events(peer)) + zlog_debug ("%s (dynamic neighbor) deleted", peer->host); + peer_delete (peer); + return -1; + } + /* Can't do this in Clearing; events are used for state transitions */ if (peer->status != Clearing) { @@ -1117,6 +1126,14 @@ bgp_stop_with_error (struct peer *peer) if (peer->v_start >= (60 * 2)) peer->v_start = (60 * 2); + if (peer_dynamic_neighbor(peer)) + { + if (bgp_debug_neighbor_events(peer)) + zlog_debug ("%s (dynamic neighbor) deleted", peer->host); + peer_delete (peer); + return -1; + } + return(bgp_stop (peer)); } @@ -1128,6 +1145,14 @@ bgp_stop_with_notify (struct peer *peer, u_char code, u_char sub_code) /* Send notify to remote peer */ bgp_notify_send (peer, code, sub_code); + if (peer_dynamic_neighbor(peer)) + { + if (bgp_debug_neighbor_events(peer)) + zlog_debug ("%s (dynamic neighbor) deleted", peer->host); + peer_delete (peer); + return -1; + } + /* Clear start timer value to default. */ peer->v_start = BGP_INIT_START_TIMER; @@ -1180,6 +1205,14 @@ bgp_connect_success (struct peer *peer) static int bgp_connect_fail (struct peer *peer) { + if (peer_dynamic_neighbor(peer)) + { + if (bgp_debug_neighbor_events(peer)) + zlog_debug ("%s (dynamic neighbor) deleted", peer->host); + peer_delete (peer); + return -1; + } + return (bgp_stop (peer)); } @@ -1737,9 +1770,11 @@ bgp_event_update (struct peer *peer, int event) int ret = 0; struct peer *other; int passive_conn = 0; + int dyn_nbr; other = peer->doppelganger; passive_conn = (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) ? 1 : 0; + dyn_nbr = peer_dynamic_neighbor(peer); /* Logging this event. */ next = FSM [peer->status -1][event - 1].next_state; @@ -1772,7 +1807,7 @@ bgp_event_update (struct peer *peer, int event) bgp_timer_set (peer); } - else if (!passive_conn && peer->bgp) + else if (!dyn_nbr && !passive_conn && peer->bgp) { /* If we got a return value of -1, that means there was an error, restart * the FSM. If the peer structure was deleted diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index eb8bad762720..63d8580aad23 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -224,11 +224,30 @@ bgp_accept (struct thread *thread) /* Check remote IP address */ peer1 = peer_lookup (NULL, &su); + + if (! peer1) + { + peer1 = peer_lookup_dynamic_neighbor (NULL, &su); + if (peer1) + { + /* Dynamic neighbor has been created, let it proceed */ + peer1->fd = bgp_sock; + bgp_fsm_change_status(peer1, Active); + BGP_TIMER_OFF(peer1->t_start); /* created in peer_create() */ + + if (peer_active (peer1)) + BGP_EVENT_ADD (peer1, TCP_connection_open); + + return 0; + } + } + if (! peer1) { if (bgp_debug_neighbor_events(peer)) { - zlog_debug ("[Event] BGP connection IP address %s is not configured", + zlog_debug ("[Event] %s connection rejected - not configured" + " and not valid for dynamic", inet_sutop (&su, buf)); } close (bgp_sock); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 7094e8ca455d..7a465314a295 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2508,6 +2508,10 @@ bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi, BGP_NOTIFY_CEASE_MAX_PREFIX, ndata, 7); } + /* Dynamic peers will just close their connection. */ + if (peer_dynamic_neighbor (peer)) + return 1; + /* restart timer start */ if (peer->pmax_restart[afi][safi]) { diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 5cfbaebeb39c..9fcebaf6edc8 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -95,6 +95,9 @@ peer_address_self_check (union sockunion *su) } /* Utility function for looking up peer from VTY. */ +/* This is used only for configuration, so disallow if attempted on + * a dynamic neighbor. + */ static struct peer * peer_lookup_vty (struct vty *vty, const char *ip_str) { @@ -124,40 +127,61 @@ peer_lookup_vty (struct vty *vty, const char *ip_str) VTY_NEWLINE); return NULL; } + if (peer_dynamic_neighbor (peer)) + { + vty_out (vty, "%% Operation not allowed on a dynamic neighbor%s", + VTY_NEWLINE); + return NULL; + } + } return peer; } /* Utility function for looking up peer or peer group. */ +/* This is used only for configuration, so disallow if attempted on + * a dynamic neighbor. + */ static struct peer * peer_and_group_lookup_vty (struct vty *vty, const char *peer_str) { int ret; struct bgp *bgp; union sockunion su; - struct peer *peer; - struct peer_group *group; + struct peer *peer = NULL; + struct peer_group *group = NULL; bgp = vty->index; ret = str2sockunion (peer_str, &su); if (ret == 0) { + /* IP address, locate peer. */ peer = peer_lookup (bgp, &su); - if (peer) - return peer; } else { + /* Not IP, could match either peer configured on interface or a group. */ peer = peer_lookup_by_conf_if (bgp, peer_str); - if (peer) - return peer; + if (!peer) + group = peer_group_lookup (bgp, peer_str); + } - group = peer_group_lookup (bgp, peer_str); - if (group) - return group->conf; + if (peer) + { + if (peer_dynamic_neighbor (peer)) + { + vty_out (vty, "%% Operation not allowed on a dynamic neighbor%s", + VTY_NEWLINE); + return NULL; + } + + return peer; } + if (group) + return group->conf; + vty_out (vty, "%% Specify remote-as or peer-group commands first%s", VTY_NEWLINE); @@ -237,6 +261,15 @@ bgp_vty_return (struct vty *vty, int ret) case BGP_ERR_AS_OVERRIDE: str = "as-override cannot be configured for IBGP peers"; break; + case BGP_ERR_INVALID_DYNAMIC_NEIGHBORS_LIMIT: + str = "Invalid limit for number of dynamic neighbors"; + break; + case BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_EXISTS: + str = "Dynamic neighbor listen range already exists"; + break; + case BGP_ERR_INVALID_FOR_DYNAMIC_PEER: + str = "Operation not allowed on a dynamic neighbor"; + break; } if (str) { @@ -1883,6 +1916,176 @@ DEFUN (no_bgp_rr_allow_outbound_policy, return CMD_SUCCESS; } +DEFUN (bgp_listen_limit, + bgp_listen_limit_cmd, + "bgp listen limit " DYNAMIC_NEIGHBOR_LIMIT_RANGE, + "BGP specific commands\n" + "Configure BGP defaults\n" + "maximum number of BGP Dynamic Neighbors that can be created\n" + "Configure Dynamic Neighbors listen limit value\n") +{ + struct bgp *bgp; + int listen_limit; + + bgp = vty->index; + + VTY_GET_INTEGER_RANGE ("listen limit", listen_limit, argv[0], + BGP_DYNAMIC_NEIGHBORS_LIMIT_MIN, + BGP_DYNAMIC_NEIGHBORS_LIMIT_MAX); + + bgp_listen_limit_set (bgp, listen_limit); + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_listen_limit, + no_bgp_listen_limit_cmd, + "no bgp listen limit", + "BGP specific commands\n" + "Configure BGP defaults\n" + "unset maximum number of BGP Dynamic Neighbors that can be created\n" + "Configure Dynamic Neighbors listen limit value to default\n") +{ + struct bgp *bgp; + + bgp = vty->index; + bgp_listen_limit_unset (bgp); + return CMD_SUCCESS; +} + + +DEFUN (bgp_listen_range, + bgp_listen_range_cmd, + LISTEN_RANGE_CMD "peer-group WORD" , + "BGP specific commands\n" + "Configure BGP Dynamic Neighbors\n" + "add a listening range for Dynamic Neighbors\n" + LISTEN_RANGE_ADDR_STR) +{ + struct bgp *bgp; + struct prefix range; + struct peer_group *group; + afi_t afi; + int ret; + + bgp = vty->index; + + //VTY_GET_IPV4_PREFIX ("listen range", range, argv[0]); + + /* Convert IP prefix string to struct prefix. */ + ret = str2prefix (argv[0], &range); + if (! ret) + { + vty_out (vty, "%% Malformed listen range%s", VTY_NEWLINE); + return CMD_WARNING; + } + + afi = family2afi(range.family); + +#ifdef HAVE_IPV6 + if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL (&range.u.prefix6)) + { + vty_out (vty, "%% Malformed listen range (link-local address)%s", + VTY_NEWLINE); + return CMD_WARNING; + } +#endif /* HAVE_IPV6 */ + + apply_mask (&range); + + + group = peer_group_lookup (bgp, argv[1]); + if (! group) + { + vty_out (vty, "%% Configure the peer-group first%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = peer_group_listen_range_add(group, &range); + return bgp_vty_return (vty, ret); +} + +DEFUN (no_bgp_listen_range, + no_bgp_listen_range_cmd, + "no bgp listen range A.B.C.D/M peer-group WORD" , + "BGP specific commands\n" + "Configure BGP defaults\n" + "delete a listening range for Dynamic Neighbors\n" + "Remove Dynamic Neighbors listening range\n") +{ + struct bgp *bgp; + struct prefix range; + struct peer_group *group; + afi_t afi; + int ret; + + bgp = vty->index; + + // VTY_GET_IPV4_PREFIX ("listen range", range, argv[0]); + + /* Convert IP prefix string to struct prefix. */ + ret = str2prefix (argv[0], &range); + if (! ret) + { + vty_out (vty, "%% Malformed listen range%s", VTY_NEWLINE); + return CMD_WARNING; + } + + afi = family2afi(range.family); + +#ifdef HAVE_IPV6 + if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL (&range.u.prefix6)) + { + vty_out (vty, "%% Malformed listen range (link-local address)%s", + VTY_NEWLINE); + return CMD_WARNING; + } +#endif /* HAVE_IPV6 */ + + apply_mask (&range); + + + group = peer_group_lookup (bgp, argv[1]); + if (! group) + { + vty_out (vty, "%% Peer-group does not exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = peer_group_listen_range_del(group, &range); + return bgp_vty_return (vty, ret); +} + +int +bgp_config_write_listen (struct vty *vty, struct bgp *bgp) +{ + struct peer_group *group; + struct listnode *node, *nnode, *rnode, *nrnode; + struct prefix *range; + afi_t afi; + char buf[128]; + + if (bgp->dynamic_neighbors_limit != BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT) + vty_out (vty, " bgp listen limit %d%s", + bgp->dynamic_neighbors_limit, VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + { + for (ALL_LIST_ELEMENTS (group->listen_range[afi], rnode, nrnode, range)) + { + prefix2str(range, buf, sizeof(buf)); + vty_out(vty, " bgp listen range %s peer-group %s%s", + buf, group->name, VTY_NEWLINE); + } + } + } + + return 0; +} + + static int peer_remote_as_vty (struct vty *vty, const char *peer_str, const char *as_str, afi_t afi, safi_t safi) @@ -2041,6 +2244,13 @@ DEFUN (no_neighbor, peer = peer_lookup (vty->index, &su); if (peer) { + if (peer_dynamic_neighbor (peer)) + { + vty_out (vty, "%% Operation not allowed on a dynamic neighbor%s", + VTY_NEWLINE); + return CMD_WARNING; + } + other = peer->doppelganger; peer_delete (peer); if (other && other->status != Deleted) @@ -2396,6 +2606,15 @@ DEFUN (neighbor_set_peer_group, VTY_NEWLINE); return CMD_WARNING; } + + /* Disallow for dynamic neighbor. */ + peer = peer_lookup (bgp, &su); + if (peer && peer_dynamic_neighbor (peer)) + { + vty_out (vty, "%% Operation not allowed on a dynamic neighbor%s", + VTY_NEWLINE); + return CMD_WARNING; + } } group = peer_group_lookup (bgp, argv[1]); @@ -7903,9 +8122,10 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi, char *del { struct peer *peer; struct listnode *node, *nnode; - unsigned int count = 0; - char timebuf[BGP_UPTIME_LEN]; + unsigned int count = 0, dn_count = 0; + char timebuf[BGP_UPTIME_LEN], dn_flag[2]; int len; + struct peer_group *group; /* Header string for each address family. */ static char header[] = "Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd"; @@ -8006,7 +8226,14 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi, char *del count++; - len = vty_out (vty, "%s", peer->host); + memset(dn_flag, '\0', sizeof(dn_flag)); + if (peer_dynamic_neighbor(peer)) + { + dn_count++; + dn_flag[0] = '*'; + } + + len = vty_out (vty, "%s%s", dn_flag, peer->host); len = 16 - len; if (len < 1) vty_out (vty, "%s%*s", VTY_NEWLINE, 16, " "); @@ -8076,6 +8303,16 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi, char *del vty_out (vty, "No %s neighbor is configured%s", afi == AFI_IP ? "IPv4" : "IPv6", VTY_NEWLINE); + + if (dn_count) + { + vty_out(vty, "* - dynamic neighbor%s", VTY_NEWLINE); + vty_out(vty, + "%d %s dynamic neighbor(s), limit %d%s", + dn_count, afi == AFI_IP ? "IPv4" : "IPv6", + bgp->dynamic_neighbors_limit, VTY_NEWLINE); + } + return CMD_SUCCESS; } @@ -8642,6 +8879,7 @@ bgp_show_peer (struct vty *vty, struct peer *p) struct bgp *bgp; char buf1[BUFSIZ], buf[SU_ADDRSTRLEN]; char timebuf[BGP_UPTIME_LEN]; + char dn_flag[2]; afi_t afi; safi_t safi; u_int16_t i; @@ -8654,7 +8892,14 @@ bgp_show_peer (struct vty *vty, struct peer *p) BGP_PEER_SU_UNSPEC(p) ? "None" : sockunion2str (&p->su, buf, SU_ADDRSTRLEN)); else /* Configured IP address. */ - vty_out (vty, "BGP neighbor is %s, ", p->host); + { + memset(dn_flag, '\0', sizeof(dn_flag)); + if (peer_dynamic_neighbor(p)) + dn_flag[0] = '*'; + + vty_out (vty, "BGP neighbor is %s%s, ", dn_flag, p->host); + } + vty_out (vty, "remote AS %u, ", p->as); vty_out (vty, "local AS %u%s%s, ", p->change_local_as ? p->change_local_as : p->local_as, @@ -8672,8 +8917,26 @@ bgp_show_peer (struct vty *vty, struct peer *p) /* Peer-group */ if (p->group) - vty_out (vty, " Member of peer-group %s for session parameters%s", - p->group->name, VTY_NEWLINE); + { + vty_out (vty, " Member of peer-group %s for session parameters%s", + p->group->name, VTY_NEWLINE); + + if (dn_flag[0]) + { + struct prefix *prefix = NULL, *range = NULL; + + prefix = sockunion2hostprefix(&(p->su)); + if (prefix) + range = peer_group_lookup_dynamic_neighbor_range (p->group, + prefix); + if (range) + { + prefix2str(range, buf1, sizeof(buf1)); + vty_out (vty, " Belongs to the subnet range group: %s%s", + buf1, VTY_NEWLINE); + } + } + } /* Administrative shutdown. */ if (CHECK_FLAG (p->flags, PEER_FLAG_SHUTDOWN)) @@ -9968,6 +10231,205 @@ DEFUN (show_bgp_updgrps_afi_adj_subgroup, show_bgp_updgrps_adj_info_aux(vty, afi, safi, argv[3], atoll(argv[2])); } +static int +bgp_show_one_peer_group (struct vty *vty, struct peer_group *group) +{ + struct listnode *node, *nnode; + struct prefix *range; + struct peer *conf; + struct peer *peer; + char buf[128]; + afi_t afi; + safi_t safi; + char *peer_status, *af_str; + int lr_count; + int dynamic; + int af_cfgd; + + conf = group->conf; + + vty_out (vty, "%sBGP peer-group %s, remote AS %d%s", + VTY_NEWLINE, group->name, conf->as, VTY_NEWLINE); + + if (group->bgp->as == conf->as) + vty_out (vty, " Peer-group type is internal%s", VTY_NEWLINE); + else + vty_out (vty, " Peer-group type is external%s", VTY_NEWLINE); + + /* Display AFs configured. */ + vty_out (vty, " Configured address-families:"); + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + { + if (conf->afc[afi][safi]) + { + af_cfgd = 1; + vty_out (vty, " %s;", afi_safi_print(afi, safi)); + } + } + if (!af_cfgd) + vty_out (vty, " none%s", VTY_NEWLINE); + else + vty_out (vty, "%s", VTY_NEWLINE); + + /* Display listen ranges (for dynamic neighbors), if any */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + { + if (afi == AFI_IP) + af_str = "IPv4"; + else if (afi == AFI_IP6) + af_str = "IPv6"; + lr_count = listcount(group->listen_range[afi]); + if (lr_count) + { + vty_out(vty, + " %d %s listen range(s)%s", + lr_count, af_str, VTY_NEWLINE); + + + for (ALL_LIST_ELEMENTS (group->listen_range[afi], node, + nnode, range)) + { + prefix2str(range, buf, sizeof(buf)); + vty_out(vty, " %s%s", buf, VTY_NEWLINE); + } + } + } + + /* Display group members and their status */ + if (listcount(group->peer)) + { + vty_out (vty, " Peer-group members:%s", VTY_NEWLINE); + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)) + peer_status = "Idle (Admin)"; + else if (CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + peer_status = "Idle (PfxCt)"; + else + peer_status = LOOKUP(bgp_status_msg, peer->status); + + dynamic = peer_dynamic_neighbor(peer); + vty_out (vty, " %s %s %s %s", + peer->host, dynamic ? "(dynamic)" : "", + peer_status, VTY_NEWLINE); + } + } + + return CMD_SUCCESS; +} + +/* Show BGP peer group's information. */ +enum show_group_type +{ + show_all_groups, + show_peer_group +}; + +static int +bgp_show_peer_group (struct vty *vty, struct bgp *bgp, + enum show_group_type type, const char *group_name) +{ + struct listnode *node, *nnode; + struct peer_group *group; + int find = 0; + + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + switch (type) + { + case show_all_groups: + bgp_show_one_peer_group (vty, group); + break; + case show_peer_group: + if (group_name && (strcmp(group->name, group_name) == 0)) + { + find = 1; + bgp_show_one_peer_group (vty, group); + } + break; + } + } + + if (type == show_peer_group && ! find) + vty_out (vty, "%% No such peer-groupr%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static int +bgp_show_peer_group_vty (struct vty *vty, const char *name, + enum show_group_type type, const char *group_name) +{ + struct bgp *bgp; + int ret = CMD_SUCCESS; + + if (name) + { + bgp = bgp_lookup_by_name (name); + + if (! bgp) + { + vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + bgp = bgp_get_default (); + + if (bgp) + ret = bgp_show_peer_group (vty, bgp, type, group_name); + + return ret; +} + +DEFUN (show_ip_bgp_peer_groups, + show_ip_bgp_peer_groups_cmd, + "show ip bgp peer-group", + SHOW_STR + IP_STR + BGP_STR + "Detailed information on all BGP peer groups\n") +{ + return bgp_show_peer_group_vty (vty, NULL, show_all_groups, NULL); +} + +DEFUN (show_ip_bgp_instance_peer_groups, + show_ip_bgp_instance_peer_groups_cmd, + "show ip bgp view WORD peer-group", + SHOW_STR + IP_STR + BGP_STR + "BGP View\n" + "Detailed information on all BGP peer groups\n") +{ + return bgp_show_peer_group_vty (vty, argv[0], show_all_groups, NULL); +} + +DEFUN (show_ip_bgp_peer_group, + show_ip_bgp_peer_group_cmd, + "show ip bgp peer-group WORD", + SHOW_STR + IP_STR + BGP_STR + "BGP peer-group name\n" + "Detailed information on a BGP peer group\n") +{ + return bgp_show_peer_group_vty (vty, NULL, show_peer_group, argv[0]); +} + +DEFUN (show_ip_bgp_instance_peer_group, + show_ip_bgp_instance_peer_group_cmd, + "show ip bgp view WORD peer-group WORD", + SHOW_STR + IP_STR + BGP_STR + "BGP View\n" + "BGP peer-group name\n" + "Detailed information on a BGP peer group\n") +{ + return bgp_show_peer_group_vty (vty, argv[0], show_peer_group, argv[1]); +} /* Redistribute VTY commands. */ @@ -10812,6 +11274,14 @@ bgp_vty_init (void) install_element (BGP_NODE, &bgp_rr_allow_outbound_policy_cmd); install_element (BGP_NODE, &no_bgp_rr_allow_outbound_policy_cmd); + /* "bgp listen limit" commands. */ + install_element (BGP_NODE, &bgp_listen_limit_cmd); + install_element (BGP_NODE, &no_bgp_listen_limit_cmd); + + /* "bgp listen range" commands. */ + install_element (BGP_NODE, &bgp_listen_range_cmd); + install_element (BGP_NODE, &no_bgp_listen_range_cmd); + /* "neighbor remote-as" commands. */ install_element (BGP_NODE, &neighbor_remote_as_cmd); install_element (BGP_NODE, &neighbor_interface_config_cmd); @@ -11806,6 +12276,16 @@ bgp_vty_init (void) install_element (ENABLE_NODE, &show_ipv6_mbgp_summary_cmd); #endif /* HAVE_IPV6 */ + /* "show ip bgp peer-group" commands. */ + install_element (VIEW_NODE, &show_ip_bgp_peer_groups_cmd); + install_element (VIEW_NODE, &show_ip_bgp_instance_peer_groups_cmd); + install_element (VIEW_NODE, &show_ip_bgp_peer_group_cmd); + install_element (VIEW_NODE, &show_ip_bgp_instance_peer_group_cmd); + install_element (ENABLE_NODE, &show_ip_bgp_peer_groups_cmd); + install_element (ENABLE_NODE, &show_ip_bgp_instance_peer_groups_cmd); + install_element (ENABLE_NODE, &show_ip_bgp_peer_group_cmd); + install_element (ENABLE_NODE, &show_ip_bgp_instance_peer_group_cmd); + /* "show ip bgp rsclient" commands. */ install_element (VIEW_NODE, &show_ip_bgp_rsclient_summary_cmd); install_element (VIEW_NODE, &show_ip_bgp_instance_rsclient_summary_cmd); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 1357e3c25ad3..7f47f263be77 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -22,11 +22,13 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define _QUAGGA_BGP_VTY_H #define CMD_AS_RANGE "<1-4294967295>" +#define DYNAMIC_NEIGHBOR_LIMIT_RANGE "<1-5000>" extern void bgp_vty_init (void); extern const char *afi_safi_print (afi_t, safi_t); extern int bgp_config_write_update_delay (struct vty *, struct bgp *); extern int bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp); +extern int bgp_config_write_listen(struct vty *vty, struct bgp *bgp); extern int bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp); #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 17bf8cf5d623..cbe8580f3a78 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -572,6 +572,29 @@ bgp_default_subgroup_pkt_queue_max_unset (struct bgp *bgp) return 0; } +/* Listen limit configuration. */ +int +bgp_listen_limit_set (struct bgp *bgp, int listen_limit) +{ + if (! bgp) + return -1; + + bgp->dynamic_neighbors_limit = listen_limit; + + return 0; +} + +int +bgp_listen_limit_unset (struct bgp *bgp) +{ + if (! bgp) + return -1; + + bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT; + + return 0; +} + struct peer_af * peer_af_create (struct peer *peer, afi_t afi, safi_t safi) { @@ -1309,6 +1332,13 @@ peer_remote_as (struct bgp *bgp, union sockunion *su, const char *conf_if, as_t if (peer) { + /* Not allowed for a dynamic peer. */ + if (peer_dynamic_neighbor (peer)) + { + *as = peer->as; + return BGP_ERR_INVALID_FOR_DYNAMIC_PEER; + } + /* When this peer is a member of peer-group. */ if (peer->group) { @@ -1551,6 +1581,9 @@ peer_delete (struct peer *peer) relationship. */ if (peer->group) { + if (peer_dynamic_neighbor(peer)) + peer_drop_dynamic_neighbor(peer); + if ((pn = listnode_lookup (peer->group->peer, peer))) { peer = peer_unlock (peer); /* group->peer list reference */ @@ -1719,6 +1752,7 @@ struct peer_group * peer_group_get (struct bgp *bgp, const char *name) { struct peer_group *group; + afi_t afi; group = peer_group_lookup (bgp, name); if (group) @@ -1728,6 +1762,8 @@ peer_group_get (struct bgp *bgp, const char *name) group->bgp = bgp; group->name = strdup (name); group->peer = list_new (); + for (afi = AFI_IP; afi < AFI_MAX; afi++) + group->listen_range[afi] = list_new (); group->conf = peer_new (bgp); if (! bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)) group->conf->afc[AFI_IP][SAFI_UNICAST] = 1; @@ -2039,8 +2075,10 @@ peer_group_delete (struct peer_group *group) { struct bgp *bgp; struct peer *peer; + struct prefix *prefix; struct peer *other; struct listnode *node, *nnode; + afi_t afi; bgp = group->bgp; @@ -2057,6 +2095,15 @@ peer_group_delete (struct peer_group *group) } list_delete (group->peer); + for (afi = AFI_IP; afi < AFI_MAX; afi++) + { + for (ALL_LIST_ELEMENTS (group->listen_range[afi], node, nnode, prefix)) + { + prefix_free(prefix); + } + list_delete (group->listen_range[afi]); + } + free (group->name); group->name = NULL; @@ -2100,6 +2147,78 @@ peer_group_remote_as_delete (struct peer_group *group) return 0; } +int +peer_group_listen_range_add (struct peer_group *group, struct prefix *range) +{ + struct prefix *prefix; + struct listnode *node, *nnode; + afi_t afi; + + afi = family2afi(range->family); + + /* Group needs remote AS configured. */ + if (! group->conf->as) + return BGP_ERR_PEER_GROUP_NO_REMOTE_AS; + + /* Ensure no duplicates. Currently we don't care about overlaps. */ + for (ALL_LIST_ELEMENTS (group->listen_range[afi], node, nnode, prefix)) + { + if (prefix_same(range, prefix)) + return BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_EXISTS; + } + + prefix = prefix_new(); + prefix_copy(prefix, range); + listnode_add(group->listen_range[afi], prefix); + return 0; +} + +int +peer_group_listen_range_del (struct peer_group *group, struct prefix *range) +{ + struct prefix *prefix, *prefix2; + struct listnode *node, *nnode; + struct peer *peer; + afi_t afi; + char buf[SU_ADDRSTRLEN]; + + afi = family2afi(range->family); + + /* Identify the listen range. */ + for (ALL_LIST_ELEMENTS (group->listen_range[afi], node, nnode, prefix)) + { + if (prefix_same(range, prefix)) + break; + } + + if (!prefix) + return BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_NOT_FOUND; + + prefix2str(prefix, buf, sizeof(buf)); + + /* Dispose off any dynamic neighbors that exist due to this listen range */ + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (!peer_dynamic_neighbor (peer)) + continue; + + prefix2 = sockunion2hostprefix(&peer->su); + if (prefix_match(prefix, prefix2)) + { + if (bgp_debug_neighbor_events(peer)) + zlog_debug ("Deleting dynamic neighbor %s group %s upon " + "delete of listen range %s", + peer->host, group->name, buf); + peer_delete (peer); + } + } + + /* Get rid of the listen range */ + listnode_delete(group->listen_range[afi], prefix); + + return 0; +} + /* Bind specified peer to peer group. */ int peer_group_bind (struct bgp *bgp, union sockunion *su, struct peer *peer, @@ -2365,6 +2484,8 @@ bgp_create (as_t *as, const char *name) bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; bgp->restart_time = BGP_DEFAULT_RESTART_TIME; bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; + bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT; + bgp->dynamic_neighbors_count = 0; bgp->as = *as; @@ -2660,6 +2781,189 @@ peer_lookup (struct bgp *bgp, union sockunion *su) return NULL; } +struct peer * +peer_create_bind_dynamic_neighbor (struct bgp *bgp, union sockunion *su, + struct peer_group *group) +{ + struct peer *peer; + afi_t afi; + safi_t safi; + as_t as; + + /* Create peer first; we've already checked group config is valid. */ + peer = peer_create (su, NULL, bgp, bgp->as, group->conf->as, 0, 0); + if (!peer) + return NULL; + + /* Link to group */ + peer->group = group; + peer = peer_lock (peer); + listnode_add (group->peer, peer); + + /* + * Bind peer for all AFs configured for the group. We don't call + * peer_group_bind as that is sub-optimal and does some stuff we don't want. + */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + if (!group->conf->afc[afi][safi]) + continue; + peer->af_group[afi][safi] = 1; + peer->afc[afi][safi] = 1; + if (!peer_af_find(peer, afi, safi) && + peer_af_create(peer, afi, safi) == NULL) + { + zlog_err("couldn't create af structure for peer %s", peer->host); + } + peer_group2peer_config_copy (group, peer, afi, safi); + } + + /* Mark as dynamic, but also as a "config node" for other things to work. */ + SET_FLAG(peer->flags, PEER_FLAG_DYNAMIC_NEIGHBOR); + SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE); + + return peer; +} + +struct prefix * +peer_group_lookup_dynamic_neighbor_range (struct peer_group * group, + struct prefix * prefix) +{ + struct listnode *node, *nnode; + struct prefix *range; + afi_t afi; + + afi = family2afi(prefix->family); + + if (group->listen_range[afi]) + for (ALL_LIST_ELEMENTS (group->listen_range[afi], node, nnode, range)) + if (prefix_match(range, prefix)) + return range; + + return NULL; +} + +struct peer_group * +peer_group_lookup_dynamic_neighbor (struct bgp *bgp, struct prefix *prefix, + struct prefix **listen_range) +{ + struct prefix *range = NULL; + struct peer_group *group = NULL; + struct listnode *node, *nnode; + + *listen_range = NULL; + if (bgp != NULL) + { + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + if ((range = peer_group_lookup_dynamic_neighbor_range(group, prefix))) + break; + } + else if (bm->bgp != NULL) + { + struct listnode *bgpnode, *nbgpnode; + + for (ALL_LIST_ELEMENTS (bm->bgp, bgpnode, nbgpnode, bgp)) + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + if ((range = peer_group_lookup_dynamic_neighbor_range(group, prefix))) + break; + } + + *listen_range = range; + return (group && range) ? group : NULL; +} + +struct peer * +peer_lookup_dynamic_neighbor (struct bgp *bgp, union sockunion *su) +{ + struct peer_group *group; + struct bgp *gbgp; + struct peer *peer; + struct prefix *prefix; + struct prefix *listen_range; + int dncount; + char buf[SU_ADDRSTRLEN]; + char buf1[SU_ADDRSTRLEN]; + + prefix = sockunion2hostprefix(su); + if (!prefix) + return NULL; + + /* See if incoming connection matches a configured listen range. */ + group = peer_group_lookup_dynamic_neighbor (bgp, prefix, &listen_range); + + if (! group) + return NULL; + + gbgp = group->bgp; + + if (! gbgp) + return NULL; + + prefix2str(prefix, buf, sizeof(buf)); + prefix2str(listen_range, buf1, sizeof(buf1)); + + if (bgp_debug_neighbor_events(NULL)) + zlog_debug ("Dynamic Neighbor %s matches group %s listen range %s", + buf, group->name, buf1); + + /* Are we within the listen limit? */ + dncount = gbgp->dynamic_neighbors_count; + + if (dncount >= gbgp->dynamic_neighbors_limit) + { + if (bgp_debug_neighbor_events(NULL)) + zlog_debug ("Dynamic Neighbor %s rejected - at limit %d", + inet_sutop (su, buf), gbgp->dynamic_neighbors_limit); + return NULL; + } + + /* Ensure group is not disabled. */ + if (CHECK_FLAG (group->conf->flags, PEER_FLAG_SHUTDOWN)) + { + if (bgp_debug_neighbor_events(NULL)) + zlog_debug ("Dynamic Neighbor %s rejected - group %s disabled", + buf, group->name); + return NULL; + } + + /* Check that at least one AF is activated for the group. */ + if (!peer_group_af_configured (group)) + { + if (bgp_debug_neighbor_events(NULL)) + zlog_debug ("Dynamic Neighbor %s rejected - no AF activated for group %s", + buf, group->name); + return NULL; + } + + /* Create dynamic peer and bind to associated group. */ + peer = peer_create_bind_dynamic_neighbor (gbgp, su, group); + assert (peer); + + gbgp->dynamic_neighbors_count = ++dncount; + + if (bgp_debug_neighbor_events(peer)) + zlog_debug ("%s Dynamic Neighbor added, group %s count %d", + peer->host, group->name, dncount); + + return peer; +} + +void peer_drop_dynamic_neighbor (struct peer *peer) +{ + int dncount = -1; + if (peer->group && peer->group->bgp) + { + dncount = peer->group->bgp->dynamic_neighbors_count; + if (dncount) + peer->group->bgp->dynamic_neighbors_count = --dncount; + } + if (bgp_debug_neighbor_events(peer)) + zlog_debug ("%s dropped from group %s, count %d", + peer->host, peer->group->name, dncount); +} + + /* If peer is configured at least one address family return 1. */ int peer_active (struct peer *peer) @@ -5468,6 +5772,10 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, char buf[SU_ADDRSTRLEN]; char *addr; + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor (peer)) + return; + if (peer->conf_if) addr = peer->conf_if; else @@ -5895,6 +6203,10 @@ bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi, } for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { + /* Skip dynamic neighbors. */ + if (peer_dynamic_neighbor (peer)) + continue; + if (peer->afc[afi][safi]) { if (CHECK_FLAG (peer->flags, PEER_FLAG_CONFIG_NODE)) @@ -6125,6 +6437,9 @@ bgp_config_write (struct vty *vty) /* Distance configuration. */ bgp_config_write_distance (vty, bgp); + /* listen range and limit for dynamic BGP neighbors */ + bgp_config_write_listen (vty, bgp); + /* No auto-summary */ if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) vty_out (vty, " no auto-summary%s", VTY_NEWLINE); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 986364952512..8b046e597cfc 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -134,6 +134,12 @@ struct bgp /* BGP peer group. */ struct list *group; + /* The maximum number of BGP dynamic neighbors that can be created */ + int dynamic_neighbors_limit; + + /* The current number of BGP dynamic neighbors */ + int dynamic_neighbors_count; + /* BGP route-server-clients. */ struct list *rsclient; @@ -302,6 +308,9 @@ struct peer_group /* Peer-group client list. */ struct list *peer; + /** Dynamic neighbor listening ranges */ + struct list *listen_range[AFI_MAX]; + /* Peer-group config */ struct peer *conf; }; @@ -570,6 +579,7 @@ struct peer #define PEER_FLAG_CONFIG_NODE (1 << 10) /* the node to update configs on */ #define PEER_FLAG_BFD (1 << 11) /* bfd */ #define PEER_FLAG_LONESOUL (1 << 12) +#define PEER_FLAG_DYNAMIC_NEIGHBOR (1 << 13) /* dynamic neighbor */ /* NSF mode (graceful restart) */ u_char nsf[AFI_MAX][SAFI_MAX]; @@ -970,6 +980,11 @@ struct bgp_nlri /* Check AS path loop when we send NLRI. */ /* #define BGP_SEND_ASPATH_CHECK */ +/* BGP Dynamic Neighbors feature */ +#define BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT 100 +#define BGP_DYNAMIC_NEIGHBORS_LIMIT_MIN 1 +#define BGP_DYNAMIC_NEIGHBORS_LIMIT_MAX 5000 + /* Flag for peer_clear_soft(). */ enum bgp_clear_type { @@ -1026,7 +1041,11 @@ enum bgp_clear_type #define BGP_ERR_NO_INTERFACE_CONFIG -32 #define BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS -33 #define BGP_ERR_AS_OVERRIDE -34 -#define BGP_ERR_MAX -35 +#define BGP_ERR_INVALID_DYNAMIC_NEIGHBORS_LIMIT -35 +#define BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_EXISTS -36 +#define BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_NOT_FOUND -37 +#define BGP_ERR_INVALID_FOR_DYNAMIC_PEER -38 +#define BGP_ERR_MAX -39 /* * Enumeration of different policy kinds a peer can be configured with. @@ -1060,6 +1079,15 @@ extern struct peer *peer_conf_interface_get(struct bgp *, const char *, afi_t, extern void bgp_peer_conf_if_to_su_update (struct peer *); extern struct peer_group *peer_group_lookup (struct bgp *, const char *); extern struct peer_group *peer_group_get (struct bgp *, const char *); +extern struct peer *peer_create_bind_dynamic_neighbor (struct bgp *, + union sockunion *, struct peer_group *); +extern struct prefix *peer_group_lookup_dynamic_neighbor_range ( + struct peer_group *, struct prefix *); +extern struct peer_group *peer_group_lookup_dynamic_neighbor (struct bgp *, + struct prefix *, struct prefix **); +extern struct peer *peer_lookup_dynamic_neighbor (struct bgp *, + union sockunion *); +extern void peer_drop_dynamic_neighbor (struct peer *); extern struct peer *peer_lock (struct peer *); extern struct peer *peer_unlock (struct peer *); extern bgp_peer_sort_t peer_sort (struct peer *peer); @@ -1113,6 +1141,9 @@ extern int bgp_default_local_preference_unset (struct bgp *); extern int bgp_default_subgroup_pkt_queue_max_set (struct bgp *bgp, u_int32_t); extern int bgp_default_subgroup_pkt_queue_max_unset (struct bgp *bgp); +extern int bgp_listen_limit_set (struct bgp *, int); +extern int bgp_listen_limit_unset (struct bgp *); + extern int bgp_update_delay_active (struct bgp *); extern int bgp_update_delay_configured (struct bgp *); extern int peer_rsclient_active (struct peer *); @@ -1123,6 +1154,7 @@ extern int peer_group_remote_as (struct bgp *, const char *, as_t *); extern int peer_delete (struct peer *peer); extern int peer_group_delete (struct peer_group *); extern int peer_group_remote_as_delete (struct peer_group *); +extern int peer_group_listen_range_add(struct peer_group *, struct prefix *); extern int peer_activate (struct peer *, afi_t, safi_t); extern int peer_deactivate (struct peer *, afi_t, safi_t); @@ -1279,6 +1311,21 @@ peer_afi_active_nego (const struct peer *peer, afi_t afi) return 0; } +/* If at least one address family activated for group, return 1. */ +static inline int +peer_group_af_configured (struct peer_group *group) +{ + struct peer *peer = group->conf; + + if (peer->afc[AFI_IP][SAFI_UNICAST] + || peer->afc[AFI_IP][SAFI_MULTICAST] + || peer->afc[AFI_IP][SAFI_MPLS_VPN] + || peer->afc[AFI_IP6][SAFI_UNICAST] + || peer->afc[AFI_IP6][SAFI_MULTICAST]) + return 1; + return 0; +} + static inline char * timestamp_string (time_t ts) { @@ -1299,4 +1346,10 @@ peer_established (struct peer *peer) return 0; } +static inline int +peer_dynamic_neighbor (struct peer *peer) +{ + return (CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_NEIGHBOR)) ? 1 : 0; +} + #endif /* _QUAGGA_BGPD_H */ diff --git a/lib/command.h b/lib/command.h index de09c7d947ef..9a548211731c 100644 --- a/lib/command.h +++ b/lib/command.h @@ -508,6 +508,15 @@ struct cmd_token #define NEIGHBOR_ADDR_STR2 "Neighbor address\nNeighbor tag\n" #endif /* HAVE_IPV6 */ +/* Dynamic neighbor (listen range) configuration */ +#ifdef HAVE_IPV6 +#define LISTEN_RANGE_CMD "bgp listen range (A.B.C.D/M|X:X::X:X/M) " +#define LISTEN_RANGE_ADDR_STR "Neighbor address\nNeighbor IPv6 address\n" +#else +#define LISTEN_RANGE_CMD "bgp listen range A.B.C.D/M " +#define LISTEN_RANGE_ADDR_STR "Neighbor address\n" +#endif /* HAVE_IPV6 */ + /* Prototypes. */ extern void install_node (struct cmd_node *, int (*) (struct vty *)); extern void install_default (enum node_type);