summaryrefslogtreecommitdiffstats
path: root/bgpd/bgpd.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /bgpd/bgpd.c
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bgpd/bgpd.c')
-rw-r--r--bgpd/bgpd.c8576
1 files changed, 8576 insertions, 0 deletions
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
new file mode 100644
index 0000000..4b72798
--- /dev/null
+++ b/bgpd/bgpd.c
@@ -0,0 +1,8576 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP-4, BGP-4+ daemon program
+ * Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "frrevent.h"
+#include "buffer.h"
+#include "stream.h"
+#include "ringbuf.h"
+#include "command.h"
+#include "sockunion.h"
+#include "sockopt.h"
+#include "network.h"
+#include "memory.h"
+#include "filter.h"
+#include "routemap.h"
+#include "log.h"
+#include "plist.h"
+#include "linklist.h"
+#include "workqueue.h"
+#include "queue.h"
+#include "zclient.h"
+#include "bfd.h"
+#include "hash.h"
+#include "jhash.h"
+#include "table.h"
+#include "lib/json.h"
+#include "lib/sockopt.h"
+#include "frr_pthread.h"
+#include "bitfield.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_community_alias.h"
+#include "bgpd/bgp_conditional_adv.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_damp.h"
+#include "bgpd/bgp_mplsvpn.h"
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+#endif
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_bfd.h"
+#include "bgpd/bgp_memory.h"
+#include "bgpd/bgp_evpn_vty.h"
+#include "bgpd/bgp_keepalives.h"
+#include "bgpd/bgp_io.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_labelpool.h"
+#include "bgpd/bgp_pbr.h"
+#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_mac.h"
+#include "bgp_trace.h"
+
+DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
+DEFINE_QOBJ_TYPE(bgp_master);
+DEFINE_QOBJ_TYPE(bgp);
+DEFINE_QOBJ_TYPE(peer);
+DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp));
+
+/* BGP process wide configuration. */
+static struct bgp_master bgp_master;
+
+/* BGP process wide configuration pointer to export. */
+struct bgp_master *bm;
+
+/* BGP community-list. */
+struct community_list_handler *bgp_clist;
+
+unsigned int multipath_num = MULTIPATH_NUM;
+
+/* Number of bgp instances configured for suppress fib config */
+unsigned int bgp_suppress_fib_count;
+
+static void bgp_if_finish(struct bgp *bgp);
+static void peer_drop_dynamic_neighbor(struct peer *peer);
+
+extern struct zclient *zclient;
+
+/* handle main socket creation or deletion */
+static int bgp_check_main_socket(bool create, struct bgp *bgp)
+{
+ static int bgp_server_main_created;
+ struct listnode *node;
+ char *address;
+
+ if (create) {
+ if (bgp_server_main_created)
+ return 0;
+ if (list_isempty(bm->addresses)) {
+ if (bgp_socket(bgp, bm->port, NULL) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
+ if (bgp_socket(bgp, bm->port, address) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ }
+ bgp_server_main_created = 1;
+ return 0;
+ }
+ if (!bgp_server_main_created)
+ return 0;
+ bgp_close();
+ bgp_server_main_created = 0;
+ return 0;
+}
+
+void bgp_session_reset(struct peer *peer)
+{
+ if (peer->doppelganger &&
+ (peer->doppelganger->connection->status != Deleted) &&
+ !(CHECK_FLAG(peer->doppelganger->flags, PEER_FLAG_CONFIG_NODE)))
+ peer_delete(peer->doppelganger);
+
+ BGP_EVENT_ADD(peer->connection, BGP_Stop);
+}
+
+/*
+ * During session reset, we may delete the doppelganger peer, which would
+ * be the next node to the current node. If the session reset was invoked
+ * during walk of peer list, we would end up accessing the freed next
+ * node. This function moves the next node along.
+ */
+static void bgp_session_reset_safe(struct peer *peer, struct listnode **nnode)
+{
+ struct listnode *n;
+ struct peer *npeer;
+
+ n = (nnode) ? *nnode : NULL;
+ npeer = (n) ? listgetdata(n) : NULL;
+
+ if (peer->doppelganger &&
+ (peer->doppelganger->connection->status != Deleted) &&
+ !(CHECK_FLAG(peer->doppelganger->flags, PEER_FLAG_CONFIG_NODE))) {
+ if (peer->doppelganger == npeer)
+ /* nnode and *nnode are confirmed to be non-NULL here */
+ *nnode = (*nnode)->next;
+ peer_delete(peer->doppelganger);
+ }
+
+ BGP_EVENT_ADD(peer->connection, BGP_Stop);
+}
+
+/* BGP global flag manipulation. */
+int bgp_option_set(int flag)
+{
+ switch (flag) {
+ case BGP_OPT_NO_FIB:
+ case BGP_OPT_NO_LISTEN:
+ case BGP_OPT_NO_ZEBRA:
+ SET_FLAG(bm->options, flag);
+ break;
+ default:
+ return BGP_ERR_INVALID_FLAG;
+ }
+ return 0;
+}
+
+int bgp_option_unset(int flag)
+{
+ switch (flag) {
+ /* Fall through. */
+ case BGP_OPT_NO_ZEBRA:
+ case BGP_OPT_NO_FIB:
+ UNSET_FLAG(bm->options, flag);
+ break;
+ default:
+ return BGP_ERR_INVALID_FLAG;
+ }
+ return 0;
+}
+
+int bgp_option_check(int flag)
+{
+ return CHECK_FLAG(bm->options, flag);
+}
+
+/* set the bgp no-rib option during runtime and remove installed routes */
+void bgp_option_norib_set_runtime(void)
+{
+ struct bgp *bgp;
+ struct listnode *node;
+ afi_t afi;
+ safi_t safi;
+
+ if (bgp_option_check(BGP_OPT_NO_FIB))
+ return;
+
+ bgp_option_set(BGP_OPT_NO_FIB);
+
+ zlog_info("Disabled BGP route installation to RIB (Zebra)");
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ /*
+ * Stop a crash, more work is needed
+ * here to properly add/remove these types of
+ * routes from zebra.
+ */
+ if (!bgp_fibupd_safi(safi))
+ continue;
+
+ bgp_zebra_withdraw_table_all_subtypes(bgp, afi, safi);
+ }
+ }
+
+ zlog_info("All routes have been withdrawn from RIB (Zebra)");
+}
+
+/* unset the bgp no-rib option during runtime and announce routes to Zebra */
+void bgp_option_norib_unset_runtime(void)
+{
+ struct bgp *bgp;
+ struct listnode *node;
+ afi_t afi;
+ safi_t safi;
+
+ if (!bgp_option_check(BGP_OPT_NO_FIB))
+ return;
+
+ bgp_option_unset(BGP_OPT_NO_FIB);
+
+ zlog_info("Enabled BGP route installation to RIB (Zebra)");
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ /*
+ * Stop a crash, more work is needed
+ * here to properly add/remove these types
+ * of routes from zebra
+ */
+ if (!bgp_fibupd_safi(safi))
+ continue;
+
+ bgp_zebra_announce_table_all_subtypes(bgp, afi, safi);
+ }
+ }
+
+ zlog_info("All routes have been installed in RIB (Zebra)");
+}
+
+/* Internal function to set BGP structure configureation flag. */
+static void bgp_config_set(struct bgp *bgp, int config)
+{
+ SET_FLAG(bgp->config, config);
+}
+
+static void bgp_config_unset(struct bgp *bgp, int config)
+{
+ UNSET_FLAG(bgp->config, config);
+}
+
+static int bgp_config_check(struct bgp *bgp, int config)
+{
+ return CHECK_FLAG(bgp->config, config);
+}
+
+/* Set BGP router identifier; distinguish between explicit config and other
+ * cases.
+ */
+static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id,
+ bool is_config)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (IPV4_ADDR_SAME(&bgp->router_id, id))
+ return 0;
+
+ /* EVPN uses router id in RD, withdraw them */
+ if (is_evpn_enabled())
+ bgp_evpn_handle_router_id_update(bgp, true);
+
+ vpn_handle_router_id_update(bgp, true, is_config);
+
+ IPV4_ADDR_COPY(&bgp->router_id, id);
+
+ /* Set all peer's local identifier with this value. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ IPV4_ADDR_COPY(&peer->local_id, id);
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ peer->last_reset = PEER_DOWN_RID_CHANGE;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+
+ /* EVPN uses router id in RD, update them */
+ if (is_evpn_enabled())
+ bgp_evpn_handle_router_id_update(bgp, false);
+
+ vpn_handle_router_id_update(bgp, false, is_config);
+
+ return 0;
+}
+
+void bgp_router_id_zebra_bump(vrf_id_t vrf_id, const struct prefix *router_id)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct in_addr *addr = NULL;
+
+ if (router_id != NULL)
+ addr = (struct in_addr *)&(router_id->u.prefix4);
+
+ if (vrf_id == VRF_DEFAULT) {
+ /* Router-id change for default VRF has to also update all
+ * views. */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ if (addr)
+ bgp->router_id_zebra = *addr;
+ else
+ addr = &bgp->router_id_zebra;
+
+ if (!bgp->router_id_static.s_addr) {
+ /* Router ID is updated if there are no active
+ * peer sessions
+ */
+ if (bgp->established_peers == 0) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "RID change : vrf %s(%u), RTR ID %pI4",
+ bgp->name_pretty,
+ bgp->vrf_id, addr);
+ /*
+ * if old router-id was 0x0, set flag
+ * to use this new value
+ */
+ bgp_router_id_set(bgp, addr,
+ (bgp->router_id.s_addr
+ == INADDR_ANY)
+ ? true
+ : false);
+ }
+ }
+ }
+ } else {
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (bgp) {
+ if (addr)
+ bgp->router_id_zebra = *addr;
+ else
+ addr = &bgp->router_id_zebra;
+
+ if (!bgp->router_id_static.s_addr) {
+ /* Router ID is updated if there are no active
+ * peer sessions
+ */
+ if (bgp->established_peers == 0) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "RID change : vrf %s(%u), RTR ID %pI4",
+ bgp->name_pretty,
+ bgp->vrf_id, addr);
+ /*
+ * if old router-id was 0x0, set flag
+ * to use this new value
+ */
+ bgp_router_id_set(bgp, addr,
+ (bgp->router_id.s_addr
+ == INADDR_ANY)
+ ? true
+ : false);
+ }
+ }
+
+ }
+ }
+}
+
+void bgp_router_id_static_set(struct bgp *bgp, struct in_addr id)
+{
+ bgp->router_id_static = id;
+ bgp_router_id_set(bgp,
+ id.s_addr != INADDR_ANY ? &id : &bgp->router_id_zebra,
+ true /* is config */);
+}
+
+void bm_wait_for_fib_set(bool set)
+{
+ bool send_msg = false;
+
+ if (bm->wait_for_fib == set)
+ return;
+
+ bm->wait_for_fib = set;
+ if (set) {
+ if (bgp_suppress_fib_count == 0)
+ send_msg = true;
+ bgp_suppress_fib_count++;
+ } else {
+ bgp_suppress_fib_count--;
+ if (bgp_suppress_fib_count == 0)
+ send_msg = true;
+ }
+
+ if (send_msg && zclient)
+ zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST,
+ zclient, set);
+}
+
+/* Set the suppress fib pending for the bgp configuration */
+void bgp_suppress_fib_pending_set(struct bgp *bgp, bool set)
+{
+ bool send_msg = false;
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
+ return;
+
+ if (set) {
+ SET_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_FIB_PENDING);
+ /* Send msg to zebra for the first instance of bgp enabled
+ * with suppress fib
+ */
+ if (bgp_suppress_fib_count == 0)
+ send_msg = true;
+ bgp_suppress_fib_count++;
+ } else {
+ UNSET_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_FIB_PENDING);
+ bgp_suppress_fib_count--;
+
+ /* Send msg to zebra if there are no instances enabled
+ * with suppress fib
+ */
+ if (bgp_suppress_fib_count == 0)
+ send_msg = true;
+ }
+ /* Send route notify request to RIB */
+ if (send_msg) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Sending ZEBRA_ROUTE_NOTIFY_REQUEST");
+
+ if (zclient)
+ zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST,
+ zclient, set);
+ }
+}
+
+/* BGP's cluster-id control. */
+void bgp_cluster_id_set(struct bgp *bgp, struct in_addr *cluster_id)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (bgp_config_check(bgp, BGP_CONFIG_CLUSTER_ID)
+ && IPV4_ADDR_SAME(&bgp->cluster_id, cluster_id))
+ return;
+
+ IPV4_ADDR_COPY(&bgp->cluster_id, cluster_id);
+ bgp_config_set(bgp, BGP_CONFIG_CLUSTER_ID);
+
+ /* Clear all IBGP peer. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->sort != BGP_PEER_IBGP)
+ continue;
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ peer->last_reset = PEER_DOWN_CLID_CHANGE;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+}
+
+void bgp_cluster_id_unset(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (!bgp_config_check(bgp, BGP_CONFIG_CLUSTER_ID))
+ return;
+
+ bgp->cluster_id.s_addr = 0;
+ bgp_config_unset(bgp, BGP_CONFIG_CLUSTER_ID);
+
+ /* Clear all IBGP peer. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->sort != BGP_PEER_IBGP)
+ continue;
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ peer->last_reset = PEER_DOWN_CLID_CHANGE;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+}
+
+/* BGP timer configuration. */
+void bgp_timers_set(struct vty *vty, struct bgp *bgp, uint32_t keepalive,
+ uint32_t holdtime, uint32_t connect_retry,
+ uint32_t delayopen)
+{
+ uint32_t default_keepalive = holdtime / 3;
+
+ if (keepalive > default_keepalive) {
+ if (vty)
+ vty_out(vty,
+ "%% keepalive value %u is larger than 1/3 of the holdtime, setting to %u\n",
+ keepalive, default_keepalive);
+ } else {
+ default_keepalive = keepalive;
+ }
+
+ bgp->default_keepalive = default_keepalive;
+ bgp->default_holdtime = holdtime;
+ bgp->default_connect_retry = connect_retry;
+ bgp->default_delayopen = delayopen;
+}
+
+/* mostly for completeness - CLI uses its own defaults */
+void bgp_timers_unset(struct bgp *bgp)
+{
+ bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
+ bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
+ bgp->default_connect_retry = BGP_DEFAULT_CONNECT_RETRY;
+ bgp->default_delayopen = BGP_DEFAULT_DELAYOPEN;
+}
+
+void bgp_tcp_keepalive_set(struct bgp *bgp, uint16_t keepalive_idle,
+ uint16_t keepalive_intvl, uint16_t keepalive_probes)
+{
+ bgp->tcp_keepalive_idle = keepalive_idle;
+ bgp->tcp_keepalive_intvl = keepalive_intvl;
+ bgp->tcp_keepalive_probes = keepalive_probes;
+}
+
+void bgp_tcp_keepalive_unset(struct bgp *bgp)
+{
+ bgp->tcp_keepalive_idle = 0;
+ bgp->tcp_keepalive_intvl = 0;
+ bgp->tcp_keepalive_probes = 0;
+}
+
+/* BGP confederation configuration. */
+void bgp_confederation_id_set(struct bgp *bgp, as_t as, const char *as_str)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+ int already_confed;
+
+ if (as == 0)
+ return;
+
+ /* Remember - were we doing confederation before? */
+ already_confed = bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION);
+ bgp->confed_id = as;
+ if (bgp->confed_id_pretty)
+ XFREE(MTYPE_BGP, bgp->confed_id_pretty);
+ bgp->confed_id_pretty = XSTRDUP(MTYPE_BGP, as_str);
+ bgp_config_set(bgp, BGP_CONFIG_CONFEDERATION);
+
+ /* If we were doing confederation already, this is just an external
+ AS change. Just Reset EBGP sessions, not CONFED sessions. If we
+ were not doing confederation before, reset all EBGP sessions. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ enum bgp_peer_sort ptype = peer_sort(peer);
+
+ /* We're looking for peers who's AS is not local or part of our
+ confederation. */
+ if (already_confed) {
+ if (ptype == BGP_PEER_EBGP) {
+ peer->local_as = as;
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->connection->status)) {
+ peer->last_reset =
+ PEER_DOWN_CONFED_ID_CHANGE;
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset_safe(peer, &nnode);
+ }
+ } else {
+ /* Not doign confederation before, so reset every
+ non-local
+ session */
+ if (ptype != BGP_PEER_IBGP) {
+ /* Reset the local_as to be our EBGP one */
+ if (ptype == BGP_PEER_EBGP)
+ peer->local_as = as;
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->connection->status)) {
+ peer->last_reset =
+ PEER_DOWN_CONFED_ID_CHANGE;
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset_safe(peer, &nnode);
+ }
+ }
+ }
+ return;
+}
+
+void bgp_confederation_id_unset(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ bgp->confed_id = 0;
+ XFREE(MTYPE_BGP, bgp->confed_id_pretty);
+ bgp_config_unset(bgp, BGP_CONFIG_CONFEDERATION);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ /* We're looking for peers who's AS is not local */
+ if (peer_sort(peer) != BGP_PEER_IBGP) {
+ peer->local_as = bgp->as;
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->connection->status)) {
+ peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+
+ else
+ bgp_session_reset_safe(peer, &nnode);
+ }
+ }
+}
+
+/* Is an AS part of the confed or not? */
+bool bgp_confederation_peers_check(struct bgp *bgp, as_t as)
+{
+ int i;
+
+ if (!bgp)
+ return false;
+
+ for (i = 0; i < bgp->confed_peers_cnt; i++)
+ if (bgp->confed_peers[i].as == as)
+ return true;
+
+ return false;
+}
+
+/* Add an AS to the confederation set. */
+void bgp_confederation_peers_add(struct bgp *bgp, as_t as, const char *as_str)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (!bgp)
+ return;
+
+ if (bgp_confederation_peers_check(bgp, as))
+ return;
+
+ bgp->confed_peers = XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers,
+ (bgp->confed_peers_cnt + 1) *
+ sizeof(struct as_confed));
+
+ bgp->confed_peers[bgp->confed_peers_cnt].as = as;
+ bgp->confed_peers[bgp->confed_peers_cnt].as_pretty =
+ XSTRDUP(MTYPE_BGP, as_str);
+ bgp->confed_peers_cnt++;
+
+ if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION)) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->as == as) {
+ peer->local_as = bgp->as;
+ (void)peer_sort(peer);
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->connection->status)) {
+ peer->last_reset =
+ PEER_DOWN_CONFED_PEER_CHANGE;
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset_safe(peer, &nnode);
+ }
+ }
+ }
+}
+
+/* Delete an AS from the confederation set. */
+void bgp_confederation_peers_remove(struct bgp *bgp, as_t as)
+{
+ int i;
+ int j;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (!bgp)
+ return;
+
+ if (!bgp_confederation_peers_check(bgp, as))
+ return;
+
+ for (i = 0; i < bgp->confed_peers_cnt; i++)
+ if (bgp->confed_peers[i].as == as) {
+ XFREE(MTYPE_BGP, bgp->confed_peers[i].as_pretty);
+ for (j = i + 1; j < bgp->confed_peers_cnt; j++) {
+ bgp->confed_peers[j - 1].as =
+ bgp->confed_peers[j].as;
+ bgp->confed_peers[j - 1].as_pretty =
+ bgp->confed_peers[j].as_pretty;
+ }
+ }
+
+ bgp->confed_peers_cnt--;
+
+ if (bgp->confed_peers_cnt == 0) {
+ if (bgp->confed_peers)
+ XFREE(MTYPE_BGP_CONFED_LIST, bgp->confed_peers);
+ bgp->confed_peers = NULL;
+ } else
+ bgp->confed_peers = XREALLOC(
+ MTYPE_BGP_CONFED_LIST, bgp->confed_peers,
+ bgp->confed_peers_cnt * sizeof(struct as_confed));
+
+ /* Now reset any peer who's remote AS has just been removed from the
+ CONFED */
+ if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION)) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->as == as) {
+ peer->local_as = bgp->confed_id;
+ (void)peer_sort(peer);
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->connection->status)) {
+ peer->last_reset =
+ PEER_DOWN_CONFED_PEER_CHANGE;
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset_safe(peer, &nnode);
+ }
+ }
+ }
+}
+
+/* Local preference configuration. */
+void bgp_default_local_preference_set(struct bgp *bgp, uint32_t local_pref)
+{
+ if (!bgp)
+ return;
+
+ bgp->default_local_pref = local_pref;
+}
+
+void bgp_default_local_preference_unset(struct bgp *bgp)
+{
+ if (!bgp)
+ return;
+
+ bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
+}
+
+/* Local preference configuration. */
+void bgp_default_subgroup_pkt_queue_max_set(struct bgp *bgp,
+ uint32_t queue_size)
+{
+ if (!bgp)
+ return;
+
+ bgp->default_subgroup_pkt_queue_max = queue_size;
+}
+
+void bgp_default_subgroup_pkt_queue_max_unset(struct bgp *bgp)
+{
+ if (!bgp)
+ return;
+ bgp->default_subgroup_pkt_queue_max =
+ BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX;
+}
+
+/* Listen limit configuration. */
+void bgp_listen_limit_set(struct bgp *bgp, int listen_limit)
+{
+ if (!bgp)
+ return;
+
+ bgp->dynamic_neighbors_limit = listen_limit;
+}
+
+void bgp_listen_limit_unset(struct bgp *bgp)
+{
+ if (!bgp)
+ return;
+
+ bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT;
+}
+
+int bgp_map_afi_safi_iana2int(iana_afi_t pkt_afi, iana_safi_t pkt_safi,
+ afi_t *afi, safi_t *safi)
+{
+ /* Map from IANA values to internal values, return error if
+ * values are unrecognized.
+ */
+ *afi = afi_iana2int(pkt_afi);
+ *safi = safi_iana2int(pkt_safi);
+ if (*afi == AFI_MAX || *safi == SAFI_MAX)
+ return -1;
+
+ return 0;
+}
+
+int bgp_map_afi_safi_int2iana(afi_t afi, safi_t safi, iana_afi_t *pkt_afi,
+ iana_safi_t *pkt_safi)
+{
+ /* Map from internal values to IANA values, return error if
+ * internal values are bad (unexpected).
+ */
+ if (afi == AFI_MAX || safi == SAFI_MAX)
+ return -1;
+ *pkt_afi = afi_int2iana(afi);
+ *pkt_safi = safi_int2iana(safi);
+ return 0;
+}
+
+struct peer_af *peer_af_create(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer_af *af;
+ int afid;
+ struct bgp *bgp;
+
+ if (!peer)
+ return NULL;
+
+ afid = afindex(afi, safi);
+ if (afid >= BGP_AF_MAX)
+ return NULL;
+
+ bgp = peer->bgp;
+ assert(peer->peer_af_array[afid] == NULL);
+
+ /* Allocate new peer af */
+ af = XCALLOC(MTYPE_BGP_PEER_AF, sizeof(struct peer_af));
+
+ peer->peer_af_array[afid] = af;
+ af->afi = afi;
+ af->safi = safi;
+ af->afid = afid;
+ af->peer = peer;
+ bgp->af_peer_count[afi][safi]++;
+
+ return af;
+}
+
+struct peer_af *peer_af_find(struct peer *peer, afi_t afi, safi_t safi)
+{
+ int afid;
+
+ if (!peer)
+ return NULL;
+
+ afid = afindex(afi, safi);
+ if (afid >= BGP_AF_MAX)
+ return NULL;
+
+ return peer->peer_af_array[afid];
+}
+
+int peer_af_delete(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer_af *af;
+ int afid;
+ struct bgp *bgp;
+
+ if (!peer)
+ return -1;
+
+ afid = afindex(afi, safi);
+ if (afid >= BGP_AF_MAX)
+ return -1;
+
+ af = peer->peer_af_array[afid];
+ if (!af)
+ return -1;
+
+ bgp = peer->bgp;
+ bgp_soft_reconfig_table_task_cancel(bgp, bgp->rib[afi][safi], peer);
+
+ bgp_stop_announce_route_timer(af);
+
+ if (PAF_SUBGRP(af)) {
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64 " remove peer %s",
+ af->subgroup->update_group->id,
+ af->subgroup->id, peer->host);
+ }
+
+
+ update_subgroup_remove_peer(af->subgroup, af);
+
+ if (bgp->af_peer_count[afi][safi])
+ bgp->af_peer_count[afi][safi]--;
+
+ peer->peer_af_array[afid] = NULL;
+ XFREE(MTYPE_BGP_PEER_AF, af);
+ return 0;
+}
+
+/* Peer comparison function for sorting. */
+int peer_cmp(struct peer *p1, struct peer *p2)
+{
+ if (p1->group && !p2->group)
+ return -1;
+
+ if (!p1->group && p2->group)
+ return 1;
+
+ if (p1->group == p2->group) {
+ if (p1->conf_if && !p2->conf_if)
+ return -1;
+
+ if (!p1->conf_if && p2->conf_if)
+ return 1;
+
+ if (p1->conf_if && p2->conf_if)
+ return if_cmp_name_func(p1->conf_if, p2->conf_if);
+ } else
+ return strcmp(p1->group->name, p2->group->name);
+
+ return sockunion_cmp(&p1->connection->su, &p2->connection->su);
+}
+
+static unsigned int peer_hash_key_make(const void *p)
+{
+ const struct peer *peer = p;
+ return sockunion_hash(&peer->connection->su);
+}
+
+static bool peer_hash_same(const void *p1, const void *p2)
+{
+ const struct peer *peer1 = p1;
+ const struct peer *peer2 = p2;
+
+ return (sockunion_same(&peer1->connection->su, &peer2->connection->su) &&
+ CHECK_FLAG(peer1->flags, PEER_FLAG_CONFIG_NODE) ==
+ CHECK_FLAG(peer2->flags, PEER_FLAG_CONFIG_NODE));
+}
+
+void peer_flag_inherit(struct peer *peer, uint64_t flag)
+{
+ bool group_val;
+
+ /* Skip if peer is not a peer-group member. */
+ if (!peer_group_active(peer))
+ return;
+
+ /* Unset override flag to signal inheritance from peer-group. */
+ UNSET_FLAG(peer->flags_override, flag);
+
+ /*
+ * Inherit flag state from peer-group. If the flag of the peer-group is
+ * not being inverted, the peer must inherit the inverse of the current
+ * peer-group flag state.
+ */
+ group_val = CHECK_FLAG(peer->group->conf->flags, flag);
+ if (!CHECK_FLAG(peer->group->conf->flags_invert, flag)
+ && CHECK_FLAG(peer->flags_invert, flag))
+ COND_FLAG(peer->flags, flag, !group_val);
+ else
+ COND_FLAG(peer->flags, flag, group_val);
+}
+
+bool peer_af_flag_check(struct peer *peer, afi_t afi, safi_t safi,
+ uint64_t flag)
+{
+ return !!CHECK_FLAG(peer->af_flags[afi][safi], flag);
+}
+
+void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi,
+ uint64_t flag)
+{
+ bool group_val;
+
+ /* Skip if peer is not a peer-group member. */
+ if (!peer_group_active(peer))
+ return;
+
+ /* Unset override flag to signal inheritance from peer-group. */
+ UNSET_FLAG(peer->af_flags_override[afi][safi], flag);
+
+ /*
+ * Inherit flag state from peer-group. If the flag of the peer-group is
+ * not being inverted, the peer must inherit the inverse of the current
+ * peer-group flag state.
+ */
+ group_val = CHECK_FLAG(peer->group->conf->af_flags[afi][safi], flag);
+ if (!CHECK_FLAG(peer->group->conf->af_flags_invert[afi][safi], flag)
+ && CHECK_FLAG(peer->af_flags_invert[afi][safi], flag))
+ COND_FLAG(peer->af_flags[afi][safi], flag, !group_val);
+ else
+ COND_FLAG(peer->af_flags[afi][safi], flag, group_val);
+}
+
+/* Check peer's AS number and determines if this peer is IBGP or EBGP */
+static inline enum bgp_peer_sort peer_calc_sort(struct peer *peer)
+{
+ struct bgp *bgp;
+ as_t local_as;
+
+ bgp = peer->bgp;
+
+ if (peer->change_local_as)
+ local_as = peer->change_local_as;
+ else
+ local_as = peer->local_as;
+
+ /* Peer-group */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (peer->as_type == AS_INTERNAL)
+ return BGP_PEER_IBGP;
+
+ else if (peer->as_type == AS_EXTERNAL)
+ return BGP_PEER_EBGP;
+
+ else if (peer->as_type == AS_SPECIFIED && peer->as) {
+ assert(bgp);
+ return (local_as == peer->as ? BGP_PEER_IBGP
+ : BGP_PEER_EBGP);
+ }
+
+ else {
+ struct peer *peer1;
+
+ assert(peer->group);
+ peer1 = listnode_head(peer->group->peer);
+
+ if (peer1)
+ return peer1->sort;
+ }
+ return BGP_PEER_INTERNAL;
+ }
+
+ /* Normal peer */
+ if (bgp && CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) {
+ if (local_as == 0)
+ return BGP_PEER_INTERNAL;
+
+ if (local_as == peer->as) {
+ if (bgp->as == bgp->confed_id) {
+ if (local_as == bgp->as)
+ return BGP_PEER_IBGP;
+ else
+ return BGP_PEER_EBGP;
+ } else {
+ if (local_as == bgp->confed_id)
+ return BGP_PEER_EBGP;
+ else
+ return BGP_PEER_IBGP;
+ }
+ }
+
+ if (bgp_confederation_peers_check(bgp, peer->as))
+ return BGP_PEER_CONFED;
+
+ return BGP_PEER_EBGP;
+ } else {
+ if (peer->as_type == AS_UNSPECIFIED) {
+ /* check if in peer-group with AS information */
+ if (peer->group
+ && (peer->group->conf->as_type != AS_UNSPECIFIED)) {
+ if (peer->group->conf->as_type
+ == AS_SPECIFIED) {
+ if (local_as == peer->group->conf->as)
+ return BGP_PEER_IBGP;
+ else
+ return BGP_PEER_EBGP;
+ } else if (peer->group->conf->as_type
+ == AS_INTERNAL)
+ return BGP_PEER_IBGP;
+ else
+ return BGP_PEER_EBGP;
+ }
+ /* no AS information anywhere, let caller know */
+ return BGP_PEER_UNSPECIFIED;
+ } else if (peer->as_type != AS_SPECIFIED)
+ return (peer->as_type == AS_INTERNAL ? BGP_PEER_IBGP
+ : BGP_PEER_EBGP);
+
+ return (local_as == 0 ? BGP_PEER_INTERNAL
+ : local_as == peer->as ? BGP_PEER_IBGP
+ : BGP_PEER_EBGP);
+ }
+}
+
+/* Calculate and cache the peer "sort" */
+enum bgp_peer_sort peer_sort(struct peer *peer)
+{
+ peer->sort = peer_calc_sort(peer);
+ return peer->sort;
+}
+
+enum bgp_peer_sort peer_sort_lookup(struct peer *peer)
+{
+ return peer->sort;
+}
+
+/*
+ * Mutex will be freed in peer_connection_free
+ * this is a convenience function to reduce cut-n-paste
+ */
+void bgp_peer_connection_buffers_free(struct peer_connection *connection)
+{
+ frr_with_mutex (&connection->io_mtx) {
+ if (connection->ibuf) {
+ stream_fifo_free(connection->ibuf);
+ connection->ibuf = NULL;
+ }
+
+ if (connection->obuf) {
+ stream_fifo_free(connection->obuf);
+ connection->obuf = NULL;
+ }
+
+ if (connection->ibuf_work) {
+ ringbuf_del(connection->ibuf_work);
+ connection->ibuf_work = NULL;
+ }
+ }
+}
+
+void bgp_peer_connection_free(struct peer_connection **connection)
+{
+ bgp_peer_connection_buffers_free(*connection);
+ pthread_mutex_destroy(&(*connection)->io_mtx);
+
+ memset(*connection, 0, sizeof(struct peer_connection));
+ XFREE(MTYPE_BGP_PEER_CONNECTION, *connection);
+
+ connection = NULL;
+}
+
+struct peer_connection *bgp_peer_connection_new(struct peer *peer)
+{
+ struct peer_connection *connection;
+
+ connection = XCALLOC(MTYPE_BGP_PEER_CONNECTION,
+ sizeof(struct peer_connection));
+
+ connection->peer = peer;
+ connection->fd = -1;
+
+ connection->ibuf = stream_fifo_new();
+ connection->obuf = stream_fifo_new();
+ pthread_mutex_init(&connection->io_mtx, NULL);
+
+ /* We use a larger buffer for peer->obuf_work in the event that:
+ * - We RX a BGP_UPDATE where the attributes alone are just
+ * under BGP_EXTENDED_MESSAGE_MAX_PACKET_SIZE.
+ * - The user configures an outbound route-map that does many as-path
+ * prepends or adds many communities. At most they can have
+ * CMD_ARGC_MAX args in a route-map so there is a finite limit on how
+ * large they can make the attributes.
+ *
+ * Having a buffer with BGP_MAX_PACKET_SIZE_OVERFLOW allows us to avoid
+ * bounds checking for every single attribute as we construct an
+ * UPDATE.
+ */
+ connection->ibuf_work =
+ ringbuf_new(BGP_MAX_PACKET_SIZE * BGP_READ_PACKET_MAX);
+
+ connection->status = Idle;
+ connection->ostatus = Idle;
+
+ return connection;
+}
+
+static void peer_free(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ assert(peer->connection->status == Deleted);
+
+ QOBJ_UNREG(peer);
+
+ /* this /ought/ to have been done already through bgp_stop earlier,
+ * but just to be sure..
+ */
+ bgp_timer_set(peer->connection);
+ bgp_reads_off(peer->connection);
+ bgp_writes_off(peer->connection);
+ event_cancel_event_ready(bm->master, peer->connection);
+ FOREACH_AFI_SAFI (afi, safi)
+ EVENT_OFF(peer->t_revalidate_all[afi][safi]);
+ assert(!peer->connection->t_write);
+ assert(!peer->connection->t_read);
+ event_cancel_event_ready(bm->master, peer->connection);
+
+ /* Free connected nexthop, if present */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)
+ && !peer_dynamic_neighbor(peer))
+ bgp_delete_connected_nexthop(family2afi(peer->connection->su.sa
+ .sa_family),
+ peer);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (peer->filter[afi][safi].advmap.aname)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ peer->filter[afi][safi].advmap.aname);
+ if (peer->filter[afi][safi].advmap.cname)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ peer->filter[afi][safi].advmap.cname);
+ }
+
+ XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message);
+
+ XFREE(MTYPE_PEER_DESC, peer->desc);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->host);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->hostname);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+ XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname);
+
+ /* Update source configuration. */
+ if (peer->update_source) {
+ sockunion_free(peer->update_source);
+ peer->update_source = NULL;
+ }
+
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+
+ XFREE(MTYPE_BGP_NOTIFICATION, peer->notify.data);
+ memset(&peer->notify, 0, sizeof(struct bgp_notify));
+
+ if (peer->clear_node_queue)
+ work_queue_free_and_null(&peer->clear_node_queue);
+
+ XFREE(MTYPE_PEER_CONF_IF, peer->conf_if);
+
+ XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
+
+ /* Remove BFD configuration. */
+ if (peer->bfd_config)
+ bgp_peer_remove_bfd_config(peer);
+
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE, 0);
+
+ if (peer->change_local_as_pretty)
+ XFREE(MTYPE_BGP, peer->change_local_as_pretty);
+ if (peer->as_pretty)
+ XFREE(MTYPE_BGP, peer->as_pretty);
+
+ bgp_peer_connection_free(&peer->connection);
+
+ bgp_unlock(peer->bgp);
+
+ stream_free(peer->last_reset_cause);
+
+ memset(peer, 0, sizeof(struct peer));
+
+ XFREE(MTYPE_BGP_PEER, peer);
+}
+
+/* increase reference count on a struct peer */
+struct peer *peer_lock_with_caller(const char *name, struct peer *peer)
+{
+ frrtrace(2, frr_bgp, bgp_peer_lock, peer, name);
+ assert(peer && (peer->lock >= 0));
+
+ peer->lock++;
+
+ return peer;
+}
+
+/* decrease reference count on a struct peer
+ * struct peer is freed and NULL returned if last reference
+ */
+struct peer *peer_unlock_with_caller(const char *name, struct peer *peer)
+{
+ frrtrace(2, frr_bgp, bgp_peer_unlock, peer, name);
+ assert(peer && (peer->lock > 0));
+
+ peer->lock--;
+
+ if (peer->lock == 0) {
+ peer_free(peer);
+ return NULL;
+ }
+
+ return peer;
+}
+/* BGP GR changes */
+
+int bgp_global_gr_init(struct bgp *bgp)
+{
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("%s called ..", __func__);
+
+ int local_GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE][BGP_GLOBAL_GR_EVENT_CMD] = {
+ /* GLOBAL_HELPER Mode */
+ {
+ /*Event -> */
+ /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/
+ GLOBAL_GR, GLOBAL_INVALID,
+ /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/
+ GLOBAL_DISABLE, GLOBAL_INVALID
+ },
+ /* GLOBAL_GR Mode */
+ {
+ /*Event -> */
+ /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/
+ GLOBAL_GR, GLOBAL_HELPER,
+ /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/
+ GLOBAL_DISABLE, GLOBAL_INVALID
+ },
+ /* GLOBAL_DISABLE Mode */
+ {
+ /*Event -> */
+ /*GLOBAL_GR_cmd */ /*no_Global_GR_cmd*/
+ GLOBAL_GR, GLOBAL_INVALID,
+ /*GLOBAL_DISABLE_cmd*//*no_Global_Disable_cmd*/
+ GLOBAL_INVALID, GLOBAL_HELPER
+ },
+ /* GLOBAL_INVALID Mode */
+ {
+ /*Event -> */
+ /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/
+ GLOBAL_INVALID, GLOBAL_INVALID,
+ /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/
+ GLOBAL_INVALID, GLOBAL_INVALID
+ }
+ };
+ memcpy(bgp->GLOBAL_GR_FSM, local_GLOBAL_GR_FSM,
+ sizeof(local_GLOBAL_GR_FSM));
+
+ bgp->global_gr_present_state = GLOBAL_HELPER;
+ bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE;
+
+ return BGP_GR_SUCCESS;
+}
+
+int bgp_peer_gr_init(struct peer *peer)
+{
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("%s called ..", __func__);
+
+ struct bgp_peer_gr local_Peer_GR_FSM[BGP_PEER_GR_MODE]
+ [BGP_PEER_GR_EVENT_CMD] = {
+ {
+ /* PEER_HELPER Mode */
+ /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
+ { PEER_GR, bgp_peer_gr_action }, {PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
+ {PEER_DISABLE, bgp_peer_gr_action }, {PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
+ { PEER_INVALID, NULL }, {PEER_GLOBAL_INHERIT,
+ bgp_peer_gr_action }
+ },
+ {
+ /* PEER_GR Mode */
+ /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
+ { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT,
+ bgp_peer_gr_action },
+ /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
+ {PEER_DISABLE, bgp_peer_gr_action }, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
+ { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL }
+ },
+ {
+ /* PEER_DISABLE Mode */
+ /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
+ { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
+ { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT,
+ bgp_peer_gr_action },
+ /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
+ { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL }
+ },
+ {
+ /* PEER_INVALID Mode */
+ /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
+ { PEER_INVALID, NULL }, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
+ { PEER_INVALID, NULL }, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
+ { PEER_INVALID, NULL }, { PEER_INVALID, NULL },
+ },
+ {
+ /* PEER_GLOBAL_INHERIT Mode */
+ /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
+ { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
+ { PEER_DISABLE, bgp_peer_gr_action}, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
+ { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL }
+ }
+ };
+ memcpy(&peer->PEER_GR_FSM, local_Peer_GR_FSM,
+ sizeof(local_Peer_GR_FSM));
+ peer->peer_gr_present_state = PEER_GLOBAL_INHERIT;
+ bgp_peer_move_to_gr_mode(peer, PEER_GLOBAL_INHERIT);
+
+ return BGP_GR_SUCCESS;
+}
+
+static void bgp_srv6_init(struct bgp *bgp)
+{
+ bgp->srv6_enabled = false;
+ memset(bgp->srv6_locator_name, 0, sizeof(bgp->srv6_locator_name));
+ bgp->srv6_locator_chunks = list_new();
+ bgp->srv6_functions = list_new();
+}
+
+static void bgp_srv6_cleanup(struct bgp *bgp)
+{
+ if (bgp->srv6_locator_chunks)
+ list_delete(&bgp->srv6_locator_chunks);
+ if (bgp->srv6_functions)
+ list_delete(&bgp->srv6_functions);
+}
+
+/* Allocate new peer object, implicitely locked. */
+struct peer *peer_new(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct peer *peer;
+ struct servent *sp;
+
+ /* bgp argument is absolutely required */
+ assert(bgp);
+
+ /* Allocate new peer. */
+ peer = XCALLOC(MTYPE_BGP_PEER, sizeof(struct peer));
+
+ /* Create buffers. */
+ peer->connection = bgp_peer_connection_new(peer);
+
+ /* Set default value. */
+ peer->v_start = BGP_INIT_START_TIMER;
+ peer->v_connect = bgp->default_connect_retry;
+ peer->cur_event = peer->last_event = peer->last_major_event = 0;
+ peer->bgp = bgp_lock(bgp);
+ peer = peer_lock(peer); /* initial reference */
+ peer->local_role = ROLE_UNDEFINED;
+ peer->remote_role = ROLE_UNDEFINED;
+ peer->password = NULL;
+ peer->max_packet_size = BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE;
+
+ /* Set default flags. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
+ SET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SEND_EXT_COMMUNITY);
+ SET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SEND_LARGE_COMMUNITY);
+
+ SET_FLAG(peer->af_flags_invert[afi][safi],
+ PEER_FLAG_SEND_COMMUNITY);
+ SET_FLAG(peer->af_flags_invert[afi][safi],
+ PEER_FLAG_SEND_EXT_COMMUNITY);
+ SET_FLAG(peer->af_flags_invert[afi][safi],
+ PEER_FLAG_SEND_LARGE_COMMUNITY);
+ peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE;
+ peer->addpath_best_selected[afi][safi] = 0;
+ peer->soo[afi][safi] = NULL;
+ }
+
+ /* set nexthop-unchanged for l2vpn evpn by default */
+ SET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+
+ SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+
+ /* Initialize per peer bgp GR FSM */
+ bgp_peer_gr_init(peer);
+
+ /* Get service port number. */
+ sp = getservbyname("bgp", "tcp");
+ peer->port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs(sp->s_port);
+
+ QOBJ_REG(peer, peer);
+ return peer;
+}
+
+/*
+ * This function is invoked when a duplicate peer structure associated with
+ * a neighbor is being deleted. If this about-to-be-deleted structure is
+ * the one with all the config, then we have to copy over the info.
+ */
+void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
+{
+ struct peer_af *paf;
+ afi_t afi;
+ safi_t safi;
+ int afidx;
+
+ assert(peer_src);
+ assert(peer_dst);
+
+ /* The following function is used by both peer group config copy to
+ * individual peer and when we transfer config
+ */
+ if (peer_src->change_local_as)
+ peer_dst->change_local_as = peer_src->change_local_as;
+
+ /* peer flags apply */
+ peer_dst->flags = peer_src->flags;
+ /*
+ * The doppelganger *must* not have a config node stored
+ */
+ UNSET_FLAG(peer_dst->flags, PEER_FLAG_CONFIG_NODE);
+ peer_dst->peer_gr_present_state = peer_src->peer_gr_present_state;
+ peer_dst->peer_gr_new_status_flag = peer_src->peer_gr_new_status_flag;
+
+ peer_dst->local_as = peer_src->local_as;
+ peer_dst->port = peer_src->port;
+ /* copy tcp_mss value */
+ peer_dst->tcp_mss = peer_src->tcp_mss;
+ (void)peer_sort(peer_dst);
+ peer_dst->rmap_type = peer_src->rmap_type;
+ peer_dst->local_role = peer_src->local_role;
+
+ peer_dst->max_packet_size = peer_src->max_packet_size;
+
+ /* Timers */
+ peer_dst->holdtime = peer_src->holdtime;
+ peer_dst->keepalive = peer_src->keepalive;
+ peer_dst->connect = peer_src->connect;
+ peer_dst->delayopen = peer_src->delayopen;
+ peer_dst->v_holdtime = peer_src->v_holdtime;
+ peer_dst->v_keepalive = peer_src->v_keepalive;
+ peer_dst->routeadv = peer_src->routeadv;
+ peer_dst->v_routeadv = peer_src->v_routeadv;
+ peer_dst->v_delayopen = peer_src->v_delayopen;
+
+ /* password apply */
+ if (peer_src->password) {
+ XFREE(MTYPE_PEER_PASSWORD, peer_dst->password);
+ peer_dst->password =
+ XSTRDUP(MTYPE_PEER_PASSWORD, peer_src->password);
+ }
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ peer_dst->afc[afi][safi] = peer_src->afc[afi][safi];
+ peer_dst->af_flags[afi][safi] = peer_src->af_flags[afi][safi];
+ peer_dst->allowas_in[afi][safi] =
+ peer_src->allowas_in[afi][safi];
+ peer_dst->weight[afi][safi] = peer_src->weight[afi][safi];
+ peer_dst->addpath_type[afi][safi] =
+ peer_src->addpath_type[afi][safi];
+ }
+
+ for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) {
+ paf = peer_src->peer_af_array[afidx];
+ if (paf != NULL) {
+ if (!peer_af_find(peer_dst, paf->afi, paf->safi))
+ peer_af_create(peer_dst, paf->afi, paf->safi);
+ }
+ }
+
+ /* update-source apply */
+ if (peer_src->update_source) {
+ if (peer_dst->update_source)
+ sockunion_free(peer_dst->update_source);
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if);
+ peer_dst->update_source =
+ sockunion_dup(peer_src->update_source);
+ } else if (peer_src->update_if) {
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if);
+ if (peer_dst->update_source) {
+ sockunion_free(peer_dst->update_source);
+ peer_dst->update_source = NULL;
+ }
+ peer_dst->update_if =
+ XSTRDUP(MTYPE_PEER_UPDATE_SOURCE, peer_src->update_if);
+ }
+
+ if (peer_src->ifname) {
+ XFREE(MTYPE_BGP_PEER_IFNAME, peer_dst->ifname);
+
+ peer_dst->ifname =
+ XSTRDUP(MTYPE_BGP_PEER_IFNAME, peer_src->ifname);
+ }
+ peer_dst->ttl = peer_src->ttl;
+}
+
+static int bgp_peer_conf_if_to_su_update_v4(struct peer_connection *connection,
+ struct interface *ifp)
+{
+ struct connected *ifc;
+ struct prefix p;
+ uint32_t addr;
+ struct listnode *node;
+
+ /* If our IPv4 address on the interface is /30 or /31, we can derive the
+ * IPv4 address of the other end.
+ */
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+ if (ifc->address && (ifc->address->family == AF_INET)) {
+ prefix_copy(&p, CONNECTED_PREFIX(ifc));
+ if (p.prefixlen == 30) {
+ connection->su.sa.sa_family = AF_INET;
+ addr = ntohl(p.u.prefix4.s_addr);
+ if (addr % 4 == 1)
+ connection->su.sin.sin_addr.s_addr =
+ htonl(addr + 1);
+ else if (addr % 4 == 2)
+ connection->su.sin.sin_addr.s_addr =
+ htonl(addr - 1);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ connection->su.sin.sin_len =
+ sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+ return 1;
+ } else if (p.prefixlen == 31) {
+ connection->su.sa.sa_family = AF_INET;
+ addr = ntohl(p.u.prefix4.s_addr);
+ if (addr % 2 == 0)
+ connection->su.sin.sin_addr.s_addr =
+ htonl(addr + 1);
+ else
+ connection->su.sin.sin_addr.s_addr =
+ htonl(addr - 1);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ connection->su.sin.sin_len =
+ sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+ return 1;
+ } else if (bgp_debug_neighbor_events(connection->peer))
+ zlog_debug("%s: IPv4 interface address is not /30 or /31, v4 session not started",
+ connection->peer->conf_if);
+ }
+ }
+
+ return 0;
+}
+
+static bool bgp_peer_conf_if_to_su_update_v6(struct peer_connection *connection,
+ struct interface *ifp)
+{
+ struct nbr_connected *ifc_nbr;
+
+ /* Have we learnt the peer's IPv6 link-local address? */
+ if (ifp->nbr_connected
+ && (ifc_nbr = listnode_head(ifp->nbr_connected))) {
+ connection->su.sa.sa_family = AF_INET6;
+ memcpy(&connection->su.sin6.sin6_addr,
+ &ifc_nbr->address->u.prefix, sizeof(struct in6_addr));
+#ifdef SIN6_LEN
+ connection->su.sin6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ connection->su.sin6.sin6_scope_id = ifp->ifindex;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Set or reset the peer address socketunion structure based on the
+ * learnt/derived peer address. If the address has changed, update the
+ * password on the listen socket, if needed.
+ */
+void bgp_peer_conf_if_to_su_update(struct peer_connection *connection)
+{
+ struct interface *ifp;
+ int prev_family;
+ int peer_addr_updated = 0;
+ struct listnode *node;
+ union sockunion old_su;
+ struct peer *peer = connection->peer;
+
+ /*
+ * This function is only ever needed when FRR an interface
+ * based peering, so this simple test will tell us if
+ * we are in an interface based configuration or not
+ */
+ if (!peer->conf_if)
+ return;
+
+ old_su = connection->su;
+
+ prev_family = connection->su.sa.sa_family;
+ if ((ifp = if_lookup_by_name(peer->conf_if, peer->bgp->vrf_id))) {
+ peer->ifp = ifp;
+ /* If BGP unnumbered is not "v6only", we first see if we can
+ * derive the
+ * peer's IPv4 address.
+ */
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))
+ peer_addr_updated =
+ bgp_peer_conf_if_to_su_update_v4(connection,
+ ifp);
+
+ /* If "v6only" or we can't derive peer's IPv4 address, see if
+ * we've
+ * learnt the peer's IPv6 link-local address. This is from the
+ * source
+ * IPv6 address in router advertisement.
+ */
+ if (!peer_addr_updated)
+ peer_addr_updated =
+ bgp_peer_conf_if_to_su_update_v6(connection,
+ ifp);
+ }
+ /* If we could derive the peer address, we may need to install the
+ * password
+ * configured for the peer, if any, on the listen socket. Otherwise,
+ * mark
+ * that peer's address is not available and uninstall the password, if
+ * needed.
+ */
+ if (peer_addr_updated) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD)
+ && prev_family == AF_UNSPEC)
+ bgp_md5_set(connection);
+ } else {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD)
+ && prev_family != AF_UNSPEC)
+ bgp_md5_unset(connection);
+ connection->su.sa.sa_family = AF_UNSPEC;
+ memset(&connection->su.sin6.sin6_addr, 0,
+ sizeof(struct in6_addr));
+ }
+
+ /*
+ * If they are the same, nothing to do here, move along
+ */
+ if (!sockunion_same(&old_su, &connection->su)) {
+ union sockunion new_su = connection->su;
+ struct bgp *bgp = peer->bgp;
+
+ /*
+ * Our peer structure is stored in the bgp->peerhash
+ * release it before we modify anything in both the
+ * hash and the list. But *only* if the peer
+ * is in the bgp->peerhash as that on deletion
+ * we call bgp_stop which calls this function :(
+ * so on deletion let's remove from the list first
+ * and then do the deletion preventing this from
+ * being added back on the list below when we
+ * fail to remove it up here.
+ */
+
+ /*
+ * listnode_lookup just scans the list
+ * for the peer structure so it's safe
+ * to use without modifying the su
+ */
+ node = listnode_lookup(bgp->peer, peer);
+ if (node) {
+ /*
+ * Let's reset the peer->su release and
+ * reset it and put it back. We have to
+ * do this because hash_release will
+ * scan through looking for a matching
+ * su if needed.
+ */
+ connection->su = old_su;
+ hash_release(peer->bgp->peerhash, peer);
+ listnode_delete(peer->bgp->peer, peer);
+
+ connection->su = new_su;
+ (void)hash_get(peer->bgp->peerhash, peer,
+ hash_alloc_intern);
+ listnode_add_sort(peer->bgp->peer, peer);
+ }
+ }
+}
+
+void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest, *ndest;
+ struct bgp_table *table;
+
+ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (table != NULL) {
+ /* Special handling for 2-level routing
+ * tables. */
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
+ || safi == SAFI_EVPN) {
+ for (ndest = bgp_table_top(table); ndest;
+ ndest = bgp_route_next(ndest))
+ bgp_process(bgp, ndest, afi, safi);
+ } else
+ bgp_process(bgp, dest, afi, safi);
+ }
+ }
+}
+
+/* Force a bestpath recalculation for all prefixes. This is used
+ * when 'bgp bestpath' commands are entered.
+ */
+void bgp_recalculate_all_bestpaths(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ bgp_recalculate_afi_safi_bestpaths(bgp, afi, safi);
+ }
+}
+
+/*
+ * Create new BGP peer.
+ *
+ * conf_if and su are mutually exclusive if configuring from the cli.
+ * If we are handing a doppelganger, then we *must* pass in both
+ * the original peer's su and conf_if, so that we can appropriately
+ * track the bgp->peerhash( ie we don't want to remove the current
+ * one from the config ).
+ */
+struct peer *peer_create(union sockunion *su, const char *conf_if,
+ struct bgp *bgp, as_t local_as, as_t remote_as,
+ int as_type, struct peer_group *group,
+ bool config_node, const char *as_str)
+{
+ int active;
+ struct peer *peer;
+ char buf[SU_ADDRSTRLEN];
+ afi_t afi;
+ safi_t safi;
+
+ peer = peer_new(bgp);
+ if (conf_if) {
+ peer->conf_if = XSTRDUP(MTYPE_PEER_CONF_IF, conf_if);
+ if (su)
+ peer->connection->su = *su;
+ else
+ bgp_peer_conf_if_to_su_update(peer->connection);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->host);
+ peer->host = XSTRDUP(MTYPE_BGP_PEER_HOST, conf_if);
+ } else if (su) {
+ peer->connection->su = *su;
+ sockunion2str(su, buf, SU_ADDRSTRLEN);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->host);
+ peer->host = XSTRDUP(MTYPE_BGP_PEER_HOST, buf);
+ }
+ peer->local_as = local_as;
+ peer->as = remote_as;
+ /* internal and external values do not use as_pretty */
+ if (as_str && asn_str2asn(as_str, NULL))
+ peer->as_pretty = XSTRDUP(MTYPE_BGP, as_str);
+ peer->as_type = as_type;
+ peer->local_id = bgp->router_id;
+ peer->v_holdtime = bgp->default_holdtime;
+ peer->v_keepalive = bgp->default_keepalive;
+ peer->v_routeadv = (peer_sort(peer) == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+ if (bgp_config_inprocess())
+ peer->shut_during_cfg = true;
+
+ peer = peer_lock(peer); /* bgp peer list reference */
+ peer->group = group;
+ listnode_add_sort(bgp->peer, peer);
+
+ if (config_node)
+ SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
+
+ (void)hash_get(bgp->peerhash, peer, hash_alloc_intern);
+
+ /* Adjust update-group coalesce timer heuristics for # peers. */
+ if (bgp->heuristic_coalesce) {
+ long ct = BGP_DEFAULT_SUBGROUP_COALESCE_TIME
+ + (bgp->peer->count
+ * BGP_PEER_ADJUST_SUBGROUP_COALESCE_TIME);
+ bgp->coalesce_time = MIN(BGP_MAX_SUBGROUP_COALESCE_TIME, ct);
+ }
+
+ active = peer_active(peer);
+ if (!active) {
+ if (peer->connection->su.sa.sa_family == AF_UNSPEC)
+ peer->last_reset = PEER_DOWN_NBR_ADDR;
+ else
+ peer->last_reset = PEER_DOWN_NOAFI_ACTIVATED;
+ }
+
+ /* Last read and reset time set */
+ peer->readtime = peer->resettime = monotime(NULL);
+
+ /* Default TTL set. */
+ peer->ttl = (peer->sort == BGP_PEER_IBGP) ? MAXTTL : BGP_DEFAULT_TTL;
+
+ /* Default configured keepalives count for shutdown rtt command */
+ peer->rtt_keepalive_conf = 1;
+
+ /* If 'bgp default <afi>-<safi>' is configured, then activate the
+ * neighbor for the corresponding address family. IPv4 Unicast is
+ * the only address family enabled by default without expliict
+ * configuration.
+ */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (bgp->default_af[afi][safi]) {
+ peer->afc[afi][safi] = 1;
+ peer_af_create(peer, afi, safi);
+ }
+ }
+
+ /* auto shutdown if configured */
+ if (bgp->autoshutdown)
+ peer_flag_set(peer, PEER_FLAG_SHUTDOWN);
+ /* Set up peer's events and timers. */
+ else if (!active && peer_active(peer))
+ bgp_timer_set(peer->connection);
+
+ bgp_peer_gr_flags_update(peer);
+ BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer);
+
+ return peer;
+}
+
+/* Make accept BGP peer. This function is only called from the test code */
+struct peer *peer_create_accept(struct bgp *bgp)
+{
+ struct peer *peer;
+
+ peer = peer_new(bgp);
+
+ peer = peer_lock(peer); /* bgp peer list reference */
+ listnode_add_sort(bgp->peer, peer);
+ (void)hash_get(bgp->peerhash, peer, hash_alloc_intern);
+
+ return peer;
+}
+
+/*
+ * Return true if we have a peer configured to use this afi/safi
+ */
+bool bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct listnode *node;
+ struct peer *peer;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ continue;
+
+ if (peer->afc[afi][safi])
+ return true;
+ }
+
+ return false;
+}
+
+/* Change peer's AS number. */
+void peer_as_change(struct peer *peer, as_t as, int as_specified,
+ const char *as_str)
+{
+ enum bgp_peer_sort origtype, newtype;
+
+ /* Stop peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ peer->last_reset = PEER_DOWN_REMOTE_AS_CHANGE;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+ }
+ origtype = peer_sort_lookup(peer);
+ peer->as = as;
+ if (as_specified == AS_SPECIFIED && as_str) {
+ if (peer->as_pretty)
+ XFREE(MTYPE_BGP, peer->as_pretty);
+ peer->as_pretty = XSTRDUP(MTYPE_BGP, as_str);
+ } else if (peer->as_type == AS_UNSPECIFIED && peer->as_pretty)
+ XFREE(MTYPE_BGP, peer->as_pretty);
+ peer->as_type = as_specified;
+
+ if (bgp_config_check(peer->bgp, BGP_CONFIG_CONFEDERATION)
+ && !bgp_confederation_peers_check(peer->bgp, as)
+ && peer->bgp->as != as)
+ peer->local_as = peer->bgp->confed_id;
+ else
+ peer->local_as = peer->bgp->as;
+
+ newtype = peer_sort(peer);
+ /* Advertisement-interval reset */
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_ROUTEADV)) {
+ peer->v_routeadv = (newtype == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+ }
+
+ /* TTL reset */
+ if (newtype == BGP_PEER_IBGP)
+ peer->ttl = MAXTTL;
+ else if (origtype == BGP_PEER_IBGP)
+ peer->ttl = BGP_DEFAULT_TTL;
+
+ /* reflector-client reset */
+ if (newtype != BGP_PEER_IBGP) {
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_UNICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_MULTICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_LABELED_UNICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_MPLS_VPN],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_ENCAP],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_FLOWSPEC],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_UNICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_MULTICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_LABELED_UNICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_MPLS_VPN],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_ENCAP],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_FLOWSPEC],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ }
+}
+
+/* If peer does not exist, create new one. If peer already exists,
+ set AS number to the peer. */
+int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if,
+ as_t *as, int as_type, const char *as_str)
+{
+ struct peer *peer;
+ as_t local_as;
+
+ if (conf_if)
+ peer = peer_lookup_by_conf_if(bgp, conf_if);
+ else
+ peer = peer_lookup(bgp, su);
+
+ 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) {
+ /* peer-group already has AS number/internal/external */
+ if (peer->group->conf->as
+ || peer->group->conf->as_type) {
+ /* Return peer group's AS number. */
+ *as = peer->group->conf->as;
+ return BGP_ERR_PEER_GROUP_MEMBER;
+ }
+
+ enum bgp_peer_sort peer_sort_type =
+ peer_sort(peer->group->conf);
+
+ /* Explicit AS numbers used, compare AS numbers */
+ if (as_type == AS_SPECIFIED) {
+ if (((peer_sort_type == BGP_PEER_IBGP)
+ && (bgp->as != *as))
+ || ((peer_sort_type == BGP_PEER_EBGP)
+ && (bgp->as == *as))) {
+ *as = peer->as;
+ return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
+ }
+ } else {
+ /* internal/external used, compare as-types */
+ if (((peer_sort_type == BGP_PEER_IBGP)
+ && (as_type != AS_INTERNAL))
+ || ((peer_sort_type == BGP_PEER_EBGP)
+ && (as_type != AS_EXTERNAL))) {
+ *as = peer->as;
+ return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
+ }
+ }
+ }
+
+ /* Existing peer's AS number change. */
+ if (((peer->as_type == AS_SPECIFIED) && peer->as != *as)
+ || (peer->as_type != as_type))
+ peer_as_change(peer, *as, as_type, as_str);
+ } else {
+ if (conf_if)
+ return BGP_ERR_NO_INTERFACE_CONFIG;
+
+ /* If the peer is not part of our confederation, and its not an
+ iBGP peer then spoof the source AS */
+ if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION) &&
+ !bgp_confederation_peers_check(bgp, *as) && *as &&
+ bgp->as != *as)
+ local_as = bgp->confed_id;
+ else
+ local_as = bgp->as;
+
+ peer_create(su, conf_if, bgp, local_as, *as, as_type, NULL,
+ true, as_str);
+ }
+
+ return 0;
+}
+
+const char *bgp_get_name_by_role(uint8_t role)
+{
+ switch (role) {
+ case ROLE_PROVIDER:
+ return "provider";
+ case ROLE_RS_SERVER:
+ return "rs-server";
+ case ROLE_RS_CLIENT:
+ return "rs-client";
+ case ROLE_CUSTOMER:
+ return "customer";
+ case ROLE_PEER:
+ return "peer";
+ case ROLE_UNDEFINED:
+ return "undefined";
+ }
+ return "unknown";
+}
+
+enum asnotation_mode bgp_get_asnotation(struct bgp *bgp)
+{
+ if (!bgp)
+ return ASNOTATION_PLAIN;
+ return bgp->asnotation;
+}
+
+static void peer_group2peer_config_copy_af(struct peer_group *group,
+ struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ int in = FILTER_IN;
+ int out = FILTER_OUT;
+ uint64_t flags_tmp;
+ uint64_t pflags_ovrd;
+ uint8_t *pfilter_ovrd;
+ struct peer *conf;
+
+ conf = group->conf;
+ pflags_ovrd = peer->af_flags_override[afi][safi];
+ pfilter_ovrd = &peer->filter_override[afi][safi][in];
+
+ /* peer af_flags apply */
+ flags_tmp = conf->af_flags[afi][safi] & ~pflags_ovrd;
+ flags_tmp ^= conf->af_flags_invert[afi][safi]
+ ^ peer->af_flags_invert[afi][safi];
+ flags_tmp &= ~pflags_ovrd;
+
+ UNSET_FLAG(peer->af_flags[afi][safi], ~pflags_ovrd);
+ SET_FLAG(peer->af_flags[afi][safi], flags_tmp);
+ SET_FLAG(peer->af_flags_invert[afi][safi],
+ conf->af_flags_invert[afi][safi]);
+
+ /* maximum-prefix */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_MAX_PREFIX)) {
+ PEER_ATTR_INHERIT(peer, group, pmax[afi][safi]);
+ PEER_ATTR_INHERIT(peer, group, pmax_threshold[afi][safi]);
+ PEER_ATTR_INHERIT(peer, group, pmax_restart[afi][safi]);
+ }
+
+ /* maximum-prefix-out */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_MAX_PREFIX_OUT))
+ PEER_ATTR_INHERIT(peer, group, pmax_out[afi][safi]);
+
+ /* allowas-in */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_ALLOWAS_IN))
+ PEER_ATTR_INHERIT(peer, group, allowas_in[afi][safi]);
+
+ /* soo */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_SOO))
+ PEER_ATTR_INHERIT(peer, group, soo[afi][safi]);
+
+ /* weight */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_WEIGHT))
+ PEER_ATTR_INHERIT(peer, group, weight[afi][safi]);
+
+ /* default-originate route-map */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_DEFAULT_ORIGINATE)) {
+ PEER_STR_ATTR_INHERIT(peer, group, default_rmap[afi][safi].name,
+ MTYPE_ROUTE_MAP_NAME);
+ PEER_ATTR_INHERIT(peer, group, default_rmap[afi][safi].map);
+ }
+
+ /* inbound filter apply */
+ if (!CHECK_FLAG(pfilter_ovrd[in], PEER_FT_DISTRIBUTE_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].dlist[in].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].dlist[in].alist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[in], PEER_FT_PREFIX_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].plist[in].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].plist[in].plist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[in], PEER_FT_FILTER_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].aslist[in].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].aslist[in].aslist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[RMAP_IN], PEER_FT_ROUTE_MAP)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].map[in].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].map[RMAP_IN].map);
+ }
+
+ /* outbound filter apply */
+ if (!CHECK_FLAG(pfilter_ovrd[out], PEER_FT_DISTRIBUTE_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].dlist[out].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].dlist[out].alist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[out], PEER_FT_PREFIX_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].plist[out].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].plist[out].plist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[out], PEER_FT_FILTER_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].aslist[out].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].aslist[out].aslist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[RMAP_OUT], PEER_FT_ROUTE_MAP)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].map[RMAP_OUT].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].map[RMAP_OUT].map);
+ }
+
+ /* nondirectional filter apply */
+ if (!CHECK_FLAG(pfilter_ovrd[0], PEER_FT_UNSUPPRESS_MAP)) {
+ PEER_STR_ATTR_INHERIT(peer, group, filter[afi][safi].usmap.name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group, filter[afi][safi].usmap.map);
+ }
+
+ /* Conditional Advertisements */
+ if (!CHECK_FLAG(pfilter_ovrd[RMAP_OUT], PEER_FT_ADVERTISE_MAP)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].advmap.aname,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group, filter[afi][safi].advmap.amap);
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].advmap.cname,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group, filter[afi][safi].advmap.cmap);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].advmap.condition);
+ }
+
+ if (peer->addpath_type[afi][safi] == BGP_ADDPATH_NONE) {
+ peer->addpath_type[afi][safi] = conf->addpath_type[afi][safi];
+ bgp_addpath_type_changed(conf->bgp);
+ }
+}
+
+static int peer_activate_af(struct peer *peer, afi_t afi, safi_t safi)
+{
+ int active;
+ struct peer *other;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ flog_err(EC_BGP_PEER_GROUP, "%s was called for peer-group %s",
+ __func__, peer->host);
+ return 1;
+ }
+
+ /* Do not activate a peer for both SAFI_UNICAST and SAFI_LABELED_UNICAST
+ */
+ if ((safi == SAFI_UNICAST && peer->afc[afi][SAFI_LABELED_UNICAST])
+ || (safi == SAFI_LABELED_UNICAST && peer->afc[afi][SAFI_UNICAST]))
+ return BGP_ERR_PEER_SAFI_CONFLICT;
+
+ /* Nothing to do if we've already activated this peer */
+ if (peer->afc[afi][safi])
+ return 0;
+
+ if (peer_af_create(peer, afi, safi) == NULL)
+ return 1;
+
+ active = peer_active(peer);
+ peer->afc[afi][safi] = 1;
+
+ if (peer->group)
+ peer_group2peer_config_copy_af(peer->group, peer, afi, safi);
+
+ if (!active && peer_active(peer)) {
+ bgp_timer_set(peer->connection);
+ } else {
+ if (peer_established(peer->connection)) {
+ if (CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV)) {
+ peer->afc_adv[afi][safi] = 1;
+ bgp_capability_send(peer, afi, safi,
+ CAPABILITY_CODE_MP,
+ CAPABILITY_ACTION_SET);
+ if (peer->afc_recv[afi][safi]) {
+ peer->afc_nego[afi][safi] = 1;
+ bgp_announce_route(peer, afi, safi,
+ false);
+ }
+ } else {
+ peer->last_reset = PEER_DOWN_AF_ACTIVATE;
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+ if (peer->connection->status == OpenSent ||
+ peer->connection->status == OpenConfirm) {
+ peer->last_reset = PEER_DOWN_AF_ACTIVATE;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ /*
+ * If we are turning on a AFI/SAFI locally and we've
+ * started bringing a peer up, we need to tell
+ * the other peer to restart because we might loose
+ * configuration here because when the doppelganger
+ * gets to a established state due to how
+ * we resolve we could just overwrite the afi/safi
+ * activation.
+ */
+ other = peer->doppelganger;
+ if (other && (other->connection->status == OpenSent ||
+ other->connection->status == OpenConfirm)) {
+ other->last_reset = PEER_DOWN_AF_ACTIVATE;
+ bgp_notify_send(other->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+
+ return 0;
+}
+
+/* Activate the peer or peer group for specified AFI and SAFI. */
+int peer_activate(struct peer *peer, afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ struct peer *tmp_peer;
+ struct bgp *bgp;
+ safi_t safi_check;
+
+ /* Nothing to do if we've already activated this peer */
+ if (peer->afc[afi][safi])
+ return ret;
+
+ bgp = peer->bgp;
+
+ /* This is a peer-group so activate all of the members of the
+ * peer-group as well */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+
+ /* Do not activate a peer for both SAFI_UNICAST and
+ * SAFI_LABELED_UNICAST */
+ if ((safi == SAFI_UNICAST
+ && peer->afc[afi][SAFI_LABELED_UNICAST])
+ || (safi == SAFI_LABELED_UNICAST
+ && peer->afc[afi][SAFI_UNICAST]))
+ return BGP_ERR_PEER_SAFI_CONFLICT;
+
+ peer->afc[afi][safi] = 1;
+ group = peer->group;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, tmp_peer)) {
+ ret |= peer_activate_af(tmp_peer, afi, safi);
+ }
+ } else {
+ ret |= peer_activate_af(peer, afi, safi);
+ }
+
+ /* If this is the first peer to be activated for this
+ * afi/labeled-unicast or afi/mpls-vpn, recalc bestpaths to trigger
+ * label allocation */
+ if (safi == SAFI_LABELED_UNICAST)
+ safi_check = SAFI_UNICAST;
+ else
+ safi_check = safi;
+ if (ret != BGP_ERR_PEER_SAFI_CONFLICT &&
+ (safi == SAFI_LABELED_UNICAST || safi == SAFI_MPLS_VPN) &&
+ !bgp->allocate_mpls_labels[afi][safi_check]) {
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "peer(s) are now active for %s, allocate MPLS labels",
+ safi2str(safi));
+ bgp->allocate_mpls_labels[afi][safi_check] = 1;
+ bgp_recalculate_afi_safi_bestpaths(bgp, afi, safi_check);
+ }
+
+ if (safi == SAFI_FLOWSPEC) {
+ /* connect to table manager */
+ bgp_zebra_init_tm_connect(bgp);
+ }
+ return ret;
+}
+
+static bool non_peergroup_deactivate_af(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ flog_err(EC_BGP_PEER_GROUP, "%s was called for peer-group %s",
+ __func__, peer->host);
+ return true;
+ }
+
+ /* Nothing to do if we've already deactivated this peer */
+ if (!peer->afc[afi][safi])
+ return false;
+
+ /* De-activate the address family configuration. */
+ peer->afc[afi][safi] = 0;
+
+ if (peer_af_delete(peer, afi, safi) != 0) {
+ flog_err(EC_BGP_PEER_DELETE,
+ "couldn't delete af structure for peer %s(%s, %s)",
+ peer->host, afi2str(afi), safi2str(safi));
+ return true;
+ }
+
+ if (peer_established(peer->connection)) {
+ if (CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV)) {
+ peer->afc_adv[afi][safi] = 0;
+ peer->afc_nego[afi][safi] = 0;
+
+ if (peer_active_nego(peer)) {
+ bgp_capability_send(peer, afi, safi,
+ CAPABILITY_CODE_MP,
+ CAPABILITY_ACTION_UNSET);
+ bgp_clear_route(peer, afi, safi);
+ peer->pcount[afi][safi] = 0;
+ } else {
+ peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ } else {
+ peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+
+ return false;
+}
+
+int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct peer_group *group;
+ struct peer *tmp_peer;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ safi_t safi_check;
+
+ /* Nothing to do if we've already de-activated this peer */
+ if (!peer->afc[afi][safi])
+ return ret;
+
+ /* This is a peer-group so de-activate all of the members of the
+ * peer-group as well */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ peer->afc[afi][safi] = 0;
+ group = peer->group;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, tmp_peer)) {
+ ret |= non_peergroup_deactivate_af(tmp_peer, afi, safi);
+ }
+ } else {
+ ret |= non_peergroup_deactivate_af(peer, afi, safi);
+ }
+
+ bgp = peer->bgp;
+
+ /* If this is the last peer to be deactivated for this
+ * afi/labeled-unicast or afi/mpls-vpn, recalc bestpaths to trigger
+ * label deallocation */
+ if (safi == SAFI_LABELED_UNICAST)
+ safi_check = SAFI_UNICAST;
+ else
+ safi_check = safi;
+ if ((safi == SAFI_LABELED_UNICAST || safi == SAFI_MPLS_VPN) &&
+ bgp->allocate_mpls_labels[afi][safi_check] &&
+ !bgp_afi_safi_peer_exists(bgp, afi, safi)) {
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "peer(s) are no longer active for %s, deallocate MPLS labels",
+ safi2str(safi));
+ bgp->allocate_mpls_labels[afi][safi_check] = 0;
+ bgp_recalculate_afi_safi_bestpaths(bgp, afi, safi_check);
+ }
+ return ret;
+}
+
+void peer_nsf_stop(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE);
+
+ FOREACH_AFI_SAFI_NSF (afi, safi) {
+ peer->nsf[afi][safi] = 0;
+ EVENT_OFF(peer->t_llgr_stale[afi][safi]);
+ }
+
+ if (peer->connection->t_gr_restart) {
+ EVENT_OFF(peer->connection->t_gr_restart);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%pBP graceful restart timer stopped", peer);
+ }
+ if (peer->connection->t_gr_stale) {
+ EVENT_OFF(peer->connection->t_gr_stale);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP graceful restart stalepath timer stopped",
+ peer);
+ }
+ bgp_clear_route_all(peer);
+}
+
+/* Delete peer from confguration.
+ *
+ * The peer is moved to a dead-end "Deleted" neighbour-state, to allow
+ * it to "cool off" and refcounts to hit 0, at which state it is freed.
+ *
+ * This function /should/ take care to be idempotent, to guard against
+ * it being called multiple times through stray events that come in
+ * that happen to result in this function being called again. That
+ * said, getting here for a "Deleted" peer is a bug in the neighbour
+ * FSM.
+ */
+int peer_delete(struct peer *peer)
+{
+ int i;
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+ struct bgp_filter *filter;
+ struct listnode *pn;
+ int accept_peer;
+
+ assert(peer->connection->status != Deleted);
+
+ bgp = peer->bgp;
+ accept_peer = CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
+
+ bgp_soft_reconfig_table_task_cancel(bgp, NULL, peer);
+
+ bgp_keepalives_off(peer->connection);
+ bgp_reads_off(peer->connection);
+ bgp_writes_off(peer->connection);
+ event_cancel_event_ready(bm->master, peer->connection);
+ FOREACH_AFI_SAFI (afi, safi)
+ EVENT_OFF(peer->t_revalidate_all[afi][safi]);
+ assert(!CHECK_FLAG(peer->connection->thread_flags,
+ PEER_THREAD_WRITES_ON));
+ assert(!CHECK_FLAG(peer->connection->thread_flags,
+ PEER_THREAD_READS_ON));
+ assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON));
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT))
+ peer_nsf_stop(peer);
+
+ SET_FLAG(peer->flags, PEER_FLAG_DELETE);
+
+ /* Remove BFD settings. */
+ if (peer->bfd_config)
+ bgp_peer_remove_bfd_config(peer);
+
+ /* If this peer belongs to peer group, clear up the
+ 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 */
+ list_delete_node(peer->group->peer, pn);
+ }
+ peer->group = NULL;
+ }
+
+ /* Withdraw all information from routing table. We can not use
+ * BGP_EVENT_ADD (peer, BGP_Stop) at here. Because the event is
+ * executed after peer structure is deleted.
+ */
+ peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
+ bgp_stop(peer->connection);
+ UNSET_FLAG(peer->flags, PEER_FLAG_DELETE);
+
+ if (peer->doppelganger) {
+ peer->doppelganger->doppelganger = NULL;
+ peer->doppelganger = NULL;
+ }
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
+ bgp_fsm_change_status(peer->connection, Deleted);
+
+ /* Remove from NHT */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ bgp_unlink_nexthop_by_peer(peer);
+
+ /* Password configuration */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD)) {
+ XFREE(MTYPE_PEER_PASSWORD, peer->password);
+ if (!accept_peer &&
+ !BGP_CONNECTION_SU_UNSPEC(peer->connection) &&
+ !CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) &&
+ !CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_NEIGHBOR))
+ bgp_md5_unset(peer->connection);
+ }
+
+ bgp_timer_set(peer->connection); /* stops all timers for Deleted */
+
+ /* Delete from all peer list. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
+ && (pn = listnode_lookup(bgp->peer, peer))) {
+ /*
+ * Removing from the list node first because
+ * peer_unlock *can* call peer_delete( I know,
+ * I know ). So let's remove it and in
+ * the su recalculate function we'll ensure
+ * it's in there or not.
+ */
+ list_delete_node(bgp->peer, pn);
+ hash_release(bgp->peerhash, peer);
+ peer_unlock(peer); /* bgp peer list reference */
+ }
+
+ /* Local and remote addresses. */
+ if (peer->su_local) {
+ sockunion_free(peer->su_local);
+ peer->su_local = NULL;
+ }
+
+ if (peer->su_remote) {
+ sockunion_free(peer->su_remote);
+ peer->su_remote = NULL;
+ }
+
+ /* Free filter related memory. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &peer->filter[afi][safi];
+
+ for (i = FILTER_IN; i < FILTER_MAX; i++) {
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->dlist[i].name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->plist[i].name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->aslist[i].name);
+ }
+
+ for (i = RMAP_IN; i < RMAP_MAX; i++) {
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->map[i].name);
+ }
+
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
+ XFREE(MTYPE_ROUTE_MAP_NAME, peer->default_rmap[afi][safi].name);
+ ecommunity_free(&peer->soo[afi][safi]);
+ }
+
+ FOREACH_AFI_SAFI (afi, safi)
+ peer_af_delete(peer, afi, safi);
+
+ XFREE(MTYPE_BGP_PEER_HOST, peer->hostname);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+ XFREE(MTYPE_BGP_SOFT_VERSION, peer->soft_version);
+
+ peer_unlock(peer); /* initial reference */
+
+ return 0;
+}
+
+static int peer_group_cmp(struct peer_group *g1, struct peer_group *g2)
+{
+ return strcmp(g1->name, g2->name);
+}
+
+/* Peer group cofiguration. */
+static struct peer_group *peer_group_new(void)
+{
+ return XCALLOC(MTYPE_PEER_GROUP, sizeof(struct peer_group));
+}
+
+static void peer_group_free(struct peer_group *group)
+{
+ XFREE(MTYPE_PEER_GROUP, group);
+}
+
+struct peer_group *peer_group_lookup(struct bgp *bgp, const char *name)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ if (strcmp(group->name, name) == 0)
+ return group;
+ }
+ return NULL;
+}
+
+struct peer_group *peer_group_get(struct bgp *bgp, const char *name)
+{
+ struct peer_group *group;
+ afi_t afi;
+ safi_t safi;
+
+ group = peer_group_lookup(bgp, name);
+ if (group)
+ return group;
+
+ group = peer_group_new();
+ group->bgp = bgp;
+ XFREE(MTYPE_PEER_GROUP_HOST, group->name);
+ group->name = XSTRDUP(MTYPE_PEER_GROUP_HOST, name);
+ group->peer = list_new();
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ group->listen_range[afi] = list_new();
+ group->conf = peer_new(bgp);
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (bgp->default_af[afi][safi])
+ group->conf->afc[afi][safi] = 1;
+ }
+ XFREE(MTYPE_BGP_PEER_HOST, group->conf->host);
+ group->conf->host = XSTRDUP(MTYPE_BGP_PEER_HOST, name);
+ group->conf->group = group;
+ group->conf->as = 0;
+ group->conf->ttl = BGP_DEFAULT_TTL;
+ group->conf->gtsm_hops = BGP_GTSM_HOPS_DISABLED;
+ group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+ SET_FLAG(group->conf->sflags, PEER_STATUS_GROUP);
+ listnode_add_sort(bgp->group, group);
+
+ return group;
+}
+
+static void peer_group2peer_config_copy(struct peer_group *group,
+ struct peer *peer)
+{
+ uint64_t flags_tmp;
+ struct peer *conf;
+ bool config_node = !!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
+
+ conf = group->conf;
+
+ /* remote-as */
+ if (conf->as)
+ peer->as = conf->as;
+
+ /* local-as */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_LOCAL_AS))
+ peer->change_local_as = conf->change_local_as;
+
+ /* If peer-group has configured TTL then override it */
+ if (conf->ttl != BGP_DEFAULT_TTL)
+ peer->ttl = conf->ttl;
+
+ /* GTSM hops */
+ peer->gtsm_hops = conf->gtsm_hops;
+
+ /* peer flags apply */
+ flags_tmp = conf->flags & ~peer->flags_override;
+ flags_tmp ^= conf->flags_invert ^ peer->flags_invert;
+ flags_tmp &= ~peer->flags_override;
+
+ UNSET_FLAG(peer->flags, ~peer->flags_override);
+ SET_FLAG(peer->flags, flags_tmp);
+ SET_FLAG(peer->flags_invert, conf->flags_invert);
+
+ if (config_node)
+ SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
+
+ /* peer timers apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_TIMER)) {
+ PEER_ATTR_INHERIT(peer, group, holdtime);
+ PEER_ATTR_INHERIT(peer, group, keepalive);
+ }
+
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_TIMER_CONNECT)) {
+ PEER_ATTR_INHERIT(peer, group, connect);
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_TIMER_CONNECT))
+ peer->v_connect = conf->connect;
+ else
+ peer->v_connect = peer->bgp->default_connect_retry;
+ }
+
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_TIMER_DELAYOPEN)) {
+ PEER_ATTR_INHERIT(peer, group, delayopen);
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_TIMER_DELAYOPEN))
+ peer->v_delayopen = conf->delayopen;
+ else
+ peer->v_delayopen = peer->bgp->default_delayopen;
+ }
+
+ /* advertisement-interval apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_ROUTEADV)) {
+ PEER_ATTR_INHERIT(peer, group, routeadv);
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_ROUTEADV))
+ peer->v_routeadv = conf->routeadv;
+ else
+ peer->v_routeadv = (peer_sort(peer) == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+ }
+
+ /* capability extended-nexthop apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_CAPABILITY_ENHE))
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_CAPABILITY_ENHE))
+ SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE);
+
+ /* capability software-version apply */
+ if (!CHECK_FLAG(peer->flags_override,
+ PEER_FLAG_CAPABILITY_SOFT_VERSION))
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_CAPABILITY_SOFT_VERSION))
+ SET_FLAG(peer->flags,
+ PEER_FLAG_CAPABILITY_SOFT_VERSION);
+
+ /* password apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
+ PEER_STR_ATTR_INHERIT(peer, group, password,
+ MTYPE_PEER_PASSWORD);
+
+ if (!BGP_CONNECTION_SU_UNSPEC(peer->connection))
+ bgp_md5_set(peer->connection);
+
+ /* update-source apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_UPDATE_SOURCE)) {
+ if (conf->update_source) {
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+ PEER_SU_ATTR_INHERIT(peer, group, update_source);
+ } else if (conf->update_if) {
+ sockunion_free(peer->update_source);
+ PEER_STR_ATTR_INHERIT(peer, group, update_if,
+ MTYPE_PEER_UPDATE_SOURCE);
+ }
+ }
+
+ /* role */
+ PEER_ATTR_INHERIT(peer, group, local_role);
+
+ /* Update GR flags for the peer. */
+ bgp_peer_gr_flags_update(peer);
+
+ /* Apply BFD settings from group to peer if it exists. */
+ if (conf->bfd_config) {
+ bgp_peer_configure_bfd(peer, false);
+ bgp_peer_config_apply(peer, group);
+ }
+}
+
+/* Peer group's remote AS configuration. */
+int peer_group_remote_as(struct bgp *bgp, const char *group_name, as_t *as,
+ int as_type, const char *as_str)
+{
+ struct peer_group *group;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ group = peer_group_lookup(bgp, group_name);
+ if (!group)
+ return -1;
+
+ if ((as_type == group->conf->as_type) && (group->conf->as == *as))
+ return 0;
+
+
+ /* When we setup peer-group AS number all peer group member's AS
+ number must be updated to same number. */
+ peer_as_change(group->conf, *as, as_type, as_str);
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ if (((peer->as_type == AS_SPECIFIED) && peer->as != *as)
+ || (peer->as_type != as_type))
+ peer_as_change(peer, *as, as_type, as_str);
+ }
+
+ return 0;
+}
+
+void peer_notify_unconfig(struct peer *peer)
+{
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status))
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_PEER_UNCONFIG);
+}
+
+static void peer_notify_shutdown(struct peer *peer)
+{
+ if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP configured Graceful-Restart, skipping shutdown notification",
+ peer);
+ return;
+ }
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status))
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+}
+
+void peer_group_notify_unconfig(struct peer_group *group)
+{
+ struct peer *peer, *other;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ other = peer->doppelganger;
+ if (other && other->connection->status != Deleted) {
+ other->group = NULL;
+ peer_notify_unconfig(other);
+ } else
+ peer_notify_unconfig(peer);
+ }
+}
+
+int 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;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ other = peer->doppelganger;
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE))
+ bgp_zebra_terminate_radv(bgp, peer);
+
+ peer_delete(peer);
+ if (other && other->connection->status != Deleted) {
+ other->group = NULL;
+ peer_delete(other);
+ }
+ }
+ 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]);
+ }
+
+ XFREE(MTYPE_PEER_GROUP_HOST, group->name);
+ group->name = NULL;
+
+ if (group->conf->bfd_config)
+ bgp_peer_remove_bfd_config(group->conf);
+
+ group->conf->group = NULL;
+ peer_delete(group->conf);
+
+ /* Delete from all peer_group list. */
+ listnode_delete(bgp->group, group);
+
+ peer_group_free(group);
+
+ return 0;
+}
+
+int peer_group_remote_as_delete(struct peer_group *group)
+{
+ struct peer *peer, *other;
+ struct listnode *node, *nnode;
+
+ if ((group->conf->as_type == AS_UNSPECIFIED)
+ || ((!group->conf->as) && (group->conf->as_type == AS_SPECIFIED)))
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ other = peer->doppelganger;
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE))
+ bgp_zebra_terminate_radv(peer->bgp, peer);
+
+ peer_delete(peer);
+
+ if (other && other->connection->status != Deleted) {
+ other->group = NULL;
+ peer_delete(other);
+ }
+ }
+ list_delete_all_node(group->peer);
+
+ group->conf->as = 0;
+ group->conf->as_type = AS_UNSPECIFIED;
+
+ 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_type == AS_UNSPECIFIED)
+ 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 0;
+ }
+
+ prefix = prefix_new();
+ prefix_copy(prefix, range);
+ listnode_add(group->listen_range[afi], prefix);
+
+ /* Update passwords for new ranges */
+ if (group->conf->password)
+ bgp_md5_set_prefix(group->bgp, prefix, group->conf->password);
+
+ 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;
+
+ 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;
+
+ /* 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;
+
+ if (sockunion2hostprefix(&peer->connection->su, &prefix2) &&
+ prefix_match(prefix, &prefix2)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "Deleting dynamic neighbor %s group %s upon delete of listen range %pFX",
+ peer->host, group->name, prefix);
+ peer_delete(peer);
+ }
+ }
+
+ /* Get rid of the listen range */
+ listnode_delete(group->listen_range[afi], prefix);
+
+ /* Remove passwords for deleted ranges */
+ if (group->conf->password)
+ bgp_md5_unset_prefix(group->bgp, prefix);
+
+ return 0;
+}
+
+/* Bind specified peer to peer group. */
+int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer,
+ struct peer_group *group, as_t *as)
+{
+ int first_member = 0;
+ afi_t afi;
+ safi_t safi;
+ enum bgp_peer_sort ptype, gtype;
+
+ /* Lookup the peer. */
+ if (!peer)
+ peer = peer_lookup(bgp, su);
+
+ /* The peer exist, bind it to the peer-group */
+ if (peer) {
+ /* When the peer already belongs to a peer-group, check the
+ * consistency. */
+ if (peer_group_active(peer)) {
+
+ /* The peer is already bound to the peer-group,
+ * nothing to do
+ */
+ if (strcmp(peer->group->name, group->name) == 0)
+ return 0;
+ else
+ return BGP_ERR_PEER_GROUP_CANT_CHANGE;
+ }
+
+ /* The peer has not specified a remote-as, inherit it from the
+ * peer-group */
+ if (peer->as_type == AS_UNSPECIFIED) {
+ peer->as_type = group->conf->as_type;
+ peer->as = group->conf->as;
+ peer->sort = group->conf->sort;
+ }
+
+ ptype = peer_sort(peer);
+ if (!group->conf->as && ptype != BGP_PEER_UNSPECIFIED) {
+ gtype = peer_sort(group->conf);
+ if ((gtype != BGP_PEER_INTERNAL) && (gtype != ptype)) {
+ if (as)
+ *as = peer->as;
+ return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
+ }
+
+ if (gtype == BGP_PEER_INTERNAL)
+ first_member = 1;
+ }
+
+ peer_group2peer_config_copy(group, peer);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (group->conf->afc[afi][safi]) {
+ peer->afc[afi][safi] = 1;
+
+ if (peer_af_find(peer, afi, safi)
+ || peer_af_create(peer, afi, safi)) {
+ peer_group2peer_config_copy_af(
+ group, peer, afi, safi);
+ }
+ } else if (peer->afc[afi][safi])
+ peer_deactivate(peer, afi, safi);
+ }
+
+ if (peer->group) {
+ assert(group && peer->group == group);
+ } else {
+ listnode_delete(bgp->peer, peer);
+
+ peer->group = group;
+ listnode_add_sort(bgp->peer, peer);
+
+ peer = peer_lock(peer); /* group->peer list reference */
+ listnode_add(group->peer, peer);
+ }
+
+ if (first_member) {
+ gtype = peer_sort(group->conf);
+ /* Advertisement-interval reset */
+ if (!CHECK_FLAG(group->conf->flags,
+ PEER_FLAG_ROUTEADV)) {
+ group->conf->v_routeadv =
+ (gtype == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+ }
+
+ /* ebgp-multihop reset */
+ if (gtype == BGP_PEER_IBGP)
+ group->conf->ttl = MAXTTL;
+ }
+
+ SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ peer->last_reset = PEER_DOWN_RMAP_BIND;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else {
+ bgp_session_reset(peer);
+ }
+ }
+
+ /* Create a new peer. */
+ else {
+ if ((group->conf->as_type == AS_SPECIFIED)
+ && (!group->conf->as)) {
+ return BGP_ERR_PEER_GROUP_NO_REMOTE_AS;
+ }
+
+ peer = peer_create(su, NULL, bgp, bgp->as, group->conf->as,
+ group->conf->as_type, group, true, NULL);
+
+ peer = peer_lock(peer); /* group->peer list reference */
+ listnode_add(group->peer, peer);
+
+ peer_group2peer_config_copy(group, peer);
+
+ /* If the peer-group is active for this afi/safi then activate
+ * for this peer */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (group->conf->afc[afi][safi]) {
+ peer->afc[afi][safi] = 1;
+
+ if (!peer_af_find(peer, afi, safi))
+ peer_af_create(peer, afi, safi);
+
+ peer_group2peer_config_copy_af(group, peer, afi,
+ safi);
+ } else if (peer->afc[afi][safi])
+ peer_deactivate(peer, afi, safi);
+ }
+
+ /* Set up peer's events and timers. */
+ if (peer_active(peer))
+ bgp_timer_set(peer->connection);
+ }
+
+ return 0;
+}
+
+static void bgp_startup_timer_expire(struct event *thread)
+{
+ struct bgp *bgp;
+
+ bgp = EVENT_ARG(thread);
+ bgp->t_startup = NULL;
+}
+
+/*
+ * On shutdown we call the cleanup function which
+ * does a free of the link list nodes, free up
+ * the data we are pointing at too.
+ */
+static void bgp_vrf_string_name_delete(void *data)
+{
+ char *vname = data;
+
+ XFREE(MTYPE_TMP, vname);
+}
+
+/* BGP instance creation by `router bgp' commands. */
+static struct bgp *bgp_create(as_t *as, const char *name,
+ enum bgp_instance_type inst_type,
+ const char *as_pretty,
+ enum asnotation_mode asnotation)
+{
+ struct bgp *bgp;
+ afi_t afi;
+ safi_t safi;
+
+ bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp));
+ bgp->as = *as;
+ if (as_pretty)
+ bgp->as_pretty = XSTRDUP(MTYPE_BGP, as_pretty);
+ else
+ bgp->as_pretty = XSTRDUP(MTYPE_BGP, asn_asn2asplain(*as));
+
+ if (asnotation != ASNOTATION_UNDEFINED) {
+ bgp->asnotation = asnotation;
+ SET_FLAG(bgp->config, BGP_CONFIG_ASNOTATION);
+ } else
+ asn_str2asn_notation(bgp->as_pretty, NULL, &bgp->asnotation);
+
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ zlog_debug("Creating Default VRF, AS %s",
+ bgp->as_pretty);
+ else
+ zlog_debug("Creating %s %s, AS %s",
+ (inst_type == BGP_INSTANCE_TYPE_VRF)
+ ? "VRF"
+ : "VIEW",
+ name, bgp->as_pretty);
+ }
+
+ /* Default the EVPN VRF to the default one */
+ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT && !bgp_master.bgp_evpn) {
+ bgp_lock(bgp);
+ bm->bgp_evpn = bgp;
+ }
+
+ bgp_lock(bgp);
+
+ bgp->allow_martian = false;
+ bgp_process_queue_init(bgp);
+ bgp->heuristic_coalesce = true;
+ bgp->inst_type = inst_type;
+ bgp->vrf_id = (inst_type == BGP_INSTANCE_TYPE_DEFAULT) ? VRF_DEFAULT
+ : VRF_UNKNOWN;
+ bgp->peer_self = peer_new(bgp);
+ XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->host);
+ bgp->peer_self->host =
+ XSTRDUP(MTYPE_BGP_PEER_HOST, "Static announcement");
+ XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->hostname);
+ if (cmd_hostname_get())
+ bgp->peer_self->hostname =
+ XSTRDUP(MTYPE_BGP_PEER_HOST, cmd_hostname_get());
+
+ XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->domainname);
+ if (cmd_domainname_get())
+ bgp->peer_self->domainname =
+ XSTRDUP(MTYPE_BGP_PEER_HOST, cmd_domainname_get());
+ bgp->peer = list_new();
+ bgp->peer->cmp = (int (*)(void *, void *))peer_cmp;
+ bgp->peerhash = hash_create(peer_hash_key_make, peer_hash_same,
+ "BGP Peer Hash");
+ bgp->peerhash->max_size = BGP_PEER_MAX_HASH_SIZE;
+
+ bgp->group = list_new();
+ bgp->group->cmp = (int (*)(void *, void *))peer_group_cmp;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ bgp->route[afi][safi] = bgp_table_init(bgp, afi, safi);
+ bgp->aggregate[afi][safi] = bgp_table_init(bgp, afi, safi);
+ bgp->rib[afi][safi] = bgp_table_init(bgp, afi, safi);
+
+ /* Enable maximum-paths */
+ bgp_maximum_paths_set(bgp, afi, safi, BGP_PEER_EBGP,
+ multipath_num, 0);
+ bgp_maximum_paths_set(bgp, afi, safi, BGP_PEER_IBGP,
+ multipath_num, 0);
+ /* Initialize graceful restart info */
+ bgp->gr_info[afi][safi].eor_required = 0;
+ bgp->gr_info[afi][safi].eor_received = 0;
+ bgp->gr_info[afi][safi].t_select_deferral = NULL;
+ bgp->gr_info[afi][safi].t_route_select = NULL;
+ bgp->gr_info[afi][safi].gr_deferred = 0;
+ }
+
+ bgp->v_update_delay = bm->v_update_delay;
+ bgp->v_establish_wait = bm->v_establish_wait;
+ bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
+ bgp->default_subgroup_pkt_queue_max =
+ BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX;
+ bgp_tcp_keepalive_unset(bgp);
+ bgp_timers_unset(bgp);
+ bgp->default_min_holdtime = 0;
+ bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
+ bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
+ bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME;
+ bgp->rib_stale_time = BGP_DEFAULT_RIB_STALE_TIME;
+ bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT;
+ bgp->dynamic_neighbors_count = 0;
+ bgp->lb_ref_bw = BGP_LINK_BW_REF_BW;
+ bgp->lb_handling = BGP_LINK_BW_ECMP;
+ bgp->reject_as_sets = false;
+ bgp->condition_check_period = DEFAULT_CONDITIONAL_ROUTES_POLL_TIME;
+ bgp_addpath_init_bgp_data(&bgp->tx_addpath);
+ bgp->fast_convergence = false;
+ bgp->llgr_stale_time = BGP_DEFAULT_LLGR_STALE_TIME;
+ bgp->rmap_def_originate_eval_timer = RMAP_DEFAULT_ORIGINATE_EVAL_TIMER;
+
+#ifdef ENABLE_BGP_VNC
+ if (inst_type != BGP_INSTANCE_TYPE_VRF) {
+ bgp->rfapi = bgp_rfapi_new(bgp);
+ assert(bgp->rfapi);
+ assert(bgp->rfapi_cfg);
+ }
+#endif /* ENABLE_BGP_VNC */
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ bgp->vpn_policy[afi].bgp = bgp;
+ bgp->vpn_policy[afi].afi = afi;
+ bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
+ bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent =
+ MPLS_LABEL_NONE;
+
+ bgp->vpn_policy[afi].import_vrf = list_new();
+ bgp->vpn_policy[afi].import_vrf->del =
+ bgp_vrf_string_name_delete;
+ bgp->vpn_policy[afi].export_vrf = list_new();
+ bgp->vpn_policy[afi].export_vrf->del =
+ bgp_vrf_string_name_delete;
+ SET_FLAG(bgp->af_flags[afi][SAFI_MPLS_VPN],
+ BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL);
+ }
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ bgp_label_per_nexthop_cache_init(
+ &bgp->mpls_labels_per_nexthop[afi]);
+
+ bgp_mplsvpn_nh_label_bind_cache_init(&bgp->mplsvpn_nh_label_bind);
+
+ if (name)
+ bgp->name = XSTRDUP(MTYPE_BGP, name);
+
+ event_add_timer(bm->master, bgp_startup_timer_expire, bgp,
+ bgp->restart_time, &bgp->t_startup);
+
+ /* printable name we can use in debug messages */
+ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ bgp->name_pretty = XSTRDUP(MTYPE_BGP, "VRF default");
+ } else {
+ const char *n;
+ int len;
+
+ if (bgp->name)
+ n = bgp->name;
+ else
+ n = "?";
+
+ len = 4 + 1 + strlen(n) + 1; /* "view foo\0" */
+
+ bgp->name_pretty = XCALLOC(MTYPE_BGP, len);
+ snprintf(bgp->name_pretty, len, "%s %s",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ ? "VRF"
+ : "VIEW",
+ n);
+ }
+
+ atomic_store_explicit(&bgp->wpkt_quanta, BGP_WRITE_PACKET_MAX,
+ memory_order_relaxed);
+ atomic_store_explicit(&bgp->rpkt_quanta, BGP_READ_PACKET_MAX,
+ memory_order_relaxed);
+ bgp->coalesce_time = BGP_DEFAULT_SUBGROUP_COALESCE_TIME;
+ bgp->default_af[AFI_IP][SAFI_UNICAST] = true;
+
+ QOBJ_REG(bgp, bgp);
+
+ update_bgp_group_init(bgp);
+
+ /* assign a unique rd id for auto derivation of vrf's RD */
+ bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id);
+
+ bgp_evpn_init(bgp);
+ bgp_evpn_vrf_es_init(bgp);
+ bgp_pbr_init(bgp);
+ bgp_srv6_init(bgp);
+
+ /*initilize global GR FSM */
+ bgp_global_gr_init(bgp);
+
+ memset(&bgp->ebgprequirespolicywarning, 0,
+ sizeof(bgp->ebgprequirespolicywarning));
+
+ return bgp;
+}
+
+/* Return the "default VRF" instance of BGP. */
+struct bgp *bgp_get_default(void)
+{
+ struct bgp *bgp;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ return bgp;
+ return NULL;
+}
+
+/* Lookup BGP entry. */
+struct bgp *bgp_lookup(as_t as, const char *name)
+{
+ struct bgp *bgp;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ if (bgp->as == as
+ && ((bgp->name == NULL && name == NULL)
+ || (bgp->name && name && strcmp(bgp->name, name) == 0)))
+ return bgp;
+ return NULL;
+}
+
+/* Lookup BGP structure by view name. */
+struct bgp *bgp_lookup_by_name(const char *name)
+{
+ struct bgp *bgp;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ if ((bgp->name == NULL && name == NULL)
+ || (bgp->name && name && strcmp(bgp->name, name) == 0))
+ return bgp;
+ return NULL;
+}
+
+/* Lookup BGP instance based on VRF id. */
+/* Note: Only to be used for incoming messages from Zebra. */
+struct bgp *bgp_lookup_by_vrf_id(vrf_id_t vrf_id)
+{
+ struct vrf *vrf;
+
+ /* Lookup VRF (in tree) and follow link. */
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (!vrf)
+ return NULL;
+ return (vrf->info) ? (struct bgp *)vrf->info : NULL;
+}
+
+/* Sets the BGP instance where EVPN is enabled */
+void bgp_set_evpn(struct bgp *bgp)
+{
+ if (bm->bgp_evpn == bgp)
+ return;
+
+ /* First, release the reference count we hold on the instance */
+ if (bm->bgp_evpn)
+ bgp_unlock(bm->bgp_evpn);
+
+ bm->bgp_evpn = bgp;
+
+ /* Increase the reference count on this new VRF */
+ if (bm->bgp_evpn)
+ bgp_lock(bm->bgp_evpn);
+}
+
+/* Returns the BGP instance where EVPN is enabled, if any */
+struct bgp *bgp_get_evpn(void)
+{
+ return bm->bgp_evpn;
+}
+
+/* handle socket creation or deletion, if necessary
+ * this is called for all new BGP instances
+ */
+int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id,
+ bool create)
+{
+ struct listnode *node;
+ char *address;
+
+ /* Create BGP server socket, if listen mode not disabled */
+ if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN))
+ return 0;
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
+ /*
+ * suppress vrf socket
+ */
+ if (!create) {
+ bgp_close_vrf_socket(bgp);
+ return 0;
+ }
+ if (vrf == NULL)
+ return BGP_ERR_INVALID_VALUE;
+ /* do nothing
+ * if vrf_id did not change
+ */
+ if (vrf->vrf_id == old_vrf_id)
+ return 0;
+ if (old_vrf_id != VRF_UNKNOWN) {
+ /* look for old socket. close it. */
+ bgp_close_vrf_socket(bgp);
+ }
+ /* if backend is not yet identified ( VRF_UNKNOWN) then
+ * creation will be done later
+ */
+ if (vrf->vrf_id == VRF_UNKNOWN)
+ return 0;
+ if (list_isempty(bm->addresses)) {
+ if (bgp_socket(bgp, bm->port, NULL) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
+ if (bgp_socket(bgp, bm->port, address) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ }
+ return 0;
+ } else
+ return bgp_check_main_socket(create, bgp);
+}
+
+int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, const char *name,
+ enum bgp_instance_type inst_type)
+{
+ struct bgp *bgp;
+
+ /* Multiple instance check. */
+ if (name)
+ bgp = bgp_lookup_by_name(name);
+ else
+ bgp = bgp_get_default();
+
+ if (bgp) {
+ *bgp_val = bgp;
+ if (bgp->as != *as) {
+ *as = bgp->as;
+ return BGP_ERR_AS_MISMATCH;
+ }
+ if (bgp->inst_type != inst_type)
+ return BGP_ERR_INSTANCE_MISMATCH;
+ return BGP_SUCCESS;
+ }
+ *bgp_val = NULL;
+
+ return BGP_SUCCESS;
+}
+
+/* Called from VTY commands. */
+int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
+ enum bgp_instance_type inst_type, const char *as_pretty,
+ enum asnotation_mode asnotation)
+{
+ struct bgp *bgp;
+ struct vrf *vrf = NULL;
+ int ret = 0;
+
+ ret = bgp_lookup_by_as_name_type(bgp_val, as, name, inst_type);
+ if (ret || *bgp_val)
+ return ret;
+
+ bgp = bgp_create(as, name, inst_type, as_pretty, asnotation);
+
+ /*
+ * view instances will never work inside of a vrf
+ * as such they must always be in the VRF_DEFAULT
+ * Also we must set this to something useful because
+ * of the vrf socket code needing an actual useful
+ * default value to send to the underlying OS.
+ *
+ * This code is currently ignoring vrf based
+ * code using the -Z option( and that is probably
+ * best addressed elsewhere in the code )
+ */
+ if (inst_type == BGP_INSTANCE_TYPE_VIEW)
+ bgp->vrf_id = VRF_DEFAULT;
+
+ bgp_router_id_set(bgp, &bgp->router_id_zebra, true);
+ bgp_address_init(bgp);
+ bgp_tip_hash_init(bgp);
+ bgp_scan_init(bgp);
+ *bgp_val = bgp;
+
+ bgp->t_rmap_def_originate_eval = NULL;
+
+ /* If Default instance or VRF, link to the VRF structure, if present. */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
+ || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
+ vrf = bgp_vrf_lookup_by_instance_type(bgp);
+ if (vrf)
+ bgp_vrf_link(bgp, vrf);
+ }
+ /* BGP server socket already processed if BGP instance
+ * already part of the list
+ */
+ bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, true);
+ listnode_add(bm->bgp, bgp);
+
+ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Registering BGP instance %s to zebra",
+ __func__, bgp->name_pretty);
+ bgp_zebra_instance_register(bgp);
+ }
+
+ return BGP_CREATED;
+}
+
+static void bgp_zclient_set_redist(afi_t afi, int type, unsigned short instance,
+ vrf_id_t vrf_id, bool set)
+{
+ if (instance) {
+ if (set)
+ redist_add_instance(&zclient->mi_redist[afi][type],
+ instance);
+ else
+ redist_del_instance(&zclient->mi_redist[afi][type],
+ instance);
+ } else {
+ if (set)
+ vrf_bitmap_set(&zclient->redist[afi][type], vrf_id);
+ else
+ vrf_bitmap_unset(&zclient->redist[afi][type], vrf_id);
+ }
+}
+
+static void bgp_set_redist_vrf_bitmaps(struct bgp *bgp, bool set)
+{
+ afi_t afi;
+ int i;
+ struct list *red_list;
+ struct listnode *node;
+ struct bgp_redist *red;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+
+ red_list = bgp->redist[afi][i];
+ if (!red_list)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(red_list, node, red))
+ bgp_zclient_set_redist(afi, i, red->instance,
+ bgp->vrf_id, set);
+ }
+ }
+}
+
+/*
+ * Make BGP instance "up". Applies only to VRFs (non-default) and
+ * implies the VRF has been learnt from Zebra.
+ */
+void bgp_instance_up(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct listnode *node, *next;
+
+ bgp_set_redist_vrf_bitmaps(bgp, true);
+
+ /* Register with zebra. */
+ bgp_zebra_instance_register(bgp);
+
+ /* Kick off any peers that may have been configured. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer)) {
+ if (!BGP_PEER_START_SUPPRESSED(peer))
+ BGP_EVENT_ADD(peer->connection, BGP_Start);
+ }
+
+ /* Process any networks that have been configured. */
+ bgp_static_add(bgp);
+}
+
+/*
+ * Make BGP instance "down". Applies only to VRFs (non-default) and
+ * implies the VRF has been deleted by Zebra.
+ */
+void bgp_instance_down(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct listnode *node;
+ struct listnode *next;
+
+ /* Stop timers. */
+ if (bgp->t_rmap_def_originate_eval)
+ EVENT_OFF(bgp->t_rmap_def_originate_eval);
+
+ /* Bring down peers, so corresponding routes are purged. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer)) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status))
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+ else
+ bgp_session_reset(peer);
+ }
+
+ /* Purge network and redistributed routes. */
+ bgp_purge_static_redist_routes(bgp);
+
+ /* Cleanup registered nexthops (flags) */
+ bgp_cleanup_nexthops(bgp);
+
+ bgp_zebra_instance_deregister(bgp);
+
+ bgp_set_redist_vrf_bitmaps(bgp, false);
+}
+
+/* Delete BGP instance. */
+int bgp_delete(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct peer_group *group;
+ struct listnode *node, *next;
+ struct vrf *vrf;
+ afi_t afi;
+ safi_t safi;
+ int i;
+ struct graceful_restart_info *gr_info;
+
+ assert(bgp);
+
+ bgp_soft_reconfig_table_task_cancel(bgp, NULL, NULL);
+
+ /* make sure we withdraw any exported routes */
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_get_default(),
+ bgp);
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp_get_default(),
+ bgp);
+
+ bgp_vpn_leak_unimport(bgp);
+
+ hook_call(bgp_inst_delete, bgp);
+
+ FOREACH_AFI_SAFI (afi, safi)
+ EVENT_OFF(bgp->t_revalidate[afi][safi]);
+
+ EVENT_OFF(bgp->t_condition_check);
+ EVENT_OFF(bgp->t_startup);
+ EVENT_OFF(bgp->t_maxmed_onstartup);
+ EVENT_OFF(bgp->t_update_delay);
+ EVENT_OFF(bgp->t_establish_wait);
+
+ /* Set flag indicating bgp instance delete in progress */
+ SET_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS);
+
+ /* Delete the graceful restart info */
+ FOREACH_AFI_SAFI (afi, safi) {
+ struct event *t;
+
+ gr_info = &bgp->gr_info[afi][safi];
+ if (!gr_info)
+ continue;
+ t = gr_info->t_select_deferral;
+ if (t) {
+ void *info = EVENT_ARG(t);
+
+ XFREE(MTYPE_TMP, info);
+ }
+ EVENT_OFF(gr_info->t_select_deferral);
+
+ t = gr_info->t_route_select;
+ if (t) {
+ void *info = EVENT_ARG(t);
+
+ XFREE(MTYPE_TMP, info);
+ }
+ EVENT_OFF(gr_info->t_route_select);
+ }
+
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ zlog_debug("Deleting Default VRF");
+ else
+ zlog_debug("Deleting %s %s",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ ? "VRF"
+ : "VIEW",
+ bgp->name);
+ }
+
+ /* unmap from RT list */
+ bgp_evpn_vrf_delete(bgp);
+
+ /* unmap bgp vrf label */
+ vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP);
+ vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP6);
+
+ /* Stop timers. */
+ if (bgp->t_rmap_def_originate_eval)
+ EVENT_OFF(bgp->t_rmap_def_originate_eval);
+
+ /* Inform peers we're going down. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer))
+ peer_notify_shutdown(peer);
+
+ /* Delete static routes (networks). */
+ bgp_static_delete(bgp);
+
+ /* Unset redistribution. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+ if (i != ZEBRA_ROUTE_BGP)
+ bgp_redistribute_unset(bgp, afi, i, 0);
+
+ /* Free peers and peer-groups. */
+ for (ALL_LIST_ELEMENTS(bgp->group, node, next, group))
+ peer_group_delete(group);
+
+ while (listcount(bgp->peer)) {
+ peer = listnode_head(bgp->peer);
+ peer_delete(peer);
+ }
+
+ if (bgp->peer_self) {
+ peer_delete(bgp->peer_self);
+ bgp->peer_self = NULL;
+ }
+
+ update_bgp_group_free(bgp);
+
+/* TODO - Other memory may need to be freed - e.g., NHT */
+
+#ifdef ENABLE_BGP_VNC
+ rfapi_delete(bgp);
+#endif
+
+ /* Free memory allocated with aggregate address configuration. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ struct bgp_aggregate *aggregate = NULL;
+
+ for (struct bgp_dest *dest =
+ bgp_table_top(bgp->aggregate[afi][safi]);
+ dest; dest = bgp_route_next(dest)) {
+ aggregate = bgp_dest_get_bgp_aggregate_info(dest);
+ if (aggregate == NULL)
+ continue;
+
+ bgp_dest_set_bgp_aggregate_info(dest, NULL);
+ bgp_free_aggregate_info(aggregate);
+ }
+ }
+
+ bgp_cleanup_routes(bgp);
+
+ for (afi = 0; afi < AFI_MAX; ++afi) {
+ if (!bgp->vpn_policy[afi].import_redirect_rtlist)
+ continue;
+ ecommunity_free(
+ &bgp->vpn_policy[afi]
+ .import_redirect_rtlist);
+ bgp->vpn_policy[afi].import_redirect_rtlist = NULL;
+ }
+
+ /* Free any memory allocated to holding routemap references */
+ for (afi = 0; afi < AFI_MAX; ++afi) {
+ for (enum vpn_policy_direction dir = 0;
+ dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
+ if (bgp->vpn_policy[afi].rmap_name[dir])
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp->vpn_policy[afi].rmap_name[dir]);
+ bgp->vpn_policy[afi].rmap[dir] = NULL;
+ }
+ }
+
+ /* Deregister from Zebra, if needed */
+ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: deregistering this bgp %s instance from zebra",
+ __func__, bgp->name);
+ bgp_zebra_instance_deregister(bgp);
+ }
+
+ /* Remove visibility via the master list - there may however still be
+ * routes to be processed still referencing the struct bgp.
+ */
+ listnode_delete(bm->bgp, bgp);
+
+ /* Free interfaces in this instance. */
+ bgp_if_finish(bgp);
+
+ vrf = bgp_vrf_lookup_by_instance_type(bgp);
+ bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
+ if (vrf)
+ bgp_vrf_unlink(bgp, vrf);
+
+ /* Update EVPN VRF pointer */
+ if (bm->bgp_evpn == bgp) {
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ bgp_set_evpn(NULL);
+ else
+ bgp_set_evpn(bgp_get_default());
+ }
+
+ if (bgp->process_queue)
+ work_queue_free_and_null(&bgp->process_queue);
+
+ event_master_free_unused(bm->master);
+ bgp_unlock(bgp); /* initial reference */
+
+ return 0;
+}
+
+void bgp_free(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_rmap *rmap;
+
+ QOBJ_UNREG(bgp);
+
+ list_delete(&bgp->group);
+ list_delete(&bgp->peer);
+
+ if (bgp->peerhash) {
+ hash_free(bgp->peerhash);
+ bgp->peerhash = NULL;
+ }
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ /* Special handling for 2-level routing tables. */
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
+ || safi == SAFI_EVPN) {
+ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ bgp_table_finish(&table);
+ }
+ }
+ if (bgp->route[afi][safi])
+ bgp_table_finish(&bgp->route[afi][safi]);
+ if (bgp->aggregate[afi][safi])
+ bgp_table_finish(&bgp->aggregate[afi][safi]);
+ if (bgp->rib[afi][safi])
+ bgp_table_finish(&bgp->rib[afi][safi]);
+ rmap = &bgp->table_map[afi][safi];
+ XFREE(MTYPE_ROUTE_MAP_NAME, rmap->name);
+ }
+
+ bgp_scan_finish(bgp);
+ bgp_address_destroy(bgp);
+ bgp_tip_hash_destroy(bgp);
+
+ /* release the auto RD id */
+ bf_release_index(bm->rd_idspace, bgp->vrf_rd_id);
+
+ bgp_evpn_cleanup(bgp);
+ bgp_pbr_cleanup(bgp);
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ enum vpn_policy_direction dir;
+
+ if (bgp->vpn_policy[afi].import_vrf)
+ list_delete(&bgp->vpn_policy[afi].import_vrf);
+ if (bgp->vpn_policy[afi].export_vrf)
+ list_delete(&bgp->vpn_policy[afi].export_vrf);
+
+ dir = BGP_VPN_POLICY_DIR_FROMVPN;
+ if (bgp->vpn_policy[afi].rtlist[dir])
+ ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]);
+ dir = BGP_VPN_POLICY_DIR_TOVPN;
+ if (bgp->vpn_policy[afi].rtlist[dir])
+ ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]);
+ if (bgp->vpn_policy[afi].tovpn_rd_pretty)
+ XFREE(MTYPE_BGP, bgp->vpn_policy[afi].tovpn_rd_pretty);
+ if (bgp->vpn_policy[afi].tovpn_sid_locator != NULL)
+ srv6_locator_chunk_free(
+ &bgp->vpn_policy[afi].tovpn_sid_locator);
+ if (bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent != NULL)
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp->vpn_policy[afi]
+ .tovpn_zebra_vrf_sid_last_sent);
+ if (bgp->vpn_policy[afi].tovpn_sid != NULL) {
+ sid_unregister(bgp, bgp->vpn_policy[afi].tovpn_sid);
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp->vpn_policy[afi].tovpn_sid);
+ }
+ }
+ bgp_srv6_cleanup(bgp);
+ bgp_confederation_id_unset(bgp);
+
+ XFREE(MTYPE_BGP, bgp->as_pretty);
+ XFREE(MTYPE_BGP, bgp->name);
+ XFREE(MTYPE_BGP, bgp->name_pretty);
+ XFREE(MTYPE_BGP, bgp->snmp_stats);
+
+ XFREE(MTYPE_BGP, bgp);
+}
+
+struct peer *peer_lookup_by_conf_if(struct bgp *bgp, const char *conf_if)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (!conf_if)
+ return NULL;
+
+ if (bgp != NULL) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ if (peer->conf_if && !strcmp(peer->conf_if, conf_if)
+ && !CHECK_FLAG(peer->sflags,
+ PEER_STATUS_ACCEPT_PEER))
+ return peer;
+ } else if (bm->bgp != NULL) {
+ struct listnode *bgpnode, *nbgpnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp))
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ if (peer->conf_if
+ && !strcmp(peer->conf_if, conf_if)
+ && !CHECK_FLAG(peer->sflags,
+ PEER_STATUS_ACCEPT_PEER))
+ return peer;
+ }
+ return NULL;
+}
+
+struct peer *peer_lookup_by_hostname(struct bgp *bgp, const char *hostname)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (!hostname)
+ return NULL;
+
+ if (bgp != NULL) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ if (peer->hostname && !strcmp(peer->hostname, hostname)
+ && !CHECK_FLAG(peer->sflags,
+ PEER_STATUS_ACCEPT_PEER))
+ return peer;
+ } else if (bm->bgp != NULL) {
+ struct listnode *bgpnode, *nbgpnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp))
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ if (peer->hostname
+ && !strcmp(peer->hostname, hostname)
+ && !CHECK_FLAG(peer->sflags,
+ PEER_STATUS_ACCEPT_PEER))
+ return peer;
+ }
+ return NULL;
+}
+
+struct peer *peer_lookup(struct bgp *bgp, union sockunion *su)
+{
+ struct peer *peer = NULL;
+ struct peer tmp_peer;
+ struct peer_connection connection;
+
+ memset(&connection, 0, sizeof(struct peer_connection));
+ memset(&tmp_peer, 0, sizeof(struct peer));
+ tmp_peer.connection = &connection;
+
+ /*
+ * We do not want to find the doppelganger peer so search for the peer
+ * in
+ * the hash that has PEER_FLAG_CONFIG_NODE
+ */
+ SET_FLAG(tmp_peer.flags, PEER_FLAG_CONFIG_NODE);
+
+ connection.su = *su;
+
+ if (bgp != NULL) {
+ peer = hash_lookup(bgp->peerhash, &tmp_peer);
+ } else if (bm->bgp != NULL) {
+ struct listnode *bgpnode, *nbgpnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) {
+ peer = hash_lookup(bgp->peerhash, &tmp_peer);
+ if (peer)
+ break;
+ }
+ }
+
+ return peer;
+}
+
+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;
+
+ /* Create peer first; we've already checked group config is valid. */
+ peer = peer_create(su, NULL, bgp, bgp->as, group->conf->as,
+ group->conf->as_type, group, true, NULL);
+ if (!peer)
+ return NULL;
+
+ /* Link to group */
+ peer = peer_lock(peer);
+ listnode_add(group->peer, peer);
+
+ peer_group2peer_config_copy(group, 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.
+ */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!group->conf->afc[afi][safi])
+ continue;
+ peer->afc[afi][safi] = 1;
+
+ if (!peer_af_find(peer, afi, safi))
+ peer_af_create(peer, afi, safi);
+
+ peer_group2peer_config_copy_af(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);
+
+ 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)))
+ goto found_range;
+ }
+
+found_range:
+ *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;
+
+ if (!sockunion2hostprefix(su, &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;
+
+ if (bgp_debug_neighbor_events(NULL))
+ zlog_debug(
+ "Dynamic Neighbor %pFX matches group %s listen range %pFX",
+ &prefix, group->name, listen_range);
+
+ /* 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 %pFX rejected - at limit %d",
+ &prefix, 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 %pFX rejected - group %s disabled",
+ &prefix, 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 %pFX rejected - no AF activated for group %s",
+ &prefix, 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;
+}
+
+static void peer_drop_dynamic_neighbor(struct peer *peer)
+{
+ int dncount = -1;
+ if (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);
+}
+
+bool bgp_path_attribute_discard(struct peer *peer, char *buf, size_t size)
+{
+ if (!buf)
+ return false;
+
+ buf[0] = '\0';
+
+ for (unsigned int i = 1; i <= BGP_ATTR_MAX; i++) {
+ if (peer->discard_attrs[i])
+ snprintf(buf + strlen(buf), size - strlen(buf), "%s%d",
+ (strlen(buf) > 0) ? " " : "", i);
+ }
+
+ if (strlen(buf) > 0)
+ return true;
+
+ return false;
+}
+
+bool bgp_path_attribute_treat_as_withdraw(struct peer *peer, char *buf,
+ size_t size)
+{
+ if (!buf)
+ return false;
+
+ buf[0] = '\0';
+
+ for (unsigned int i = 1; i <= BGP_ATTR_MAX; i++) {
+ if (peer->withdraw_attrs[i])
+ snprintf(buf + strlen(buf), size - strlen(buf), "%s%d",
+ (strlen(buf) > 0) ? " " : "", i);
+ }
+
+ if (strlen(buf) > 0)
+ return true;
+
+ return false;
+}
+
+/* If peer is configured at least one address family return 1. */
+bool peer_active(struct peer *peer)
+{
+ if (BGP_CONNECTION_SU_UNSPEC(peer->connection))
+ return false;
+ if (peer->afc[AFI_IP][SAFI_UNICAST] || peer->afc[AFI_IP][SAFI_MULTICAST]
+ || peer->afc[AFI_IP][SAFI_LABELED_UNICAST]
+ || peer->afc[AFI_IP][SAFI_MPLS_VPN] || peer->afc[AFI_IP][SAFI_ENCAP]
+ || peer->afc[AFI_IP][SAFI_FLOWSPEC]
+ || peer->afc[AFI_IP6][SAFI_UNICAST]
+ || peer->afc[AFI_IP6][SAFI_MULTICAST]
+ || peer->afc[AFI_IP6][SAFI_LABELED_UNICAST]
+ || peer->afc[AFI_IP6][SAFI_MPLS_VPN]
+ || peer->afc[AFI_IP6][SAFI_ENCAP]
+ || peer->afc[AFI_IP6][SAFI_FLOWSPEC]
+ || peer->afc[AFI_L2VPN][SAFI_EVPN])
+ return true;
+ return false;
+}
+
+/* If peer is negotiated at least one address family return 1. */
+bool peer_active_nego(struct peer *peer)
+{
+ if (peer->afc_nego[AFI_IP][SAFI_UNICAST]
+ || peer->afc_nego[AFI_IP][SAFI_MULTICAST]
+ || peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST]
+ || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
+ || peer->afc_nego[AFI_IP][SAFI_ENCAP]
+ || peer->afc_nego[AFI_IP][SAFI_FLOWSPEC]
+ || peer->afc_nego[AFI_IP6][SAFI_UNICAST]
+ || peer->afc_nego[AFI_IP6][SAFI_MULTICAST]
+ || peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST]
+ || peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN]
+ || peer->afc_nego[AFI_IP6][SAFI_ENCAP]
+ || peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC]
+ || peer->afc_nego[AFI_L2VPN][SAFI_EVPN])
+ return true;
+ return false;
+}
+
+/* If peer received at least one address family MP, return true */
+bool peer_afc_received(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ if (peer->afc_recv[afi][safi])
+ return true;
+
+ return false;
+}
+
+/* If peer advertised at least one address family MP, return true */
+bool peer_afc_advertised(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ if (peer->afc_adv[afi][safi])
+ return true;
+
+ return false;
+}
+
+void peer_change_action(struct peer *peer, afi_t afi, safi_t safi,
+ enum peer_change_type type)
+{
+ struct peer_af *paf;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return;
+
+ if (!peer_established(peer->connection))
+ return;
+
+ if (type == peer_change_reset) {
+ /* If we're resetting session, we've to delete both peer struct
+ */
+ if ((peer->doppelganger) &&
+ (peer->doppelganger->connection->status != Deleted) &&
+ (!CHECK_FLAG(peer->doppelganger->flags,
+ PEER_FLAG_CONFIG_NODE)))
+ peer_delete(peer->doppelganger);
+
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else if (type == peer_change_reset_in) {
+ if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_RCV))
+ bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ else {
+ if ((peer->doppelganger) &&
+ (peer->doppelganger->connection->status != Deleted) &&
+ (!CHECK_FLAG(peer->doppelganger->flags,
+ PEER_FLAG_CONFIG_NODE)))
+ peer_delete(peer->doppelganger);
+
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ } else if (type == peer_change_reset_out) {
+ paf = peer_af_find(peer, afi, safi);
+ if (paf && paf->subgroup)
+ SET_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_FORCE_UPDATES);
+
+ update_group_adjust_peer(paf);
+ bgp_announce_route(peer, afi, safi, false);
+ }
+}
+
+struct peer_flag_action {
+ /* Peer's flag. */
+ uint64_t flag;
+
+ /* This flag can be set for peer-group member. */
+ uint8_t not_for_member;
+
+ /* Action when the flag is changed. */
+ enum peer_change_type type;
+};
+
+static const struct peer_flag_action peer_flag_action_list[] = {
+ {PEER_FLAG_PASSIVE, 0, peer_change_reset},
+ {PEER_FLAG_SHUTDOWN, 0, peer_change_reset},
+ {PEER_FLAG_RTT_SHUTDOWN, 0, peer_change_none},
+ {PEER_FLAG_DONT_CAPABILITY, 0, peer_change_none},
+ {PEER_FLAG_OVERRIDE_CAPABILITY, 0, peer_change_none},
+ {PEER_FLAG_STRICT_CAP_MATCH, 0, peer_change_none},
+ {PEER_FLAG_DYNAMIC_CAPABILITY, 0, peer_change_reset},
+ {PEER_FLAG_DISABLE_CONNECTED_CHECK, 0, peer_change_reset},
+ {PEER_FLAG_CAPABILITY_ENHE, 0, peer_change_reset},
+ {PEER_FLAG_ENFORCE_FIRST_AS, 0, peer_change_reset_in},
+ {PEER_FLAG_IFPEER_V6ONLY, 0, peer_change_reset},
+ {PEER_FLAG_ROUTEADV, 0, peer_change_none},
+ {PEER_FLAG_TIMER, 0, peer_change_none},
+ {PEER_FLAG_TIMER_CONNECT, 0, peer_change_none},
+ {PEER_FLAG_TIMER_DELAYOPEN, 0, peer_change_none},
+ {PEER_FLAG_PASSWORD, 0, peer_change_none},
+ {PEER_FLAG_LOCAL_AS, 0, peer_change_reset},
+ {PEER_FLAG_LOCAL_AS_NO_PREPEND, 0, peer_change_reset},
+ {PEER_FLAG_LOCAL_AS_REPLACE_AS, 0, peer_change_reset},
+ {PEER_FLAG_UPDATE_SOURCE, 0, peer_change_none},
+ {PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE, 0, peer_change_none},
+ {PEER_FLAG_EXTENDED_OPT_PARAMS, 0, peer_change_reset},
+ {PEER_FLAG_ROLE_STRICT_MODE, 0, peer_change_none},
+ {PEER_FLAG_ROLE, 0, peer_change_none},
+ {PEER_FLAG_PORT, 0, peer_change_reset},
+ {PEER_FLAG_AIGP, 0, peer_change_none},
+ {PEER_FLAG_GRACEFUL_SHUTDOWN, 0, peer_change_none},
+ {PEER_FLAG_CAPABILITY_SOFT_VERSION, 0, peer_change_none},
+ {0, 0, 0}};
+
+static const struct peer_flag_action peer_af_flag_action_list[] = {
+ {PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out},
+ {PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out},
+ {PEER_FLAG_SEND_LARGE_COMMUNITY, 1, peer_change_reset_out},
+ {PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out},
+ {PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset},
+ {PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset},
+ {PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in},
+ {PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out},
+ {PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out},
+ {PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out},
+ {PEER_FLAG_DEFAULT_ORIGINATE, 0, peer_change_none},
+ {PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out},
+ {PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in},
+ {PEER_FLAG_ALLOWAS_IN_ORIGIN, 0, peer_change_reset_in},
+ {PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset},
+ {PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset},
+ {PEER_FLAG_MAX_PREFIX, 0, peer_change_none},
+ {PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none},
+ {PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none},
+ {PEER_FLAG_MAX_PREFIX_OUT, 0, peer_change_none},
+ {PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out},
+ {PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out},
+ {PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out},
+ {PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE, 1, peer_change_reset_out},
+ {PEER_FLAG_AS_OVERRIDE, 1, peer_change_reset_out},
+ {PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, 1, peer_change_reset_out},
+ {PEER_FLAG_WEIGHT, 0, peer_change_reset_in},
+ {PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset},
+ {PEER_FLAG_SOO, 0, peer_change_reset},
+ {PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset},
+ {0, 0, 0}};
+
+/* Proper action set. */
+static int peer_flag_action_set(const struct peer_flag_action *action_list,
+ int size, struct peer_flag_action *action,
+ uint64_t flag)
+{
+ int i;
+ int found = 0;
+ int reset_in = 0;
+ int reset_out = 0;
+ const struct peer_flag_action *match = NULL;
+
+ /* Check peer's frag action. */
+ for (i = 0; i < size; i++) {
+ match = &action_list[i];
+
+ if (match->flag == 0)
+ break;
+
+ if (match->flag & flag) {
+ found = 1;
+
+ if (match->type == peer_change_reset_in)
+ reset_in = 1;
+ if (match->type == peer_change_reset_out)
+ reset_out = 1;
+ if (match->type == peer_change_reset) {
+ reset_in = 1;
+ reset_out = 1;
+ }
+ if (match->not_for_member)
+ action->not_for_member = 1;
+ }
+ }
+
+ /* Set peer clear type. */
+ if (reset_in && reset_out)
+ action->type = peer_change_reset;
+ else if (reset_in)
+ action->type = peer_change_reset_in;
+ else if (reset_out)
+ action->type = peer_change_reset_out;
+ else
+ action->type = peer_change_none;
+
+ return found;
+}
+
+static void peer_flag_modify_action(struct peer *peer, uint64_t flag)
+{
+ if (flag == PEER_FLAG_SHUTDOWN) {
+ if (CHECK_FLAG(peer->flags, flag)) {
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT))
+ peer_nsf_stop(peer);
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
+
+ if (peer->connection->t_pmax_restart) {
+ EVENT_OFF(peer->connection->t_pmax_restart);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP Maximum-prefix restart timer canceled",
+ peer);
+ }
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->connection->status)) {
+ char *msg = peer->tx_shutdown_message;
+ size_t msglen;
+ uint8_t msgbuf[BGP_ADMIN_SHUTDOWN_MSG_LEN + 1];
+
+ if (!msg && peer_group_active(peer))
+ msg = peer->group->conf
+ ->tx_shutdown_message;
+ msglen = msg ? strlen(msg) : 0;
+ if (msglen > BGP_ADMIN_SHUTDOWN_MSG_LEN)
+ msglen = BGP_ADMIN_SHUTDOWN_MSG_LEN;
+
+ if (msglen) {
+ msgbuf[0] = msglen;
+ memcpy(msgbuf + 1, msg, msglen);
+
+ bgp_notify_send_with_data(
+ peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN,
+ msgbuf, msglen + 1);
+ } else
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+ } else
+ bgp_session_reset(peer);
+ } else {
+ peer->v_start = BGP_INIT_START_TIMER;
+ BGP_EVENT_ADD(peer->connection, BGP_Stop);
+ }
+ } else if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ if (flag == PEER_FLAG_DYNAMIC_CAPABILITY)
+ peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+ else if (flag == PEER_FLAG_PASSIVE)
+ peer->last_reset = PEER_DOWN_PASSIVE_CHANGE;
+ else if (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK)
+ peer->last_reset = PEER_DOWN_MULTIHOP_CHANGE;
+
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+}
+
+/* Enable global administrative shutdown of all peers of BGP instance */
+void bgp_shutdown_enable(struct bgp *bgp, const char *msg)
+{
+ struct peer *peer;
+ struct listnode *node;
+ /* length(1) + message(N) */
+ uint8_t data[BGP_ADMIN_SHUTDOWN_MSG_LEN + 1];
+
+ /* do nothing if already shut down */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN))
+ return;
+
+ /* informational log message */
+ zlog_info("Enabled administrative shutdown on BGP instance AS %u",
+ bgp->as);
+
+ /* iterate through peers of BGP instance */
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ /* continue, if peer is already in administrative shutdown. */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN))
+ continue;
+
+ /* send a RFC 4486 notification message if necessary */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ if (msg) {
+ size_t datalen = strlen(msg);
+
+ if (datalen > BGP_ADMIN_SHUTDOWN_MSG_LEN)
+ datalen = BGP_ADMIN_SHUTDOWN_MSG_LEN;
+
+ data[0] = datalen;
+ memcpy(data + 1, msg, datalen);
+
+ bgp_notify_send_with_data(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN,
+ data, datalen + 1);
+ } else {
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+ }
+ }
+
+ /* reset start timer to initial value */
+ peer->v_start = BGP_INIT_START_TIMER;
+
+ /* trigger a RFC 4271 ManualStop event */
+ BGP_EVENT_ADD(peer->connection, BGP_Stop);
+ }
+
+ /* set the BGP instances shutdown flag */
+ SET_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN);
+}
+
+/* Disable global administrative shutdown of all peers of BGP instance */
+void bgp_shutdown_disable(struct bgp *bgp)
+{
+ const struct listnode *node;
+ struct peer *peer;
+
+ /* do nothing if not shut down. */
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN))
+ return;
+
+ /* informational log message */
+ zlog_info("Disabled administrative shutdown on BGP instance AS %u",
+ bgp->as);
+
+ /* clear the BGP instances shutdown flag */
+ UNSET_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN);
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer))
+ bgp_timer_set(peer->connection);
+}
+
+/* Change specified peer flag. */
+static int peer_flag_modify(struct peer *peer, uint64_t flag, int set)
+{
+ int found;
+ int size;
+ bool invert, member_invert;
+ struct peer *member;
+ struct listnode *node, *nnode;
+ struct peer_flag_action action;
+
+ memset(&action, 0, sizeof(struct peer_flag_action));
+ size = sizeof(peer_flag_action_list) / sizeof(struct peer_flag_action);
+
+ invert = CHECK_FLAG(peer->flags_invert, flag);
+ found = peer_flag_action_set(peer_flag_action_list, size, &action,
+ flag);
+
+ /* Abort if no flag action exists. */
+ if (!found)
+ return BGP_ERR_INVALID_FLAG;
+
+ /* Check for flag conflict: STRICT_CAP_MATCH && OVERRIDE_CAPABILITY */
+ if (set && CHECK_FLAG(peer->flags | flag, PEER_FLAG_STRICT_CAP_MATCH)
+ && CHECK_FLAG(peer->flags | flag, PEER_FLAG_OVERRIDE_CAPABILITY))
+ return BGP_ERR_PEER_FLAG_CONFLICT;
+
+ /* Handle flag updates where desired state matches current state. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (set && CHECK_FLAG(peer->flags, flag)) {
+ COND_FLAG(peer->flags_override, flag, !invert);
+ return 0;
+ }
+
+ if (!set && !CHECK_FLAG(peer->flags, flag)) {
+ COND_FLAG(peer->flags_override, flag, invert);
+ return 0;
+ }
+ }
+
+ /* Inherit from peer-group or set/unset flags accordingly. */
+ if (peer_group_active(peer) && set == invert)
+ peer_flag_inherit(peer, flag);
+ else
+ COND_FLAG(peer->flags, flag, set);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Update flag override state accordingly. */
+ COND_FLAG(peer->flags_override, flag, set != invert);
+
+ /*
+ * For the extended next-hop encoding flag we need to turn RAs
+ * on if flag is being set, but only turn RAs off if the flag
+ * is being unset on this peer and if this peer is a member of a
+ * peer-group, the peer-group also doesn't have the flag set.
+ */
+ if (flag == PEER_FLAG_CAPABILITY_ENHE) {
+ if (set) {
+ bgp_zebra_initiate_radv(peer->bgp, peer);
+ } else if (peer_group_active(peer)) {
+ if (!CHECK_FLAG(peer->group->conf->flags,
+ flag) &&
+ !peer->conf_if)
+ bgp_zebra_terminate_radv(peer->bgp,
+ peer);
+ } else
+ bgp_zebra_terminate_radv(peer->bgp, peer);
+ }
+
+ /* Execute flag action on peer. */
+ if (action.type == peer_change_reset)
+ peer_flag_modify_action(peer, flag);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Update peer-group members, unless they are explicitly overriding
+ * peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, flag))
+ continue;
+
+ /* Check if only member without group is inverted. */
+ member_invert =
+ CHECK_FLAG(member->flags_invert, flag) && !invert;
+
+ /* Skip peers with equivalent configuration. */
+ if (set != member_invert && CHECK_FLAG(member->flags, flag))
+ continue;
+
+ if (set == member_invert && !CHECK_FLAG(member->flags, flag))
+ continue;
+
+ /* Update flag on peer-group member. */
+ COND_FLAG(member->flags, flag, set != member_invert);
+
+ if (flag == PEER_FLAG_CAPABILITY_ENHE && !member->conf_if)
+ set ? bgp_zebra_initiate_radv(member->bgp, member)
+ : bgp_zebra_terminate_radv(member->bgp, member);
+
+ /* Execute flag action on peer-group member. */
+ if (action.type == peer_change_reset)
+ peer_flag_modify_action(member, flag);
+ }
+
+ return 0;
+}
+
+int peer_flag_set(struct peer *peer, uint64_t flag)
+{
+ return peer_flag_modify(peer, flag, 1);
+}
+
+int peer_flag_unset(struct peer *peer, uint64_t flag)
+{
+ return peer_flag_modify(peer, flag, 0);
+}
+
+static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
+ uint64_t flag, bool set)
+{
+ int found;
+ int size;
+ bool invert, member_invert;
+ struct peer *member;
+ struct listnode *node, *nnode;
+ struct peer_flag_action action;
+ enum bgp_peer_sort ptype;
+
+ memset(&action, 0, sizeof(struct peer_flag_action));
+ size = sizeof(peer_af_flag_action_list)
+ / sizeof(struct peer_flag_action);
+
+ invert = CHECK_FLAG(peer->af_flags_invert[afi][safi], flag);
+ found = peer_flag_action_set(peer_af_flag_action_list, size, &action,
+ flag);
+
+ /* Abort if flag action exists. */
+ if (!found)
+ return BGP_ERR_INVALID_FLAG;
+
+ ptype = peer_sort(peer);
+ /* Special check for reflector client. */
+ if (flag & PEER_FLAG_REFLECTOR_CLIENT && ptype != BGP_PEER_IBGP)
+ return BGP_ERR_NOT_INTERNAL_PEER;
+
+ /* Special check for remove-private-AS. */
+ if (flag & PEER_FLAG_REMOVE_PRIVATE_AS && ptype == BGP_PEER_IBGP)
+ return BGP_ERR_REMOVE_PRIVATE_AS;
+
+ /* as-override is not allowed for IBGP peers */
+ if (flag & PEER_FLAG_AS_OVERRIDE && ptype == BGP_PEER_IBGP)
+ return BGP_ERR_AS_OVERRIDE;
+
+ /* Handle flag updates where desired state matches current state. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (set && CHECK_FLAG(peer->af_flags[afi][safi], flag)) {
+ COND_FLAG(peer->af_flags_override[afi][safi], flag,
+ !invert);
+ return 0;
+ }
+
+ if (!set && !CHECK_FLAG(peer->af_flags[afi][safi], flag)) {
+ COND_FLAG(peer->af_flags_override[afi][safi], flag,
+ invert);
+ return 0;
+ }
+ }
+
+ /*
+ * For EVPN we implicitly set the NEXTHOP_UNCHANGED flag,
+ * if we are setting/unsetting flags which conflict with this flag
+ * handle accordingly
+ */
+ if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
+ if (set) {
+
+ /*
+ * if we are setting NEXTHOP_SELF, we need to unset the
+ * NEXTHOP_UNCHANGED flag
+ */
+ if (CHECK_FLAG(flag, PEER_FLAG_NEXTHOP_SELF) ||
+ CHECK_FLAG(flag, PEER_FLAG_FORCE_NEXTHOP_SELF))
+ UNSET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ } else {
+
+ /*
+ * if we are unsetting NEXTHOP_SELF, we need to set the
+ * NEXTHOP_UNCHANGED flag to reset the defaults for EVPN
+ */
+ if (CHECK_FLAG(flag, PEER_FLAG_NEXTHOP_SELF) ||
+ CHECK_FLAG(flag, PEER_FLAG_FORCE_NEXTHOP_SELF))
+ SET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ }
+ }
+
+ /*
+ * If the peer is a route server client let's not
+ * muck with the nexthop on the way out the door
+ */
+ if (flag & PEER_FLAG_RSERVER_CLIENT) {
+ if (set)
+ SET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ else
+ UNSET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ }
+
+ /* Inherit from peer-group or set/unset flags accordingly. */
+ if (peer_group_active(peer) && set == invert)
+ peer_af_flag_inherit(peer, afi, safi, flag);
+ else
+ COND_FLAG(peer->af_flags[afi][safi], flag, set);
+
+ /* Execute action when peer is established. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) &&
+ peer_established(peer->connection)) {
+ if (!set && flag == PEER_FLAG_SOFT_RECONFIG)
+ bgp_clear_adj_in(peer, afi, safi);
+ else {
+ if (flag == PEER_FLAG_REFLECTOR_CLIENT)
+ peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE;
+ else if (flag == PEER_FLAG_RSERVER_CLIENT)
+ peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE;
+ else if (flag == PEER_FLAG_ORF_PREFIX_SM)
+ peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+ else if (flag == PEER_FLAG_ORF_PREFIX_RM)
+ peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+
+ peer_change_action(peer, afi, safi, action.type);
+ }
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ COND_FLAG(peer->af_flags_override[afi][safi], flag,
+ set != invert);
+ } else {
+ /*
+ * Update peer-group members, unless they are explicitly
+ * overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode,
+ member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ flag))
+ continue;
+
+ /* Check if only member without group is inverted. */
+ member_invert =
+ CHECK_FLAG(member->af_flags_invert[afi][safi],
+ flag)
+ && !invert;
+
+ /* Skip peers with equivalent configuration. */
+ if (set != member_invert
+ && CHECK_FLAG(member->af_flags[afi][safi], flag))
+ continue;
+
+ if (set == member_invert
+ && !CHECK_FLAG(member->af_flags[afi][safi], flag))
+ continue;
+
+ /* Update flag on peer-group member. */
+ COND_FLAG(member->af_flags[afi][safi], flag,
+ set != member_invert);
+
+ /* Execute flag action on peer-group member. */
+ if (peer_established(member->connection)) {
+ if (!set && flag == PEER_FLAG_SOFT_RECONFIG)
+ bgp_clear_adj_in(member, afi, safi);
+ else {
+ if (flag == PEER_FLAG_REFLECTOR_CLIENT)
+ member->last_reset =
+ PEER_DOWN_RR_CLIENT_CHANGE;
+ else if (flag
+ == PEER_FLAG_RSERVER_CLIENT)
+ member->last_reset =
+ PEER_DOWN_RS_CLIENT_CHANGE;
+ else if (flag
+ == PEER_FLAG_ORF_PREFIX_SM)
+ member->last_reset =
+ PEER_DOWN_CAPABILITY_CHANGE;
+ else if (flag
+ == PEER_FLAG_ORF_PREFIX_RM)
+ member->last_reset =
+ PEER_DOWN_CAPABILITY_CHANGE;
+
+ peer_change_action(member, afi, safi,
+ action.type);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int peer_af_flag_set(struct peer *peer, afi_t afi, safi_t safi, uint64_t flag)
+{
+ return peer_af_flag_modify(peer, afi, safi, flag, 1);
+}
+
+int peer_af_flag_unset(struct peer *peer, afi_t afi, safi_t safi, uint64_t flag)
+{
+ return peer_af_flag_modify(peer, afi, safi, flag, 0);
+}
+
+
+void peer_tx_shutdown_message_set(struct peer *peer, const char *msg)
+{
+ XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message);
+ peer->tx_shutdown_message =
+ msg ? XSTRDUP(MTYPE_PEER_TX_SHUTDOWN_MSG, msg) : NULL;
+}
+
+void peer_tx_shutdown_message_unset(struct peer *peer)
+{
+ XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message);
+}
+
+
+/* EBGP multihop configuration. */
+int peer_ebgp_multihop_set(struct peer *peer, int ttl)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ struct peer *peer1;
+
+ if (peer->sort == BGP_PEER_IBGP || peer->conf_if)
+ return 0;
+
+ /* is there anything to do? */
+ if (peer->ttl == ttl)
+ 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 != BGP_GTSM_HOPS_DISABLED)
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode,
+ peer1)) {
+ if (peer1->sort == BGP_PEER_IBGP)
+ continue;
+
+ if (peer1->gtsm_hops != BGP_GTSM_HOPS_DISABLED)
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+ }
+ } else {
+ if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED)
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+ }
+ }
+
+ peer->ttl = ttl;
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (peer->sort != BGP_PEER_IBGP) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->connection->status))
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+
+ /* Reconfigure BFD peer with new TTL. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+ }
+ } else {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ if (peer->sort == BGP_PEER_IBGP)
+ continue;
+
+ peer->ttl = group->conf->ttl;
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->connection->status))
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+
+ /* Reconfigure BFD peer with new TTL. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+ }
+ }
+ return 0;
+}
+
+int peer_ebgp_multihop_unset(struct peer *peer)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ int ttl;
+
+ if (peer->sort == BGP_PEER_IBGP)
+ return 0;
+
+ if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED && peer->ttl != MAXTTL)
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+
+ if (peer_group_active(peer))
+ ttl = peer->group->conf->ttl;
+ else
+ ttl = BGP_DEFAULT_TTL;
+
+ if (ttl == peer->ttl)
+ return 0;
+
+ peer->ttl = ttl;
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status))
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+
+ /* Reconfigure BFD peer with new TTL. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+ } else {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ if (peer->sort == BGP_PEER_IBGP)
+ continue;
+
+ peer->ttl = BGP_DEFAULT_TTL;
+
+ if (peer->connection->fd >= 0) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->connection->status))
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+ }
+
+ /* Reconfigure BFD peer with new TTL. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+ }
+ }
+ return 0;
+}
+
+/* Set Open Policy Role and check its correctness */
+int peer_role_set(struct peer *peer, uint8_t role, bool strict_mode)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ peer_flag_set(peer, PEER_FLAG_ROLE);
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (peer->sort != BGP_PEER_EBGP)
+ return BGP_ERR_INVALID_INTERNAL_ROLE;
+
+ if (peer->local_role == role) {
+ if (CHECK_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE) &&
+ !strict_mode)
+ /* TODO: Is session restart needed if it was
+ * down?
+ */
+ UNSET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ if (!CHECK_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE) &&
+ strict_mode) {
+ SET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ /* Restart session to throw Role Mismatch
+ * Notification
+ */
+ if (peer->remote_role == ROLE_UNDEFINED)
+ bgp_session_reset(peer);
+ }
+ } else {
+ peer->local_role = role;
+ if (strict_mode)
+ SET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ else
+ UNSET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ }
+
+ return CMD_SUCCESS;
+ }
+
+ peer->local_role = role;
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ if (member->sort != BGP_PEER_EBGP)
+ return BGP_ERR_INVALID_INTERNAL_ROLE;
+
+ if (member->local_role == role) {
+ if (CHECK_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE) &&
+ !strict_mode)
+ /* TODO: Is session restart needed if it was
+ * down?
+ */
+ UNSET_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ if (!CHECK_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE) &&
+ strict_mode) {
+ SET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ SET_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ /* Restart session to throw Role Mismatch
+ * Notification
+ */
+ if (member->remote_role == ROLE_UNDEFINED)
+ bgp_session_reset(member);
+ }
+ } else {
+ member->local_role = role;
+
+ if (strict_mode) {
+ SET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ SET_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ } else {
+ UNSET_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+int peer_role_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ peer_flag_unset(peer, PEER_FLAG_ROLE);
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return peer_role_set(peer, ROLE_UNDEFINED, 0);
+
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member))
+ peer_role_set(member, ROLE_UNDEFINED, 0);
+
+ return CMD_SUCCESS;
+}
+
+/* Neighbor description. */
+void peer_description_set(struct peer *peer, const char *desc)
+{
+ XFREE(MTYPE_PEER_DESC, peer->desc);
+
+ peer->desc = XSTRDUP(MTYPE_PEER_DESC, desc);
+}
+
+void peer_description_unset(struct peer *peer)
+{
+ XFREE(MTYPE_PEER_DESC, peer->desc);
+}
+
+/* Neighbor update-source. */
+int peer_update_source_if_set(struct peer *peer, const char *ifname)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_UPDATE_SOURCE);
+ if (peer->update_if) {
+ if (strcmp(peer->update_if, ifname) == 0)
+ return 0;
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+ }
+ peer->update_if = XSTRDUP(MTYPE_PEER_UPDATE_SOURCE, ifname);
+ sockunion_free(peer->update_source);
+ peer->update_source = NULL;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+
+ /* Apply new source configuration to BFD session. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_UPDATE_SOURCE))
+ continue;
+
+ /* Skip peers with the same configuration. */
+ if (member->update_if) {
+ if (strcmp(member->update_if, ifname) == 0)
+ continue;
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, member->update_if);
+ }
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_UPDATE_SOURCE);
+ member->update_if = XSTRDUP(MTYPE_PEER_UPDATE_SOURCE, ifname);
+ sockunion_free(member->update_source);
+ member->update_source = NULL;
+
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->connection->status)) {
+ member->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(member->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(member);
+
+ /* Apply new source configuration to BFD session. */
+ if (member->bfd_config)
+ bgp_peer_bfd_update_source(member);
+ }
+
+ return 0;
+}
+
+void peer_update_source_addr_set(struct peer *peer, const union sockunion *su)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_UPDATE_SOURCE);
+ if (peer->update_source) {
+ if (sockunion_cmp(peer->update_source, su) == 0)
+ return;
+ sockunion_free(peer->update_source);
+ }
+ peer->update_source = sockunion_dup(su);
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+
+ /* Apply new source configuration to BFD session. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_UPDATE_SOURCE))
+ continue;
+
+ /* Skip peers with the same configuration. */
+ if (member->update_source) {
+ if (sockunion_cmp(member->update_source, su) == 0)
+ continue;
+ sockunion_free(member->update_source);
+ }
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_UPDATE_SOURCE);
+ member->update_source = sockunion_dup(su);
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, member->update_if);
+
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->connection->status)) {
+ member->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(member->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(member);
+
+ /* Apply new source configuration to BFD session. */
+ if (member->bfd_config)
+ bgp_peer_bfd_update_source(member);
+ }
+}
+
+void peer_update_source_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+ bool src_unchanged = false;
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_UPDATE_SOURCE))
+ return;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ /* Don't reset peer if the update_source we'll inherit from
+ * the peer-group matches the peer's existing update_source
+ */
+ src_unchanged =
+ (peer->update_source &&
+ peer->group->conf->update_source &&
+ sockunion_cmp(peer->update_source,
+ peer->group->conf->update_source) == 0);
+
+ peer_flag_inherit(peer, PEER_FLAG_UPDATE_SOURCE);
+ PEER_SU_ATTR_INHERIT(peer, peer->group, update_source);
+ PEER_STR_ATTR_INHERIT(peer, peer->group, update_if,
+ MTYPE_PEER_UPDATE_SOURCE);
+
+ if (src_unchanged)
+ return;
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_UPDATE_SOURCE);
+ sockunion_free(peer->update_source);
+ peer->update_source = NULL;
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+
+ /* Apply new source configuration to BFD session. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_UPDATE_SOURCE))
+ continue;
+
+ /* Skip peers with the same configuration. */
+ if (!CHECK_FLAG(member->flags, PEER_FLAG_UPDATE_SOURCE)
+ && !member->update_source && !member->update_if)
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_UPDATE_SOURCE);
+ sockunion_free(member->update_source);
+ member->update_source = NULL;
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, member->update_if);
+
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->connection->status)) {
+ member->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(member->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(member);
+
+ /* Apply new source configuration to BFD session. */
+ if (member->bfd_config)
+ bgp_peer_bfd_update_source(member);
+ }
+}
+
+int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi,
+ const char *rmap, struct route_map *route_map)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+ struct update_subgroup *subgrp;
+
+ /* Set flag and configuration on peer. */
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE);
+
+ subgrp = peer_subgroup(peer, afi, safi);
+
+ if (rmap) {
+ if (!peer->default_rmap[afi][safi].name
+ || strcmp(rmap, peer->default_rmap[afi][safi].name) != 0) {
+ struct route_map *map = NULL;
+
+ if (peer->default_rmap[afi][safi].name) {
+ map = route_map_lookup_by_name(
+ peer->default_rmap[afi][safi].name);
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ peer->default_rmap[afi][safi].name);
+ }
+
+ /*
+ * When there is a change in route-map policy,
+ * this flow gets triggered. Since, the default
+ * route is already originated, the flag is set.
+ * The flag should be unset here,
+ * to trigger the flow of sending update message.
+ */
+ if (subgrp)
+ UNSET_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+
+ route_map_counter_decrement(map);
+ peer->default_rmap[afi][safi].name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
+ peer->default_rmap[afi][safi].map = route_map;
+ route_map_counter_increment(route_map);
+ }
+ } else if (!rmap) {
+ struct route_map *map = NULL;
+
+ if (peer->default_rmap[afi][safi].name) {
+ map = route_map_lookup_by_name(
+ peer->default_rmap[afi][safi].name);
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ peer->default_rmap[afi][safi].name);
+ }
+
+ /*
+ * This is triggered in case of route-map deletion.
+ * The flag needs to be unset, to trigger the flow
+ * of sending an update message.
+ */
+ if (subgrp)
+ UNSET_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+
+ route_map_counter_decrement(map);
+ peer->default_rmap[afi][safi].name = NULL;
+ peer->default_rmap[afi][safi].map = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Update peer route announcements. */
+ if (peer_established(peer->connection) &&
+ peer->afc_nego[afi][safi]) {
+ update_group_adjust_peer(peer_af_find(peer, afi, safi));
+ bgp_default_originate(peer, afi, safi, 0);
+ bgp_announce_route(peer, afi, safi, false);
+ }
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE);
+ if (rmap) {
+ struct route_map *map = NULL;
+
+ if (member->default_rmap[afi][safi].name) {
+ map = route_map_lookup_by_name(
+ member->default_rmap[afi][safi].name);
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ member->default_rmap[afi][safi].name);
+ }
+
+ route_map_counter_decrement(map);
+ member->default_rmap[afi][safi].name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
+ member->default_rmap[afi][safi].map = route_map;
+ route_map_counter_increment(route_map);
+ }
+
+ /* Update peer route announcements. */
+ if (peer_established(member->connection) &&
+ member->afc_nego[afi][safi]) {
+ update_group_adjust_peer(
+ peer_af_find(member, afi, safi));
+ bgp_default_originate(member, afi, safi, 0);
+ bgp_announce_route(member, afi, safi, false);
+ }
+ }
+
+ return 0;
+}
+
+int peer_default_originate_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_af_flag_inherit(peer, afi, safi,
+ PEER_FLAG_DEFAULT_ORIGINATE);
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ default_rmap[afi][safi].name,
+ MTYPE_ROUTE_MAP_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ default_rmap[afi][safi].map);
+ } else {
+ struct route_map *map = NULL;
+
+ /* Otherwise remove flag and configuration from peer. */
+ peer_af_flag_unset(peer, afi, safi,
+ PEER_FLAG_DEFAULT_ORIGINATE);
+ if (peer->default_rmap[afi][safi].name) {
+ map = route_map_lookup_by_name(
+ peer->default_rmap[afi][safi].name);
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ peer->default_rmap[afi][safi].name);
+ }
+ route_map_counter_decrement(map);
+ peer->default_rmap[afi][safi].name = NULL;
+ peer->default_rmap[afi][safi].map = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Update peer route announcements. */
+ if (peer_established(peer->connection) &&
+ peer->afc_nego[afi][safi]) {
+ update_group_adjust_peer(peer_af_find(peer, afi, safi));
+ bgp_default_originate(peer, afi, safi, 1);
+ bgp_announce_route(peer, afi, safi, false);
+ }
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ struct route_map *map;
+
+ map = NULL;
+
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE);
+ if (member->default_rmap[afi][safi].name) {
+ map = route_map_lookup_by_name(
+ member->default_rmap[afi][safi].name);
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ member->default_rmap[afi][safi].name);
+ }
+ route_map_counter_decrement(map);
+ member->default_rmap[afi][safi].name = NULL;
+ member->default_rmap[afi][safi].map = NULL;
+
+ /* Update peer route announcements. */
+ if (peer_established(member->connection) &&
+ member->afc_nego[afi][safi]) {
+ update_group_adjust_peer(peer_af_find(member, afi, safi));
+ bgp_default_originate(member, afi, safi, 1);
+ bgp_announce_route(member, afi, safi, false);
+ }
+ }
+
+ return 0;
+}
+
+void peer_port_set(struct peer *peer, uint16_t port)
+{
+ peer->port = port;
+ peer_flag_set(peer, PEER_FLAG_PORT);
+}
+
+void peer_port_unset(struct peer *peer)
+{
+ peer->port = BGP_PORT_DEFAULT;
+ peer_flag_unset(peer, PEER_FLAG_PORT);
+}
+
+/* Set the TCP-MSS value in the peer structure,
+ * This gets applied only after connection reset
+ * So this value will be used in bgp_connect.
+ */
+void peer_tcp_mss_set(struct peer *peer, uint32_t tcp_mss)
+{
+ peer->tcp_mss = tcp_mss;
+ SET_FLAG(peer->flags, PEER_FLAG_TCP_MSS);
+ bgp_tcp_mss_set(peer);
+}
+
+/* Reset the TCP-MSS value in the peer structure,
+ * This gets applied only after connection reset
+ * So this value will be used in bgp_connect.
+ */
+void peer_tcp_mss_unset(struct peer *peer)
+{
+ UNSET_FLAG(peer->flags, PEER_FLAG_TCP_MSS);
+ peer->tcp_mss = 0;
+ bgp_tcp_mss_set(peer);
+}
+
+/*
+ * Helper function that is called after the name of the policy
+ * being used by a peer has changed (AF specific). Automatically
+ * initiates inbound or outbound processing as needed.
+ */
+void peer_on_policy_change(struct peer *peer, afi_t afi, safi_t safi,
+ int outbound)
+{
+ if (outbound) {
+ update_group_adjust_peer(peer_af_find(peer, afi, safi));
+ if (peer_established(peer->connection))
+ bgp_announce_route(peer, afi, safi, false);
+ } else {
+ if (!peer_established(peer->connection))
+ return;
+
+ if (bgp_soft_reconfig_in(peer, afi, safi))
+ return;
+
+ if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_RCV))
+ bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ }
+}
+
+
+/* neighbor weight. */
+int peer_weight_set(struct peer *peer, afi_t afi, safi_t safi, uint16_t weight)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set flag and configuration on peer. */
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_WEIGHT);
+ if (peer->weight[afi][safi] != weight) {
+ peer->weight[afi][safi] = weight;
+ peer_on_policy_change(peer, afi, safi, 0);
+ }
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_WEIGHT))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->af_flags[afi][safi], PEER_FLAG_WEIGHT);
+ if (member->weight[afi][safi] != weight) {
+ member->weight[afi][safi] = weight;
+ peer_on_policy_change(member, afi, safi, 0);
+ }
+ }
+
+ return 0;
+}
+
+int peer_weight_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_WEIGHT))
+ return 0;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_WEIGHT);
+ PEER_ATTR_INHERIT(peer, peer->group, weight[afi][safi]);
+
+ peer_on_policy_change(peer, afi, safi, 0);
+ return 0;
+ }
+
+ /* Remove flag and configuration from peer. */
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_WEIGHT);
+ peer->weight[afi][safi] = 0;
+ peer_on_policy_change(peer, afi, safi, 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_WEIGHT))
+ continue;
+
+ /* Skip peers where flag is already disabled. */
+ if (!CHECK_FLAG(member->af_flags[afi][safi], PEER_FLAG_WEIGHT))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_WEIGHT);
+ member->weight[afi][safi] = 0;
+ peer_on_policy_change(member, afi, safi, 0);
+ }
+
+ return 0;
+}
+
+int peer_timers_set(struct peer *peer, uint32_t keepalive, uint32_t holdtime)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (keepalive > UINT16_MAX)
+ return BGP_ERR_INVALID_VALUE;
+
+ if (holdtime > UINT16_MAX)
+ return BGP_ERR_INVALID_VALUE;
+
+ if (holdtime < 3 && holdtime != 0)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_TIMER);
+ peer->holdtime = holdtime;
+ peer->keepalive = (keepalive < holdtime / 3 ? keepalive : holdtime / 3);
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_TIMER))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_TIMER);
+ PEER_ATTR_INHERIT(member, peer->group, holdtime);
+ PEER_ATTR_INHERIT(member, peer->group, keepalive);
+ }
+
+ return 0;
+}
+
+int peer_timers_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_TIMER);
+ PEER_ATTR_INHERIT(peer, peer->group, holdtime);
+ PEER_ATTR_INHERIT(peer, peer->group, keepalive);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_TIMER);
+ peer->holdtime = 0;
+ peer->keepalive = 0;
+ }
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_TIMER))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_TIMER);
+ member->holdtime = 0;
+ member->keepalive = 0;
+ }
+
+ return 0;
+}
+
+int peer_timers_connect_set(struct peer *peer, uint32_t connect)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (connect > UINT16_MAX)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_TIMER_CONNECT);
+ peer->connect = connect;
+ peer->v_connect = connect;
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (!peer_established(peer->connection)) {
+ if (peer_active(peer))
+ BGP_EVENT_ADD(peer->connection, BGP_Stop);
+ BGP_EVENT_ADD(peer->connection, BGP_Start);
+ }
+ return 0;
+ }
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_TIMER_CONNECT))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_TIMER_CONNECT);
+ member->connect = connect;
+ member->v_connect = connect;
+
+ if (!peer_established(member->connection)) {
+ if (peer_active(member))
+ BGP_EVENT_ADD(member->connection, BGP_Stop);
+ BGP_EVENT_ADD(member->connection, BGP_Start);
+ }
+ }
+
+ return 0;
+}
+
+int peer_timers_connect_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_TIMER_CONNECT);
+ PEER_ATTR_INHERIT(peer, peer->group, connect);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_TIMER_CONNECT);
+ peer->connect = 0;
+ }
+
+ /* Set timer with fallback to default value. */
+ if (peer->connect)
+ peer->v_connect = peer->connect;
+ else
+ peer->v_connect = peer->bgp->default_connect_retry;
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (!peer_established(peer->connection)) {
+ if (peer_active(peer))
+ BGP_EVENT_ADD(peer->connection, BGP_Stop);
+ BGP_EVENT_ADD(peer->connection, BGP_Start);
+ }
+ return 0;
+ }
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_TIMER_CONNECT))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_TIMER_CONNECT);
+ member->connect = 0;
+ member->v_connect = peer->bgp->default_connect_retry;
+
+ if (!peer_established(member->connection)) {
+ if (peer_active(member))
+ BGP_EVENT_ADD(member->connection, BGP_Stop);
+ BGP_EVENT_ADD(member->connection, BGP_Start);
+ }
+ }
+
+ return 0;
+}
+
+int peer_advertise_interval_set(struct peer *peer, uint32_t routeadv)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (routeadv > 600)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_ROUTEADV);
+ peer->routeadv = routeadv;
+ peer->v_routeadv = routeadv;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Update peer route announcements. */
+ update_group_adjust_peer_afs(peer);
+ if (peer_established(peer->connection))
+ bgp_announce_route_all(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_ROUTEADV))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_ROUTEADV);
+ member->routeadv = routeadv;
+ member->v_routeadv = routeadv;
+
+ /* Update peer route announcements. */
+ update_group_adjust_peer_afs(member);
+ if (peer_established(member->connection))
+ bgp_announce_route_all(member);
+ }
+
+ return 0;
+}
+
+int peer_advertise_interval_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_ROUTEADV);
+ PEER_ATTR_INHERIT(peer, peer->group, routeadv);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_ROUTEADV);
+ peer->routeadv = 0;
+ }
+
+ /* Set timer with fallback to default value. */
+ if (peer->routeadv)
+ peer->v_routeadv = peer->routeadv;
+ else
+ peer->v_routeadv = (peer->sort == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Update peer route announcements. */
+ update_group_adjust_peer_afs(peer);
+ if (peer_established(peer->connection))
+ bgp_announce_route_all(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_ROUTEADV))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_ROUTEADV);
+ member->routeadv = 0;
+ member->v_routeadv = (member->sort == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+
+ /* Update peer route announcements. */
+ update_group_adjust_peer_afs(member);
+ if (peer_established(member->connection))
+ bgp_announce_route_all(member);
+ }
+
+ return 0;
+}
+
+/* set the peers RFC 4271 DelayOpen session attribute flag and DelayOpenTimer
+ * interval
+ */
+int peer_timers_delayopen_set(struct peer *peer, uint32_t delayopen)
+{
+ struct peer *member;
+ struct listnode *node;
+
+ /* Set peers session attribute flag and timer interval. */
+ peer_flag_set(peer, PEER_FLAG_TIMER_DELAYOPEN);
+ peer->delayopen = delayopen;
+ peer->v_delayopen = delayopen;
+
+ /* Skip group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /* Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override,
+ PEER_FLAG_TIMER_DELAYOPEN))
+ continue;
+
+ /* Set session attribute flag and timer intervals on peer-group
+ * member.
+ */
+ SET_FLAG(member->flags, PEER_FLAG_TIMER_DELAYOPEN);
+ member->delayopen = delayopen;
+ member->v_delayopen = delayopen;
+ }
+
+ return 0;
+}
+
+/* unset the peers RFC 4271 DelayOpen session attribute flag and reset the
+ * DelayOpenTimer interval to the default value.
+ */
+int peer_timers_delayopen_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_TIMER_DELAYOPEN);
+ PEER_ATTR_INHERIT(peer, peer->group, delayopen);
+ } else {
+ /* Otherwise remove session attribute flag and set timer
+ * interval to default value.
+ */
+ peer_flag_unset(peer, PEER_FLAG_TIMER_DELAYOPEN);
+ peer->delayopen = peer->bgp->default_delayopen;
+ }
+
+ /* Set timer value to zero */
+ peer->v_delayopen = 0;
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /* Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override,
+ PEER_FLAG_TIMER_DELAYOPEN))
+ continue;
+
+ /* Remove session attribute flag, reset the timer interval to
+ * the default value and set the timer value to zero.
+ */
+ UNSET_FLAG(member->flags, PEER_FLAG_TIMER_DELAYOPEN);
+ member->delayopen = peer->bgp->default_delayopen;
+ member->v_delayopen = 0;
+ }
+
+ return 0;
+}
+
+/* neighbor interface */
+void peer_interface_set(struct peer *peer, const char *str)
+{
+ XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname);
+ peer->ifname = XSTRDUP(MTYPE_BGP_PEER_IFNAME, str);
+}
+
+void peer_interface_unset(struct peer *peer)
+{
+ XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname);
+}
+
+/* Allow-as in. */
+int peer_allowas_in_set(struct peer *peer, afi_t afi, safi_t safi,
+ int allow_num, int origin)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (!origin && (allow_num < 1 || allow_num > 10))
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set flag and configuration on peer. */
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_ALLOWAS_IN);
+ if (origin) {
+ if (peer->allowas_in[afi][safi] != 0
+ || !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+ peer_af_flag_set(peer, afi, safi,
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ peer->allowas_in[afi][safi] = 0;
+ peer_on_policy_change(peer, afi, safi, 0);
+ }
+ } else {
+ if (peer->allowas_in[afi][safi] != allow_num
+ || CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+
+ peer_af_flag_unset(peer, afi, safi,
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ peer->allowas_in[afi][safi] = allow_num;
+ peer_on_policy_change(peer, afi, safi, 0);
+ }
+ }
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Set flag and configuration on all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_ALLOWAS_IN))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN);
+ if (origin) {
+ if (member->allowas_in[afi][safi] != 0
+ || !CHECK_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+ SET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ member->allowas_in[afi][safi] = 0;
+ peer_on_policy_change(peer, afi, safi, 0);
+ }
+ } else {
+ if (member->allowas_in[afi][safi] != allow_num
+ || CHECK_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ member->allowas_in[afi][safi] = allow_num;
+ peer_on_policy_change(peer, afi, safi, 0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int peer_allowas_in_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Skip peer if flag is already disabled. */
+ if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN))
+ return 0;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_ALLOWAS_IN);
+ peer_af_flag_inherit(peer, afi, safi,
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ PEER_ATTR_INHERIT(peer, peer->group, allowas_in[afi][safi]);
+ peer_on_policy_change(peer, afi, safi, 0);
+
+ return 0;
+ }
+
+ /* Remove flag and configuration from peer. */
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ALLOWAS_IN);
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ peer->allowas_in[afi][safi] = 0;
+ peer_on_policy_change(peer, afi, safi, 0);
+
+ /* Skip peer-group mechanics if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Remove flags and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_ALLOWAS_IN))
+ continue;
+
+ /* Remove flags and configuration on peer-group member. */
+ UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN);
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ member->allowas_in[afi][safi] = 0;
+ peer_on_policy_change(member, afi, safi, 0);
+ }
+
+ return 0;
+}
+
+int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend,
+ bool replace_as, const char *as_str)
+{
+ bool old_no_prepend, old_replace_as;
+ struct bgp *bgp = peer->bgp;
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (bgp->as == as)
+ return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS;
+
+ /* Save previous flag states. */
+ old_no_prepend =
+ !!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ old_replace_as =
+ !!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_LOCAL_AS);
+ peer_flag_modify(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND, no_prepend);
+ peer_flag_modify(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS, replace_as);
+
+ if (peer->change_local_as == as && old_no_prepend == no_prepend
+ && old_replace_as == replace_as)
+ return 0;
+ peer->change_local_as = as;
+ if (as_str) {
+ if (peer->change_local_as_pretty)
+ XFREE(MTYPE_BGP, peer->change_local_as_pretty);
+ peer->change_local_as_pretty = XSTRDUP(MTYPE_BGP, as_str);
+ }
+
+ (void)peer_sort(peer);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_LOCAL_AS))
+ continue;
+
+ /* Skip peers with the same configuration. */
+ old_no_prepend = CHECK_FLAG(member->flags,
+ PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ old_replace_as = CHECK_FLAG(member->flags,
+ PEER_FLAG_LOCAL_AS_REPLACE_AS);
+ if (member->change_local_as == as
+ && CHECK_FLAG(member->flags, PEER_FLAG_LOCAL_AS)
+ && old_no_prepend == no_prepend
+ && old_replace_as == replace_as)
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_LOCAL_AS);
+ COND_FLAG(member->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND,
+ no_prepend);
+ COND_FLAG(member->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS,
+ replace_as);
+ member->change_local_as = as;
+ if (as_str)
+ member->change_local_as_pretty =
+ XSTRDUP(MTYPE_BGP, as_str);
+ }
+
+ return 0;
+}
+
+int peer_local_as_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS))
+ return 0;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_LOCAL_AS);
+ peer_flag_inherit(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ peer_flag_inherit(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+ PEER_ATTR_INHERIT(peer, peer->group, change_local_as);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_LOCAL_AS);
+ peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+ peer->change_local_as = 0;
+ XFREE(MTYPE_BGP, peer->change_local_as_pretty);
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or stop peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status)) {
+ peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ BGP_EVENT_ADD(peer->connection, BGP_Stop);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_LOCAL_AS))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS);
+ UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+ member->change_local_as = 0;
+ XFREE(MTYPE_BGP, member->change_local_as_pretty);
+
+ /* Send notification or stop peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->connection->status)) {
+ member->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
+ bgp_notify_send(member->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(member);
+ }
+
+ return 0;
+}
+
+/* Set password for authenticating with the peer. */
+int peer_password_set(struct peer *peer, const char *password)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+ int len = password ? strlen(password) : 0;
+ int ret = BGP_SUCCESS;
+
+ if ((len < PEER_PASSWORD_MINLEN) || (len > PEER_PASSWORD_MAXLEN))
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_PASSWORD);
+ if (peer->password && strcmp(peer->password, password) == 0)
+ return 0;
+ XFREE(MTYPE_PEER_PASSWORD, peer->password);
+ peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status))
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+
+ /*
+ * Attempt to install password on socket and skip peer-group
+ * mechanics.
+ */
+ if (BGP_CONNECTION_SU_UNSPEC(peer->connection))
+ return BGP_SUCCESS;
+ return (bgp_md5_set(peer->connection) >= 0)
+ ? BGP_SUCCESS
+ : BGP_ERR_TCPSIG_FAILED;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_PASSWORD))
+ continue;
+
+ /* Skip peers with the same password. */
+ if (member->password && strcmp(member->password, password) == 0)
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_PASSWORD);
+ if (member->password)
+ XFREE(MTYPE_PEER_PASSWORD, member->password);
+ member->password = XSTRDUP(MTYPE_PEER_PASSWORD, password);
+
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->connection->status))
+ bgp_notify_send(member->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(member);
+
+ /* Attempt to install password on socket. */
+ if (!BGP_CONNECTION_SU_UNSPEC(member->connection) &&
+ bgp_md5_set(member->connection) < 0)
+ ret = BGP_ERR_TCPSIG_FAILED;
+ }
+
+ /* Set flag and configuration on all peer-group listen ranges */
+ struct listnode *ln;
+ struct prefix *lr;
+
+ for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP], ln, lr))
+ bgp_md5_set_prefix(peer->bgp, lr, password);
+ for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP6], ln, lr))
+ bgp_md5_set_prefix(peer->bgp, lr, password);
+
+ return ret;
+}
+
+int peer_password_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD))
+ return 0;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_PASSWORD);
+ PEER_STR_ATTR_INHERIT(peer, peer->group, password,
+ MTYPE_PEER_PASSWORD);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_PASSWORD);
+ XFREE(MTYPE_PEER_PASSWORD, peer->password);
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status))
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+
+ /* Attempt to uninstall password on socket. */
+ if (!BGP_CONNECTION_SU_UNSPEC(peer->connection))
+ bgp_md5_unset(peer->connection);
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_PASSWORD))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_PASSWORD);
+ XFREE(MTYPE_PEER_PASSWORD, member->password);
+
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->connection->status))
+ bgp_notify_send(member->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(member);
+
+ /* Attempt to uninstall password on socket. */
+ if (!BGP_CONNECTION_SU_UNSPEC(member->connection))
+ bgp_md5_unset(member->connection);
+ }
+
+ /* Set flag and configuration on all peer-group listen ranges */
+ struct listnode *ln;
+ struct prefix *lr;
+
+ for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP], ln, lr))
+ bgp_md5_unset_prefix(peer->bgp, lr);
+ for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP6], ln, lr))
+ bgp_md5_unset_prefix(peer->bgp, lr);
+
+ return 0;
+}
+
+
+/* Set distribute list to the peer. */
+int peer_distribute_set(struct peer *peer, afi_t afi, safi_t safi, int direct,
+ const char *name)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set configuration on peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->plist[direct].name)
+ return BGP_ERR_PEER_FILTER_CONFLICT;
+ if (filter->dlist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->dlist[direct].name);
+ filter->dlist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->dlist[direct].alist = access_list_lookup(afi, name);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_DISTRIBUTE_LIST);
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, un less they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_DISTRIBUTE_LIST))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->dlist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->dlist[direct].name);
+ filter->dlist[direct].name =
+ XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->dlist[direct].alist = access_list_lookup(afi, name);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+int peer_distribute_unset(struct peer *peer, afi_t afi, safi_t safi, int direct)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_DISTRIBUTE_LIST);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].dlist[direct].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].dlist[direct].alist);
+ } else {
+ /* Otherwise remove configuration from peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->dlist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->dlist[direct].name);
+ filter->dlist[direct].name = NULL;
+ filter->dlist[direct].alist = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_DISTRIBUTE_LIST))
+ continue;
+
+ /* Remove configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->dlist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->dlist[direct].name);
+ filter->dlist[direct].name = NULL;
+ filter->dlist[direct].alist = NULL;
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+/* Update distribute list. */
+static void peer_distribute_update(struct access_list *access)
+{
+ afi_t afi;
+ safi_t safi;
+ int direct;
+ struct listnode *mnode, *mnnode;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer_group *group;
+ struct bgp_filter *filter;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+ if (access->name)
+ update_group_policy_update(bgp,
+ BGP_POLICY_DISTRIBUTE_LIST,
+ access->name, true, 0);
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &peer->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->dlist[direct].name)
+ filter->dlist[direct]
+ .alist = access_list_lookup(
+ afi,
+ filter->dlist[direct]
+ .name);
+ else
+ filter->dlist[direct].alist =
+ NULL;
+ }
+ }
+ }
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &group->conf->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->dlist[direct].name)
+ filter->dlist[direct]
+ .alist = access_list_lookup(
+ afi,
+ filter->dlist[direct]
+ .name);
+ else
+ filter->dlist[direct].alist =
+ NULL;
+ }
+ }
+ }
+#ifdef ENABLE_BGP_VNC
+ vnc_prefix_list_update(bgp);
+#endif
+ }
+}
+
+/* Set prefix list to the peer. */
+int peer_prefix_list_set(struct peer *peer, afi_t afi, safi_t safi, int direct,
+ const char *name)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set configuration on peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->dlist[direct].name)
+ return BGP_ERR_PEER_FILTER_CONFLICT;
+ if (filter->plist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->plist[direct].name);
+ filter->plist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->plist[direct].plist = prefix_list_lookup(afi, name);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_PREFIX_LIST);
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_PREFIX_LIST))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->plist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->plist[direct].name);
+ filter->plist[direct].name =
+ XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->plist[direct].plist = prefix_list_lookup(afi, name);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+int peer_prefix_list_unset(struct peer *peer, afi_t afi, safi_t safi,
+ int direct)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_PREFIX_LIST);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].plist[direct].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].plist[direct].plist);
+ } else {
+ /* Otherwise remove configuration from peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->plist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->plist[direct].name);
+ filter->plist[direct].name = NULL;
+ filter->plist[direct].plist = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_PREFIX_LIST))
+ continue;
+
+ /* Remove configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->plist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->plist[direct].name);
+ filter->plist[direct].name = NULL;
+ filter->plist[direct].plist = NULL;
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+/* Update prefix-list list. */
+static void peer_prefix_list_update(struct prefix_list *plist)
+{
+ struct listnode *mnode, *mnnode;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer_group *group;
+ struct bgp_filter *filter;
+ afi_t afi;
+ safi_t safi;
+ int direct;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+
+ /*
+ * Update the prefix-list on update groups.
+ */
+ update_group_policy_update(
+ bgp, BGP_POLICY_PREFIX_LIST,
+ plist ? prefix_list_name(plist) : NULL, true, 0);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &peer->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->plist[direct].name)
+ filter->plist[direct]
+ .plist = prefix_list_lookup(
+ afi,
+ filter->plist[direct]
+ .name);
+ else
+ filter->plist[direct].plist =
+ NULL;
+ }
+
+ /* If we touch prefix-list, we need to process
+ * new updates. This is important for ORF to
+ * work correctly.
+ */
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_ADV) &&
+ CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV))
+ peer_clear_soft(
+ peer, afi, safi,
+ BGP_CLEAR_SOFT_IN_ORF_PREFIX);
+ }
+ }
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &group->conf->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->plist[direct].name)
+ filter->plist[direct]
+ .plist = prefix_list_lookup(
+ afi,
+ filter->plist[direct]
+ .name);
+ else
+ filter->plist[direct].plist =
+ NULL;
+ }
+ }
+ }
+ }
+}
+
+int peer_aslist_set(struct peer *peer, afi_t afi, safi_t safi, int direct,
+ const char *name)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set configuration on peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->aslist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->aslist[direct].name);
+ filter->aslist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->aslist[direct].aslist = as_list_lookup(name);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_FILTER_LIST);
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_FILTER_LIST))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->aslist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->aslist[direct].name);
+ filter->aslist[direct].name =
+ XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->aslist[direct].aslist = as_list_lookup(name);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+int peer_aslist_unset(struct peer *peer, afi_t afi, safi_t safi, int direct)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_FILTER_LIST);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].aslist[direct].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].aslist[direct].aslist);
+ } else {
+ /* Otherwise remove configuration from peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->aslist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->aslist[direct].name);
+ filter->aslist[direct].name = NULL;
+ filter->aslist[direct].aslist = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_FILTER_LIST))
+ continue;
+
+ /* Remove configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->aslist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->aslist[direct].name);
+ filter->aslist[direct].name = NULL;
+ filter->aslist[direct].aslist = NULL;
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+static void peer_aslist_update(const char *aslist_name)
+{
+ afi_t afi;
+ safi_t safi;
+ int direct;
+ struct listnode *mnode, *mnnode;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer_group *group;
+ struct bgp_filter *filter;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+ update_group_policy_update(bgp, BGP_POLICY_FILTER_LIST,
+ aslist_name, true, 0);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &peer->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->aslist[direct].name)
+ filter->aslist[direct]
+ .aslist = as_list_lookup(
+ filter->aslist[direct]
+ .name);
+ else
+ filter->aslist[direct].aslist =
+ NULL;
+ }
+ }
+ }
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &group->conf->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->aslist[direct].name)
+ filter->aslist[direct]
+ .aslist = as_list_lookup(
+ filter->aslist[direct]
+ .name);
+ else
+ filter->aslist[direct].aslist =
+ NULL;
+ }
+ }
+ }
+ }
+}
+
+static void peer_aslist_add(char *aslist_name)
+{
+ peer_aslist_update(aslist_name);
+ route_map_notify_dependencies(aslist_name, RMAP_EVENT_ASLIST_ADDED);
+}
+
+static void peer_aslist_del(const char *aslist_name)
+{
+ peer_aslist_update(aslist_name);
+ route_map_notify_dependencies(aslist_name, RMAP_EVENT_ASLIST_DELETED);
+}
+
+
+int peer_route_map_set(struct peer *peer, afi_t afi, safi_t safi, int direct,
+ const char *name, struct route_map *route_map)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+ struct route_map *map = NULL;
+
+ if (direct != RMAP_IN && direct != RMAP_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set configuration on peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->map[direct].name) {
+ /* If the neighbor is configured with the same route-map
+ * again then, ignore the duplicate configuration.
+ */
+ if (strcmp(filter->map[direct].name, name) == 0)
+ return 0;
+
+ map = route_map_lookup_by_name(filter->map[direct].name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name);
+ }
+ route_map_counter_decrement(map);
+ filter->map[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->map[direct].map = route_map;
+ route_map_counter_increment(route_map);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_ROUTE_MAP);
+ peer_on_policy_change(peer, afi, safi,
+ (direct == RMAP_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ map = NULL;
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_ROUTE_MAP))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->map[direct].name) {
+ map = route_map_lookup_by_name(filter->map[direct].name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name);
+ }
+ route_map_counter_decrement(map);
+ filter->map[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->map[direct].map = route_map;
+ route_map_counter_increment(route_map);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == RMAP_OUT) ? 1 : 0);
+ }
+ return 0;
+}
+
+/* Unset route-map from the peer. */
+int peer_route_map_unset(struct peer *peer, afi_t afi, safi_t safi, int direct)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != RMAP_IN && direct != RMAP_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][direct], PEER_FT_ROUTE_MAP);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].map[direct].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].map[direct].map);
+ } else {
+ struct route_map *map = NULL;
+
+ /* Otherwise remove configuration from peer. */
+ filter = &peer->filter[afi][safi];
+
+ if (filter->map[direct].name) {
+ map = route_map_lookup_by_name(filter->map[direct].name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name);
+ }
+ route_map_counter_decrement(map);
+ filter->map[direct].name = NULL;
+ filter->map[direct].map = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi,
+ (direct == RMAP_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ struct route_map *map;
+
+ map = NULL;
+
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_ROUTE_MAP))
+ continue;
+
+ /* Remove configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->map[direct].name) {
+ map = route_map_lookup_by_name(filter->map[direct].name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name);
+ }
+ route_map_counter_decrement(map);
+ filter->map[direct].name = NULL;
+ filter->map[direct].map = NULL;
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == RMAP_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+/* Set unsuppress-map to the peer. */
+int peer_unsuppress_map_set(struct peer *peer, afi_t afi, safi_t safi,
+ const char *name, struct route_map *route_map)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ /* Set configuration on peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->usmap.name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
+ route_map_counter_decrement(filter->usmap.map);
+ filter->usmap.name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->usmap.map = route_map;
+ route_map_counter_increment(route_map);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][0],
+ PEER_FT_UNSUPPRESS_MAP);
+ peer_on_policy_change(peer, afi, safi, 1);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ struct route_map *map;
+
+ map = NULL;
+
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][0],
+ PEER_FT_UNSUPPRESS_MAP))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->usmap.name) {
+ map = route_map_lookup_by_name(filter->usmap.name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
+ }
+ route_map_counter_decrement(map);
+ filter->usmap.name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->usmap.map = route_map;
+ route_map_counter_increment(route_map);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi, 1);
+ }
+
+ return 0;
+}
+
+/* Unset route-map from the peer. */
+int peer_unsuppress_map_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][0], PEER_FT_UNSUPPRESS_MAP);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].usmap.name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].usmap.map);
+ } else {
+ struct route_map *map = NULL;
+
+ /* Otherwise remove configuration from peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->usmap.name) {
+ map = route_map_lookup_by_name(filter->usmap.name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
+ }
+ route_map_counter_decrement(map);
+ filter->usmap.name = NULL;
+ filter->usmap.map = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi, 1);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ struct route_map *map;
+
+ map = NULL;
+
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][0],
+ PEER_FT_UNSUPPRESS_MAP))
+ continue;
+
+ /* Remove configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->usmap.name) {
+ map = route_map_lookup_by_name(filter->usmap.name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
+ }
+ route_map_counter_decrement(map);
+ filter->usmap.name = NULL;
+ filter->usmap.map = NULL;
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi, 1);
+ }
+
+ return 0;
+}
+
+static bool peer_maximum_prefix_clear_overflow(struct peer *peer)
+{
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+ return false;
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
+ if (peer->connection->t_pmax_restart) {
+ EVENT_OFF(peer->connection->t_pmax_restart);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP Maximum-prefix restart timer cancelled",
+ peer);
+ }
+ BGP_EVENT_ADD(peer->connection, BGP_Start);
+ return true;
+}
+
+int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi,
+ uint32_t max, uint8_t threshold, int warning,
+ uint16_t restart, bool force)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set flags and configuration on peer. */
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
+
+ if (force)
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
+ else
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
+
+ if (warning)
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING);
+ else
+ peer_af_flag_unset(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_WARNING);
+
+ peer->pmax[afi][safi] = max;
+ peer->pmax_threshold[afi][safi] = threshold;
+ peer->pmax_restart[afi][safi] = restart;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Re-check if peer violates maximum-prefix. */
+ if ((peer_established(peer->connection)) &&
+ (peer->afc[afi][safi]))
+ bgp_maximum_prefix_overflow(peer, afi, safi, 1);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set flags and configuration on all peer-group members, unless they
+ * are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ member->pmax[afi][safi] = max;
+ member->pmax_threshold[afi][safi] = threshold;
+ member->pmax_restart[afi][safi] = restart;
+
+ if (force)
+ SET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ else
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+
+ if (warning)
+ SET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ else
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+
+ /* Re-check if peer violates maximum-prefix. */
+ if ((peer_established(member->connection)) &&
+ (member->afc[afi][safi]))
+ bgp_maximum_prefix_overflow(member, afi, safi, 1);
+ }
+
+ return 0;
+}
+
+int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
+ peer_af_flag_inherit(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ peer_af_flag_inherit(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ PEER_ATTR_INHERIT(peer, peer->group, pmax[afi][safi]);
+ PEER_ATTR_INHERIT(peer, peer->group, pmax_threshold[afi][safi]);
+ PEER_ATTR_INHERIT(peer, peer->group, pmax_restart[afi][safi]);
+
+ return 0;
+ }
+
+ /* Remove flags and configuration from peer. */
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING);
+ peer->pmax[afi][safi] = 0;
+ peer->pmax_threshold[afi][safi] = 0;
+ peer->pmax_restart[afi][safi] = 0;
+
+ /*
+ * Remove flags and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ struct peer *member;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX))
+ continue;
+
+ /* Remove flag and configuration on peer-group member.
+ */
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX);
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ member->pmax[afi][safi] = 0;
+ member->pmax_threshold[afi][safi] = 0;
+ member->pmax_restart[afi][safi] = 0;
+
+ peer_maximum_prefix_clear_overflow(member);
+ }
+ } else {
+ peer_maximum_prefix_clear_overflow(peer);
+ }
+
+ return 0;
+}
+
+void peer_maximum_prefix_out_refresh_routes(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ update_group_adjust_peer(peer_af_find(peer, afi, safi));
+
+ if (peer_established(peer->connection))
+ bgp_announce_route(peer, afi, safi, false);
+}
+
+int peer_maximum_prefix_out_set(struct peer *peer, afi_t afi, safi_t safi,
+ uint32_t max)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set flag on peer and peer-group member if any */
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT);
+ /* Set configuration on peer. */
+ peer->pmax_out[afi][safi] = max;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Skip peer-group mechanics for regular peers. */
+ peer_maximum_prefix_out_refresh_routes(peer, afi, safi);
+ return 0;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they
+ * are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ member->pmax_out[afi][safi] = max;
+
+ peer_maximum_prefix_out_refresh_routes(member, afi, safi);
+ }
+ return 0;
+}
+
+int peer_maximum_prefix_out_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *member;
+ struct listnode *node;
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT);
+ PEER_ATTR_INHERIT(peer, peer->group, pmax_out[afi][safi]);
+
+ peer_maximum_prefix_out_refresh_routes(peer, afi, safi);
+ return 0;
+ }
+
+ /* Remove flag and configuration from peer. */
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT);
+ peer->pmax_out[afi][safi] = 0;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Skip peer-group mechanics for regular peers. */
+ peer_maximum_prefix_out_refresh_routes(peer, afi, safi);
+ return 0;
+ }
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT))
+ continue;
+
+ /* Remove flag and configuration on peer-group member.
+ */
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT);
+ member->pmax_out[afi][safi] = 0;
+
+ peer_maximum_prefix_out_refresh_routes(member, afi, safi);
+ }
+ return 0;
+}
+
+int is_ebgp_multihop_configured(struct peer *peer)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ struct peer *peer1;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ group = peer->group;
+ if ((peer_sort(peer) != BGP_PEER_IBGP)
+ && (group->conf->ttl != BGP_DEFAULT_TTL))
+ return 1;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer1)) {
+ if ((peer_sort(peer1) != BGP_PEER_IBGP)
+ && (peer1->ttl != BGP_DEFAULT_TTL))
+ return 1;
+ }
+ } else {
+ if ((peer_sort(peer) != BGP_PEER_IBGP)
+ && (peer->ttl != BGP_DEFAULT_TTL))
+ return 1;
+ }
+ 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 peer *gpeer;
+ struct listnode *node, *nnode;
+ int ret;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s: set gtsm_hops to %d for %s", __func__,
+ gtsm_hops, peer->host);
+
+ /* 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 ((peer->gtsm_hops == BGP_GTSM_HOPS_DISABLED)
+ && (peer->sort != BGP_PEER_IBGP)) {
+ if (is_ebgp_multihop_configured(peer))
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ peer->gtsm_hops = gtsm_hops;
+
+ /* Calling ebgp multihop also resets the session.
+ * On restart, NHT will get setup correctly as will the
+ * min & max ttls on the socket. The return value is
+ * irrelevant.
+ */
+ ret = peer_ebgp_multihop_set(peer, MAXTTL);
+
+ if (ret != 0)
+ return ret;
+ } else {
+ group = peer->group;
+ group->conf->gtsm_hops = gtsm_hops;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode,
+ gpeer)) {
+ gpeer->gtsm_hops = group->conf->gtsm_hops;
+
+ /* Calling ebgp multihop also resets the
+ * session.
+ * On restart, NHT will get setup correctly as
+ * will the
+ * min & max ttls on the socket. The return
+ * value is
+ * irrelevant.
+ */
+ peer_ebgp_multihop_set(gpeer, MAXTTL);
+ }
+ }
+ } else {
+ /* Post the first gtsm setup or if its ibgp, maxttl setting
+ * isn't
+ * necessary, just set the minttl.
+ */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ peer->gtsm_hops = gtsm_hops;
+
+ if (peer->connection->fd >= 0)
+ sockopt_minttl(peer->connection->su.sa.sa_family,
+ peer->connection->fd,
+ MAXTTL + 1 - gtsm_hops);
+ if ((peer->connection->status < Established) &&
+ peer->doppelganger &&
+ (peer->doppelganger->connection->fd >= 0))
+ sockopt_minttl(peer->connection->su.sa.sa_family,
+ peer->doppelganger->connection->fd,
+ MAXTTL + 1 - gtsm_hops);
+ } else {
+ group = peer->group;
+ group->conf->gtsm_hops = gtsm_hops;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode,
+ gpeer)) {
+ struct peer_connection *connection =
+ gpeer->connection;
+ gpeer->gtsm_hops = group->conf->gtsm_hops;
+
+ /* Change setting of existing peer
+ * established then change value (may break
+ * connectivity)
+ * not established yet (teardown session and
+ * restart)
+ * no session then do nothing (will get
+ * handled by next connection)
+ */
+ if (connection->fd >= 0 &&
+ gpeer->gtsm_hops != BGP_GTSM_HOPS_DISABLED)
+ sockopt_minttl(connection->su.sa.sa_family,
+ connection->fd,
+ MAXTTL + 1 -
+ gpeer->gtsm_hops);
+ if ((connection->status < Established) &&
+ gpeer->doppelganger &&
+ (gpeer->doppelganger->connection->fd >= 0))
+ sockopt_minttl(connection->su.sa.sa_family,
+ gpeer->doppelganger
+ ->connection->fd,
+ MAXTTL + 1 - gtsm_hops);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int peer_ttl_security_hops_unset(struct peer *peer)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ int ret = 0;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s: set gtsm_hops to zero for %s", __func__,
+ peer->host);
+
+ /* 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 = BGP_GTSM_HOPS_DISABLED;
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Invoking ebgp_multihop_set will set the TTL back to the
+ * original
+ * value as well as restting the NHT and such. The session is
+ * reset.
+ */
+ if (peer->sort == BGP_PEER_EBGP)
+ ret = peer_ebgp_multihop_unset(peer);
+ else {
+ if (peer->connection->fd >= 0)
+ sockopt_minttl(peer->connection->su.sa.sa_family,
+ peer->connection->fd, 0);
+
+ if ((peer->connection->status < Established) &&
+ peer->doppelganger &&
+ (peer->doppelganger->connection->fd >= 0))
+ sockopt_minttl(peer->connection->su.sa.sa_family,
+ peer->doppelganger->connection->fd,
+ 0);
+ }
+ } else {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ peer->gtsm_hops = BGP_GTSM_HOPS_DISABLED;
+ if (peer->sort == BGP_PEER_EBGP)
+ ret = peer_ebgp_multihop_unset(peer);
+ else {
+ if (peer->connection->fd >= 0)
+ sockopt_minttl(peer->connection->su.sa
+ .sa_family,
+ peer->connection->fd, 0);
+
+ if ((peer->connection->status < Established) &&
+ peer->doppelganger &&
+ (peer->doppelganger->connection->fd >= 0))
+ sockopt_minttl(peer->connection->su.sa
+ .sa_family,
+ peer->doppelganger
+ ->connection->fd,
+ 0);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void peer_reset_message_stats(struct peer *peer)
+{
+ if (peer) {
+ atomic_store_explicit(&peer->open_in, 0, memory_order_relaxed);
+ atomic_store_explicit(&peer->open_out, 0, memory_order_relaxed);
+ atomic_store_explicit(&peer->update_in, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->update_out, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->keepalive_in, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->keepalive_out, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->notify_in, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->notify_out, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->refresh_in, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->refresh_out, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->dynamic_cap_in, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->dynamic_cap_out, 0,
+ memory_order_relaxed);
+ }
+}
+
+/*
+ * If peer clear is invoked in a loop for all peers on the BGP instance,
+ * it may end up freeing the doppelganger, and if this was the next node
+ * to the current node, we would end up accessing the freed next node.
+ * Pass along additional parameter which can be updated if next node
+ * is freed; only required when walking the peer list on BGP instance.
+ */
+int peer_clear(struct peer *peer, struct listnode **nnode)
+{
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)
+ || !CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN)) {
+ if (peer_maximum_prefix_clear_overflow(peer))
+ return 0;
+
+ peer->v_start = BGP_INIT_START_TIMER;
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->connection->status))
+ bgp_notify_send(peer->connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_RESET);
+ else
+ bgp_session_reset_safe(peer, nnode);
+ }
+ return 0;
+}
+
+int peer_clear_soft(struct peer *peer, afi_t afi, safi_t safi,
+ enum bgp_clear_type stype)
+{
+ struct peer_af *paf;
+
+ if (!peer_established(peer->connection))
+ return 0;
+
+ if (!peer->afc[afi][safi])
+ return BGP_ERR_AF_UNCONFIGURED;
+
+ peer->rtt = sockopt_tcp_rtt(peer->connection->fd);
+
+ if (stype == BGP_CLEAR_SOFT_OUT || stype == BGP_CLEAR_SOFT_BOTH) {
+ /* Clear the "neighbor x.x.x.x default-originate" flag */
+ paf = peer_af_find(peer, afi, safi);
+ if (paf && paf->subgroup
+ && CHECK_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE))
+ UNSET_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+
+ bgp_announce_route(peer, afi, safi, false);
+ }
+
+ if (stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) {
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_ADV) &&
+ CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV)) {
+ struct bgp_filter *filter = &peer->filter[afi][safi];
+ uint8_t prefix_type;
+
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV))
+ prefix_type = ORF_TYPE_PREFIX;
+
+ if (filter->plist[FILTER_IN].plist) {
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_PREFIX_SEND))
+ bgp_route_refresh_send(
+ peer, afi, safi, prefix_type,
+ REFRESH_DEFER, 1,
+ BGP_ROUTE_REFRESH_NORMAL);
+ bgp_route_refresh_send(
+ peer, afi, safi, prefix_type,
+ REFRESH_IMMEDIATE, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ } else {
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_PREFIX_SEND))
+ bgp_route_refresh_send(
+ peer, afi, safi, prefix_type,
+ REFRESH_IMMEDIATE, 1,
+ BGP_ROUTE_REFRESH_NORMAL);
+ else
+ bgp_route_refresh_send(
+ peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ }
+ return 0;
+ }
+ }
+
+ if (stype == BGP_CLEAR_SOFT_IN || stype == BGP_CLEAR_SOFT_BOTH
+ || stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) {
+ /* If neighbor has soft reconfiguration inbound flag.
+ Use Adj-RIB-In database. */
+ if (!bgp_soft_reconfig_in(peer, afi, safi)) {
+ /* If neighbor has route refresh capability, send route
+ refresh
+ message to the peer. */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_RCV))
+ bgp_route_refresh_send(
+ peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ else
+ return BGP_ERR_SOFT_RECONFIG_UNCONFIGURED;
+ }
+ }
+
+ if (stype == BGP_CLEAR_MESSAGE_STATS)
+ peer_reset_message_stats(peer);
+
+ return 0;
+}
+
+/* Display peer uptime.*/
+char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json,
+ json_object *json)
+{
+ time_t uptime1, epoch_tbuf;
+ struct tm tm;
+
+ /* If there is no connection has been done before print `never'. */
+ if (uptime2 == 0) {
+ if (use_json) {
+ json_object_string_add(json, "peerUptime", "never");
+ json_object_int_add(json, "peerUptimeMsec", 0);
+ } else
+ snprintf(buf, len, "never");
+ return buf;
+ }
+
+ /* Get current time. */
+ uptime1 = monotime(NULL);
+ uptime1 -= uptime2;
+ gmtime_r(&uptime1, &tm);
+
+ if (uptime1 < ONE_DAY_SECOND)
+ snprintf(buf, len, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min,
+ tm.tm_sec);
+ else if (uptime1 < ONE_WEEK_SECOND)
+ snprintf(buf, len, "%dd%02dh%02dm", tm.tm_yday, tm.tm_hour,
+ tm.tm_min);
+ else if (uptime1 < ONE_YEAR_SECOND)
+ snprintf(buf, len, "%02dw%dd%02dh", tm.tm_yday / 7,
+ tm.tm_yday - ((tm.tm_yday / 7) * 7), tm.tm_hour);
+ else
+ snprintf(buf, len, "%02dy%02dw%dd", tm.tm_year - 70,
+ tm.tm_yday / 7,
+ tm.tm_yday - ((tm.tm_yday / 7) * 7));
+
+ if (use_json) {
+ epoch_tbuf = time(NULL) - uptime1;
+ json_object_string_add(json, "peerUptime", buf);
+ json_object_int_add(json, "peerUptimeMsec", uptime1 * 1000);
+ json_object_int_add(json, "peerUptimeEstablishedEpoch",
+ epoch_tbuf);
+ }
+
+ return buf;
+}
+
+void bgp_master_init(struct event_loop *master, const int buffer_size,
+ struct list *addresses)
+{
+ qobj_init();
+
+ memset(&bgp_master, 0, sizeof(bgp_master));
+
+ bm = &bgp_master;
+ bm->bgp = list_new();
+ bm->listen_sockets = list_new();
+ bm->port = BGP_PORT_DEFAULT;
+ bm->addresses = addresses;
+ bm->master = master;
+ bm->start_time = monotime(NULL);
+ bm->t_rmap_update = NULL;
+ bm->rmap_update_timer = RMAP_DEFAULT_UPDATE_TIMER;
+ bm->v_update_delay = BGP_UPDATE_DELAY_DEF;
+ bm->v_establish_wait = BGP_UPDATE_DELAY_DEF;
+ bm->terminating = false;
+ bm->socket_buffer = buffer_size;
+ bm->wait_for_fib = false;
+ bm->tcp_dscp = IPTOS_PREC_INTERNETCONTROL;
+ bm->inq_limit = BM_DEFAULT_Q_LIMIT;
+ bm->outq_limit = BM_DEFAULT_Q_LIMIT;
+ bm->t_bgp_sync_label_manager = NULL;
+ bm->t_bgp_start_label_manager = NULL;
+
+ bgp_mac_init();
+ /* init the rd id space.
+ assign 0th index in the bitfield,
+ so that we start with id 1
+ */
+ bf_init(bm->rd_idspace, UINT16_MAX);
+ bf_assign_zero_index(bm->rd_idspace);
+
+ /* mpls label dynamic allocation pool */
+ bgp_lp_init(bm->master, &bm->labelpool);
+
+ bgp_l3nhg_init();
+ bgp_evpn_mh_init();
+ QOBJ_REG(bm, bgp_master);
+}
+
+/*
+ * Free up connected routes and interfaces for a BGP instance. Invoked upon
+ * instance delete (non-default only) or BGP exit.
+ */
+static void bgp_if_finish(struct bgp *bgp)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+
+ vrf = bgp_vrf_lookup_by_instance_type(bgp);
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW || !vrf)
+ return;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ struct listnode *c_node, *c_nnode;
+ struct connected *c;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, c_node, c_nnode, c))
+ bgp_connected_delete(bgp, c);
+ }
+}
+
+static void bgp_viewvrf_autocomplete(vector comps, struct cmd_token *token)
+{
+ struct vrf *vrf = NULL;
+ struct listnode *next;
+ struct bgp *bgp;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, vrf->name));
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) {
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW)
+ continue;
+
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, bgp->name));
+ }
+}
+
+static void bgp_instasn_autocomplete(vector comps, struct cmd_token *token)
+{
+ struct listnode *next, *next2;
+ struct bgp *bgp, *bgp2;
+ char buf[ASN_STRING_MAX_SIZE];
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) {
+ /* deduplicate */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, next2, bgp2)) {
+ if (bgp2->as == bgp->as)
+ break;
+ if (bgp2 == bgp)
+ break;
+ }
+ if (bgp2 != bgp)
+ continue;
+
+ snprintf(buf, sizeof(buf), "%s", bgp->as_pretty);
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, buf));
+ }
+}
+
+static const struct cmd_variable_handler bgp_viewvrf_var_handlers[] = {
+ {.tokenname = "VIEWVRFNAME", .completions = bgp_viewvrf_autocomplete},
+ {.varname = "instasn", .completions = bgp_instasn_autocomplete},
+ {.completions = NULL},
+};
+
+struct frr_pthread *bgp_pth_io;
+struct frr_pthread *bgp_pth_ka;
+
+static void bgp_pthreads_init(void)
+{
+ assert(!bgp_pth_io);
+ assert(!bgp_pth_ka);
+
+ struct frr_pthread_attr io = {
+ .start = frr_pthread_attr_default.start,
+ .stop = frr_pthread_attr_default.stop,
+ };
+ struct frr_pthread_attr ka = {
+ .start = bgp_keepalives_start,
+ .stop = bgp_keepalives_stop,
+ };
+ bgp_pth_io = frr_pthread_new(&io, "BGP I/O thread", "bgpd_io");
+ bgp_pth_ka = frr_pthread_new(&ka, "BGP Keepalives thread", "bgpd_ka");
+}
+
+void bgp_pthreads_run(void)
+{
+ frr_pthread_run(bgp_pth_io, NULL);
+ frr_pthread_run(bgp_pth_ka, NULL);
+
+ /* Wait until threads are ready. */
+ frr_pthread_wait_running(bgp_pth_io);
+ frr_pthread_wait_running(bgp_pth_ka);
+}
+
+void bgp_pthreads_finish(void)
+{
+ frr_pthread_stop_all();
+}
+
+static int peer_unshut_after_cfg(struct bgp *bgp)
+{
+ struct listnode *node;
+ struct peer *peer;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (!peer->shut_during_cfg)
+ continue;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s: released from config-pending hold",
+ peer->host);
+
+ peer->shut_during_cfg = false;
+ if (peer_active(peer) &&
+ peer->connection->status != Established) {
+ if (peer->connection->status != Idle)
+ BGP_EVENT_ADD(peer->connection, BGP_Stop);
+ BGP_EVENT_ADD(peer->connection, BGP_Start);
+ }
+ }
+
+ return 0;
+}
+
+void bgp_init(unsigned short instance)
+{
+ hook_register(bgp_config_end, peer_unshut_after_cfg);
+
+ /* allocates some vital data structures used by peer commands in
+ * vty_init */
+
+ /* pre-init pthreads */
+ bgp_pthreads_init();
+
+ /* Init zebra. */
+ bgp_zebra_init(bm->master, instance);
+
+#ifdef ENABLE_BGP_VNC
+ vnc_zebra_init(bm->master);
+#endif
+
+ /* BGP VTY commands installation. */
+ bgp_vty_init();
+
+ /* BGP inits. */
+ bgp_attr_init();
+ bgp_debug_init();
+ bgp_community_alias_init();
+ bgp_dump_init();
+ bgp_route_init();
+ bgp_route_map_init();
+ bgp_scan_vty_init();
+ bgp_mplsvpn_init();
+#ifdef ENABLE_BGP_VNC
+ rfapi_init();
+#endif
+ bgp_ethernetvpn_init();
+ bgp_flowspec_vty_init();
+
+ /* Access list initialize. */
+ access_list_init();
+ access_list_add_hook(peer_distribute_update);
+ access_list_delete_hook(peer_distribute_update);
+
+ /* Filter list initialize. */
+ bgp_filter_init();
+ as_list_add_hook(peer_aslist_add);
+ as_list_delete_hook(peer_aslist_del);
+
+ /* Prefix list initialize.*/
+ prefix_list_init();
+ prefix_list_add_hook(peer_prefix_list_update);
+ prefix_list_delete_hook(peer_prefix_list_update);
+
+ /* Community list initialize. */
+ bgp_clist = community_list_init();
+
+ /* BFD init */
+ bgp_bfd_init(bm->master);
+
+ bgp_lp_vty_init();
+
+ bgp_label_per_nexthop_init();
+ bgp_mplsvpn_nexthop_init();
+
+ cmd_variable_handler_register(bgp_viewvrf_var_handlers);
+}
+
+void bgp_terminate(void)
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+ struct listnode *mnode, *mnnode;
+
+ QOBJ_UNREG(bm);
+
+ /* Close the listener sockets first as this prevents peers from
+ * attempting
+ * to reconnect on receiving the peer unconfig message. In the presence
+ * of a large number of peers this will ensure that no peer is left with
+ * a dangling connection
+ */
+
+ bgp_close();
+ /* reverse bgp_master_init */
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+ bgp_close_vrf_socket(bgp);
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP configured Graceful-Restart, skipping unconfig notification",
+ peer);
+ continue;
+ }
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->connection->status))
+ bgp_notify_send(peer->connection,
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_PEER_UNCONFIG);
+ }
+ }
+
+ if (bm->listen_sockets)
+ list_delete(&bm->listen_sockets);
+
+ EVENT_OFF(bm->t_rmap_update);
+ EVENT_OFF(bm->t_bgp_sync_label_manager);
+ EVENT_OFF(bm->t_bgp_start_label_manager);
+
+ bgp_mac_finish();
+}
+
+struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,
+ const char *ip_str, bool use_json)
+{
+ int ret;
+ struct peer *peer;
+ union sockunion su;
+ struct peer_group *group;
+
+ /* Get peer sockunion. */
+ ret = str2sockunion(ip_str, &su);
+ if (ret < 0) {
+ peer = peer_lookup_by_conf_if(bgp, ip_str);
+ if (!peer) {
+ peer = peer_lookup_by_hostname(bgp, ip_str);
+
+ if (!peer) {
+ group = peer_group_lookup(bgp, ip_str);
+ if (group)
+ peer = listnode_head(group->peer);
+ }
+
+ if (!peer) {
+ if (use_json) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(
+ json_no,
+ "malformedAddressOrName",
+ ip_str);
+ vty_json(vty, json_no);
+ } else
+ vty_out(vty,
+ "%% Malformed address or name: %s\n",
+ ip_str);
+ return NULL;
+ }
+ }
+ return peer;
+ }
+
+ /* Peer structure lookup. */
+ peer = peer_lookup(bgp, &su);
+ if (!peer) {
+ if (use_json) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "No such neighbor in this view/vrf");
+ vty_json(vty, json_no);
+ } else
+ vty_out(vty, "No such neighbor in this view/vrf\n");
+ return NULL;
+ }
+
+ return peer;
+}
+
+void bgp_gr_apply_running_config(void)
+{
+ struct peer *peer = NULL;
+ struct bgp *bgp = NULL;
+ struct listnode *node, *nnode;
+ bool gr_router_detected = false;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] %s called !", __func__);
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ bgp_peer_gr_flags_update(peer);
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART))
+ gr_router_detected = true;
+ }
+
+ if (gr_router_detected
+ && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) {
+ bgp_zebra_send_capabilities(bgp, true);
+ } else if (!gr_router_detected
+ && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) {
+ bgp_zebra_send_capabilities(bgp, false);
+ }
+
+ gr_router_detected = false;
+ }
+}
+
+printfrr_ext_autoreg_p("BP", printfrr_bp);
+static ssize_t printfrr_bp(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *ptr)
+{
+ const struct peer *peer = ptr;
+
+ if (!peer)
+ return bputs(buf, "(null)");
+
+ return bprintfrr(buf, "%s(%s)", peer->host,
+ peer->hostname ? peer->hostname : "Unknown");
+}
+
+const struct message bgp_martian_type_str[] = {
+ {BGP_MARTIAN_IF_IP, "Self Interface IP"},
+ {BGP_MARTIAN_TUN_IP, "Self Tunnel IP"},
+ {BGP_MARTIAN_IF_MAC, "Self Interface MAC"},
+ {BGP_MARTIAN_RMAC, "Self RMAC"},
+ {BGP_MARTIAN_SOO, "Self Site-of-Origin"},
+ {0}};
+
+const char *bgp_martian_type2str(enum bgp_martian_type mt)
+{
+ return lookup_msg(bgp_martian_type_str, mt, "Unknown Martian Type");
+}