Skip to content
Permalink
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
8031 lines (6774 sloc) 218 KB
/* BGP-4, BGP-4+ daemon program
* Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
*
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* GNU Zebra is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
#include "prefix.h"
#include "thread.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 "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_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"
#if 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_mac.h"
DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
DEFINE_MTYPE_STATIC(BGPD, BGP_EVPN_INFO, "BGP EVPN instance information");
DEFINE_QOBJ_TYPE(bgp_master)
DEFINE_QOBJ_TYPE(bgp)
DEFINE_QOBJ_TYPE(peer)
/* 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;
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;
if (create) {
if (bgp_server_main_created)
return 0;
if (bgp_socket(bgp, bm->port, bm->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->status != Deleted)
&& !(CHECK_FLAG(peer->doppelganger->flags, PEER_FLAG_CONFIG_NODE)))
peer_delete(peer->doppelganger);
BGP_EVENT_ADD(peer, 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->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, BGP_Stop);
}
/* BGP global flag manipulation. */
int bgp_option_set(int flag)
{
switch (flag) {
case BGP_OPT_NO_FIB:
case BGP_OPT_MULTIPLE_INSTANCE:
case BGP_OPT_CONFIG_CISCO:
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) {
case BGP_OPT_MULTIPLE_INSTANCE:
if (listcount(bm->bgp) > 1)
return BGP_ERR_MULTIPLE_INSTANCE_USED;
/* Fall through. */
case BGP_OPT_NO_ZEBRA:
case BGP_OPT_NO_FIB:
case BGP_OPT_CONFIG_CISCO:
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);
}
/* BGP flag manipulation. */
int bgp_flag_set(struct bgp *bgp, int flag)
{
SET_FLAG(bgp->flags, flag);
return 0;
}
int bgp_flag_unset(struct bgp *bgp, int flag)
{
UNSET_FLAG(bgp->flags, flag);
return 0;
}
int bgp_flag_check(struct bgp *bgp, int flag)
{
return CHECK_FLAG(bgp->flags, flag);
}
/* 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. */
static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id)
{
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);
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->status)) {
peer->last_reset = PEER_DOWN_RID_CHANGE;
bgp_notify_send(peer, 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);
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 %u, RTR ID %s",
bgp->vrf_id, inet_ntoa(*addr));
bgp_router_id_set(bgp, addr);
}
}
}
} 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 %u, RTR ID %s",
bgp->vrf_id, inet_ntoa(*addr));
bgp_router_id_set(bgp, addr);
}
}
}
}
}
int 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 ? &id : &bgp->router_id_zebra);
return 0;
}
/* BGP's cluster-id control. */
int 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 0;
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->status)) {
peer->last_reset = PEER_DOWN_CLID_CHANGE;
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
}
return 0;
}
int bgp_cluster_id_unset(struct bgp *bgp)
{
struct peer *peer;
struct listnode *node, *nnode;
if (!bgp_config_check(bgp, BGP_CONFIG_CLUSTER_ID))
return 0;
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->status)) {
peer->last_reset = PEER_DOWN_CLID_CHANGE;
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
}
return 0;
}
/* time_t value that is monotonicly increasing
* and uneffected by adjustments to system clock
*/
time_t bgp_clock(void)
{
struct timeval tv;
monotime(&tv);
return tv.tv_sec;
}
/* BGP timer configuration. */
int bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime)
{
bgp->default_keepalive =
(keepalive < holdtime / 3 ? keepalive : holdtime / 3);
bgp->default_holdtime = holdtime;
return 0;
}
int bgp_timers_unset(struct bgp *bgp)
{
bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
return 0;
}
/* BGP confederation configuration. */
int bgp_confederation_id_set(struct bgp *bgp, as_t as)
{
struct peer *peer;
struct listnode *node, *nnode;
int already_confed;
if (as == 0)
return BGP_ERR_INVALID_AS;
/* Remember - were we doing confederation before? */
already_confed = bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION);
bgp->confed_id = as;
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)) {
/* We're looking for peers who's AS is not local or part of our
confederation. */
if (already_confed) {
if (peer_sort(peer) == BGP_PEER_EBGP) {
peer->local_as = as;
if (BGP_IS_VALID_STATE_FOR_NOTIF(
peer->status)) {
peer->last_reset =
PEER_DOWN_CONFED_ID_CHANGE;
bgp_notify_send(
peer, 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 (peer_sort(peer) != BGP_PEER_IBGP) {
/* Reset the local_as to be our EBGP one */
if (peer_sort(peer) == BGP_PEER_EBGP)
peer->local_as = as;
if (BGP_IS_VALID_STATE_FOR_NOTIF(
peer->status)) {
peer->last_reset =
PEER_DOWN_CONFED_ID_CHANGE;
bgp_notify_send(
peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
} else
bgp_session_reset_safe(peer, &nnode);
}
}
}
return 0;
}
int bgp_confederation_id_unset(struct bgp *bgp)
{
struct peer *peer;
struct listnode *node, *nnode;
bgp->confed_id = 0;
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->status)) {
peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
else
bgp_session_reset_safe(peer, &nnode);
}
}
return 0;
}
/* Is an AS part of the confed or not? */
int bgp_confederation_peers_check(struct bgp *bgp, as_t as)
{
int i;
if (!bgp)
return 0;
for (i = 0; i < bgp->confed_peers_cnt; i++)
if (bgp->confed_peers[i] == as)
return 1;
return 0;
}
/* Add an AS to the confederation set. */
int bgp_confederation_peers_add(struct bgp *bgp, as_t as)
{
struct peer *peer;
struct listnode *node, *nnode;
if (!bgp)
return BGP_ERR_INVALID_BGP;
if (bgp->as == as)
return BGP_ERR_INVALID_AS;
if (bgp_confederation_peers_check(bgp, as))
return -1;
if (bgp->confed_peers)
bgp->confed_peers =
XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers,
(bgp->confed_peers_cnt + 1) * sizeof(as_t));
else
bgp->confed_peers =
XMALLOC(MTYPE_BGP_CONFED_LIST,
(bgp->confed_peers_cnt + 1) * sizeof(as_t));
bgp->confed_peers[bgp->confed_peers_cnt] = as;
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;
if (BGP_IS_VALID_STATE_FOR_NOTIF(
peer->status)) {
peer->last_reset =
PEER_DOWN_CONFED_PEER_CHANGE;
bgp_notify_send(
peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
} else
bgp_session_reset_safe(peer, &nnode);
}
}
}
return 0;
}
/* Delete an AS from the confederation set. */
int bgp_confederation_peers_remove(struct bgp *bgp, as_t as)
{
int i;
int j;
struct peer *peer;
struct listnode *node, *nnode;
if (!bgp)
return -1;
if (!bgp_confederation_peers_check(bgp, as))
return -1;
for (i = 0; i < bgp->confed_peers_cnt; i++)
if (bgp->confed_peers[i] == as)
for (j = i + 1; j < bgp->confed_peers_cnt; j++)
bgp->confed_peers[j - 1] = bgp->confed_peers[j];
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(as_t));
/* 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;
if (BGP_IS_VALID_STATE_FOR_NOTIF(
peer->status)) {
peer->last_reset =
PEER_DOWN_CONFED_PEER_CHANGE;
bgp_notify_send(
peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
} else
bgp_session_reset_safe(peer, &nnode);
}
}
}
return 0;
}
/* Local preference configuration. */
int bgp_default_local_preference_set(struct bgp *bgp, uint32_t local_pref)
{
if (!bgp)
return -1;
bgp->default_local_pref = local_pref;
return 0;
}
int bgp_default_local_preference_unset(struct bgp *bgp)
{
if (!bgp)
return -1;
bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
return 0;
}
/* Local preference configuration. */
int bgp_default_subgroup_pkt_queue_max_set(struct bgp *bgp, uint32_t queue_size)
{
if (!bgp)
return -1;
bgp->default_subgroup_pkt_queue_max = queue_size;
return 0;
}
int bgp_default_subgroup_pkt_queue_max_unset(struct bgp *bgp)
{
if (!bgp)
return -1;
bgp->default_subgroup_pkt_queue_max =
BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX;
return 0;
}
/* Listen limit configuration. */
int bgp_listen_limit_set(struct bgp *bgp, int listen_limit)
{
if (!bgp)
return -1;
bgp->dynamic_neighbors_limit = listen_limit;
return 0;
}
int bgp_listen_limit_unset(struct bgp *bgp)
{
if (!bgp)
return -1;
bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT;
return 0;
}
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_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->su, &p2->su);
}
static unsigned int peer_hash_key_make(void *p)
{
struct peer *peer = p;
return sockunion_hash(&peer->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->su, &peer2->su)
&& CHECK_FLAG(peer1->flags, PEER_FLAG_CONFIG_NODE)
== CHECK_FLAG(peer2->flags, PEER_FLAG_CONFIG_NODE));
}
void peer_flag_inherit(struct peer *peer, uint32_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);
}
int peer_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, uint32_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,
uint32_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);
}
static bool peergroup_flag_check(struct peer *peer, uint32_t flag)
{
if (!peer_group_active(peer)) {
if (CHECK_FLAG(peer->flags_invert, flag))
return !CHECK_FLAG(peer->flags, flag);
else
return !!CHECK_FLAG(peer->flags, flag);
}
return !!CHECK_FLAG(peer->flags_override, flag);
}
static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi,
uint32_t flag)
{
if (!peer_group_active(peer)) {
if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag))
return !peer_af_flag_check(peer, afi, safi, flag);
else
return !!peer_af_flag_check(peer, afi, safi, flag);
}
return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag);
}
static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi,
uint8_t type, int direct)
{
struct bgp_filter *filter;
if (peer_group_active(peer))
return !!CHECK_FLAG(peer->filter_override[afi][safi][direct],
type);
filter = &peer->filter[afi][safi];
switch (type) {
case PEER_FT_DISTRIBUTE_LIST:
return !!(filter->dlist[direct].name);
case PEER_FT_FILTER_LIST:
return !!(filter->aslist[direct].name);
case PEER_FT_PREFIX_LIST:
return !!(filter->plist[direct].name);
case PEER_FT_ROUTE_MAP:
return !!(filter->map[direct].name);
case PEER_FT_UNSUPPRESS_MAP:
return !!(filter->usmap.name);
default:
return false;
}
}
/* Return true if the addpath type is set for peer and different from
* peer-group.
*/
static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi)
{
enum bgp_addpath_strat type, g_type;
type = peer->addpath_type[afi][safi];
if (type != BGP_ADDPATH_NONE) {
if (peer_group_active(peer)) {
g_type = peer->group->conf->addpath_type[afi][safi];
if (type != g_type)
return 1;
else
return 0;
}
return 1;
}
return 0;
}
/* Check peer's AS number and determines if this peer is IBGP or EBGP */
static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer)
{
struct bgp *bgp;
bgp = peer->bgp;
/* 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 (bgp->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 (peer->local_as == 0)
return BGP_PEER_INTERNAL;
if (peer->local_as == peer->as) {
if (bgp->as == bgp->confed_id) {
if (peer->local_as == bgp->as)
return BGP_PEER_IBGP;
else
return BGP_PEER_EBGP;
} else {
if (peer->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 (peer->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 (peer->local_as == 0
? BGP_PEER_INTERNAL
: peer->local_as == peer->as ? BGP_PEER_IBGP
: BGP_PEER_EBGP);
}
}
/* Calculate and cache the peer "sort" */
bgp_peer_sort_t peer_sort(struct peer *peer)
{
peer->sort = peer_calc_sort(peer);
return peer->sort;
}
static void peer_free(struct peer *peer)
{
afi_t afi;
safi_t safi;
assert(peer->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);
bgp_reads_off(peer);
bgp_writes_off(peer);
assert(!peer->t_write);
assert(!peer->t_read);
BGP_EVENT_FLUSH(peer);
pthread_mutex_destroy(&peer->io_mtx);
/* Free connected nexthop, if present */
if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)
&& !peer_dynamic_neighbor(peer))
bgp_delete_connected_nexthop(family2afi(peer->su.sa.sa_family),
peer);
XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message);
if (peer->desc) {
XFREE(MTYPE_PEER_DESC, peer->desc);
peer->desc = NULL;
}
/* Free allocated host character. */
if (peer->host) {
XFREE(MTYPE_BGP_PEER_HOST, peer->host);
peer->host = NULL;
}
if (peer->domainname) {
XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
peer->domainname = NULL;
}
if (peer->ifname) {
XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname);
peer->ifname = NULL;
}
/* Update source configuration. */
if (peer->update_source) {
sockunion_free(peer->update_source);
peer->update_source = NULL;
}
if (peer->update_if) {
XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
peer->update_if = NULL;
}
XFREE(MTYPE_TMP, 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);
bgp_sync_delete(peer);
if (peer->conf_if) {
XFREE(MTYPE_PEER_CONF_IF, peer->conf_if);
peer->conf_if = NULL;
}
bfd_info_free(&(peer->bfd_info));
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
bgp_addpath_set_peer_type(peer, afi, safi,
BGP_ADDPATH_NONE);
}
}
bgp_unlock(peer->bgp);
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)
{
assert(peer && (peer->lock >= 0));
#if 0
zlog_debug("%s peer_lock %p %d", name, peer, peer->lock);
#endif
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)
{
assert(peer && (peer->lock > 0));
#if 0
zlog_debug("%s peer_unlock %p %d", name, peer, peer->lock);
#endif
peer->lock--;
if (peer->lock == 0) {
peer_free(peer);
return NULL;
}
return peer;
}
/* 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);
if (!bgp)
return NULL;
/* Allocate new peer. */
peer = XCALLOC(MTYPE_BGP_PEER, sizeof(struct peer));
/* Set default value. */
peer->fd = -1;
peer->v_start = BGP_INIT_START_TIMER;
peer->v_connect = BGP_DEFAULT_CONNECT_RETRY;
peer->status = Idle;
peer->ostatus = Idle;
peer->cur_event = peer->last_event = peer->last_major_event = 0;
peer->bgp = bgp_lock(bgp);
peer = peer_lock(peer); /* initial reference */
peer->password = NULL;
/* Set default flags. */
FOREACH_AFI_SAFI (afi, safi) {
if (!bgp_option_check(BGP_OPT_CONFIG_CISCO)) {
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;
}
/* 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);
/* Create buffers. */
peer->ibuf = stream_fifo_new();
peer->obuf = stream_fifo_new();
pthread_mutex_init(&peer->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_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.
*/
peer->obuf_work =
stream_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW);
peer->ibuf_work =
ringbuf_new(BGP_MAX_PACKET_SIZE * BGP_READ_PACKET_MAX);
peer->scratch = stream_new(BGP_MAX_PACKET_SIZE);
bgp_sync_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;
peer_dst->cap = peer_src->cap;
peer_dst->local_as = peer_src->local_as;
peer_dst->port = peer_src->port;
(void)peer_sort(peer_dst);
peer_dst->rmap_type = peer_src->rmap_type;
/* Timers */
peer_dst->holdtime = peer_src->holdtime;
peer_dst->keepalive = peer_src->keepalive;
peer_dst->connect = peer_src->connect;
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;
/* password apply */
if (peer_src->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)
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);
if (peer_dst->update_if) {
XFREE(MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if);
peer_dst->update_if = NULL;
}
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);
}
}
static int bgp_peer_conf_if_to_su_update_v4(struct peer *peer,
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_IPV4(&p, CONNECTED_PREFIX(ifc));
if (p.prefixlen == 30) {
peer->su.sa.sa_family = AF_INET;
addr = ntohl(p.u.prefix4.s_addr);
if (addr % 4 == 1)
peer->su.sin.sin_addr.s_addr =
htonl(addr + 1);
else if (addr % 4 == 2)
peer->su.sin.sin_addr.s_addr =
htonl(addr - 1);
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
peer->su.sin.sin_len =
sizeof(struct sockaddr_in);
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
return 1;
} else if (p.prefixlen == 31) {
peer->su.sa.sa_family = AF_INET;
addr = ntohl(p.u.prefix4.s_addr);
if (addr % 2 == 0)
peer->su.sin.sin_addr.s_addr =
htonl(addr + 1);
else
peer->su.sin.sin_addr.s_addr =
htonl(addr - 1);
#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
peer->su.sin.sin_len =
sizeof(struct sockaddr_in);
#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
return 1;
} else if (bgp_debug_neighbor_events(peer))
zlog_debug(
"%s: IPv4 interface address is not /30 or /31, v4 session not started",
peer->conf_if);
}
}
return 0;
}
static int bgp_peer_conf_if_to_su_update_v6(struct peer *peer,
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))) {
peer->su.sa.sa_family = AF_INET6;
memcpy(&peer->su.sin6.sin6_addr, &ifc_nbr->address->u.prefix,
sizeof(struct in6_addr));
#ifdef SIN6_LEN
peer->su.sin6.sin6_len = sizeof(struct sockaddr_in6);
#endif
peer->su.sin6.sin6_scope_id = ifp->ifindex;
return 1;
}
return 0;
}
/*
* 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 *peer)
{
struct interface *ifp;
int prev_family;
int peer_addr_updated = 0;
if (!peer->conf_if)
return;
/*
* Our peer structure is stored in the bgp->peerhash
* release it before we modify anything.
*/
hash_release(peer->bgp->peerhash, peer);
prev_family = peer->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(peer, 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(peer, 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(peer);
} else {
if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD)
&& prev_family != AF_UNSPEC)
bgp_md5_unset(peer);
peer->su.sa.sa_family = AF_UNSPEC;
memset(&peer->su.sin6.sin6_addr, 0, sizeof(struct in6_addr));
}
/*
* Since our su changed we need to del/add peer to the peerhash
*/
hash_get(peer->bgp->peerhash, peer, hash_alloc_intern);
}
static void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi,
safi_t safi)
{
struct bgp_node *rn, *nrn;
struct bgp_table *table;
for (rn = bgp_table_top(bgp->rib[afi][safi]); rn;
rn = bgp_route_next(rn)) {
table = bgp_node_get_bgp_table_info(rn);
if (table != NULL) {
/* Special handling for 2-level routing
* tables. */
if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
|| safi == SAFI_EVPN) {
for (nrn = bgp_table_top(table);
nrn; nrn = bgp_route_next(nrn))
bgp_process(bgp, nrn, afi, safi);
} else
bgp_process(bgp, rn, 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, afi_t afi, safi_t safi,
struct peer_group *group)
{
int active;
struct peer *peer;
char buf[SU_ADDRSTRLEN];
peer = peer_new(bgp);
if (conf_if) {
peer->conf_if = XSTRDUP(MTYPE_PEER_CONF_IF, conf_if);
if (su)
peer->su = *su;
else
bgp_peer_conf_if_to_su_update(peer);
XFREE(MTYPE_BGP_PEER_HOST, peer->host);
peer->host = XSTRDUP(MTYPE_BGP_PEER_HOST, conf_if);
} else if (su) {
peer->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;
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;
peer = peer_lock(peer); /* bgp peer list reference */
peer->group = group;
listnode_add_sort(bgp->peer, peer);
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);
/* Last read and reset time set */
peer->readtime = peer->resettime = bgp_clock();
/* Default TTL set. */
peer->ttl = (peer->sort == BGP_PEER_IBGP) ? MAXTTL : 1;
SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
if (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);
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);
return peer;
}
/*
* Return true if we have a peer configured to use this afi/safi
*/
int 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 1;
}
return 0;
}
/* Change peer's AS number. */
void peer_as_change(struct peer *peer, as_t as, int as_specified)
{
bgp_peer_sort_t type;
/* Stop peer. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
peer->last_reset = PEER_DOWN_REMOTE_AS_CHANGE;
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
} else
bgp_session_reset(peer);
}
type = peer_sort(peer);
peer->as = as;
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;
/* Advertisement-interval reset */
if (!CHECK_FLAG(peer->flags, PEER_FLAG_ROUTEADV)) {
peer->v_routeadv = (peer_sort(peer) == BGP_PEER_IBGP)
? BGP_DEFAULT_IBGP_ROUTEADV
: BGP_DEFAULT_EBGP_ROUTEADV;
}
/* TTL reset */
if (peer_sort(peer) == BGP_PEER_IBGP)
peer->ttl = MAXTTL;
else if (type == BGP_PEER_IBGP)
peer->ttl = 1;
/* reflector-client reset */
if (peer_sort(peer) != 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);
}
/* local-as reset */
if (peer_sort(peer) != BGP_PEER_EBGP) {
peer->change_local_as = 0;
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);
}
}
/* 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, afi_t afi, safi_t safi)
{
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;
}
bgp_peer_sort_t 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);
} 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)
&& bgp->as != *as)
local_as = bgp->confed_id;
else
local_as = bgp->as;
/* If this is IPv4 unicast configuration and "no bgp default
ipv4-unicast" is specified. */
if (bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4)
&& afi == AFI_IP && safi == SAFI_UNICAST)
peer_create(su, conf_if, bgp, local_as, *as, as_type, 0,
0, NULL);
else
peer_create(su, conf_if, bgp, local_as, *as, as_type,
afi, safi, NULL);
}
return 0;
}
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;
uint32_t flags_tmp;
uint32_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]);
}
/* allowas-in */
if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_ALLOWAS_IN))
PEER_ATTR_INHERIT(peer, group, allowas_in[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);
}
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);
} else {
if (peer->status == Established) {
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);
}
} else {
peer->last_reset = PEER_DOWN_AF_ACTIVATE;
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
}
if (peer->status == OpenSent || peer->status == OpenConfirm) {
peer->last_reset = PEER_DOWN_AF_ACTIVATE;
bgp_notify_send(peer, 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->status == OpenSent
|| other->status == OpenConfirm)) {
other->last_reset = PEER_DOWN_AF_ACTIVATE;
bgp_notify_send(other, 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;
/* 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 recalc bestpaths to trigger label allocation */
if (safi == SAFI_LABELED_UNICAST
&& !bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) {
if (BGP_DEBUG(zebra, ZEBRA))
zlog_info(
"peer(s) are now active for labeled-unicast, allocate MPLS labels");
bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 1;
bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST);
}
if (safi == SAFI_FLOWSPEC) {
/* connect to table manager */
bgp_zebra_init_tm_connect(bgp);
}
return ret;
}
static int 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 1;
}
/* Nothing to do if we've already deactivated this peer */
if (!peer->afc[afi][safi])
return 0;
/* 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",
peer->host);
return 1;
}
if (peer->status == Established) {
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, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
} else {
peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
}
return 0;
}
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;
/* 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;
if (peer_af_delete(peer, afi, safi) != 0) {
flog_err(EC_BGP_PEER_DELETE,
"couldn't delete af structure for peer %s",
peer->host);
}
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 recalc bestpaths to trigger label deallocation */
if (safi == SAFI_LABELED_UNICAST
&& bgp->allocate_mpls_labels[afi][SAFI_UNICAST]
&& !bgp_afi_safi_peer_exists(bgp, afi, safi)) {
if (BGP_DEBUG(zebra, ZEBRA))
zlog_info(
"peer(s) are no longer active for labeled-unicast, deallocate MPLS labels");
bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 0;
bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST);
}
return ret;
}
int peer_afc_set(struct peer *peer, afi_t afi, safi_t safi, int enable)
{
if (enable)
return peer_activate(peer, afi, safi);
else
return peer_deactivate(peer, afi, safi);
}
static 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);
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (safi = SAFI_UNICAST; safi <= SAFI_MPLS_VPN; safi++)
peer->nsf[afi][safi] = 0;
if (peer->t_gr_restart) {
BGP_TIMER_OFF(peer->t_gr_restart);
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s graceful restart timer stopped",
peer->host);
}
if (peer->t_gr_stale) {
BGP_TIMER_OFF(peer->t_gr_stale);
if (bgp_debug_neighbor_events(peer))
zlog_debug(
"%s graceful restart stalepath timer stopped",
peer->host);
}
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->status != Deleted);
bgp = peer->bgp;
accept_peer = CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
bgp_reads_off(peer);
bgp_writes_off(peer);
assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON));
assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON));
if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT))
peer_nsf_stop(peer);
SET_FLAG(peer->flags, PEER_FLAG_DELETE);
/* 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);
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, 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_PEER_SU_UNSPEC(peer)
&& !CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
bgp_md5_unset(peer);
}
bgp_timer_set(peer); /* stops all timers for Deleted */
/* Delete from all peer list. */
if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
&& (pn = listnode_lookup(bgp->peer, peer))) {
peer_unlock(peer); /* bgp peer list reference */
list_delete_node(bgp->peer, pn);
hash_release(bgp->peerhash, peer);
}
/* Buffers. */
if (peer->ibuf) {
stream_fifo_free(peer->ibuf);
peer->ibuf = NULL;
}
if (peer->obuf) {
stream_fifo_free(peer->obuf);
peer->obuf = NULL;
}
if (peer->ibuf_work) {
ringbuf_del(peer->ibuf_work);
peer->ibuf_work = NULL;
}
if (peer->obuf_work) {
stream_free(peer->obuf_work);
peer->obuf_work = NULL;
}
if (peer->scratch) {
stream_free(peer->scratch);
peer->scratch = NULL;
}
/* 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++) {
if (filter->dlist[i].name) {
XFREE(MTYPE_BGP_FILTER_NAME,
filter->dlist[i].name);
filter->dlist[i].name = NULL;
}
if (filter->plist[i].name) {
XFREE(MTYPE_BGP_FILTER_NAME,
filter->plist[i].name);
filter->plist[i].name = NULL;
}
if (filter->aslist[i].name) {
XFREE(MTYPE_BGP_FILTER_NAME,
filter->aslist[i].name);
filter->aslist[i].name = NULL;
}
}
for (i = RMAP_IN; i < RMAP_MAX; i++) {
if (filter->map[i].name) {
XFREE(MTYPE_BGP_FILTER_NAME,
filter->map[i].name);
filter->map[i].name = NULL;
}
}
if (filter->usmap.name) {
XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
filter->usmap.name = NULL;
}
if (peer->default_rmap[afi][safi].name) {
XFREE(MTYPE_ROUTE_MAP_NAME,
peer->default_rmap[afi][safi].name);
peer->default_rmap[afi][safi].name = NULL;
}
}
FOREACH_AFI_SAFI (afi, safi)
peer_af_delete(peer, afi, safi);
if (peer->hostname) {
XFREE(MTYPE_BGP_PEER_HOST, peer->hostname);
peer->hostname = NULL;
}
if (peer->domainname) {
XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
peer->domainname = NULL;
}
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;
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);
if (!bgp_flag_check(bgp, BGP_FLAG_NO_DEFAULT_IPV4))
group->conf->afc[AFI_IP][SAFI_UNICAST] = 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 = 1;
group->conf->gtsm_hops = 0;
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)
{
uint32_t flags_tmp;
struct peer *conf;
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;
/* 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);
/* 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 = BGP_DEFAULT_CONNECT_RETRY;
}
/* 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;
}
/* password apply */
if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
PEER_STR_ATTR_INHERIT(peer, group, password,
MTYPE_PEER_PASSWORD);
if (!BGP_PEER_SU_UNSPEC(peer))
bgp_md5_set(peer);
/* 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);
}
}
bgp_bfd_peer_group2peer_copy(conf, peer);
}
/* Peer group's remote AS configuration. */
int peer_group_remote_as(struct bgp *bgp, const char *group_name, as_t *as,
int as_type)
{
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);
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);
}
return 0;
}
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;
peer_delete(peer);
if (other && other->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;
bfd_info_free(&(group->conf->bfd_info));
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;
peer_delete(peer);
if (other && other->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);
return 0;
}
int peer_group_listen_range_del(struct peer_group *group, struct prefix *range)
{
struct prefix *prefix, prefix2;
struct listnode *node, *nnode;
struct peer *peer;
afi_t afi;
char buf[PREFIX2STR_BUFFER];
afi = family2afi(range->family);
/* Identify the listen range. */
for (ALL_LIST_ELEMENTS(group->listen_range[afi], node, nnode, prefix)) {
if (prefix_same(range, prefix))
break;
}
if (!prefix)
return BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_NOT_FOUND;
prefix2str(prefix, buf, sizeof(buf));
/* Dispose off any dynamic neighbors that exist due to this listen range
*/
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
if (!peer_dynamic_neighbor(peer))
continue;
sockunion2hostprefix(&peer->su, &prefix2);
if (prefix_match(prefix, &prefix2)) {
if (bgp_debug_neighbor_events(peer))
zlog_debug(
"Deleting dynamic neighbor %s group %s upon "
"delete of listen range %s",
peer->host, group->name, buf);
peer_delete(peer);
}
}
/* Get rid of the listen range */
listnode_delete(group->listen_range[afi], prefix);
return 0;
}
/* Bind specified peer to peer group. */
int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer,
struct peer_group *group, as_t *as)
{
int first_member = 0;
afi_t afi;
safi_t safi;
/* 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;
}
if (!group->conf->as && peer_sort(peer)) {
if (peer_sort(group->conf) != BGP_PEER_INTERNAL
&& peer_sort(group->conf) != peer_sort(peer)) {
if (as)
*as = peer->as;
return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
}
if (peer_sort(group->conf) == 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) {
/* Advertisement-interval reset */
if (!CHECK_FLAG(group->conf->flags,
PEER_FLAG_ROUTEADV)) {
group->conf->v_routeadv =
(peer_sort(group->conf)
== BGP_PEER_IBGP)
? BGP_DEFAULT_IBGP_ROUTEADV
: BGP_DEFAULT_EBGP_ROUTEADV;
}
/* ebgp-multihop reset */
if (peer_sort(group->conf) == BGP_PEER_IBGP)
group->conf->ttl = MAXTTL;
/* local-as reset */
if (peer_sort(group->conf) != BGP_PEER_EBGP) {
group->conf->change_local_as = 0;
peer_flag_unset(group->conf,
PEER_FLAG_LOCAL_AS);
peer_flag_unset(group->conf,
PEER_FLAG_LOCAL_AS_NO_PREPEND);
peer_flag_unset(group->conf,
PEER_FLAG_LOCAL_AS_REPLACE_AS);
}
}
SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
peer->last_reset = PEER_DOWN_RMAP_BIND;
bgp_notify_send(peer, 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, 0, 0, group);
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;
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_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
/* Set up peer's events and timers. */
if (peer_active(peer))
bgp_timer_set(peer);
}
return 0;
}
static int bgp_startup_timer_expire(struct thread *thread)
{
struct bgp *bgp;
bgp = THREAD_ARG(thread);
bgp->t_startup = NULL;
return 0;
}
/*
* 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)
{
struct bgp *bgp;
afi_t afi;
safi_t safi;
if ((bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp))) == NULL)
return NULL;
if (BGP_DEBUG(zebra, ZEBRA)) {
if (inst_type == BGP_INSTANCE_TYPE_DEFAULT)
zlog_debug("Creating Default VRF, AS %u", *as);
else
zlog_debug("Creating %s %s, AS %u",
(inst_type == BGP_INSTANCE_TYPE_VRF)
? "VRF"
: "VIEW",
name, *as);
}
bgp_lock(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");
if (bgp->peer_self->hostname != NULL) {
XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->hostname);
bgp->peer_self->hostname = NULL;
}
if (cmd_hostname_get())
bgp->peer_self->hostname =
XSTRDUP(MTYPE_BGP_PEER_HOST, cmd_hostname_get());
if (bgp->peer_self->domainname != NULL) {
XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->domainname);
bgp->peer_self->domainname = NULL;
}
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);
}
bgp->v_update_delay = BGP_UPDATE_DELAY_DEF;
bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
bgp->default_subgroup_pkt_queue_max =
BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX;
bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT;
bgp->dynamic_neighbors_count = 0;
bgp->ebgp_requires_policy = DEFAULT_EBGP_POLICY_DISABLED;
#if DFLT_BGP_IMPORT_CHECK
bgp_flag_set(bgp, BGP_FLAG_IMPORT_CHECK);
#endif
#if DFLT_BGP_SHOW_HOSTNAME
bgp_flag_set(bgp, BGP_FLAG_SHOW_HOSTNAME);
#endif
#if DFLT_BGP_LOG_NEIGHBOR_CHANGES
bgp_flag_set(bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
#endif
#if DFLT_BGP_DETERMINISTIC_MED
bgp_flag_set(bgp, BGP_FLAG_DETERMINISTIC_MED);
#endif
bgp_addpath_init_bgp_data(&bgp->tx_addpath);
bgp->as = *as;
#if 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;
}
if (name) {
bgp->name = XSTRDUP(MTYPE_BGP, name);
} else {
/* TODO - The startup timer needs to be run for the whole of BGP
*/
thread_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;
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_info = XCALLOC(MTYPE_BGP_EVPN_INFO,
sizeof(struct bgp_evpn_info));
bgp_evpn_init(bgp);
bgp_pbr_init(bgp);
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;
}
/* 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)
{
int ret = 0;
/* 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 == FALSE) {
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;
ret = bgp_socket(bgp, bm->port, bm->address);
if (ret < 0)
return BGP_ERR_INVALID_VALUE;
return 0;
} else
return bgp_check_main_socket(create, bgp);
}
/* Called from VTY commands. */
int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
enum bgp_instance_type inst_type)
{
struct bgp *bgp;
struct vrf *vrf = NULL;
/* Multiple instance check. */
if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) {
if (name)
bgp = bgp_lookup_by_name(name);
else
bgp = bgp_get_default();
/* Already exists. */
if (bgp) {
if (bgp->as != *as) {
*as = bgp->as;
return BGP_ERR_INSTANCE_MISMATCH;
}
if (bgp->inst_type != inst_type)
return BGP_ERR_INSTANCE_MISMATCH;
*bgp_val = bgp;
return 0;
}
} else {
/* BGP instance name can not be specified for single instance.
*/
if (name)
return BGP_ERR_MULTIPLE_INSTANCE_NOT_SET;
/* Get default BGP structure if exists. */
bgp = bgp_get_default();
if (bgp) {
if (bgp->as != *as) {
*as = bgp->as;
return BGP_ERR_AS_MISMATCH;
}
*bgp_val = bgp;
return 0;
}
}
bgp = bgp_create(as, name, inst_type);
if (bgp_option_check(BGP_OPT_NO_ZEBRA) && name)
bgp->vrf_id = vrf_generate_id();
bgp_router_id_set(bgp, &bgp->router_id_zebra);
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",
__PRETTY_FUNCTION__, name);
bgp_zebra_instance_register(bgp);
}
return 0;
}
/*
* 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;
/* 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, 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) {
BGP_TIMER_OFF(bgp->t_rmap_def_originate_eval);
bgp_unlock(bgp); /* TODO - This timer is started with a lock -
why? */
}
/* 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->status))
bgp_notify_send(peer, 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);
}
/* 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;
int i;
assert(bgp);
THREAD_OFF(bgp->t_startup);
THREAD_OFF(bgp->t_maxmed_onstartup);
THREAD_OFF(bgp->t_update_delay);
THREAD_OFF(bgp->t_establish_wait);
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) {
BGP_TIMER_OFF(bgp->t_rmap_def_originate_eval);
bgp_unlock(bgp); /* TODO - This timer is started with a lock -
why? */
}
/* Inform peers we're going down. */
for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer)) {
if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
}
/* 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);
for (ALL_LIST_ELEMENTS(bgp->peer, node, next, 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 */
#if ENABLE_BGP_VNC
rfapi_delete(bgp);
#endif
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;
}
/* 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",
__PRETTY_FUNCTION__, 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);
thread_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_node *rn;
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 (rn = bgp_table_top(bgp->rib[afi][safi]); rn;
rn = bgp_route_next(rn)) {
table = bgp_node_get_bgp_table_info(rn);
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);
XFREE(MTYPE_BGP_EVPN_INFO, bgp->evpn_info);
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
vpn_policy_direction_t 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]);
}
XFREE(MTYPE_BGP, bgp->name);
XFREE(MTYPE_BGP, bgp->name_pretty);
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;
memset(&tmp_peer, 0, sizeof(struct peer));
/*
* 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);
tmp_peer.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, 0, 0, group);
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);
SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
return peer;
}
struct prefix *
peer_group_lookup_dynamic_neighbor_range(struct peer_group *group,
struct prefix *prefix)
{
struct listnode *node, *nnode;
struct prefix *range;
afi_t afi;
afi = family2afi(prefix->family);
if (group->listen_range[afi])
for (ALL_LIST_ELEMENTS(group->listen_range[afi], node, nnode,
range))
if (prefix_match(range, prefix))
return range;
return NULL;
}
struct peer_group *
peer_group_lookup_dynamic_neighbor(struct bgp *bgp, struct prefix *prefix,
struct prefix **listen_range)
{
struct prefix *range = NULL;
struct peer_group *group = NULL;
struct listnode *node, *nnode;
*listen_range = NULL;
if (bgp != NULL) {
for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
if ((range = peer_group_lookup_dynamic_neighbor_range(
group, prefix)))
break;
} else if (bm->bgp != NULL) {
struct listnode *bgpnode, *nbgpnode;
for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp))
for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
if ((range = peer_group_lookup_dynamic_neighbor_range(
group, prefix)))
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;
char buf[PREFIX2STR_BUFFER];
char buf1[PREFIX2STR_BUFFER];
sockunion2hostprefix(su, &prefix);
/* See if incoming connection matches a configured listen range. */
group = peer_group_lookup_dynamic_neighbor(bgp, &prefix, &listen_range);
if (!group)
return NULL;
gbgp = group->bgp;
if (!gbgp)
return NULL;
prefix2str(&prefix, buf, sizeof(buf));
prefix2str(listen_range, buf1, sizeof(buf1));
if (bgp_debug_neighbor_events(NULL))
zlog_debug(
"Dynamic Neighbor %s matches group %s listen range %s",
buf, group->name, buf1);
/* Are we within the listen limit? */
dncount = gbgp->dynamic_neighbors_count;
if (dncount >= gbgp->dynamic_neighbors_limit) {
if (bgp_debug_neighbor_events(NULL))
zlog_debug("Dynamic Neighbor %s rejected - at limit %d",
inet_sutop(su, buf),
gbgp->dynamic_neighbors_limit);
return NULL;
}
/* Ensure group is not disabled. */
if (CHECK_FLAG(group->conf->flags, PEER_FLAG_SHUTDOWN)) {
if (bgp_debug_neighbor_events(NULL))
zlog_debug(
"Dynamic Neighbor %s rejected - group %s disabled",
buf, group->name);
return NULL;
}
/* Check that at least one AF is activated for the group. */
if (!peer_group_af_configured(group)) {
if (bgp_debug_neighbor_events(NULL))
zlog_debug(
"Dynamic Neighbor %s rejected - no AF activated for group %s",
buf, group->name);
return NULL;
}
/* Create dynamic peer and bind to associated group. */
peer = peer_create_bind_dynamic_neighbor(gbgp, su, group);
assert(peer);
gbgp->dynamic_neighbors_count = ++dncount;
if (bgp_debug_neighbor_events(peer))
zlog_debug("%s Dynamic Neighbor added, group %s count %d",
peer->host, group->name, dncount);
return peer;
}
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);
}
/* If peer is configured at least one address family return 1. */
int peer_active(struct peer *peer)
{
if (BGP_PEER_SU_UNSPEC(peer))
return 0;
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 1;
return 0;
}
/* If peer is negotiated at least one address family return 1. */
int 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 1;
return 0;
}
void peer_change_action(struct peer *peer, afi_t afi, safi_t safi,
enum peer_change_type type)
{
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
return;
if (peer->status != Established)
return;
if (type == peer_change_reset) {
/* If we're resetting session, we've to delete both peer struct
*/
if ((peer->doppelganger)
&& (peer->doppelganger->status != Deleted)
&& (!CHECK_FLAG(peer->doppelganger->flags,
PEER_FLAG_CONFIG_NODE)))
peer_delete(peer->doppelganger);
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
} else if (type == peer_change_reset_in) {
if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV)
|| CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV))
bgp_route_refresh_send(peer, afi, safi, 0, 0, 0);
else {
if ((peer->doppelganger)
&& (peer->doppelganger->status != Deleted)
&& (!CHECK_FLAG(peer->doppelganger->flags,
PEER_FLAG_CONFIG_NODE)))
peer_delete(peer->doppelganger);
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
} else if (type == peer_change_reset_out) {
update_group_adjust_peer(peer_af_find(peer, afi, safi));
bgp_announce_route(peer, afi, safi);
}
}
struct peer_flag_action {
/* Peer's flag. */
uint32_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_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_PASSWORD, 0, peer_change_none},
{PEER_FLAG_LOCAL_AS, 0, peer_change_none},
{PEER_FLAG_LOCAL_AS_NO_PREPEND, 0, peer_change_none},
{PEER_FLAG_LOCAL_AS_REPLACE_AS, 0, peer_change_none},
{PEER_FLAG_UPDATE_SOURCE, 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_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},
{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,
uint32_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, uint32_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->t_pmax_restart) {
BGP_TIMER_OFF(peer->t_pmax_restart);
if (bgp_debug_neighbor_events(peer))
zlog_debug(
"%s Maximum-prefix restart timer canceled",
peer->host);
}
if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT))
peer_nsf_stop(peer);
if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
char *msg = peer->tx_shutdown_message;
size_t msglen;
if (!msg && peer_group_active(peer))
msg = peer->group->conf
->tx_shutdown_message;
msglen = msg ? strlen(msg) : 0;
if (msglen > 128)
msglen = 128;
if (msglen) {
uint8_t msgbuf[129];
msgbuf[0] = msglen;
memcpy(msgbuf + 1, msg, msglen);
bgp_notify_send_with_data(
peer, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN,
msgbuf, msglen + 1);
} else
bgp_notify_send(
peer, 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, BGP_Stop);
}
} else if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->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, BGP_NOTIFY_CEASE,
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
} else
bgp_session_reset(peer);
}
/* Change specified peer flag. */
static int peer_flag_modify(struct peer *peer, uint32_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);
/* 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;
}
if (set && flag == PEER_FLAG_CAPABILITY_ENHE)
bgp_nht_register_enhe_capability_interfaces(peer);
/*
* Update peer-group members, unless they are explicitely 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);