summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_fsm.c37
-rw-r--r--bgpd/bgp_network.c21
-rw-r--r--bgpd/bgp_route.c4
-rw-r--r--bgpd/bgp_vty.c510
-rw-r--r--bgpd/bgp_vty.h2
-rw-r--r--bgpd/bgpd.c315
-rw-r--r--bgpd/bgpd.h55
-rw-r--r--lib/command.h9
8 files changed, 935 insertions, 18 deletions
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 28cfb45af2..5683c15444 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 eb8bad7627..63d8580aad 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 7094e8ca45..7a465314a2 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 5cfbaebeb3..9fcebaf6ed 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 1357e3c25a..7f47f263be 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 17bf8cf5d6..cbe8580f3a 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 9863649525..8b046e597c 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 de09c7d947..9a54821173 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);