summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfdd/bfd.c16
-rw-r--r--bfdd/bfd.h4
-rw-r--r--bfdd/bfdd_nb_config.c6
-rw-r--r--bfdd/bfdd_nb_state.c8
-rw-r--r--bfdd/event.c4
-rw-r--r--bgpd/bgp_bmp.c18
-rw-r--r--bgpd/bgp_evpn_mh.c10
-rw-r--r--bgpd/bgp_evpn_vty.c17
-rw-r--r--bgpd/bgp_mplsvpn.c46
-rw-r--r--bgpd/bgp_open.c1
-rw-r--r--bgpd/bgp_packet.c8
-rw-r--r--bgpd/bgp_packet.h3
-rw-r--r--bgpd/bgp_route.c119
-rw-r--r--bgpd/bgp_route.h3
-rw-r--r--bgpd/bgp_zebra.c7
-rw-r--r--bgpd/bgpd.c23
-rw-r--r--doc/user/bgp.rst5
-rw-r--r--doc/user/ospfd.rst43
-rw-r--r--doc/user/pim.rst8
-rw-r--r--doc/user/pimv6.rst12
-rw-r--r--isisd/isis_nb.c12
-rw-r--r--isisd/isis_nb_config.c40
-rw-r--r--lib/event.c85
-rw-r--r--lib/frr_pthread.c8
-rw-r--r--lib/frrcu.c15
-rw-r--r--lib/frrevent.h19
-rw-r--r--lib/sigevent.h27
-rw-r--r--nhrpd/netlink_arp.c2
-rw-r--r--ospfd/ospf_apiserver.c79
-rw-r--r--ospfd/ospf_dump.c45
-rw-r--r--ospfd/ospf_dump.h4
-rw-r--r--ospfd/ospf_lsa.c16
-rw-r--r--ospfd/ospf_lsa.h6
-rw-r--r--ospfd/ospf_lsdb.c8
-rw-r--r--ospfd/ospf_lsdb.h6
-rw-r--r--ospfd/ospf_opaque.c129
-rw-r--r--ospfd/ospf_te.c55
-rw-r--r--pimd/pim6_cmd.c67
-rw-r--r--pimd/pim6_mld.c80
-rw-r--r--pimd/pim6_mld.h2
-rw-r--r--pimd/pim_cmd.c39
-rw-r--r--pimd/pim_iface.c2
-rw-r--r--pimd/pim_iface.h2
-rw-r--r--pimd/pim_igmp.c8
-rw-r--r--pimd/pim_igmpv3.c9
-rw-r--r--pimd/pim_nb.c12
-rw-r--r--pimd/pim_nb.h2
-rw-r--r--pimd/pim_nb_config.c66
-rw-r--r--pimd/pim_vty.c14
-rw-r--r--tests/helpers/c/prng.c2
-rw-r--r--tests/isisd/test_isis_lspdb.c4
-rw-r--r--tests/lib/test_nexthop_iter.c8
-rw-r--r--tests/topotests/bgp_bmp/test_bgp_bmp_1.py7
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf10
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_add_zita.json155
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_del_donna_prefix.json103
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_eva_down.json120
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_init.json120
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_zita_up.json137
-rw-r--r--tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py234
-rw-r--r--tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py75
-rwxr-xr-xtests/topotests/lib/mcast-tester.py61
-rw-r--r--tests/topotests/multicast_features/__init__.py0
-rw-r--r--tests/topotests/multicast_features/r1/frr.conf48
-rw-r--r--tests/topotests/multicast_features/r2/frr.conf37
-rw-r--r--tests/topotests/multicast_features/r3/frr.conf34
-rw-r--r--tests/topotests/multicast_features/test_multicast_features.py367
-rw-r--r--yang/frr-gmp.yang14
-rw-r--r--yang/frr-pim-rp.yang14
-rw-r--r--zebra/fpm_listener.c2
70 files changed, 2238 insertions, 534 deletions
diff --git a/bfdd/bfd.c b/bfdd/bfd.c
index 164910556b..fade89d04f 100644
--- a/bfdd/bfd.c
+++ b/bfdd/bfd.c
@@ -280,7 +280,7 @@ struct bfd_session *bs_peer_find(struct bfd_peer_cfg *bpc)
gen_bfd_key(&key, &bpc->bpc_peer, &bpc->bpc_local, bpc->bpc_mhop, bpc->bpc_localif,
bpc->bpc_vrfname, bpc->bfd_name);
- return bfd_key_lookup(key);
+ return bfd_key_lookup(&key);
}
/*
@@ -353,7 +353,7 @@ int bfd_session_enable(struct bfd_session *bs)
*/
if (bs->bfd_mode == BFD_MODE_TYPE_SBFD_ECHO || bs->bfd_mode == BFD_MODE_TYPE_SBFD_INIT) {
psock = bp_peer_srh_socketv6(bs);
- if (psock <= 0) {
+ if (psock < 0) {
zlog_err("bp_peer_srh_socketv6 error");
return 0;
}
@@ -770,7 +770,7 @@ struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp,
vrf ? vrf->name : VRF_DEFAULT_NAME, NULL);
/* XXX maybe remoteDiscr should be checked for remoteHeard cases. */
- return bfd_key_lookup(key);
+ return bfd_key_lookup(&key);
}
void bfd_xmt_cb(struct event *t)
@@ -982,7 +982,7 @@ void bfd_session_free(struct bfd_session *bs)
/* Remove session from data plane if any. */
bfd_dplane_delete_session(bs);
- bfd_key_delete(bs->key);
+ bfd_key_delete(&bs->key);
bfd_id_delete(bs->discrs.my_discr);
/* Remove observer if any. */
@@ -1962,11 +1962,11 @@ struct bfd_session *bfd_id_lookup(uint32_t id)
return hash_lookup(bfd_id_hash, &bs);
}
-struct bfd_session *bfd_key_lookup(struct bfd_key key)
+struct bfd_session *bfd_key_lookup(struct bfd_key *key)
{
struct bfd_session bs;
- bs.key = key;
+ bs.key = *key;
return hash_lookup(bfd_key_hash, &bs);
}
@@ -1999,11 +1999,11 @@ struct bfd_session *bfd_id_delete(uint32_t id)
return hash_release(bfd_id_hash, &bs);
}
-struct bfd_session *bfd_key_delete(struct bfd_key key)
+struct bfd_session *bfd_key_delete(struct bfd_key *key)
{
struct bfd_session bs;
- bs.key = key;
+ bs.key = *key;
return hash_release(bfd_key_hash, &bs);
}
diff --git a/bfdd/bfd.h b/bfdd/bfd.h
index d9119d16c2..645a76596f 100644
--- a/bfdd/bfd.h
+++ b/bfdd/bfd.h
@@ -698,10 +698,10 @@ void bfd_vrf_init(void);
void bfd_vrf_terminate(void);
struct bfd_vrf_global *bfd_vrf_look_by_session(struct bfd_session *bfd);
struct bfd_session *bfd_id_lookup(uint32_t id);
-struct bfd_session *bfd_key_lookup(struct bfd_key key);
+struct bfd_session *bfd_key_lookup(struct bfd_key *key);
struct sbfd_reflector *sbfd_discr_lookup(uint32_t discr);
struct bfd_session *bfd_id_delete(uint32_t id);
-struct bfd_session *bfd_key_delete(struct bfd_key key);
+struct bfd_session *bfd_key_delete(struct bfd_key *key);
struct sbfd_reflector *sbfd_discr_delete(uint32_t discr);
bool bfd_id_insert(struct bfd_session *bs);
diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c
index 15da1e2440..f553d56652 100644
--- a/bfdd/bfdd_nb_config.c
+++ b/bfdd/bfdd_nb_config.c
@@ -220,7 +220,7 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop, uint32_
case NB_EV_PREPARE:
if (bfd_mode == BFD_MODE_TYPE_BFD) {
bfd_session_get_key(mhop, args->dnode, &bk);
- bs = bfd_key_lookup(bk);
+ bs = bfd_key_lookup(&bk);
/* This session was already configured by another daemon. */
if (bs != NULL) {
@@ -249,7 +249,7 @@ static int bfd_session_create(struct nb_cb_create_args *args, bool mhop, uint32_
} else if (bfd_mode == BFD_MODE_TYPE_SBFD_ECHO ||
bfd_mode == BFD_MODE_TYPE_SBFD_INIT) {
sbfd_session_get_key(mhop, args->dnode, &bk);
- bs = bfd_key_lookup(bk);
+ bs = bfd_key_lookup(&bk);
/* This session was already configured by another daemon. */
if (bs != NULL) {
@@ -369,7 +369,7 @@ static int bfd_session_destroy(enum nb_event event, const struct lyd_node *dnode
else
sbfd_session_get_key(mhop, dnode, &bk);
- if (bfd_key_lookup(bk) == NULL)
+ if (bfd_key_lookup(&bk) == NULL)
return NB_ERR_INCONSISTENCY;
break;
diff --git a/bfdd/bfdd_nb_state.c b/bfdd/bfdd_nb_state.c
index c528478231..152d01e568 100644
--- a/bfdd/bfdd_nb_state.c
+++ b/bfdd/bfdd_nb_state.c
@@ -52,7 +52,7 @@ bfdd_bfd_sessions_single_hop_lookup_entry(struct nb_cb_lookup_entry_args *args)
memset(&lsa, 0, sizeof(lsa));
gen_bfd_key(&bk, &psa, &lsa, false, ifname, vrf, NULL);
- return bfd_key_lookup(bk);
+ return bfd_key_lookup(&bk);
}
/*
@@ -356,7 +356,7 @@ bfdd_bfd_sessions_multi_hop_lookup_entry(struct nb_cb_lookup_entry_args *args)
strtosa(source_addr, &lsa);
gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf, NULL);
- return bfd_key_lookup(bk);
+ return bfd_key_lookup(&bk);
}
/*
@@ -394,7 +394,7 @@ const void *bfdd_bfd_sessions_sbfd_echo_lookup_entry(struct nb_cb_lookup_entry_a
memset(&psa, 0, sizeof(psa));
gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf, bfdname);
- return bfd_key_lookup(bk);
+ return bfd_key_lookup(&bk);
}
/*
@@ -436,5 +436,5 @@ const void *bfdd_bfd_sessions_sbfd_init_lookup_entry(struct nb_cb_lookup_entry_a
strtosa(dest_addr, &psa);
gen_bfd_key(&bk, &psa, &lsa, true, NULL, vrf, bfdname);
- return bfd_key_lookup(bk);
+ return bfd_key_lookup(&bk);
}
diff --git a/bfdd/event.c b/bfdd/event.c
index e5f43b6cc6..f85b4335be 100644
--- a/bfdd/event.c
+++ b/bfdd/event.c
@@ -107,6 +107,7 @@ void sbfd_init_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
tv_normalize(&tv);
event_add_timer_tv(master, sbfd_init_xmt_cb, bs, &tv, &bs->xmttimer_ev);
+ event_set_tardy_threshold(bs->xmttimer_ev, bs->xmt_TO / 2);
}
void sbfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
@@ -123,6 +124,7 @@ void sbfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
tv_normalize(&tv);
event_add_timer_tv(master, sbfd_echo_xmt_cb, bs, &tv, &bs->echo_xmttimer_ev);
+ event_set_tardy_threshold(bs->echo_xmttimer_ev, bs->echo_xmt_TO / 2);
}
void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
@@ -140,6 +142,7 @@ void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
tv_normalize(&tv);
event_add_timer_tv(master, bfd_xmt_cb, bs, &tv, &bs->xmttimer_ev);
+ event_set_tardy_threshold(bs->xmttimer_ev, bs->xmt_TO / 2);
}
void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
@@ -158,6 +161,7 @@ void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter)
event_add_timer_tv(master, bfd_echo_xmt_cb, bs, &tv,
&bs->echo_xmttimer_ev);
+ event_set_tardy_threshold(bs->echo_xmttimer_ev, bs->echo_xmt_TO / 2);
}
void bfd_recvtimer_delete(struct bfd_session *bs)
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c
index e458e5e5ac..b1bff82b05 100644
--- a/bgpd/bgp_bmp.c
+++ b/bgpd/bgp_bmp.c
@@ -56,6 +56,7 @@ static int bmp_route_update_bgpbmp(struct bmp_targets *bt, afi_t afi, safi_t saf
static void bmp_send_all_bgp(struct peer *peer, bool down);
static struct bmp_imported_bgp *bmp_imported_bgp_find(struct bmp_targets *bt, char *name);
static void bmp_stats_per_instance(struct bgp *bgp, struct bmp_targets *bt);
+static void bmp_bgp_peer_vrf(struct bmp_bgp_peer *bbpeer, struct bgp *bgp);
DEFINE_MGROUP(BMP, "BMP (BGP Monitoring Protocol)");
@@ -551,9 +552,12 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down)
bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
- if (bbpeer && bbpeer->open_tx)
+ if (bbpeer && bbpeer->open_tx) {
+ if (is_locrib)
+ /* update bgp id each time peer up LOC-RIB message is to be sent */
+ bmp_bgp_peer_vrf(bbpeer, peer->bgp);
stream_put(s, bbpeer->open_tx, bbpeer->open_tx_len);
- else {
+ } else {
stream_put(s, dummy_open, sizeof(dummy_open));
zlog_warn("bmp: missing TX OPEN message for peer %s",
peer->host);
@@ -2209,6 +2213,8 @@ static void bmp_bgp_peer_vrf(struct bmp_bgp_peer *bbpeer, struct bgp *bgp)
struct peer *peer = bgp->peer_self;
uint16_t send_holdtime;
as_t local_as;
+ struct stream *s;
+ size_t open_len;
if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
send_holdtime = peer->holdtime;
@@ -2221,8 +2227,8 @@ static void bmp_bgp_peer_vrf(struct bmp_bgp_peer *bbpeer, struct bgp *bgp)
else
local_as = peer->local_as;
- struct stream *s = bgp_open_make(peer, send_holdtime, local_as);
- size_t open_len = stream_get_endp(s);
+ s = bgp_open_make(peer, send_holdtime, local_as, &peer->local_id);
+ open_len = stream_get_endp(s);
bbpeer->open_rx_len = open_len;
if (bbpeer->open_rx)
@@ -2230,6 +2236,10 @@ static void bmp_bgp_peer_vrf(struct bmp_bgp_peer *bbpeer, struct bgp *bgp)
bbpeer->open_rx = XMALLOC(MTYPE_BMP_OPEN, open_len);
memcpy(bbpeer->open_rx, s->data, open_len);
+ stream_free(s);
+
+ s = bgp_open_make(peer, send_holdtime, bgp->as, &bgp->router_id);
+ open_len = stream_get_endp(s);
bbpeer->open_tx_len = open_len;
if (bbpeer->open_tx)
XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c
index b9861acad2..0ef3740d88 100644
--- a/bgpd/bgp_evpn_mh.c
+++ b/bgpd/bgp_evpn_mh.c
@@ -1198,10 +1198,9 @@ int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi,
struct prefix_rd prd;
esi_t esi;
uint32_t eth_tag;
- mpls_label_t label;
+ mpls_label_t label[BGP_MAX_LABELS] = {};
struct in_addr vtep_ip;
struct prefix_evpn p;
- uint8_t num_labels = 0;
if (psize != BGP_EVPN_TYPE1_PSIZE) {
flog_err(EC_BGP_EVPN_ROUTE_INVALID,
@@ -1225,8 +1224,7 @@ int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi,
eth_tag = ntohl(eth_tag);
pfx += EVPN_ETH_TAG_BYTES;
- memcpy(&label, pfx, BGP_LABEL_BYTES);
- num_labels++;
+ memcpy(&label[0], pfx, BGP_LABEL_BYTES);
/* EAD route prefix doesn't include the nexthop in the global
* table
@@ -1236,10 +1234,10 @@ int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi,
/* Process the route. */
if (attr) {
bgp_update(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP,
- BGP_ROUTE_NORMAL, &prd, &label, num_labels, 0, NULL);
+ BGP_ROUTE_NORMAL, &prd, &label[0], 1, 0, NULL);
} else {
bgp_withdraw(peer, (struct prefix *)&p, addpath_id, afi, safi, ZEBRA_ROUTE_BGP,
- BGP_ROUTE_NORMAL, &prd, &label, num_labels);
+ BGP_ROUTE_NORMAL, &prd, &label[0], 1);
}
return 0;
}
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index dc6e0d33c2..8a0c6e10d6 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -766,7 +766,7 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi,
if (detail)
route_vty_out_detail(vty, bgp, bd, bgp_dest_get_prefix(bd), pi,
AFI_L2VPN, SAFI_EVPN, RPKI_NOT_BEING_USED,
- json_path, NULL);
+ json_path, NULL, 0);
else
route_vty_out(vty, &bd->rn->p, pi, 0, SAFI_EVPN,
json_path, false);
@@ -893,7 +893,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn,
if (detail)
route_vty_out_detail(vty, bgp, dest, &tmp_p, pi, AFI_L2VPN,
SAFI_EVPN, RPKI_NOT_BEING_USED, json_path,
- NULL);
+ NULL, 0);
else
route_vty_out(vty, &tmp_p, pi, 0, SAFI_EVPN,
@@ -2569,7 +2569,7 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp,
json_path = json_object_new_array();
route_vty_out_detail(vty, bgp, dest, bgp_dest_get_prefix(dest), pi, afi, safi,
- RPKI_NOT_BEING_USED, json_path, NULL);
+ RPKI_NOT_BEING_USED, json_path, NULL, 0);
if (json)
json_object_array_add(json_paths, json_path);
@@ -2697,7 +2697,7 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
}
route_vty_out_detail(vty, bgp, dest, (struct prefix *)&tmp_p, pi, afi, safi,
- RPKI_NOT_BEING_USED, json_path, NULL);
+ RPKI_NOT_BEING_USED, json_path, NULL, 0);
if (json)
json_object_array_add(json_paths, json_path);
@@ -2807,7 +2807,7 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp,
json_path = json_object_new_array();
route_vty_out_detail(vty, bgp, dest, bgp_dest_get_prefix(dest), pi, afi, safi,
- RPKI_NOT_BEING_USED, json_path, NULL);
+ RPKI_NOT_BEING_USED, json_path, NULL, 0);
if (json)
json_object_array_add(json_paths, json_path);
@@ -2919,7 +2919,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp,
json_path = json_object_new_array();
route_vty_out_detail(vty, bgp, dest, bgp_dest_get_prefix(dest), pi, afi,
- safi, RPKI_NOT_BEING_USED, json_path, NULL);
+ safi, RPKI_NOT_BEING_USED, json_path, NULL, 0);
if (json)
json_object_array_add(json_paths, json_path);
@@ -3055,7 +3055,7 @@ static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp,
json_path = json_object_new_array();
route_vty_out_detail(vty, bgp, dest, p, pi, AFI_L2VPN, SAFI_EVPN,
- RPKI_NOT_BEING_USED, json_path, NULL);
+ RPKI_NOT_BEING_USED, json_path, NULL, 0);
if (json)
json_object_array_add(json_paths, json_path);
@@ -3227,7 +3227,8 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
route_vty_out_detail(vty, bgp, dest,
bgp_dest_get_prefix(dest), pi,
AFI_L2VPN, SAFI_EVPN,
- RPKI_NOT_BEING_USED, json_path, NULL);
+ RPKI_NOT_BEING_USED, json_path, NULL,
+ 0);
} else
route_vty_out(vty, p, pi, 0, SAFI_EVPN,
json_path, false);
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index 580561aa9e..ac2f90e867 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -1052,6 +1052,7 @@ static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn,
struct bgp_path_info *bpi_ultimate;
struct bgp *bgp_nexthop;
struct bgp_table *table;
+ struct interface *ifp;
bool nh_valid;
bpi_ultimate = bgp_get_imported_bpi_ultimate(source_bpi);
@@ -1062,6 +1063,15 @@ static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn,
else
bgp_nexthop = bgp_orig;
+ /* The nexthop is invalid if its VRF does not exist */
+ if (bgp_nexthop->vrf_id == VRF_UNKNOWN)
+ return false;
+
+ /* The nexthop is invalid if its VRF interface is down*/
+ ifp = if_get_vrf_loopback(bgp_nexthop->vrf_id);
+ if (ifp && !if_is_up(ifp))
+ return false;
+
/*
* No nexthop tracking for redistributed routes, for
* EVPN-imported routes that get leaked, or for routes
@@ -1126,8 +1136,8 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
struct bgp_path_info *bpi;
struct bgp_path_info *new;
struct bgp_path_info_extra *extra;
- struct bgp_path_info *parent = source_bpi;
struct bgp_labels bgp_labels = {};
+ struct bgp *bgp_nexthop;
bool labelssame;
uint8_t i;
@@ -1159,8 +1169,7 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
* match parent
*/
for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) {
- if (bpi->extra && bpi->extra->vrfleak &&
- bpi->extra->vrfleak->parent == parent)
+ if (bpi->extra && bpi->extra->vrfleak && bpi->extra->vrfleak->parent == source_bpi)
break;
}
@@ -1174,6 +1183,16 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
labelssame = bgp_path_info_labels_same(bpi, bgp_labels.label,
bgp_labels.num_labels);
+ bgp_nexthop = bpi->extra->vrfleak->bgp_orig ?: bgp_orig;
+ if (bgp_nexthop->vrf_id == VRF_UNKNOWN) {
+ if (debug) {
+ zlog_debug("%s: ->%s(s_flags: 0x%x b_flags: 0x%x): %pFX: Found route, origin VRF does not exist, not leaking",
+ __func__, to_bgp->name_pretty, source_bpi->flags,
+ bpi->flags, p);
+ }
+ return NULL;
+ }
+
if (CHECK_FLAG(source_bpi->flags, BGP_PATH_REMOVED)
&& CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
if (debug) {
@@ -1185,9 +1204,11 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
return NULL;
}
- if (attrhash_cmp(bpi->attr, new_attr) && labelssame
- && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
-
+ if (attrhash_cmp(bpi->attr, new_attr) && labelssame &&
+ !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) &&
+ leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi, source_bpi, bpi,
+ bgp_orig, p,
+ debug) == !!CHECK_FLAG(bpi->flags, BGP_PATH_VALID)) {
bgp_attr_unintern(&new_attr);
if (debug)
zlog_debug(
@@ -1274,6 +1295,14 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
return NULL;
}
+ if (bgp_orig->vrf_id == VRF_UNKNOWN) {
+ if (debug) {
+ zlog_debug("%s: ->%s(s_flags: 0x%x): %pFX: New route, origin VRF does not exist, not leaking",
+ __func__, to_bgp->name_pretty, source_bpi->flags, p);
+ }
+ return NULL;
+ }
+
new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_IMPORTED, 0,
to_bgp->peer_self, new_attr, bn);
@@ -1297,9 +1326,8 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
if (bgp_labels.num_labels)
new->extra->labels = bgp_labels_intern(&bgp_labels);
- new->extra->vrfleak->parent = bgp_path_info_lock(parent);
- bgp_dest_lock_node(
- (struct bgp_dest *)parent->net);
+ new->extra->vrfleak->parent = bgp_path_info_lock(source_bpi);
+ bgp_dest_lock_node((struct bgp_dest *)source_bpi->net);
new->extra->vrfleak->bgp_orig = bgp_lock(bgp_orig);
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index be04d87b74..e03239f014 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -1069,7 +1069,6 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_ROLE:
case CAPABILITY_CODE_SOFT_VERSION:
case CAPABILITY_CODE_PATHS_LIMIT:
- case CAPABILITY_CODE_LINK_LOCAL:
/* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code]) {
zlog_info(
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 3e90d7881c..5ac7c7176e 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -637,7 +637,8 @@ void bgp_keepalive_send(struct peer *peer)
bgp_writes_on(peer->connection);
}
-struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t local_as)
+struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t local_as,
+ struct in_addr *id)
{
struct stream *s = stream_new(BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE);
bool ext_opt_params = false;
@@ -650,7 +651,7 @@ struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t loc
stream_putw(s, (local_as <= BGP_AS_MAX) ? (uint16_t)local_as
: BGP_AS_TRANS);
stream_putw(s, send_holdtime); /* Hold Time */
- stream_put_in_addr(s, &peer->local_id); /* BGP Identifier */
+ stream_put_in_addr(s, id); /* BGP Identifier */
/* Set capabilities */
if (CHECK_FLAG(peer->flags, PEER_FLAG_EXTENDED_OPT_PARAMS)) {
@@ -705,7 +706,7 @@ void bgp_open_send(struct peer_connection *connection)
else
local_as = peer->local_as;
- s = bgp_open_make(peer, send_holdtime, local_as);
+ s = bgp_open_make(peer, send_holdtime, local_as, &peer->local_id);
/* Dump packet if debug option is set. */
/* bgp_packet_dump (s); */
@@ -3781,7 +3782,6 @@ static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
case CAPABILITY_CODE_ROLE:
case CAPABILITY_CODE_SOFT_VERSION:
case CAPABILITY_CODE_PATHS_LIMIT:
- case CAPABILITY_CODE_LINK_LOCAL:
if (hdr->length < cap_minsizes[hdr->code]) {
zlog_info("%pBP: %s Capability length error: got %u, expected at least %u",
peer, capability, hdr->length,
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
index 866b8f617d..ae81aade70 100644
--- a/bgpd/bgp_packet.h
+++ b/bgpd/bgp_packet.h
@@ -49,7 +49,8 @@ DECLARE_HOOK(bgp_packet_send,
/* Packet send and receive function prototypes. */
extern void bgp_keepalive_send(struct peer *peer);
-extern struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t local_as);
+extern struct stream *bgp_open_make(struct peer *peer, uint16_t send_holdtime, as_t local_as,
+ struct in_addr *id);
extern void bgp_open_send(struct peer_connection *connection);
extern void bgp_notify_send(struct peer_connection *connection, uint8_t code,
uint8_t sub_code);
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index a1a5068a7f..2cda03f81b 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -10795,7 +10795,7 @@ static void route_vty_out_detail_es_info(struct vty *vty,
void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
const struct prefix *p, struct bgp_path_info *path, afi_t afi,
safi_t safi, enum rpki_states rpki_curr_state, json_object *json_paths,
- struct attr *pattr)
+ struct attr *pattr, uint16_t show_opts)
{
char buf[INET6_ADDRSTRLEN];
char vni_buf[30] = {};
@@ -11824,6 +11824,16 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
llgr_remaining);
}
+ /* Display internal data if asked */
+ if (CHECK_FLAG(show_opts, BGP_SHOW_OPT_INTERNAL_DATA)) {
+ if (!json_paths) {
+ vty_out(vty, " net: %p, path: %p, pathext: %p, attr: %p\n", path->net,
+ path, path->extra, attr);
+ vty_out(vty, " flags net: 0x%u, path: 0x%u, attr: 0x%" PRIu64 "\n",
+ path->net->flags, path->flags, attr->flag);
+ }
+ }
+
/* Output some debug about internal state of the dest flags */
if (json_paths) {
if (CHECK_FLAG(bn->flags, BGP_NODE_PROCESS_SCHEDULED))
@@ -12255,7 +12265,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t sa
route_vty_out_detail(vty, bgp, dest, dest_p, pi,
family2afi(dest_p->family), safi,
- rpki_curr_state, json_paths, NULL);
+ rpki_curr_state, json_paths, NULL,
+ show_flags);
} else {
route_vty_out(vty, dest_p, pi, display,
safi, json_paths, wide);
@@ -12425,10 +12436,9 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
memcpy(&prd, dest_p, sizeof(struct prefix_rd));
prefix_rd2str(&prd, rd, sizeof(rd), bgp->asnotation);
- bgp_show_table(vty, bgp, afi, safi, itable, type, output_arg,
- rd, next == NULL, &output_cum,
- &total_cum, &json_header_depth,
- show_flags, RPKI_NOT_BEING_USED);
+ bgp_show_table(vty, bgp, afi, safi, itable, type, output_arg, rd,
+ !bgp_dest_get_bgp_table_info(next), &output_cum, &total_cum,
+ &json_header_depth, show_flags, RPKI_NOT_BEING_USED);
if (next == NULL)
show_msg = false;
}
@@ -12773,7 +12783,8 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
static void bgp_show_path_info(const struct prefix_rd *pfx_rd, struct bgp_dest *bgp_node,
struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
json_object *json, enum bgp_path_type pathtype, int *display,
- enum rpki_states rpki_target_state, struct attr *attr)
+ enum rpki_states rpki_target_state, struct attr *attr,
+ uint16_t show_opts)
{
struct bgp_path_info *pi;
int header = 1;
@@ -12817,7 +12828,8 @@ static void bgp_show_path_info(const struct prefix_rd *pfx_rd, struct bgp_dest *
&& (CHECK_FLAG(pi->flags, BGP_PATH_MULTIPATH)
|| CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))))
route_vty_out_detail(vty, bgp, bgp_node, bgp_dest_get_prefix(bgp_node), pi,
- afi, safi, rpki_curr_state, json_paths, attr);
+ afi, safi, rpki_curr_state, json_paths, attr,
+ show_opts);
}
if (json && json_paths) {
@@ -12854,12 +12866,11 @@ const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
}
/* Display specified route of BGP table. */
-static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
- struct bgp_table *rib, const char *ip_str,
- afi_t afi, safi_t safi,
- enum rpki_states rpki_target_state,
- struct prefix_rd *prd, int prefix_check,
- enum bgp_path_type pathtype, bool use_json)
+static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, struct bgp_table *rib,
+ const char *ip_str, afi_t afi, safi_t safi,
+ enum rpki_states rpki_target_state, struct prefix_rd *prd,
+ int prefix_check, enum bgp_path_type pathtype, bool use_json,
+ uint16_t show_opts)
{
int ret;
int display = 0;
@@ -12904,8 +12915,8 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
continue;
}
- bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi,
- json, pathtype, &display, rpki_target_state, NULL);
+ bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi, json,
+ pathtype, &display, rpki_target_state, NULL, show_opts);
bgp_dest_unlock_node(rm);
}
@@ -12964,8 +12975,8 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
rm = longest_pfx;
bgp_dest_lock_node(rm);
- bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi,
- json, pathtype, &display, rpki_target_state, NULL);
+ bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi, json,
+ pathtype, &display, rpki_target_state, NULL, show_opts);
bgp_dest_unlock_node(rm);
}
@@ -12992,7 +13003,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
if (!prefix_check
|| dest_p->prefixlen == match.prefixlen) {
bgp_show_path_info(NULL, dest, vty, bgp, afi, safi, json, pathtype,
- &display, rpki_target_state, NULL);
+ &display, rpki_target_state, NULL, show_opts);
}
bgp_dest_unlock_node(dest);
@@ -13012,10 +13023,10 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
}
/* Display specified route of Main RIB */
-static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str,
- afi_t afi, safi_t safi, struct prefix_rd *prd,
- int prefix_check, enum bgp_path_type pathtype,
- enum rpki_states rpki_target_state, bool use_json)
+static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str, afi_t afi,
+ safi_t safi, struct prefix_rd *prd, int prefix_check,
+ enum bgp_path_type pathtype, enum rpki_states rpki_target_state,
+ bool use_json, uint16_t show_opts)
{
if (!bgp) {
bgp = bgp_get_default();
@@ -13032,9 +13043,9 @@ static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str,
if (safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
- return bgp_show_route_in_table(vty, bgp, bgp->rib[afi][safi], ip_str,
- afi, safi, rpki_target_state, prd,
- prefix_check, pathtype, use_json);
+ return bgp_show_route_in_table(vty, bgp, bgp->rib[afi][safi], ip_str, afi, safi,
+ rpki_target_state, prd, prefix_check, pathtype, use_json,
+ show_opts);
}
static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc,
@@ -13396,7 +13407,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd,
|A.B.C.D/M longer-prefixes\
|X:X::X:X/M longer-prefixes\
|"BGP_SELF_ORIG_CMD_STR"\
- |detail-routes$detail_routes\
+ |detail-routes$detail_routes [internal$internal]\
] [json$uj [detail$detail_json] | wide$wide]",
SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
BGP_SAFI_WITH_LABEL_HELP_STR
@@ -13447,6 +13458,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd,
"Display route and more specific routes\n"
BGP_SELF_ORIG_HELP_STR
"Display detailed version of all routes\n"
+ "Display detailed version of all routes including internal data\n"
JSON_STR
"Display detailed version of JSON output\n"
"Increase table width for longer prefixes\n")
@@ -13475,6 +13487,9 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd,
if (detail_routes)
SET_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL);
+ if (internal)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_INTERNAL_DATA);
+
/* [<ipv4|ipv6> [all]] */
if (all) {
SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL);
@@ -13763,7 +13778,7 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd,
DEFUN (show_ip_bgp_route,
show_ip_bgp_route_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [<bestpath|multipath>] [rpki <valid|invalid|notfound>] [json]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [internal] [<bestpath|multipath>] [rpki <valid|invalid|notfound>] [json]",
SHOW_STR
IP_STR
BGP_STR
@@ -13774,6 +13789,7 @@ DEFUN (show_ip_bgp_route,
"IPv4 prefix\n"
"Network in the BGP routing table to display\n"
"IPv6 prefix\n"
+ "Display internal data additionally\n"
"Display only the bestpath\n"
"Display only multipaths\n"
"Display only paths that match the specified rpki state\n"
@@ -13790,6 +13806,7 @@ DEFUN (show_ip_bgp_route,
struct bgp *bgp = NULL;
enum bgp_path_type path_type;
bool uj = use_json(argc, argv);
+ uint16_t show_opts = 0;
int idx = 0;
@@ -13827,6 +13844,10 @@ DEFUN (show_ip_bgp_route,
prefix = argv[idx]->arg;
+ /* Display internal data also */
+ if (argv_find(argv, argc, "internal", &idx))
+ SET_FLAG(show_opts, BGP_SHOW_OPT_INTERNAL_DATA);
+
/* [<bestpath|multipath>] */
if (argv_find(argv, argc, "bestpath", &idx))
path_type = BGP_PATH_SHOW_BESTPATH;
@@ -13835,8 +13856,8 @@ DEFUN (show_ip_bgp_route,
else
path_type = BGP_PATH_SHOW_ALL;
- return bgp_show_route(vty, bgp, prefix, afi, safi, NULL, prefix_check,
- path_type, RPKI_NOT_BEING_USED, uj);
+ return bgp_show_route(vty, bgp, prefix, afi, safi, NULL, prefix_check, path_type,
+ RPKI_NOT_BEING_USED, uj, show_opts);
}
DEFUN (show_ip_bgp_regexp,
@@ -14694,9 +14715,8 @@ DEFUN (show_ip_bgp_vpn_all_route_prefix,
return CMD_WARNING;
}
- return bgp_show_route(vty, bgp, network, AFI_IP, SAFI_MPLS_VPN, NULL, 0,
- BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED,
- use_json(argc, argv));
+ return bgp_show_route(vty, bgp, network, AFI_IP, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_SHOW_ALL,
+ RPKI_NOT_BEING_USED, use_json(argc, argv), 0);
}
#endif /* KEEP_OLD_VPN_COMMANDS */
@@ -14728,9 +14748,8 @@ DEFUN (show_bgp_l2vpn_evpn_route_prefix,
vty_out(vty, "Unable to figure out Network\n");
return CMD_WARNING;
}
- return bgp_show_route(vty, NULL, network, AFI_L2VPN, SAFI_EVPN, NULL,
- prefix_check, BGP_PATH_SHOW_ALL,
- RPKI_NOT_BEING_USED, use_json(argc, argv));
+ return bgp_show_route(vty, NULL, network, AFI_L2VPN, SAFI_EVPN, NULL, prefix_check,
+ BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED, use_json(argc, argv), 0);
}
static void show_adj_route_header(struct vty *vty, struct peer *peer,
@@ -14886,8 +14905,9 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
if (use_json)
json_net = json_object_new_object();
- bgp_show_path_info(NULL /* prefix_rd */, dest, vty, bgp, afi, safi, json_net,
- BGP_PATH_SHOW_ALL, &display, RPKI_NOT_BEING_USED, NULL);
+ bgp_show_path_info(NULL /* prefix_rd */, dest, vty, bgp, afi, safi,
+ json_net, BGP_PATH_SHOW_ALL, &display,
+ RPKI_NOT_BEING_USED, NULL, show_flags);
if (use_json)
json_object_object_addf(json_ar, json_net,
"%pFX", rn_p);
@@ -15023,7 +15043,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
pass_in = dest;
bgp_show_path_info(NULL, pass_in, vty, bgp, afi, safi,
json_net, BGP_PATH_SHOW_ALL, &display,
- RPKI_NOT_BEING_USED, NULL);
+ RPKI_NOT_BEING_USED, NULL, show_flags);
if (use_json)
json_object_object_addf(
json_ar, json_net,
@@ -15075,7 +15095,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
bgp_show_path_info(NULL, dest, vty, bgp, afi, safi,
json_net, BGP_PATH_SHOW_ALL,
&display, RPKI_NOT_BEING_USED,
- adj->attr);
+ adj->attr, show_flags);
if (use_json)
json_object_object_addf(json_ar, json_net,
"%pFX", rn_p);
@@ -15121,9 +15141,10 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
if (use_json)
json_net =
json_object_new_object();
- bgp_show_path_info(NULL /* prefix_rd */, dest, vty, bgp, afi,
- safi, json_net, BGP_PATH_SHOW_BESTPATH,
- &display, RPKI_NOT_BEING_USED, NULL);
+ bgp_show_path_info(NULL /* prefix_rd */, dest, vty, bgp,
+ afi, safi, json_net,
+ BGP_PATH_SHOW_BESTPATH, &display,
+ RPKI_NOT_BEING_USED, NULL, show_flags);
if (use_json)
json_object_object_addf(
json_ar, json_net,
@@ -15767,10 +15788,9 @@ DEFUN (show_bgp_afi_vpn_rd_route,
}
if (!strcmp(argv[5]->arg, "all"))
- return bgp_show_route(vty, NULL, argv[6]->arg, afi,
- SAFI_MPLS_VPN, NULL, 0, BGP_PATH_SHOW_ALL,
- RPKI_NOT_BEING_USED,
- use_json(argc, argv));
+ return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, NULL, 0,
+ BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED, use_json(argc, argv),
+ 0);
ret = str2prefix_rd(argv[5]->arg, &prd);
if (!ret) {
@@ -15778,9 +15798,8 @@ DEFUN (show_bgp_afi_vpn_rd_route,
return CMD_WARNING;
}
- return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, &prd,
- 0, BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED,
- use_json(argc, argv));
+ return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, &prd, 0,
+ BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED, use_json(argc, argv), 0);
}
static struct bgp_distance *bgp_distance_new(void)
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index c4cbbee0c7..1f854cad19 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -752,6 +752,7 @@ DECLARE_HOOK(bgp_route_update,
#define BGP_SHOW_OPT_JSON_DETAIL (1 << 7)
#define BGP_SHOW_OPT_TERSE (1 << 8)
#define BGP_SHOW_OPT_ROUTES_DETAIL (1 << 9)
+#define BGP_SHOW_OPT_INTERNAL_DATA (1 << 10)
/* Prototypes. */
extern void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi,
@@ -969,7 +970,7 @@ extern void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
extern void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
const struct prefix *p, struct bgp_path_info *path, afi_t afi,
safi_t safi, enum rpki_states, json_object *json_paths,
- struct attr *attr);
+ struct attr *attr, uint16_t show_opts);
extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
struct bgp_table *table, struct prefix_rd *prd,
enum bgp_show_type type, void *output_arg,
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 1669aabc60..84ff88be16 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -2349,6 +2349,13 @@ void bgp_zebra_instance_register(struct bgp *bgp)
bgp_zebra_advertise_all_vni(bgp, 1);
bgp_nht_register_nexthops(bgp);
+
+ /*
+ * Request SRv6 locator information from Zebra, if SRv6 is enabled
+ * and a locator is configured for this BGP instance.
+ */
+ if (bgp->srv6_enabled && bgp->srv6_locator_name[0] != '\0' && !bgp->srv6_locator)
+ bgp_zebra_srv6_manager_get_locator(bgp->srv6_locator_name);
}
/* Deregister this instance with Zebra. Invoked upon the instance
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 1034e49bae..2672f38291 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -3418,17 +3418,6 @@ static struct bgp *bgp_create(as_t *as, const char *name,
}
bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp));
- bgp->as = *as;
- if (as_pretty)
- bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, as_pretty);
- else
- bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, asn_asn2asplain(*as));
-
- if (asnotation != ASNOTATION_UNDEFINED) {
- bgp->asnotation = asnotation;
- SET_FLAG(bgp->config, BGP_CONFIG_ASNOTATION);
- } else
- asn_str2asn_notation(bgp->as_pretty, NULL, &bgp->asnotation);
if (BGP_DEBUG(zebra, ZEBRA)) {
if (inst_type == BGP_INSTANCE_TYPE_DEFAULT)
@@ -3472,6 +3461,18 @@ static struct bgp *bgp_create(as_t *as, const char *name,
bgp->peer = list_new();
peer_init:
+ bgp->as = *as;
+ if (as_pretty)
+ bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, as_pretty);
+ else
+ bgp->as_pretty = XSTRDUP(MTYPE_BGP_NAME, asn_asn2asplain(*as));
+
+ if (asnotation != ASNOTATION_UNDEFINED) {
+ bgp->asnotation = asnotation;
+ SET_FLAG(bgp->config, BGP_CONFIG_ASNOTATION);
+ } else
+ asn_str2asn_notation(bgp->as_pretty, NULL, &bgp->asnotation);
+
bgp->peer->cmp = (int (*)(void *, void *))peer_cmp;
bgp->peerhash = hash_create(peer_hash_key_make, peer_hash_same,
"BGP Peer Hash");
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 42f7ca84dd..a1dd799392 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -4647,7 +4647,7 @@ incoming/outgoing directions.
If ``json`` option is specified, output is displayed in JSON format.
-.. clicmd:: show [ip] bgp [afi] [safi] [all] detail-routes
+.. clicmd:: show [ip] bgp [afi] [safi] [all] detail-routes [internal]
Display the detailed version of all routes. The same format as using
``show [ip] bgp [afi] [safi] PREFIX``, but for the whole BGP table.
@@ -4655,6 +4655,9 @@ incoming/outgoing directions.
If ``all`` option is specified, ``ip`` keyword is ignored and,
routes displayed for all AFIs and SAFIs.
+ ``internal`` option is used to display internal data additionally. JSON
+ output is not supported with this option.
+
If ``afi`` is specified, with ``all`` option, routes will be displayed for
each SAFI in the selected AFI.
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index b2571dfd70..eaf91c7a1f 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -1284,76 +1284,67 @@ Debugging OSPF
.. clicmd:: debug ospf [(1-65535)] client-api
- Show debug information for the OSPF opaque data client API.
+ Enable or disable debugging for the OSPF opaque data client API.
.. clicmd:: debug ospf [(1-65535)] default-information
- Show debug information of default information
+ Enable or disable debugging of default information origination
.. clicmd:: debug ospf [(1-65535)] packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail]
-
- Dump Packet for debugging
+ Enable or disable debugging for received and transmitted OSPF packets
.. clicmd:: debug ospf [(1-65535)] ism [status|events|timers]
-
-
- Show debug information of Interface State Machine
+ Enable or disable debugging of Interface State Machine
.. clicmd:: debug ospf [(1-65535)] nsm [status|events|timers]
-
-
- Show debug information of Network State Machine
+ Enable or disable debugging of Neighbor State Machine transitions
.. clicmd:: debug ospf [(1-65535)] event
-
- Show debug information of OSPF event
+ Enable or disable debugging of OSPF events
.. clicmd:: debug ospf [(1-65535)] nssa
-
- Show debug information about Not So Stub Area
+ Enable or disable debugging about Not-So-Stubby-Areas (NSSAs)
.. clicmd:: debug ospf [(1-65535)] ldp-sync
- Show debug information about LDP-Sync
+ Enable or disable debugging about LDP-Sync
.. clicmd:: debug ospf [(1-65535)] lsa [aggregate|flooding|generate|install|refresh]
-
-
- Show debug detail of Link State messages
+ Enable or disable detail debuggin of Link State Advertisements (LSAs)
.. clicmd:: debug ospf [(1-65535)] sr
- Show debug information about Segment Routing
+ Enable or disable debugging about Segment Routing
.. clicmd:: debug ospf [(1-65535)] te
-
- Show debug information about Traffic Engineering LSA
+ Enable or disable debugging about MPLS Traffic Engineering LSA
.. clicmd:: debug ospf [(1-65535)] ti-lfa
- Show debug information about SR TI-LFA
+ Enable or disable debugging about SR TI-LFA
.. clicmd:: debug ospf [(1-65535)] zebra [interface|redistribute]
+ Enable or disable debugging of ZEBRA API
+.. clicmd:: debug ospf [(1-65535)] graceful-restart
- Show debug information of ZEBRA API
+ Enable or disable debugying for OSPF Graceful Restart Helper
.. clicmd:: debug ospf [(1-65535)] graceful-restart
-
- Enable/disable debug information for OSPF Graceful Restart Helper
+ Enable or disable debugging for OSPF Opaque LSA processing
.. clicmd:: show debugging ospf
-
+ Show enabled OSPF debugging options
Sample Configuration
====================
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index c139e64880..9e4c7bb94a 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -437,6 +437,14 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
Set the IGMP version used on this interface. The default value is 3.
+.. clicmd:: ip igmp max-groups (0-4294967295)
+
+ Set the maximum number of IGMP groups that the can be joined on an interface.
+
+.. clicmd:: ip igmp max-sources (0-4294967295)
+
+ Set the maximum number of IGMP sources to learn per group.
+
.. clicmd:: ip multicast boundary oil WORD
Set a PIM multicast boundary, based upon the WORD prefix-list. If a PIM join
diff --git a/doc/user/pimv6.rst b/doc/user/pimv6.rst
index 555c830615..bd5430f51e 100644
--- a/doc/user/pimv6.rst
+++ b/doc/user/pimv6.rst
@@ -258,6 +258,14 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
Set the MLD version used on this interface. The default value is 2.
+.. clicmd:: ipv6 mld max-groups (0-4294967295)
+
+ Set the maximum number of MLD groups that the can be joined on an interface.
+
+.. clicmd:: ipv6 mld max-sources (0-4294967295)
+
+ Set the maximum number of MLD sources to learn per group.
+
.. clicmd:: ipv6 multicast boundary oil WORD
Set a PIMv6 multicast boundary, based upon the WORD prefix-list. If a PIMv6
@@ -481,6 +489,10 @@ PIMv6 Clear Commands
Clear commands reset various variables.
+.. clicmd:: clear ipv6 mld [vrf NAME] interfaces
+
+ Reset learned multicast groups / sources.
+
.. clicmd:: clear ipv6 mroute
Reset multicast routes.
diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c
index 3024bb57ea..5f95fe48a4 100644
--- a/isisd/isis_nb.c
+++ b/isisd/isis_nb.c
@@ -561,12 +561,6 @@ const struct frr_yang_module_info frr_isisd_info = {
}
},
{
- .xpath = "/frr-isisd:isis/instance/fast-reroute/level-1/lfa/tiebreaker/type",
- .cbs = {
- .modify = isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify,
- }
- },
- {
.xpath = "/frr-isisd:isis/instance/fast-reroute/level-1/remote-lfa/prefix-list",
.cbs = {
.cli_show = cli_show_isis_frr_remote_lfa_plist,
@@ -598,12 +592,6 @@ const struct frr_yang_module_info frr_isisd_info = {
}
},
{
- .xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/lfa/tiebreaker/type",
- .cbs = {
- .modify = isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify,
- }
- },
- {
.xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/remote-lfa/prefix-list",
.cbs = {
.cli_show = cli_show_isis_frr_remote_lfa_plist,
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index fb391534e2..6bd3b39bc7 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -1763,26 +1763,6 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_destroy(
}
/*
- * XPath: /frr-isisd:isis/instance/fast-reroute/level-1/lfa/tiebreaker/type
- */
-int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
- struct nb_cb_modify_args *args)
-{
- struct lfa_tiebreaker *tie_b;
- struct isis_area *area;
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
-
- tie_b = nb_running_get_entry(args->dnode, NULL, true);
- area = tie_b->area;
- tie_b->type = yang_dnode_get_enum(args->dnode, NULL);
- lsp_regenerate_schedule(area, area->is_type, 0);
-
- return NB_OK;
-}
-
-/*
* XPath: /frr-isisd:isis/instance/fast-reroute/level-1/remote-lfa/prefix-list
*/
int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify(
@@ -1912,26 +1892,6 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_destroy(
}
/*
- * XPath: /frr-isisd:isis/instance/fast-reroute/level-2/lfa/tiebreaker/type
- */
-int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify(
- struct nb_cb_modify_args *args)
-{
- struct lfa_tiebreaker *tie_b;
- struct isis_area *area;
-
- if (args->event != NB_EV_APPLY)
- return NB_OK;
-
- tie_b = nb_running_get_entry(args->dnode, NULL, true);
- area = tie_b->area;
- tie_b->type = yang_dnode_get_enum(args->dnode, NULL);
- lsp_regenerate_schedule(area, area->is_type, 0);
-
- return NB_OK;
-}
-
-/*
* XPath: /frr-isisd:isis/instance/fast-reroute/level-2/remote-lfa/prefix-list
*/
int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify(
diff --git a/lib/event.c b/lib/event.c
index 6081ba4727..8c09debf91 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -571,6 +571,11 @@ struct event_loop *event_master_create(const char *name)
rv->spin = true;
rv->handle_signals = true;
+ /* tardy event warnings */
+ monotime(&rv->last_tardy_warning);
+ rv->last_tardy_warning.tv_sec -= (TARDY_WARNING_INTERVAL + TIMER_SECOND_MICRO - 1) /
+ TIMER_SECOND_MICRO;
+
/* Set pthread owner, should be updated by actual owner */
rv->owner = pthread_self();
rv->cancel_req = list_new();
@@ -770,13 +775,13 @@ static struct event *thread_get(struct event_loop *m, uint8_t type,
thread->master = m;
thread->arg = arg;
thread->yield = EVENT_YIELD_TIME_SLOT; /* default */
+ thread->tardy_threshold = 0;
/* thread->ref is zeroed either by XCALLOC above or by memset before
* being put on the "unuse" list by thread_add_unuse().
* Setting it here again makes coverity complain about a missing
* lock :(
*/
/* thread->ref = NULL; */
- thread->ignore_timer_late = false;
/*
* So if the passed in funcname is not what we have
@@ -1023,6 +1028,8 @@ static void _event_add_timer_timeval(const struct xref_eventsched *xref,
return;
thread = thread_get(m, EVENT_TIMER, func, arg, xref);
+ /* default lateness warning: 4s */
+ thread->tardy_threshold = TARDY_DEFAULT_THRESHOLD;
frr_with_mutex (&thread->mtx) {
thread->u.sands = t;
@@ -1685,34 +1692,12 @@ static void thread_process_io(struct event_loop *m, unsigned int num)
static unsigned int thread_process_timers(struct event_loop *m,
struct timeval *timenow)
{
- struct timeval prev = *timenow;
- bool displayed = false;
struct event *thread;
unsigned int ready = 0;
while ((thread = event_timer_list_first(&m->timer))) {
if (timercmp(timenow, &thread->u.sands, <))
break;
- prev = thread->u.sands;
- prev.tv_sec += 4;
- /*
- * If the timer would have popped 4 seconds in the
- * past then we are in a situation where we are
- * really getting behind on handling of events.
- * Let's log it and do the right thing with it.
- */
- if (timercmp(timenow, &prev, >)) {
- atomic_fetch_add_explicit(
- &thread->hist->total_starv_warn, 1,
- memory_order_seq_cst);
- if (!displayed && !thread->ignore_timer_late) {
- flog_warn(
- EC_LIB_STARVE_THREAD,
- "Thread Starvation: %pTHD was scheduled to pop greater than 4s ago",
- thread);
- displayed = true;
- }
- }
event_timer_list_pop(&m->timer);
thread->type = EVENT_READY;
@@ -1946,6 +1931,29 @@ void event_getrusage(RUSAGE_T *r)
#endif
}
+static void event_tardy_warn(struct event *thread, unsigned long since_us)
+{
+ char buf[64];
+ struct fbuf fb = { .buf = buf, .pos = buf, .len = sizeof(buf) };
+ double loadavg[3];
+ int rv;
+
+ rv = getloadavg(loadavg, array_size(loadavg));
+ if (rv < 0)
+ bprintfrr(&fb, "not available");
+ else {
+ for (int i = 0; i < rv; i++) {
+ bprintfrr(&fb, "%.2f", loadavg[i]);
+ if (i < rv - 1)
+ bputs(&fb, ", ");
+ }
+ }
+
+ flog_warn(EC_LIB_STARVE_THREAD,
+ "CPU starvation: %pTHD getting executed %lums late, warning threshold %lums. System load: %pFB",
+ thread, (since_us + 999) / 1000, (thread->tardy_threshold + 999) / 1000, &fb);
+}
+
/*
* Call a thread.
*
@@ -1962,6 +1970,33 @@ void event_call(struct event *thread)
RUSAGE_T before, after;
bool suppress_warnings = EVENT_ARG(thread);
+ if (thread->tardy_threshold) {
+ int64_t timer_late_us = monotime_since(&thread->u.sands, NULL);
+
+ /* Timers have a tardiness warning defaulting to 4s.
+ * It can be customized with event_set_tardy_threshold()
+ * (bfdd does that since the protocol has really short timers)
+ *
+ * If we are more than that threshold late, print a warning
+ * since we're running behind in calling timers (probably due
+ * to high system load.)
+ */
+ if (timer_late_us > (int64_t)thread->tardy_threshold) {
+ int64_t since_last_warning;
+ struct timeval *tw;
+
+ atomic_fetch_add_explicit(&thread->hist->total_starv_warn, 1,
+ memory_order_seq_cst);
+
+ tw = &thread->master->last_tardy_warning;
+ since_last_warning = monotime_since(tw, NULL);
+ if (since_last_warning > TARDY_WARNING_INTERVAL) {
+ event_tardy_warn(thread, timer_late_us);
+ monotime(tw);
+ }
+ }
+ }
+
/* if the thread being called is the CLI, it may change cputime_enabled
* ("service cputime-stats" command), which can result in nonsensical
* and very confusing warnings
@@ -2145,9 +2180,9 @@ static ssize_t printfrr_thread_dbg(struct fbuf *buf, struct printfrr_eargs *ea,
char info[16] = "";
if (!thread)
- return bputs(buf, "{(thread *)NULL}");
+ return bputs(buf, "{(event *)NULL}");
- rv += bprintfrr(buf, "{(thread *)%p arg=%p", thread, thread->arg);
+ rv += bprintfrr(buf, "{(event *)%p arg=%p", thread, thread->arg);
if (thread->type < array_size(types) && types[thread->type])
rv += bprintfrr(buf, " %-6s", types[thread->type]);
diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c
index 3a4bc712fc..0b4d7c77ae 100644
--- a/lib/frr_pthread.c
+++ b/lib/frr_pthread.c
@@ -20,6 +20,7 @@
#include "zlog.h"
#include "libfrr.h"
#include "libfrr_trace.h"
+#include "sigevent.h"
DEFINE_MTYPE_STATIC(LIB, FRR_PTHREAD, "FRR POSIX Thread");
DEFINE_MTYPE_STATIC(LIB, PTHREAD_PRIM, "POSIX sync primitives");
@@ -185,10 +186,9 @@ int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
assert(frr_is_after_fork || !"trying to start thread before fork()");
- /* Ensure we never handle signals on a background thread by blocking
- * everything here (new thread inherits signal mask)
- */
- sigfillset(&blocksigs);
+ sigemptyset(&blocksigs);
+ frr_sigset_add_mainonly(&blocksigs);
+ /* new thread inherits mask */
pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs);
frrtrace(1, frr_libfrr, frr_pthread_run, fpt->name);
diff --git a/lib/frrcu.c b/lib/frrcu.c
index b85c525c58..1e7ed99eff 100644
--- a/lib/frrcu.c
+++ b/lib/frrcu.c
@@ -42,6 +42,7 @@
#include "frrcu.h"
#include "seqlock.h"
#include "atomlist.h"
+#include "sigevent.h"
DEFINE_MTYPE_STATIC(LIB, RCU_THREAD, "RCU thread");
DEFINE_MTYPE_STATIC(LIB, RCU_NEXT, "RCU sequence barrier");
@@ -346,7 +347,19 @@ static void rcu_start(void)
*/
sigset_t oldsigs, blocksigs;
- sigfillset(&blocksigs);
+ /* technically, the RCU thread is very poorly suited to run even just a
+ * crashlog handler, since zlog_sigsafe() could deadlock on transiently
+ * invalid (due to RCU) logging data structures
+ *
+ * but given that when we try to write a crashlog, we're already in
+ * b0rked territory anyway - give the crashlog handler a chance.
+ *
+ * (also cf. the SIGALRM usage in writing crashlogs to avoid hung
+ * processes on any kind of deadlock in crash handlers)
+ */
+ sigemptyset(&blocksigs);
+ frr_sigset_add_mainonly(&blocksigs);
+ /* new thread inherits mask */
pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs);
rcu_active = true;
diff --git a/lib/frrevent.h b/lib/frrevent.h
index c35b39a147..61baf7919c 100644
--- a/lib/frrevent.h
+++ b/lib/frrevent.h
@@ -95,6 +95,7 @@ struct event_loop {
bool ready_run_loop;
RUSAGE_T last_getrusage;
+ struct timeval last_tardy_warning;
};
/* Event types. */
@@ -126,11 +127,17 @@ struct event {
struct timeval real;
struct cpu_event_history *hist; /* cache pointer to cpu_history */
unsigned long yield; /* yield time in microseconds */
+ /* lateness warning threshold, usec. 0 if it's not a timer. */
+ unsigned long tardy_threshold;
const struct xref_eventsched *xref; /* origin location */
pthread_mutex_t mtx; /* mutex for thread.c functions */
- bool ignore_timer_late;
};
+/* rate limit late timer warnings */
+#define TARDY_WARNING_INTERVAL 10 * TIMER_SECOND_MICRO
+/* default threshold for late timer warning */
+#define TARDY_DEFAULT_THRESHOLD 4 * TIMER_SECOND_MICRO
+
#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pTH"(struct event *)
#endif
@@ -305,9 +312,17 @@ static inline bool event_is_scheduled(struct event *thread)
/* Debug signal mask */
void debug_signals(const sigset_t *sigs);
+/* getting called more than given microseconds late will print a warning.
+ * Default if not called: 4s. Don't call this on non-timers.
+ */
+static inline void event_set_tardy_threshold(struct event *event, unsigned long thres)
+{
+ event->tardy_threshold = thres;
+}
+
static inline void event_ignore_late_timer(struct event *event)
{
- event->ignore_timer_late = true;
+ event->tardy_threshold = 0;
}
#ifdef __cplusplus
diff --git a/lib/sigevent.h b/lib/sigevent.h
index 0b07f594c1..2c51ba3767 100644
--- a/lib/sigevent.h
+++ b/lib/sigevent.h
@@ -45,6 +45,33 @@ bool frr_sigevent_check(sigset_t *setp);
/* check whether there are signals to handle, process any found */
extern int frr_sigevent_process(void);
+/* Ensure we don't handle "application-type" signals on a secondary thread by
+ * blocking these signals when creating threads
+ *
+ * NB: SIGSEGV, SIGABRT, etc. must be allowed on all threads or we get no
+ * crashlogs. Since signals vary a little bit between platforms, below is a
+ * list of known things to go to the main thread. Any unknown signals should
+ * stay thread-local.
+ */
+static inline void frr_sigset_add_mainonly(sigset_t *blocksigs)
+{
+ /* signals we actively handle */
+ sigaddset(blocksigs, SIGHUP);
+ sigaddset(blocksigs, SIGINT);
+ sigaddset(blocksigs, SIGTERM);
+ sigaddset(blocksigs, SIGUSR1);
+
+ /* signals we don't actively use but that semantically belong */
+ sigaddset(blocksigs, SIGUSR2);
+ sigaddset(blocksigs, SIGQUIT);
+ sigaddset(blocksigs, SIGCHLD);
+ sigaddset(blocksigs, SIGPIPE);
+ sigaddset(blocksigs, SIGTSTP);
+ sigaddset(blocksigs, SIGTTIN);
+ sigaddset(blocksigs, SIGTTOU);
+ sigaddset(blocksigs, SIGWINCH);
+}
+
#ifdef __cplusplus
}
#endif
diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c
index be909c862a..564e04deba 100644
--- a/nhrpd/netlink_arp.c
+++ b/nhrpd/netlink_arp.c
@@ -184,7 +184,7 @@ int nhrp_neighbor_operation(ZAPI_CALLBACK_ARGS)
: (cmd == ZEBRA_NEIGH_ADDED) ? "new-neigh"
: "del-neigh",
&addr, ifp->name, &lladdr, ndm_state, c->used, c->cur.type);
- if (cmd == ZEBRA_NEIGH_GET) {
+ if (cmd == ZEBRA_NEIGH_GET && ndm_state != ZEBRA_NEIGH_STATE_INCOMPLETE) {
if (c->cur.type >= NHRP_CACHE_CACHED) {
nhrp_cache_set_used(c, 1);
debugf(NHRP_DEBUG_KERNEL,
diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c
index fcc28c6f9f..6582db5390 100644
--- a/ospfd/ospf_apiserver.c
+++ b/ospfd/ospf_apiserver.c
@@ -234,26 +234,6 @@ static struct ospf_apiserver *lookup_apiserver_by_lsa(struct ospf_lsa *lsa)
return found;
}
-/* -----------------------------------------------------------
- * Following are functions to manage client connections.
- * -----------------------------------------------------------
- */
-static int ospf_apiserver_new_lsa_hook(struct ospf_lsa *lsa)
-{
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("API: Put LSA(%p)[%s] into reserve, total=%ld",
- (void *)lsa, dump_lsa_key(lsa), lsa->lsdb->total);
- return 0;
-}
-
-static int ospf_apiserver_del_lsa_hook(struct ospf_lsa *lsa)
-{
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("API: Get LSA(%p)[%s] from reserve, total=%ld",
- (void *)lsa, dump_lsa_key(lsa), lsa->lsdb->total);
- return 0;
-}
-
/* Allocate new connection structure. */
struct ospf_apiserver *ospf_apiserver_new(int fd_sync, int fd_async)
{
@@ -270,12 +250,11 @@ struct ospf_apiserver *ospf_apiserver_new(int fd_sync, int fd_async)
new->opaque_types = list_new();
/* Initialize temporary strage for LSA instances to be refreshed. */
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("API: Initiallize the reserve LSDB");
memset(&new->reserve, 0, sizeof(struct ospf_lsdb));
ospf_lsdb_init(&new->reserve);
- new->reserve.new_lsa_hook = ospf_apiserver_new_lsa_hook; /* debug */
- new->reserve.del_lsa_hook = ospf_apiserver_del_lsa_hook; /* debug */
-
new->out_sync_fifo = msg_fifo_new();
new->out_async_fifo = msg_fifo_new();
new->t_sync_read = NULL;
@@ -363,6 +342,9 @@ void ospf_apiserver_free(struct ospf_apiserver *apiserv)
msg_fifo_free(apiserv->out_async_fifo);
/* Clear temporary strage for LSA instances to be refreshed. */
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("API: Delete all LSAs from reserve LSDB, total=%ld",
+ apiserv->reserve.total);
ospf_lsdb_delete_all(&apiserv->reserve);
ospf_lsdb_cleanup(&apiserv->reserve);
@@ -371,7 +353,7 @@ void ospf_apiserver_free(struct ospf_apiserver *apiserv)
XFREE(MTYPE_APISERVER_MSGFILTER, apiserv->filter);
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
zlog_debug("API: Delete apiserv(%p), total#(%d)",
(void *)apiserv, apiserver_list->count);
@@ -393,7 +375,7 @@ void ospf_apiserver_read(struct event *thread)
event = OSPF_APISERVER_SYNC_READ;
apiserv->t_sync_read = NULL;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
zlog_debug("API: %s: Peer: %pI4/%u", __func__,
&apiserv->peer_sync.sin_addr,
ntohs(apiserv->peer_sync.sin_port));
@@ -403,7 +385,7 @@ void ospf_apiserver_read(struct event *thread)
event = OSPF_APISERVER_ASYNC_READ;
apiserv->t_async_read = NULL;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
zlog_debug("API: %s: Peer: %pI4/%u", __func__,
&apiserv->peer_async.sin_addr,
ntohs(apiserv->peer_async.sin_port));
@@ -426,7 +408,7 @@ void ospf_apiserver_read(struct event *thread)
return;
}
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
msg_print(msg);
/* Dispatch to corresponding message handler. */
@@ -457,7 +439,7 @@ void ospf_apiserver_sync_write(struct event *thread)
goto out;
}
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
zlog_debug("API: %s: Peer: %pI4/%u", __func__,
&apiserv->peer_sync.sin_addr,
ntohs(apiserv->peer_sync.sin_port));
@@ -469,7 +451,7 @@ void ospf_apiserver_sync_write(struct event *thread)
return;
}
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
msg_print(msg);
rc = msg_write(fd, msg);
@@ -517,7 +499,7 @@ void ospf_apiserver_async_write(struct event *thread)
goto out;
}
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
zlog_debug("API: %s: Peer: %pI4/%u", __func__,
&apiserv->peer_async.sin_addr,
ntohs(apiserv->peer_async.sin_port));
@@ -529,7 +511,7 @@ void ospf_apiserver_async_write(struct event *thread)
return;
}
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
msg_print(msg);
rc = msg_write(fd, msg);
@@ -639,7 +621,7 @@ void ospf_apiserver_accept(struct event *thread)
return;
}
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
zlog_debug("API: %s: New peer: %pI4/%u", __func__,
&peer_sync.sin_addr, ntohs(peer_sync.sin_port));
@@ -701,7 +683,7 @@ void ospf_apiserver_accept(struct event *thread)
apiserv);
#endif /* USE_ASYNC_READ */
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
zlog_debug("API: New apiserv(%p), total#(%d)", (void *)apiserv,
apiserver_list->count);
}
@@ -888,7 +870,7 @@ int ospf_apiserver_register_opaque_type(struct ospf_apiserver *apiserv,
/* Add to list of registered opaque types */
listnode_add(apiserv->opaque_types, regtype);
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
zlog_debug(
"API: Add LSA-type(%d)/Opaque-type(%d) into apiserv(%p), total#(%d)",
lsa_type, opaque_type, (void *)apiserv,
@@ -919,7 +901,7 @@ int ospf_apiserver_unregister_opaque_type(struct ospf_apiserver *apiserv,
listnode_delete(apiserv->opaque_types, regtype);
XFREE(MTYPE_APISERVER, regtype);
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_CLIENT_API)
zlog_debug(
"API: Del LSA-type(%d)/Opaque-type(%d) from apiserv(%p), total#(%d)",
lsa_type, opaque_type, (void *)apiserv,
@@ -1553,7 +1535,7 @@ struct ospf_lsa *ospf_apiserver_opaque_lsa_new(struct ospf_area *area,
options |= OSPF_OPTION_O; /* Don't forget to set option bit */
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+ if (IS_DEBUG_OSPF_CLIENT_API) {
zlog_debug("LSA[Type%d:%pI4]: Creating an Opaque-LSA instance",
protolsa->type, &protolsa->id);
}
@@ -1716,6 +1698,9 @@ int ospf_apiserver_handle_originate_request(struct ospf_apiserver *apiserv,
*/
new->lsdb = &apiserv->reserve;
ospf_lsdb_add(&apiserv->reserve, new);
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("API: Add LSA(%p)[%s] into reserve LSDB, total=%ld", (void *)new,
+ dump_lsa_key(new), new->lsdb->total);
/* Kick the scheduler function. */
ospf_opaque_lsa_refresh_schedule(old);
@@ -1878,7 +1863,7 @@ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa)
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
assert(ospf);
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
+ if (IS_DEBUG_OSPF_CLIENT_API) {
zlog_debug("LSA[Type%d:%pI4]: OSPF API Server LSA Refresher",
lsa->data->type, &lsa->data->id);
}
@@ -1909,6 +1894,9 @@ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa)
}
} else {
/* This is a forcible refresh, requested by OSPF-API client. */
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("API: Delete LSA(%p)[%s] from reserve LSDB, total=%ld",
+ (void *)new, dump_lsa_key(new), new->lsdb->total);
ospf_lsdb_delete(&apiserv->reserve, new);
new->lsdb = NULL;
}
@@ -2597,18 +2585,23 @@ static int apiserver_clients_lsa_change_notify(uint8_t msgtype,
int ospf_apiserver_lsa_update(struct ospf_lsa *lsa)
{
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("API: LSA Update Hook Type-%u Opaque-LSA: [opaque-type=%u, opaque-id=%x] Seq: 0x%x %s",
+ lsa->data->type, GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)),
+ GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)), ntohl(lsa->data->ls_seqnum),
+ IS_LSA_MAXAGE(lsa) ? "MaxAged " : "");
- /* Only notify this update if the LSA's age is smaller than
- MAXAGE. Otherwise clients would see LSA updates with max age just
- before they are deleted from the LSDB. LSA delete messages have
- MAXAGE too but should not be filtered. */
- if (IS_LSA_MAXAGE(lsa))
- return 0;
return apiserver_clients_lsa_change_notify(MSG_LSA_UPDATE_NOTIFY, lsa);
}
int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa)
{
+ if (IS_DEBUG_OSPF_CLIENT_API)
+ zlog_debug("API: LSA Delete Hook Type-%u Opaque-LSA: [opaque-type=%u, opaque-id=%x] Seq: 0x%x %s",
+ lsa->data->type, GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)),
+ GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)), ntohl(lsa->data->ls_seqnum),
+ IS_LSA_MAXAGE(lsa) ? "MaxAged " : "");
+
return apiserver_clients_lsa_change_notify(MSG_LSA_DELETE_NOTIFY, lsa);
}
diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c
index dbe6dd97d0..124b38219d 100644
--- a/ospfd/ospf_dump.c
+++ b/ospfd/ospf_dump.c
@@ -46,6 +46,7 @@ unsigned long conf_debug_ospf_ldp_sync;
unsigned long conf_debug_ospf_gr;
unsigned long conf_debug_ospf_bfd;
unsigned long conf_debug_ospf_client_api;
+unsigned long conf_debug_ospf_opaque_lsa;
/* Enable debug option variables -- valid only session. */
unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0};
@@ -64,6 +65,7 @@ unsigned long term_debug_ospf_ldp_sync;
unsigned long term_debug_ospf_gr;
unsigned long term_debug_ospf_bfd;
unsigned long term_debug_ospf_client_api;
+unsigned long term_debug_ospf_opaque_lsa;
const char *ospf_redist_string(unsigned int route_type)
{
@@ -1577,6 +1579,33 @@ DEFPY (debug_ospf_client_api,
return CMD_SUCCESS;
}
+DEFPY (debug_ospf_opaque_lsa,
+ debug_ospf_opaque_lsa_cmd,
+ "[no$no] debug ospf [(1-65535)$instance] opaque-lsa",
+ NO_STR
+ DEBUG_STR
+ OSPF_STR
+ "Instance ID\n"
+ "OSPF Opaque LSA information\n")
+{
+ if (instance && instance != ospf_instance)
+ return CMD_NOT_MY_INSTANCE;
+
+ if (vty->node == CONFIG_NODE) {
+ if (no)
+ DEBUG_OFF(opaque_lsa, OPAQUE_LSA);
+ else
+ DEBUG_ON(opaque_lsa, OPAQUE_LSA);
+ } else {
+ if (no)
+ TERM_DEBUG_OFF(opaque_lsa, OPAQUE_LSA);
+ else
+ TERM_DEBUG_ON(opaque_lsa, OPAQUE_LSA);
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN (no_debug_ospf,
no_debug_ospf_cmd,
"no debug ospf",
@@ -1612,6 +1641,7 @@ DEFUN (no_debug_ospf,
DEBUG_OFF(sr, SR);
DEBUG_OFF(ti_lfa, TI_LFA);
DEBUG_OFF(client_api, CLIENT_API);
+ DEBUG_OFF(opaque_lsa, OPAQUE_LSA);
/* BFD debugging is two parts: OSPF and library. */
DEBUG_OFF(bfd, BFD_LIB);
@@ -1649,6 +1679,7 @@ DEFUN (no_debug_ospf,
TERM_DEBUG_OFF(ti_lfa, TI_LFA);
TERM_DEBUG_OFF(bfd, BFD_LIB);
TERM_DEBUG_OFF(client_api, CLIENT_API);
+ TERM_DEBUG_OFF(opaque_lsa, OPAQUE_LSA);
return CMD_SUCCESS;
}
@@ -1774,10 +1805,14 @@ static int show_debugging_ospf_common(struct vty *vty)
vty_out(vty,
" OSPF BFD integration library debugging is on\n");
- /* Show debug status for LDP-SYNC. */
+ /* Show debug status for CLIENT-API. */
if (IS_DEBUG_OSPF(client_api, CLIENT_API) == OSPF_DEBUG_CLIENT_API)
vty_out(vty, " OSPF client-api debugging is on\n");
+ /* Show debug status for OPAQUE-LSA. */
+ if (IS_DEBUG_OSPF(opaque_lsa, OPAQUE_LSA) == OSPF_DEBUG_OPAQUE_LSA)
+ vty_out(vty, " OSPF opaque-lsa debugging is on\n");
+
return CMD_SUCCESS;
}
@@ -1983,6 +2018,12 @@ static int config_write_debug(struct vty *vty)
write = 1;
}
+ /* debug ospf opaque-lsa */
+ if (IS_CONF_DEBUG_OSPF(opaque_lsa, OPAQUE_LSA) == OSPF_DEBUG_OPAQUE_LSA) {
+ vty_out(vty, "debug ospf%s opaque-lsa\n", str);
+ write = 1;
+ }
+
/* debug ospf default-information */
if (IS_CONF_DEBUG_OSPF(defaultinfo, DEFAULTINFO) ==
OSPF_DEBUG_DEFAULTINFO) {
@@ -2011,6 +2052,7 @@ void ospf_debug_init(void)
install_element(ENABLE_NODE, &debug_ospf_default_info_cmd);
install_element(ENABLE_NODE, &debug_ospf_ldp_sync_cmd);
install_element(ENABLE_NODE, &debug_ospf_client_api_cmd);
+ install_element(ENABLE_NODE, &debug_ospf_opaque_lsa_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd);
install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd);
@@ -2050,6 +2092,7 @@ void ospf_debug_init(void)
install_element(CONFIG_NODE, &debug_ospf_default_info_cmd);
install_element(CONFIG_NODE, &debug_ospf_ldp_sync_cmd);
install_element(CONFIG_NODE, &debug_ospf_client_api_cmd);
+ install_element(CONFIG_NODE, &debug_ospf_opaque_lsa_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd);
install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd);
diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h
index 0d47be2565..7e36fb0430 100644
--- a/ospfd/ospf_dump.h
+++ b/ospfd/ospf_dump.h
@@ -55,6 +55,8 @@
#define OSPF_DEBUG_CLIENT_API 0x01
+#define OSPF_DEBUG_OPAQUE_LSA 0x01
+
/* Macro for setting debug option. */
#define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b)
#define CONF_DEBUG_PACKET_OFF(a, b) conf_debug_ospf_packet[a] &= ~(b)
@@ -106,6 +108,7 @@
#define IS_DEBUG_OSPF_LDP_SYNC IS_DEBUG_OSPF(ldp_sync, LDP_SYNC)
#define IS_DEBUG_OSPF_GR IS_DEBUG_OSPF(gr, GR)
#define IS_DEBUG_OSPF_CLIENT_API IS_DEBUG_OSPF(client_api, CLIENT_API)
+#define IS_DEBUG_OSPF_OPAQUE_LSA IS_DEBUG_OSPF(opaque_lsa, OPAQUE_LSA)
#define IS_CONF_DEBUG_OSPF_PACKET(a, b) \
(conf_debug_ospf_packet[a] & OSPF_DEBUG_##b)
@@ -131,6 +134,7 @@ extern unsigned long term_debug_ospf_ldp_sync;
extern unsigned long term_debug_ospf_gr;
extern unsigned long term_debug_ospf_bfd;
extern unsigned long term_debug_ospf_client_api;
+extern unsigned long term_debug_ospf_opaque_lsa;
/* Message Strings. */
extern char *ospf_lsa_type_str[];
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index ac53f3a19f..1d5c0be5c4 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -61,6 +61,12 @@ static struct ospf_lsa *
ospf_exnl_lsa_prepare_and_flood(struct ospf *ospf, struct external_info *ei,
struct in_addr id);
+/*
+ * LSA Update and Delete Hook LSAs.
+ */
+DEFINE_HOOK(ospf_lsa_update, (struct ospf_lsa *lsa), (lsa));
+DEFINE_HOOK(ospf_lsa_delete, (struct ospf_lsa *lsa), (lsa));
+
uint32_t get_metric(uint8_t *metric)
{
uint32_t m;
@@ -3146,6 +3152,11 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi,
zlog_debug("LSA[%s]: Install LSA %p, MaxAge",
dump_lsa_key(new), lsa);
ospf_lsa_maxage(ospf, lsa);
+ } else {
+ /*
+ * Invoke the LSA update hook.
+ */
+ hook_call(ospf_lsa_update, new);
}
return new;
@@ -3364,6 +3375,11 @@ void ospf_lsa_maxage(struct ospf *ospf, struct ospf_lsa *lsa)
zlog_debug("LSA[%s]: MaxAge LSA remover scheduled.",
dump_lsa_key(lsa));
+ /*
+ * Invoke the LSA delete hook.
+ */
+ hook_call(ospf_lsa_delete, lsa);
+
OSPF_TIMER_ON(ospf->t_maxage, ospf_maxage_lsa_remover,
ospf->maxage_delay);
}
diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h
index 418675cb10..f58bbde07a 100644
--- a/ospfd/ospf_lsa.h
+++ b/ospfd/ospf_lsa.h
@@ -363,4 +363,10 @@ static inline bool ospf_check_indication_lsa(struct ospf_lsa *lsa)
return false;
}
+
+/*
+ * LSA Update and Delete Hook LSAs.
+ */
+DECLARE_HOOK(ospf_lsa_update, (struct ospf_lsa *lsa), (lsa));
+DECLARE_HOOK(ospf_lsa_delete, (struct ospf_lsa *lsa), (lsa));
#endif /* _ZEBRA_OSPF_LSA_H */
diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c
index d1b3eb0d35..8e36940043 100644
--- a/ospfd/ospf_lsdb.c
+++ b/ospfd/ospf_lsdb.c
@@ -147,10 +147,6 @@ static void ospf_lsdb_delete_entry(struct ospf_lsdb *lsdb,
rn->info = NULL;
route_unlock_node(rn);
-#ifdef MONITOR_LSDB_CHANGE
- if (lsdb->del_lsa_hook != NULL)
- (*lsdb->del_lsa_hook)(lsa);
-#endif /* MONITOR_LSDB_CHANGE */
ospf_lsa_unlock(&lsa); /* lsdb */
return;
}
@@ -187,10 +183,6 @@ void ospf_lsdb_add(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa)
CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC))
lsa->area->fr_info.router_lsas_recv_dc_bit++;
-#ifdef MONITOR_LSDB_CHANGE
- if (lsdb->new_lsa_hook != NULL)
- (*lsdb->new_lsa_hook)(lsa);
-#endif /* MONITOR_LSDB_CHANGE */
lsdb->type[lsa->data->type].checksum += ntohs(lsa->data->checksum);
rn->info = ospf_lsa_lock(lsa); /* lsdb */
}
diff --git a/ospfd/ospf_lsdb.h b/ospfd/ospf_lsdb.h
index e5e3be8baa..e09c2937a5 100644
--- a/ospfd/ospf_lsdb.h
+++ b/ospfd/ospf_lsdb.h
@@ -19,12 +19,6 @@ struct ospf_lsdb {
struct route_table *db;
} type[OSPF_MAX_LSA];
unsigned long total;
-#define MONITOR_LSDB_CHANGE 1 /* XXX */
-#ifdef MONITOR_LSDB_CHANGE
- /* Hooks for callback functions to catch every add/del event. */
- int (*new_lsa_hook)(struct ospf_lsa *);
- int (*del_lsa_hook)(struct ospf_lsa *);
-#endif /* MONITOR_LSDB_CHANGE */
};
/* Macros. */
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index d8b272d60b..0634178ca7 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -62,8 +62,13 @@ static void ospf_opaque_funclist_init(void);
static void ospf_opaque_funclist_term(void);
static void free_opaque_info_per_type_del(void *val);
static void free_opaque_info_per_id(void *val);
-static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa);
+
+/*
+ * OSPF Opaque specific hooks and state.
+ */
+static int ospf_opaque_lsa_update_hook(struct ospf_lsa *lsa);
static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa);
+static bool ospf_opaque_lsa_hooks_registered;
void ospf_opaque_init(void)
{
@@ -152,19 +157,19 @@ int ospf_opaque_type10_lsa_init(struct ospf_area *area)
area->opaque_lsa_self = list_new();
area->opaque_lsa_self->del = free_opaque_info_per_type_del;
area->t_opaque_lsa_self = NULL;
-
-#ifdef MONITOR_LSDB_CHANGE
- area->lsdb->new_lsa_hook = ospf_opaque_lsa_install_hook;
- area->lsdb->del_lsa_hook = ospf_opaque_lsa_delete_hook;
-#endif /* MONITOR_LSDB_CHANGE */
+ if (!ospf_opaque_lsa_hooks_registered) {
+ hook_register(ospf_lsa_update, ospf_opaque_lsa_update_hook);
+ hook_register(ospf_lsa_delete, ospf_opaque_lsa_delete_hook);
+ ospf_opaque_lsa_hooks_registered = true;
+ }
return 0;
}
void ospf_opaque_type10_lsa_term(struct ospf_area *area)
{
-#ifdef MONITOR_LSDB_CHANGE
- area->lsdb->new_lsa_hook = area->lsdb->del_lsa_hook = NULL;
-#endif /* MONITOR_LSDB_CHANGE */
+ hook_unregister(ospf_lsa_update, ospf_opaque_lsa_update_hook);
+ hook_unregister(ospf_lsa_delete, ospf_opaque_lsa_delete_hook);
+ ospf_opaque_lsa_hooks_registered = false;
EVENT_OFF(area->t_opaque_lsa_self);
if (area->opaque_lsa_self != NULL)
@@ -181,19 +186,11 @@ int ospf_opaque_type11_lsa_init(struct ospf *top)
top->opaque_lsa_self->del = free_opaque_info_per_type_del;
top->t_opaque_lsa_self = NULL;
-#ifdef MONITOR_LSDB_CHANGE
- top->lsdb->new_lsa_hook = ospf_opaque_lsa_install_hook;
- top->lsdb->del_lsa_hook = ospf_opaque_lsa_delete_hook;
-#endif /* MONITOR_LSDB_CHANGE */
return 0;
}
void ospf_opaque_type11_lsa_term(struct ospf *top)
{
-#ifdef MONITOR_LSDB_CHANGE
- top->lsdb->new_lsa_hook = top->lsdb->del_lsa_hook = NULL;
-#endif /* MONITOR_LSDB_CHANGE */
-
EVENT_OFF(top->t_opaque_lsa_self);
if (top->opaque_lsa_self != NULL)
list_delete(&top->opaque_lsa_self);
@@ -297,7 +294,7 @@ static void ospf_opaque_del_functab(void *val)
{
struct ospf_opaque_functab *functab = (struct ospf_opaque_functab *)val;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Opaque LSA functab list deletion callback type %u (%p)",
__func__, functab->opaque_type, functab);
@@ -309,7 +306,7 @@ static void ospf_opaque_funclist_init(void)
{
struct list *funclist;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Function list initialize", __func__);
funclist = ospf_opaque_wildcard_funclist = list_new();
@@ -330,7 +327,7 @@ static void ospf_opaque_funclist_term(void)
{
struct list *funclist;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Function list terminate", __func__);
funclist = ospf_opaque_wildcard_funclist;
@@ -408,7 +405,7 @@ int ospf_register_opaque_functab(
for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab))
if (functab->opaque_type == opaque_type) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Opaque LSA functab found type %u, (%p)",
__func__, functab->opaque_type,
functab);
@@ -419,7 +416,7 @@ int ospf_register_opaque_functab(
new = XCALLOC(MTYPE_OSPF_OPAQUE_FUNCTAB,
sizeof(struct ospf_opaque_functab));
else {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Re-register Opaque LSA type %u, opaque type %u, (%p)",
__func__, lsa_type, opaque_type, functab);
return 0;
@@ -439,7 +436,7 @@ int ospf_register_opaque_functab(
new->new_lsa_hook = new_lsa_hook;
new->del_lsa_hook = del_lsa_hook;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Register Opaque LSA type %u, opaque type %u, (%p)",
__func__, lsa_type, opaque_type, new);
@@ -458,7 +455,7 @@ void ospf_delete_opaque_functab(uint8_t lsa_type, uint8_t opaque_type)
if ((funclist = ospf_get_opaque_funclist(lsa_type)) != NULL)
for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) {
if (functab->opaque_type == opaque_type) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Delete Opaque functab LSA type %u, opaque type %u, (%p)",
__func__, lsa_type,
opaque_type, functab);
@@ -608,7 +605,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab,
oipt->id_list = list_new();
oipt->id_list->del = free_opaque_info_per_id;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Register Opaque info-per-type LSA type %u, opaque type %u, (%p), Functab (%p)",
__func__, oipt->lsa_type, oipt->opaque_type, oipt,
oipt->functab);
@@ -662,7 +659,7 @@ static void free_opaque_info_per_type(struct opaque_info_per_type *oipt,
if (oipt->functab)
ospf_opaque_functab_deref(oipt->functab);
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Free Opaque info-per-type LSA type %u, opaque type %u, (%p), Functab (%p)",
__func__, oipt->lsa_type, oipt->opaque_type, oipt,
oipt->functab);
@@ -825,7 +822,7 @@ void ospf_opaque_type9_lsa_if_cleanup(struct ospf_interface *oi)
* is removed from the interface opaque info list.
*/
if (lsa->oi == oi) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("Delete Type-9 Opaque-LSA on interface delete: [opaque-type=%u, opaque-id=%x]",
GET_OPAQUE_TYPE(
ntohl(lsa->data->id.s_addr)),
@@ -833,6 +830,12 @@ void ospf_opaque_type9_lsa_if_cleanup(struct ospf_interface *oi)
lsa->data->id.s_addr)));
ospf_lsdb_delete(lsdb, lsa);
lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+
+ /*
+ * Invoke the delete hook directly since it bypasses the normal MAXAGE
+ * processing.
+ */
+ ospf_opaque_lsa_delete_hook(lsa);
lsa->oi = NULL;
ospf_lsa_discard(lsa);
}
@@ -859,7 +862,7 @@ DEFUN (capability_opaque,
/* Turn on the "master switch" of opaque-lsa capability. */
if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("Opaque capability: OFF -> ON");
SET_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE);
@@ -888,7 +891,7 @@ DEFUN (no_capability_opaque,
/* Turn off the "master switch" of opaque-lsa capability. */
if (CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("Opaque capability: ON -> OFF");
UNSET_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE);
@@ -1151,7 +1154,7 @@ void ospf_opaque_nsm_change(struct ospf_neighbor *nbr, int old_state)
if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) {
if (!CHECK_FLAG(top->opaque,
OPAQUE_OPERATION_READY_BIT)) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Opaque-LSA: Now get operational!");
@@ -1316,11 +1319,15 @@ void ospf_opaque_lsa_dump(struct stream *s, uint16_t length)
return;
}
-static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa)
+static int ospf_opaque_lsa_update_hook(struct ospf_lsa *lsa)
{
struct list *funclist;
int rc = -1;
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
+ zlog_debug("%s: LSA [Type %d: %pI4] Seq: 0x%x %sLSA Update Hook ", __func__,
+ lsa->data->type, &lsa->data->id, ntohl(lsa->data->ls_seqnum),
+ IS_LSA_MAXAGE(lsa) ? "MaxAged " : "");
/*
* Some Opaque-LSA user may want to monitor every LSA installation
* into the LSDB, regardless with target LSA type.
@@ -1351,6 +1358,10 @@ static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa)
struct list *funclist;
int rc = -1;
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
+ zlog_debug("%s: LSA [Type %d: %pI4] Seq: 0x%x %sLSA Delete Hook ", __func__,
+ lsa->data->type, &lsa->data->id, ntohl(lsa->data->ls_seqnum),
+ IS_LSA_MAXAGE(lsa) ? "MaxAged " : "");
/*
* Some Opaque-LSA user may want to monitor every LSA deletion
* from the LSDB, regardless with target LSA type.
@@ -1394,14 +1405,14 @@ void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0)
int delay = 0;
if ((top = oi_to_top(oi)) == NULL || (area = oi->area) == NULL) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Invalid argument?", __func__);
return;
}
/* It may not a right time to schedule origination now. */
if (!CHECK_FLAG(top->opaque, OPAQUE_OPERATION_READY_BIT)) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Not operational.", __func__);
return; /* This is not an error. */
}
@@ -1423,7 +1434,7 @@ void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0)
if (!list_isempty(ospf_opaque_type9_funclist)
&& list_isempty(oi->opaque_lsa_self)
&& oi->t_opaque_lsa_self == NULL) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Schedule Type-9 Opaque-LSA origination in %d ms later.",
delay);
@@ -1441,7 +1452,7 @@ void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0)
* conditions prevent from scheduling the originate function
* again and again.
*/
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Schedule Type-10 Opaque-LSA origination in %d ms later.",
delay);
@@ -1459,7 +1470,7 @@ void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0)
* conditions prevent from scheduling the originate function
* again and again.
*/
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Schedule Type-11 Opaque-LSA origination in %d ms later.",
delay);
@@ -1559,7 +1570,7 @@ static void ospf_opaque_type9_lsa_originate(struct event *t)
oi = EVENT_ARG(t);
oi->t_opaque_lsa_self = NULL;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("Timer[Type9-LSA]: Originate Opaque-LSAs for OI %s",
IF_NAME(oi));
@@ -1573,7 +1584,7 @@ static void ospf_opaque_type10_lsa_originate(struct event *t)
area = EVENT_ARG(t);
area->t_opaque_lsa_self = NULL;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Timer[Type10-LSA]: Originate Opaque-LSAs for Area %pI4",
&area->area_id);
@@ -1588,7 +1599,7 @@ static void ospf_opaque_type11_lsa_originate(struct event *t)
top = EVENT_ARG(t);
top->t_opaque_lsa_self = NULL;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Timer[Type11-LSA]: Originate AS-External Opaque-LSAs");
@@ -1643,12 +1654,10 @@ struct ospf_lsa *ospf_opaque_lsa_install(struct ospf_lsa *lsa, int rt_recalc)
goto out;
}
- if (IS_DEBUG_OSPF(lsa, LSA_INSTALL))
- zlog_debug(
- "Install Type-%u Opaque-LSA: [opaque-type=%u, opaque-id=%x]",
- lsa->data->type,
- GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)),
- GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)));
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
+ zlog_debug("%s: Install Type-%u Opaque-LSA: [opaque-type=%u, opaque-id=%x]",
+ __func__, lsa->data->type, GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)),
+ GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)));
/* Replace the existing lsa with the new one. */
if ((oipt = lookup_opaque_info_by_type(lsa)) != NULL
@@ -1722,7 +1731,7 @@ struct ospf_lsa *ospf_opaque_lsa_refresh(struct ospf_lsa *lsa)
* Anyway, this node still has a responsibility to flush this
* LSA from the routing domain.
*/
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("LSA[Type%d:%pI4]: Flush stray Opaque-LSA",
lsa->data->type, &lsa->data->id);
@@ -1841,7 +1850,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent,
/* It may not a right time to schedule reorigination now. */
if (!CHECK_FLAG(top->opaque, OPAQUE_OPERATION_READY_BIT)) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("%s: Not operational.", __func__);
goto out; /* This is not an error. */
}
@@ -1870,7 +1879,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent,
}
if (oipt->t_opaque_lsa_self != NULL) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Type-%u Opaque-LSA has already scheduled to RE-ORIGINATE: [opaque-type=%u]",
lsa_type,
@@ -1887,7 +1896,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent,
*/
delay = top->min_ls_interval; /* XXX */
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d ms later: [opaque-type=%u]",
lsa_type, delay,
@@ -1943,7 +1952,7 @@ static void ospf_opaque_type9_lsa_reoriginate_timer(struct event *t)
if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE) ||
!OSPF_IF_PARAM(oi, opaque_capable) || !ospf_if_is_enable(oi) ||
ospf_nbr_count_opaque_capable(oi) == 0) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Suspend re-origination of Type-9 Opaque-LSAs (opaque-type=%u) for a while...",
oipt->opaque_type);
@@ -1952,7 +1961,7 @@ static void ospf_opaque_type9_lsa_reoriginate_timer(struct event *t)
return;
}
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Timer[Type9-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for OI (%s)",
oipt->opaque_type, IF_NAME(oi));
@@ -1992,7 +2001,7 @@ static void ospf_opaque_type10_lsa_reoriginate_timer(struct event *t)
}
if (n == 0 || !CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE)) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Suspend re-origination of Type-10 Opaque-LSAs (opaque-type=%u) for a while...",
oipt->opaque_type);
@@ -2001,7 +2010,7 @@ static void ospf_opaque_type10_lsa_reoriginate_timer(struct event *t)
return;
}
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Timer[Type10-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for Area %pI4",
oipt->opaque_type, &area->area_id);
@@ -2029,7 +2038,7 @@ static void ospf_opaque_type11_lsa_reoriginate_timer(struct event *t)
}
if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE)) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Suspend re-origination of Type-11 Opaque-LSAs (opaque-type=%u) for a while...",
oipt->opaque_type);
@@ -2038,7 +2047,7 @@ static void ospf_opaque_type11_lsa_reoriginate_timer(struct event *t)
return;
}
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Timer[Type11-LSA]: Re-originate Opaque-LSAs (opaque-type=%u).",
oipt->opaque_type);
@@ -2067,7 +2076,7 @@ void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0)
}
if (oipi->t_opaque_lsa_self != NULL) {
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Type-%u Opaque-LSA has already scheduled to REFRESH: [opaque-type=%u, opaque-id=%x]",
lsa->data->type,
@@ -2098,7 +2107,7 @@ void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0)
delay = ospf_lsa_refresh_delay(ospf, lsa);
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("Schedule Type-%u Opaque-LSA to REFRESH in %d msec later: [opaque-type=%u, opaque-id=%x]",
lsa->data->type, delay, GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)),
GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)));
@@ -2114,7 +2123,7 @@ static void ospf_opaque_lsa_refresh_timer(struct event *t)
struct ospf_opaque_functab *functab;
struct ospf_lsa *lsa;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug("Timer[Opaque-LSA]: (Opaque-LSA Refresh expire)");
oipi = EVENT_ARG(t);
@@ -2180,7 +2189,7 @@ void ospf_opaque_lsa_flush_schedule(struct ospf_lsa *lsa0)
/* Dequeue listnode entry from the list. */
listnode_delete(oipt->id_list, oipi);
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"Schedule Type-%u Opaque-LSA to FLUSH: [opaque-type=%u, opaque-id=%x]",
lsa->data->type,
@@ -2202,7 +2211,7 @@ void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr,
if ((top = oi_to_top(nbr->oi)) == NULL)
return;
- if (IS_DEBUG_OSPF_EVENT)
+ if (IS_DEBUG_OSPF_OPAQUE_LSA)
zlog_debug(
"LSA[Type%d:%pI4]: processing self-originated Opaque-LSA",
lsa->data->type, &lsa->data->id);
diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c
index d57990e1a1..b6f432b1bb 100644
--- a/ospfd/ospf_te.c
+++ b/ospfd/ospf_te.c
@@ -3015,7 +3015,7 @@ static int ospf_te_delete_opaque_lsa(struct ls_ted *ted, struct ospf_lsa *lsa)
/**
* Update Traffic Engineering Database Elements that correspond to the received
- * OSPF LSA. If LSA age is equal to MAX_AGE, call deletion function instead.
+ * OSPF LSA.
*
* @param lsa OSPF Link State Advertisement
*
@@ -3037,34 +3037,18 @@ static int ospf_mpls_te_lsa_update(struct ospf_lsa *lsa)
return -1;
}
- /* If LSA is MAX_AGE, remove corresponding Link State element */
- if (IS_LSA_MAXAGE(lsa)) {
- switch (lsa->data->type) {
- case OSPF_ROUTER_LSA:
- rc = ospf_te_delete_router_lsa(OspfMplsTE.ted, lsa);
- break;
- case OSPF_OPAQUE_AREA_LSA:
- case OSPF_OPAQUE_AS_LSA:
- rc = ospf_te_delete_opaque_lsa(OspfMplsTE.ted, lsa);
- break;
- default:
- rc = 0;
- break;
- }
- } else {
- /* Parse LSA to Update corresponding Link State element */
- switch (lsa->data->type) {
- case OSPF_ROUTER_LSA:
- rc = ospf_te_parse_router_lsa(OspfMplsTE.ted, lsa);
- break;
- case OSPF_OPAQUE_AREA_LSA:
- case OSPF_OPAQUE_AS_LSA:
- rc = ospf_te_parse_opaque_lsa(OspfMplsTE.ted, lsa);
- break;
- default:
- rc = 0;
- break;
- }
+ /* Parse LSA to Update corresponding Link State element */
+ switch (lsa->data->type) {
+ case OSPF_ROUTER_LSA:
+ rc = ospf_te_parse_router_lsa(OspfMplsTE.ted, lsa);
+ break;
+ case OSPF_OPAQUE_AREA_LSA:
+ case OSPF_OPAQUE_AS_LSA:
+ rc = ospf_te_parse_opaque_lsa(OspfMplsTE.ted, lsa);
+ break;
+ default:
+ rc = 0;
+ break;
}
return rc;
@@ -3096,19 +3080,6 @@ static int ospf_mpls_te_lsa_delete(struct ospf_lsa *lsa)
return -1;
}
- /*
- * Process only self LSAs that reach MAX_AGE. Indeed, when the router
- * need to update or refresh an LSA, it first removes the old LSA from
- * the LSDB and then insert the new one. Thus, to avoid removing
- * corresponding Link State element and loosing some parameters
- * instead of just updating it, only self LSAs that reach MAX_AGE are
- * processed here. Other LSAs are processed by ospf_mpls_te_lsa_update()
- * and eventually removed when LSA age is MAX_AGE i.e. LSA is flushed
- * by the originator.
- */
- if (!IS_LSA_SELF(lsa) || !IS_LSA_MAXAGE(lsa))
- return 0;
-
/* Parse Link State information */
switch (lsa->data->type) {
case OSPF_ROUTER_LSA:
diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c
index 12493b7dbb..40bd7caf7d 100644
--- a/pimd/pim6_cmd.c
+++ b/pimd/pim6_cmd.c
@@ -1612,6 +1612,43 @@ DEFPY (interface_no_ipv6_mld_version,
"frr-routing:ipv6");
}
+DEFPY_YANG(interface_ipv6_mld_limits,
+ interface_ipv6_mld_limits_cmd,
+ "[no] ipv6 mld <max-sources$do_src (0-4294967295)$val"
+ "|max-groups$do_grp (0-4294967295)$val>",
+ NO_STR
+ IPV6_STR
+ IFACE_MLD_STR
+ "Limit number of MLDv2 sources to track\n"
+ "Permitted number of sources\n"
+ "Limit number of MLD group memberships to track\n"
+ "Permitted number of groups\n")
+{
+ const char *xpath;
+
+ assert(do_src || do_grp);
+ if (do_src)
+ xpath = "./max-sources";
+ else
+ xpath = "./max-groups";
+
+ if (no)
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ else
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, val_str);
+
+ return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL);
+}
+
+ALIAS_YANG(interface_ipv6_mld_limits,
+ no_interface_ipv6_mld_limits_cmd,
+ "no ipv6 mld <max-sources$do_src|max-groups$do_grp>",
+ NO_STR
+ IPV6_STR
+ IFACE_MLD_STR
+ "Limit number of MLDv2 sources to track\n"
+ "Limit number of MLD group memberships to track\n")
+
DEFPY (interface_ipv6_mld_query_interval,
interface_ipv6_mld_query_interval_cmd,
"ipv6 mld query-interval (1-65535)$q_interval",
@@ -2341,6 +2378,32 @@ DEFPY (show_ipv6_pim_bsrp,
return pim_show_group_rp_mappings_info_helper(vrf, vty, !!json);
}
+DEFPY(clear_ipv6_mld_interfaces,
+ clear_ipv6_mld_interfaces_cmd,
+ "clear ipv6 mld [vrf NAME$vrf_name] interfaces",
+ CLEAR_STR
+ IPV6_STR
+ "MLD clear commands\n"
+ VRF_CMD_HELP_STR
+ "Reset MLD interfaces\n")
+{
+ struct interface *ifp;
+ struct vrf *vrf;
+
+ vrf = vrf_name ? vrf_lookup_by_name(vrf_name) : vrf_lookup_by_id(VRF_DEFAULT);
+ if (!vrf) {
+ vty_out(vty, "Specified VRF: %s does not exist\n", vrf_name);
+ return CMD_WARNING;
+ }
+
+ FOR_ALL_INTERFACES (vrf, ifp)
+ pim_if_addr_del_all(ifp);
+ FOR_ALL_INTERFACES (vrf, ifp)
+ pim_if_addr_add_all(ifp);
+
+ return CMD_SUCCESS;
+}
+
DEFPY (clear_ipv6_pim_statistics,
clear_ipv6_pim_statistics_cmd,
"clear ipv6 pim statistics [vrf NAME]$name",
@@ -2865,6 +2928,9 @@ void pim_cmd_init(void)
install_element(INTERFACE_NODE, &interface_no_ipv6_pim_boundary_oil_cmd);
install_element(INTERFACE_NODE, &interface_ipv6_mroute_cmd);
install_element(INTERFACE_NODE, &interface_no_ipv6_mroute_cmd);
+ install_element(INTERFACE_NODE, &interface_ipv6_mld_limits_cmd);
+ install_element(INTERFACE_NODE, &no_interface_ipv6_mld_limits_cmd);
+
/* Install BSM command */
install_element(INTERFACE_NODE, &ipv6_pim_bsm_cmd);
install_element(INTERFACE_NODE, &no_ipv6_pim_bsm_cmd);
@@ -2935,6 +3001,7 @@ void pim_cmd_init(void)
install_element(VIEW_NODE, &show_ipv6_pim_bsr_cmd);
install_element(VIEW_NODE, &show_ipv6_pim_bsm_db_cmd);
install_element(VIEW_NODE, &show_ipv6_pim_bsrp_cmd);
+ install_element(ENABLE_NODE, &clear_ipv6_mld_interfaces_cmd);
install_element(ENABLE_NODE, &clear_ipv6_pim_statistics_cmd);
install_element(ENABLE_NODE, &clear_ipv6_mroute_cmd);
install_element(ENABLE_NODE, &clear_ipv6_pim_oil_cmd);
diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c
index acfb0c3af3..d7e0314d3b 100644
--- a/pimd/pim6_mld.c
+++ b/pimd/pim6_mld.c
@@ -190,11 +190,26 @@ static struct gm_sg *gm_sg_find(struct gm_if *gm_ifp, pim_addr grp,
return gm_sgs_find(gm_ifp->sgs, &ref);
}
+static bool gm_sg_has_group(struct gm_sgs_head *sgs, const pim_addr group)
+{
+ struct gm_sg *sg;
+
+ frr_each (gm_sgs, sgs, sg)
+ if (pim_addr_cmp(sg->sgaddr.grp, group) == 0)
+ return true;
+
+ return false;
+}
+
static struct gm_sg *gm_sg_make(struct gm_if *gm_ifp, pim_addr grp,
pim_addr src)
{
struct gm_sg *ret, *prev;
+ /* Count all unique group members. */
+ if (!gm_sg_has_group(gm_ifp->sgs, grp))
+ gm_ifp->groups_count++;
+
ret = XCALLOC(MTYPE_GM_SG, sizeof(*ret));
ret->sgaddr.grp = grp;
ret->sgaddr.src = src;
@@ -212,6 +227,47 @@ static struct gm_sg *gm_sg_make(struct gm_if *gm_ifp, pim_addr grp,
return ret;
}
+static size_t gm_sg_source_count(struct gm_sgs_head *sgs, const pim_addr group)
+{
+ struct gm_sg *sg;
+ size_t source_count;
+
+ source_count = 0;
+ frr_each (gm_sgs, sgs, sg)
+ if (pim_addr_cmp(sg->sgaddr.grp, group) == 0)
+ source_count++;
+
+ return source_count;
+}
+
+static bool gm_sg_limit_reached(struct gm_if *gm_if, const pim_addr source, const pim_addr group)
+{
+ const struct pim_interface *pim_interface = gm_if->ifp->info;
+
+ if (!gm_sg_has_group(gm_if->sgs, group)) {
+ if (gm_if->groups_count >= pim_interface->gm_group_limit) {
+ if (PIM_DEBUG_GM_TRACE)
+ zlog_debug("interface %s has reached group limit (%u), refusing to add group %pPA",
+ gm_if->ifp->name, pim_interface->gm_group_limit, &group);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ if (gm_sg_source_count(gm_if->sgs, group) >= pim_interface->gm_source_limit) {
+ if (PIM_DEBUG_GM_TRACE) {
+ zlog_debug("interface %s has reached source limit (%u), refusing to add source %pPA (group %pPA)",
+ gm_if->ifp->name, pim_interface->gm_source_limit, &source,
+ &group);
+ }
+ return true;
+ }
+
+ return false;
+}
+
/*
* interface -> packets, sorted by expiry (because add_tail insert order)
*/
@@ -471,6 +527,11 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired)
zlog_debug(log_sg(sg, "dropping"));
gm_sgs_del(gm_ifp->sgs, sg);
+
+ /* Decrement unique group members counter. */
+ if (!gm_sg_has_group(gm_ifp->sgs, sg->sgaddr.grp))
+ gm_ifp->groups_count--;
+
gm_sg_free(sg);
}
}
@@ -634,8 +695,12 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt,
case MLD_RECTYPE_CHANGE_TO_EXCLUDE:
/* this always replaces or creates state */
is_excl = true;
- if (!grp)
+ if (!grp) {
+ if (gm_sg_limit_reached(pkt->iface, PIMADDR_ANY, rechdr->grp))
+ return;
+
grp = gm_sg_make(pkt->iface, rechdr->grp, PIMADDR_ANY);
+ }
item = gm_packet_sg_setup(pkt, grp, is_excl, false);
item->n_exclude = n_src;
@@ -700,9 +765,13 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt,
struct gm_sg *sg;
sg = gm_sg_find(pkt->iface, rechdr->grp, rechdr->srcs[j]);
- if (!sg)
+ if (!sg) {
+ if (gm_sg_limit_reached(pkt->iface, rechdr->srcs[j], rechdr->grp))
+ return;
+
sg = gm_sg_make(pkt->iface, rechdr->grp,
rechdr->srcs[j]);
+ }
gm_packet_sg_setup(pkt, sg, is_excl, true);
}
@@ -952,6 +1021,10 @@ static void gm_handle_v1_report(struct gm_if *gm_ifp,
hdr = (struct mld_v1_pkt *)data;
+ if (!gm_sg_has_group(gm_ifp->sgs, hdr->grp) &&
+ gm_sg_limit_reached(gm_ifp, PIMADDR_ANY, hdr->grp))
+ return;
+
max_entries = 1;
pkt = XCALLOC(MTYPE_GM_STATE,
offsetof(struct gm_packet_state, items[max_entries]));
@@ -1255,6 +1328,9 @@ static void gm_handle_q_groupsrc(struct gm_if *gm_ifp,
for (i = 0; i < n_src; i++) {
sg = gm_sg_find(gm_ifp, grp, srcs[i]);
+ if (sg == NULL)
+ continue;
+
GM_UPDATE_SG_STATE(sg);
gm_sg_timer_start(gm_ifp, sg, timers->expire_wait);
}
diff --git a/pimd/pim6_mld.h b/pimd/pim6_mld.h
index 183ab2fc50..c5a9708961 100644
--- a/pimd/pim6_mld.h
+++ b/pimd/pim6_mld.h
@@ -350,6 +350,8 @@ struct gm_if {
struct gm_subscribers_head subscribers[1];
struct gm_packet_expires_head expires[1];
+ size_t groups_count;
+
struct timeval started;
struct gm_if_stats stats;
};
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index a1ad261869..fa9c6f9537 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -5656,6 +5656,43 @@ DEFUN (interface_no_ip_igmp_last_member_query_interval,
return gm_process_no_last_member_query_interval_cmd(vty);
}
+DEFPY_YANG(interface_ip_igmp_limits,
+ interface_ip_igmp_limits_cmd,
+ "[no] ip igmp <max-sources$do_src (0-4294967295)$val"
+ "|max-groups$do_grp (0-4294967295)$val>",
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR
+ "Limit number of IGMPv3 sources to track\n"
+ "Permitted number of sources\n"
+ "Limit number of IGMP group memberships to track\n"
+ "Permitted number of groups\n")
+{
+ const char *xpath;
+
+ assert(do_src || do_grp);
+ if (do_src)
+ xpath = "./max-sources";
+ else
+ xpath = "./max-groups";
+
+ if (no)
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ else
+ nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, val_str);
+
+ return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH, FRR_PIM_AF_XPATH_VAL);
+}
+
+ALIAS_YANG(interface_ip_igmp_limits,
+ no_interface_ip_igmp_limits_cmd,
+ "no ip igmp <max-sources$do_src|max-groups$do_grp>",
+ NO_STR
+ IP_STR
+ IFACE_IGMP_STR
+ "Limit number of IGMPv3 sources to track\n"
+ "Limit number of IGMP group memberships to track\n")
+
DEFUN (interface_ip_pim_drprio,
interface_ip_pim_drprio_cmd,
"ip pim drpriority (0-4294967295)",
@@ -9101,6 +9138,8 @@ void pim_cmd_init(void)
install_element(INTERFACE_NODE,
&interface_no_ip_igmp_last_member_query_interval_cmd);
install_element(INTERFACE_NODE, &interface_ip_igmp_proxy_cmd);
+ install_element(INTERFACE_NODE, &interface_ip_igmp_limits_cmd);
+ install_element(INTERFACE_NODE, &no_interface_ip_igmp_limits_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_activeactive_cmd);
install_element(INTERFACE_NODE, &interface_ip_pim_ssm_cmd);
install_element(INTERFACE_NODE, &interface_no_ip_pim_ssm_cmd);
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index 9316cebc0a..8ec51ddc39 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -128,6 +128,8 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool gm, bool pim,
pim_ifp->gm_specific_query_max_response_time_dsec =
GM_SPECIFIC_QUERY_MAX_RESPONSE_TIME_DSEC;
pim_ifp->gm_last_member_query_count = GM_DEFAULT_ROBUSTNESS_VARIABLE;
+ pim_ifp->gm_group_limit = UINT32_MAX;
+ pim_ifp->gm_source_limit = UINT32_MAX;
/* BSM config on interface: true by default */
pim_ifp->bsm_enable = true;
diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h
index 90a81a21d0..0a7993fd27 100644
--- a/pimd/pim_iface.h
+++ b/pimd/pim_iface.h
@@ -105,6 +105,8 @@ struct pim_interface {
struct gm_if *mld;
+ uint32_t gm_source_limit, gm_group_limit;
+
int pim_sock_fd; /* PIM socket file descriptor */
struct event *t_pim_sock_read; /* thread for reading PIM socket */
int64_t pim_sock_creation; /* timestamp of PIM socket creation */
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 12f424248f..b1b4566499 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -1416,6 +1416,14 @@ struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp,
__func__, &group_addr);
return NULL;
}
+
+ if (listcount(pim_ifp->gm_group_list) >= pim_ifp->gm_group_limit) {
+ if (PIM_DEBUG_GM_TRACE)
+ zlog_debug("interface %s has reached group limit (%u), refusing to add group %pI4",
+ igmp->interface->name, pim_ifp->gm_group_limit, &group_addr);
+ return NULL;
+ }
+
/*
Non-existant group is created as INCLUDE {empty}:
diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c
index 7348d8130f..7cb168dc5d 100644
--- a/pimd/pim_igmpv3.c
+++ b/pimd/pim_igmpv3.c
@@ -423,6 +423,7 @@ struct gm_source *igmp_find_source_by_addr(struct gm_group *group,
struct gm_source *igmp_get_source_by_addr(struct gm_group *group,
struct in_addr src_addr, bool *new)
{
+ const struct pim_interface *pim_interface = group->interface->info;
struct gm_source *src;
if (new)
@@ -432,6 +433,14 @@ struct gm_source *igmp_get_source_by_addr(struct gm_group *group,
if (src)
return src;
+ if (listcount(group->group_source_list) >= pim_interface->gm_source_limit) {
+ if (PIM_DEBUG_GM_TRACE)
+ zlog_debug("interface %s has reached source limit (%u), refusing to add source %pI4 (group %pI4)",
+ group->interface->name, pim_interface->gm_source_limit,
+ &src_addr, &group->group_addr);
+ return NULL;
+ }
+
if (PIM_DEBUG_GM_TRACE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
diff --git a/pimd/pim_nb.c b/pimd/pim_nb.c
index ea9ce3cecb..62c5d531d9 100644
--- a/pimd/pim_nb.c
+++ b/pimd/pim_nb.c
@@ -725,6 +725,18 @@ const struct frr_yang_module_info frr_gmp_info = {
.destroy = lib_interface_gmp_address_family_join_group_destroy,
}
},
+ {
+ .xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/max-sources",
+ .cbs = {
+ .modify = lib_interface_gm_max_sources_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/max-groups",
+ .cbs = {
+ .modify = lib_interface_gm_max_groups_modify,
+ }
+ },
{
.xpath = "/frr-interface:lib/interface/frr-gmp:gmp/address-family/proxy",
.cbs = {
diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h
index a15c6e6d9f..1656313fc2 100644
--- a/pimd/pim_nb.h
+++ b/pimd/pim_nb.h
@@ -287,6 +287,8 @@ int lib_interface_gmp_address_family_static_group_create(
struct nb_cb_create_args *args);
int lib_interface_gmp_address_family_static_group_destroy(
struct nb_cb_destroy_args *args);
+int lib_interface_gm_max_sources_modify(struct nb_cb_modify_args *args);
+int lib_interface_gm_max_groups_modify(struct nb_cb_modify_args *args);
/*
* Callback registered with routing_nb lib to validate only
diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c
index 5203f78b92..c926696610 100644
--- a/pimd/pim_nb_config.c
+++ b/pimd/pim_nb_config.c
@@ -4397,6 +4397,72 @@ int lib_interface_gmp_address_family_last_member_query_interval_modify(
}
/*
+ * XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/max-groups
+ */
+int lib_interface_gm_max_groups_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ const char *ifp_name;
+ const struct lyd_node *if_dnode;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ if_dnode = yang_dnode_get_parent(args->dnode, "interface");
+ if (!is_pim_interface(if_dnode)) {
+ ifp_name = yang_dnode_get_string(if_dnode, "name");
+ snprintf(args->errmsg, args->errmsg_len,
+ "multicast not enabled on interface %s", ifp_name);
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ pim_ifp = ifp->info;
+ pim_ifp->gm_group_limit = yang_dnode_get_uint32(args->dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/max-sources
+ */
+int lib_interface_gm_max_sources_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ const char *ifp_name;
+ const struct lyd_node *if_dnode;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ if_dnode = yang_dnode_get_parent(args->dnode, "interface");
+ if (!is_pim_interface(if_dnode)) {
+ ifp_name = yang_dnode_get_string(if_dnode, "name");
+ snprintf(args->errmsg, args->errmsg_len,
+ "multicast not enabled on interface %s", ifp_name);
+ return NB_ERR_VALIDATION;
+ }
+ break;
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ pim_ifp = ifp->info;
+ pim_ifp->gm_source_limit = yang_dnode_get_uint32(args->dnode, NULL);
+ break;
+ }
+
+ return NB_OK;
+}
+
+/*
* XPath: /frr-interface:lib/interface/frr-gmp:gmp/address-family/robustness-variable
*/
int lib_interface_gmp_address_family_robustness_variable_modify(
diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c
index a972a38c72..64750a22f6 100644
--- a/pimd/pim_vty.c
+++ b/pimd/pim_vty.c
@@ -457,6 +457,20 @@ int pim_config_write(struct vty *vty, int writes, struct interface *ifp,
++writes;
}
+ /* IF igmp/mld max-sources */
+ if (pim_ifp->gm_source_limit != UINT32_MAX) {
+ vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " max-sources %u\n",
+ pim_ifp->gm_source_limit);
+ ++writes;
+ }
+
+ /* IF igmp/mld max-groups */
+ if (pim_ifp->gm_group_limit != UINT32_MAX) {
+ vty_out(vty, " " PIM_AF_NAME " " GM_AF_DBG " max-groups %u\n",
+ pim_ifp->gm_group_limit);
+ ++writes;
+ }
+
/* IF ip pim drpriority */
if (pim_ifp->pim_dr_priority != PIM_DEFAULT_DR_PRIORITY) {
vty_out(vty, " " PIM_AF_NAME " pim drpriority %u\n",
diff --git a/tests/helpers/c/prng.c b/tests/helpers/c/prng.c
index c72615475b..e4241aa44f 100644
--- a/tests/helpers/c/prng.c
+++ b/tests/helpers/c/prng.c
@@ -24,7 +24,7 @@ struct prng {
struct prng *prng_new(unsigned long long seed)
{
- struct prng *rv = calloc(sizeof(*rv), 1);
+ struct prng *rv = calloc(1, sizeof(*rv));
assert(rv);
rv->state = seed;
diff --git a/tests/isisd/test_isis_lspdb.c b/tests/isisd/test_isis_lspdb.c
index cc95c4a132..0d8c37449b 100644
--- a/tests/isisd/test_isis_lspdb.c
+++ b/tests/isisd/test_isis_lspdb.c
@@ -16,7 +16,7 @@ static void test_lsp_build_list_nonzero_ht(void)
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00
};
- struct isis_area *area = calloc(sizeof(*area), 1);
+ struct isis_area *area = calloc(1, sizeof(*area));
area->lsp_mtu = 1500;
@@ -75,7 +75,7 @@ static void test_lsp_build_list_nonzero_ht(void)
int main(int argc, char **argv)
{
struct isis *isis = NULL;
- isis = calloc(sizeof(*isis), 1);
+ isis = calloc(1, sizeof(*isis));
test_lsp_build_list_nonzero_ht();
return 0;
}
diff --git a/tests/lib/test_nexthop_iter.c b/tests/lib/test_nexthop_iter.c
index 33ff116890..b70ef5606c 100644
--- a/tests/lib/test_nexthop_iter.c
+++ b/tests/lib/test_nexthop_iter.c
@@ -62,7 +62,7 @@ static struct nexthop_chain *nexthop_chain_new(void)
{
struct nexthop_chain *rv;
- rv = calloc(sizeof(*rv), 1);
+ rv = calloc(1, sizeof(*rv));
assert(rv);
return rv;
}
@@ -71,7 +71,7 @@ static void nexthop_chain_add_top(struct nexthop_chain *nc)
{
struct nexthop *nh;
- nh = calloc(sizeof(*nh), 1);
+ nh = calloc(1, sizeof(*nh));
assert(nh);
if (nc->head.nexthop) {
@@ -109,7 +109,7 @@ static void nexthop_chain_add_recursive(struct nexthop_chain *nc)
{
struct nexthop *nh;
- nh = calloc(sizeof(*nh), 1);
+ nh = calloc(1, sizeof(*nh));
assert(nh);
assert(nc->current_top);
@@ -128,7 +128,7 @@ static void nexthop_chain_add_recursive_level(struct nexthop_chain *nc)
{
struct nexthop *nh;
- nh = calloc(sizeof(*nh), 1);
+ nh = calloc(1, sizeof(*nh));
assert(nh);
assert(nc->current_top);
diff --git a/tests/topotests/bgp_bmp/test_bgp_bmp_1.py b/tests/topotests/bgp_bmp/test_bgp_bmp_1.py
index 1d7aa97473..c454c9c4a4 100644
--- a/tests/topotests/bgp_bmp/test_bgp_bmp_1.py
+++ b/tests/topotests/bgp_bmp/test_bgp_bmp_1.py
@@ -78,6 +78,13 @@ def setup_module(mod):
"tcpdump -nni r1-eth0 -s 0 -w {} &".format(pcap_file), stdout=None
)
+ tgen.net["r2"].cmd(
+ """
+ip link add vrf1 type vrf table 10
+ip link set vrf1 up
+"""
+ )
+
for _, (rname, router) in enumerate(tgen.routers().items(), 1):
logger.info("Loading router %s" % rname)
router.load_frr_config(
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf
index d3ababde3a..e3f8b242a1 100644
--- a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/frr.conf
@@ -1,3 +1,7 @@
+vrf DONNA
+ ip route 172.16.3.0/24 10.0.0.254
+exit-vrf
+!
int dummy0
ip address 10.0.4.1/24
no shut
@@ -28,6 +32,9 @@ ip router-id 10.0.4.1
!
router bgp 99
no bgp ebgp-requires-policy
+ ! 10.0.4.254 peer session will not be established
+ ! it is there just to activate the ipv4 vpn table
+ neighbor 10.0.4.254 remote-as external
address-family ipv4 unicast
redistribute connected
rd vpn export 10.0.4.1:1
@@ -36,11 +43,14 @@ router bgp 99
export vpn
import vpn
!
+ address-family ipv4 vpn
+ neighbor 10.0.4.254 activate
!
router bgp 99 vrf DONNA
no bgp ebgp-requires-policy
address-family ipv4 unicast
redistribute connected
+ network 172.16.3.0/24
label vpn export 101
rd vpn export 10.0.4.1:1
rt vpn export 10.0.4.1:101
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_add_zita.json b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_add_zita.json
new file mode 100644
index 0000000000..c7ff2f4f80
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_add_zita.json
@@ -0,0 +1,155 @@
+{
+ "routerId": "10.0.4.1",
+ "localAS": 99,
+ "routes": {
+ "routeDistinguishers": {
+ "10.0.4.1:1": {
+ "10.0.0.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.101.0/24": [
+ {
+ "valid": null,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "ZITA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_del_donna_prefix.json b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_del_donna_prefix.json
new file mode 100644
index 0000000000..1797d78582
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_del_donna_prefix.json
@@ -0,0 +1,103 @@
+{
+ "routerId": "10.0.4.1",
+ "localAS": 99,
+ "routes": {
+ "routeDistinguishers": {
+ "10.0.4.1:1": {
+ "10.0.0.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": null,
+ "172.16.101.0/24": null
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_eva_down.json b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_eva_down.json
new file mode 100644
index 0000000000..4b0eaaa052
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_eva_down.json
@@ -0,0 +1,120 @@
+{
+ "routerId": "10.0.4.1",
+ "localAS": 99,
+ "routes": {
+ "routeDistinguishers": {
+ "10.0.4.1:1": {
+ "10.0.0.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24": [
+ {
+ "valid": null,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24": [
+ {
+ "valid": null,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.101.0/24": null
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_init.json b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_init.json
new file mode 100644
index 0000000000..de18bc8463
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_init.json
@@ -0,0 +1,120 @@
+{
+ "routerId": "10.0.4.1",
+ "localAS": 99,
+ "routes": {
+ "routeDistinguishers": {
+ "10.0.4.1:1": {
+ "10.0.0.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.101.0/24": null
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_zita_up.json b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_zita_up.json
new file mode 100644
index 0000000000..0c249e241e
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/r1/show_bgp_ipv4_vpn_zita_up.json
@@ -0,0 +1,137 @@
+{
+ "routerId": "10.0.4.1",
+ "localAS": 99,
+ "routes": {
+ "routeDistinguishers": {
+ "10.0.4.1:1": {
+ "10.0.0.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "EVA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.3.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "DONNA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "172.16.101.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "path": "",
+ "origin": "IGP",
+ "announceNexthopSelf": true,
+ "nhVrfName": "ZITA",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "hostname": "r1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py b/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py
index a44f07b560..67e53cb0cc 100644
--- a/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py
+++ b/tests/topotests/bgp_vpnv4_route_leak_basic/test_bgp_vpnv4_route_leak_basic.py
@@ -13,6 +13,7 @@
Test basic VPNv4 route leaking
"""
+import json
import os
import sys
from functools import partial
@@ -60,6 +61,22 @@ def teardown_module(mod):
tgen.stop_topology()
+def test_bgp_convergence():
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/{}/show_bgp_ipv4_vpn_init.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
+
def test_vrf_route_leak_donna():
logger.info("Ensure that routes are leaked back and forth")
tgen = get_topogen()
@@ -119,20 +136,20 @@ def test_vrf_route_leak_donna():
],
},
],
- "172.16.101.0/24": [
+ "172.16.3.0/24": [
{
- "protocol": "bgp",
- "selected": None,
+ "protocol": "static",
+ "selected": True,
"nexthops": [
{
- "fib": None,
- "interfaceName": "unknown",
- "vrf": "Unknown",
- "active": None,
- },
+ "fib": True,
+ "interfaceName": "dummy1",
+ "active": True,
+ }
],
},
],
+ "172.16.101.0/24": None,
}
test_func = partial(
@@ -191,20 +208,21 @@ def test_vrf_route_leak_eva():
"protocol": "connected",
}
],
- "172.16.101.0/24": [
+ "172.16.3.0/24": [
{
"protocol": "bgp",
- "selected": None,
+ "selected": True,
"nexthops": [
{
- "fib": None,
- "interfaceName": "unknown",
- "vrf": "Unknown",
- "active": None,
- },
+ "fib": True,
+ "interfaceName": "DONNA",
+ "vrf": "DONNA",
+ "active": True,
+ }
],
},
],
+ "172.16.101.0/24": None,
}
test_func = partial(
@@ -258,6 +276,20 @@ def test_vrf_route_leak_default():
"protocol": "connected",
}
],
+ "172.16.3.0/24": [
+ {
+ "protocol": "bgp",
+ "selected": True,
+ "nexthops": [
+ {
+ "fib": True,
+ "interfaceName": "DONNA",
+ "vrf": "DONNA",
+ "active": True,
+ }
+ ],
+ },
+ ],
}
test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expect)
@@ -298,34 +330,8 @@ interface EVA
# Test DONNA VRF.
expect = {
- "10.0.1.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "EVA",
- "vrf": "EVA",
- "active": None,
- },
- ],
- },
- ],
- "10.0.3.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "EVA",
- "vrf": "EVA",
- "active": None,
- },
- ],
- },
- ],
+ "10.0.1.0/24": None,
+ "10.0.3.0/24": None,
}
test_func = partial(
@@ -349,6 +355,14 @@ interface EVA
result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_eva_down.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
def test_vrf_route_leak_donna_after_eva_up():
logger.info("Ensure that route states change after EVA interface goes up")
@@ -404,6 +418,14 @@ interface EVA
result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_init.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
def test_vrf_route_leak_donna_add_vrf_zita():
logger.info("Add VRF ZITA and ensure that the route from VRF ZITA is updated")
@@ -417,20 +439,7 @@ def test_vrf_route_leak_donna_add_vrf_zita():
# Test DONNA VRF.
expect = {
- "172.16.101.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "ZITA",
- "vrf": "ZITA",
- "active": None,
- },
- ],
- },
- ],
+ "172.16.101.0/24": None,
}
test_func = partial(
@@ -439,6 +448,14 @@ def test_vrf_route_leak_donna_add_vrf_zita():
result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_add_zita.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
def test_vrf_route_leak_donna_set_zita_up():
logger.info("Set VRF ZITA up and ensure that the route from VRF ZITA is updated")
@@ -480,6 +497,14 @@ interface ZITA
result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_zita_up.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
def test_vrf_route_leak_donna_delete_vrf_zita():
logger.info("Delete VRF ZITA and ensure that the route from VRF ZITA is deleted")
@@ -502,6 +527,101 @@ def test_vrf_route_leak_donna_delete_vrf_zita():
result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_init.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
+
+def test_vrf_route_leak_default_delete_prefix():
+ logger.info(
+ "Remove BGP static prefix 172.16.3.0/24 from VRF DONNA and ensure that the route is deleted on default"
+ )
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd(
+ """
+configure
+router bgp 99 vrf DONNA
+ address-family ipv4 unicast
+ no network 172.16.3.0/24
+"""
+ )
+
+ # Test default VRF.
+ expect = {
+ "172.16.3.0/24": None,
+ }
+
+ test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP VRF default check failed:\n{}".format(diff)
+
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_del_donna_prefix.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
+
+def test_vrf_route_leak_default_prefix_back():
+ logger.info(
+ "Set back BGP static prefix 172.16.3.0/24 to VRF DONNA and ensure that the route is set on default"
+ )
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd(
+ """
+configure
+router bgp 99 vrf DONNA
+ address-family ipv4 unicast
+ network 172.16.3.0/24
+"""
+ )
+
+ # Test default VRF.
+ expect = {
+ "172.16.3.0/24": [
+ {
+ "protocol": "bgp",
+ "selected": True,
+ "nexthops": [
+ {
+ "fib": True,
+ "interfaceName": "DONNA",
+ "vrf": "DONNA",
+ "active": True,
+ }
+ ],
+ },
+ ],
+ }
+
+ test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP VRF default check failed:\n{}".format(diff)
+
+ # check BGP IPv4 VPN table
+ json_file = "{}/{}/show_bgp_ipv4_vpn_init.json".format(CWD, r1.name)
+ expect = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, r1, "show bgp ipv4 vpn json", expect)
+ result, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result, "BGP IPv4 VPN table check failed:\n{}".format(diff)
+
def test_memory_leak():
"Run the memory leak test and report results."
diff --git a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
index 6d4b436bcc..3bc36862bf 100644
--- a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
+++ b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
@@ -123,20 +123,7 @@ def test_vrf_route_leak_donna():
],
},
],
- "172.16.101.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "unknown",
- "vrf": "Unknown",
- "active": None,
- },
- ],
- },
- ],
+ "172.16.101.0/24": None,
}
test_func = partial(
@@ -195,20 +182,7 @@ def test_vrf_route_leak_eva():
"protocol": "connected",
}
],
- "172.16.101.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "unknown",
- "vrf": "Unknown",
- "active": None,
- },
- ],
- },
- ],
+ "172.16.101.0/24": None,
}
test_func = partial(
@@ -302,34 +276,8 @@ interface EVA
# Test DONNA VRF.
expect = {
- "10.0.1.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "EVA",
- "vrf": "EVA",
- "active": None,
- },
- ],
- },
- ],
- "10.0.3.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "EVA",
- "vrf": "EVA",
- "active": None,
- },
- ],
- },
- ],
+ "10.0.1.0/24": None,
+ "10.0.3.0/24": None,
}
test_func = partial(
@@ -421,20 +369,7 @@ def test_vrf_route_leak_donna_add_vrf_zita():
# Test DONNA VRF.
expect = {
- "172.16.101.0/24": [
- {
- "protocol": "bgp",
- "selected": None,
- "nexthops": [
- {
- "fib": None,
- "interfaceName": "ZITA",
- "vrf": "ZITA",
- "active": None,
- },
- ],
- },
- ],
+ "172.16.101.0/24": None,
}
test_func = partial(
diff --git a/tests/topotests/lib/mcast-tester.py b/tests/topotests/lib/mcast-tester.py
index 5efbecd5e5..3645eef25e 100755
--- a/tests/topotests/lib/mcast-tester.py
+++ b/tests/topotests/lib/mcast-tester.py
@@ -34,16 +34,64 @@ def interface_name_to_index(name):
return None
-def multicast_join(sock, ifindex, group, port):
+def interface_index_to_address(index, iptype="inet"):
+ "Gets the interface main address using its name. Returns None on failure."
+ interfaces = json.loads(subprocess.check_output("ip -j addr show", shell=True))
+
+ for interface in interfaces:
+ if interface["ifindex"] == index:
+ break
+
+ for address in interface["addr_info"]:
+ if address["family"] == iptype:
+ break
+
+ local_address = ipaddress.ip_address(address["local"])
+
+ return local_address.packed
+
+
+def group_source_req(ifindex, group, source):
+ "Packs the information into 'struct group_source_req' format."
+ mreq = struct.pack("<I", ifindex)
+ group_bytes = (
+ struct.pack("<IHHI", 0, socket.AF_INET6, 0, 0)
+ + group.packed
+ + struct.pack("<I", 0)
+ )
+ group_bytes += struct.pack(f"<{128 - len(group_bytes)}x")
+
+ source_bytes = (
+ struct.pack("<IHHI", 0, socket.AF_INET6, 0, 0)
+ + source.packed
+ + struct.pack("<I", 0)
+ )
+ source_bytes += struct.pack(f"<{128 - len(source_bytes)}x")
+
+ return mreq + group_bytes + source_bytes + struct.pack("<4x")
+
+
+def multicast_join(sock, ifindex, group, port, source=None):
"Joins a multicast group."
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if ip_version == 4:
- mreq = group.packed + struct.pack("@II", socket.INADDR_ANY, ifindex)
- opt = socket.IP_ADD_MEMBERSHIP
+ if source is None:
+ mreq = group.packed + struct.pack("@II", socket.INADDR_ANY, ifindex)
+ opt = socket.IP_ADD_MEMBERSHIP
+ else:
+ source = ipaddress.ip_address(source)
+ mreq = group.packed + interface_index_to_address(ifindex) + source.packed
+ opt = 39
else:
- mreq = group.packed + struct.pack("@I", ifindex)
- opt = socket.IPV6_JOIN_GROUP
+ if source is None:
+ mreq = group.packed + struct.pack("@I", ifindex)
+ opt = socket.IPV6_JOIN_GROUP
+ else:
+ mreq = group_source_req(ifindex, group, ipaddress.ip_address(source))
+ print(mreq)
+ opt = 46
+
sock.bind((str(group), port))
sock.setsockopt(ip_proto, opt, mreq)
@@ -57,6 +105,7 @@ parser.add_argument("interface", help="Interface name")
parser.add_argument("--port", type=int, default=1000, help="port to send to")
parser.add_argument("--ttl", type=int, default=16, help="TTL/hops for sending packets")
parser.add_argument("--socket", help="Point to topotest UNIX socket")
+parser.add_argument("--source", help="Source address for multicast")
parser.add_argument(
"--send", help="Transmit instead of join with interval", type=float, default=0
)
@@ -112,7 +161,7 @@ if args.send > 0:
# Block to ensure packet send.
msock.setblocking(True)
else:
- multicast_join(msock, ifindex, args.group, args.port)
+ multicast_join(msock, ifindex, args.group, args.port, args.source)
def should_exit():
diff --git a/tests/topotests/multicast_features/__init__.py b/tests/topotests/multicast_features/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/multicast_features/__init__.py
diff --git a/tests/topotests/multicast_features/r1/frr.conf b/tests/topotests/multicast_features/r1/frr.conf
new file mode 100644
index 0000000000..bd1cc4103c
--- /dev/null
+++ b/tests/topotests/multicast_features/r1/frr.conf
@@ -0,0 +1,48 @@
+log commands
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+ ip pim
+ ipv6 address 2001:db8:1::1/64
+ ipv6 pim
+!
+interface r1-eth1
+ ip address 192.168.2.1/24
+ ip pim
+ ipv6 address 2001:db8:2::1/64
+ ipv6 pim
+!
+interface r1-eth2
+ ip address 192.168.100.1/24
+ ip igmp
+ ip pim passive
+ ipv6 address 2001:db8:ffff::1/128
+ ipv6 mld
+ ipv6 pim passive
+!
+interface lo
+ ip address 10.254.254.1/32
+ ip pim
+ ip pim use-source 10.254.254.1
+!
+router bgp 65100
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as 65200
+ neighbor 192.168.2.2 remote-as 65300
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.2.2 activate
+ exit-address-family
+!
+router pim
+ rp 10.254.254.1
+!
+router pim6
+ rp 2001:db8:ffff::1
+! \ No newline at end of file
diff --git a/tests/topotests/multicast_features/r2/frr.conf b/tests/topotests/multicast_features/r2/frr.conf
new file mode 100644
index 0000000000..f647b93ca3
--- /dev/null
+++ b/tests/topotests/multicast_features/r2/frr.conf
@@ -0,0 +1,37 @@
+log commands
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+ ip pim
+ ipv6 address 2001:db8:1::2/64
+ ipv6 pim
+!
+interface r2-eth1
+ ip address 192.168.101.1/24
+ ip igmp
+ ip pim passive
+!
+interface lo
+ ip address 10.254.254.2/32
+ ipv6 address 2001:db8:ffff::2/128
+ ipv6 pim passive
+!
+router bgp 65200
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.1 remote-as 65100
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 192.168.1.1 activate
+ exit-address-family
+!
+router pim
+ rp 10.254.254.2
+!
+router pim6
+ rp 2001:db8:ffff::2
+! \ No newline at end of file
diff --git a/tests/topotests/multicast_features/r3/frr.conf b/tests/topotests/multicast_features/r3/frr.conf
new file mode 100644
index 0000000000..2ac00c8798
--- /dev/null
+++ b/tests/topotests/multicast_features/r3/frr.conf
@@ -0,0 +1,34 @@
+log commands
+!
+interface r3-eth0
+ ip address 192.168.2.2/24
+ ip pim
+ ipv6 address 2001:db8:2::2/64
+ ipv6 pim
+!
+interface lo
+ ip address 10.254.254.3/32
+ ip pim
+ ip pim use-source 10.254.254.3
+ ipv6 address 2001:db8:ffff::3/128
+ ipv6 pim passive
+!
+router bgp 65300
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.2.1 remote-as 65100
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 192.168.2.1 activate
+ exit-address-family
+!
+router pim
+ rp 10.254.254.1
+!
+router pim6
+ rp 2001:db8:ffff::1
+! \ No newline at end of file
diff --git a/tests/topotests/multicast_features/test_multicast_features.py b/tests/topotests/multicast_features/test_multicast_features.py
new file mode 100644
index 0000000000..9c1f4af99f
--- /dev/null
+++ b/tests/topotests/multicast_features/test_multicast_features.py
@@ -0,0 +1,367 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_multicast_features.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2025 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_multicast_features.py: Test the FRR PIM multicast features.
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import re
+import pytest
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+
+# Required to instantiate the topology builder class.
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+from lib.pim import McastTesterHelper
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.pimd]
+
+app_helper = McastTesterHelper()
+
+
+def build_topo(tgen):
+ """
+ +----+ +----+ +----+ +----+
+ | h1 | <-> | r1 | <-> | r2 | <-> | h2 |
+ +----+ +----+ +----+ +----+
+ ^
+ |
+ v
+ +----+
+ | r3 |
+ +----+
+ """
+
+ # Create 3 routers
+ for routern in range(1, 4):
+ tgen.add_router(f"r{routern}")
+
+ # R1 interface eth0 and R2 interface eth0
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # R1 interface eth1
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ # R1 interface eth2
+ switch = tgen.add_switch("s3")
+ tgen.add_host("h1", "192.168.100.100/24", "via 192.168.100.1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["h1"])
+
+ # R2 interface eth1
+ switch = tgen.add_switch("s4")
+ tgen.add_host("h2", "192.168.101.100/24", "via 192.168.101.1")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["h2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for _, router in router_list.items():
+ router.load_frr_config(os.path.join(CWD, f"{router.name}/frr.conf"))
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ app_helper.init(tgen)
+
+
+def teardown_module():
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ app_helper.cleanup()
+ tgen.stop_topology()
+
+
+def test_bgp_convergence():
+ "Wait for BGP protocol convergence"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_loopback_route(router, iptype, route, proto):
+ "Wait until route is present on RIB for protocol."
+ logger.info(f"waiting route {route} in {router}")
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ f"show {iptype} route json",
+ {route: [{"protocol": proto}]},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ # Wait for R1
+ expect_loopback_route("r1", "ip", "10.254.254.2/32", "bgp")
+ expect_loopback_route("r1", "ip", "10.254.254.3/32", "bgp")
+ expect_loopback_route("r1", "ipv6", "2001:db8:ffff::2/128", "bgp")
+ expect_loopback_route("r1", "ipv6", "2001:db8:ffff::3/128", "bgp")
+
+ # Wait for R2
+ expect_loopback_route("r2", "ip", "10.254.254.1/32", "bgp")
+ expect_loopback_route("r2", "ip", "10.254.254.3/32", "bgp")
+ expect_loopback_route("r2", "ipv6", "2001:db8:ffff::1/128", "bgp")
+ expect_loopback_route("r2", "ipv6", "2001:db8:ffff::3/128", "bgp")
+
+ # Wait for R3
+ expect_loopback_route("r3", "ip", "10.254.254.1/32", "bgp")
+ expect_loopback_route("r3", "ip", "10.254.254.2/32", "bgp")
+ expect_loopback_route("r3", "ipv6", "2001:db8:ffff::1/128", "bgp")
+ expect_loopback_route("r3", "ipv6", "2001:db8:ffff::2/128", "bgp")
+
+
+def test_pim_convergence():
+ "Wait for PIM peers find each other."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def expect_pim_peer(router, iptype, interface, peer):
+ "Wait until peer is present."
+ logger.info(f"waiting peer {peer} in {router}")
+ expected = {interface: {peer: {"upTime": "*"}}}
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ f"show {iptype} pim neighbor json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=2)
+ assertmsg = f'"{router}" convergence failure'
+ assert result is None, assertmsg
+
+ expect_pim_peer("r1", "ip", "r1-eth0", "192.168.1.2")
+ expect_pim_peer("r2", "ip", "r2-eth0", "192.168.1.1")
+ expect_pim_peer("r1", "ip", "r1-eth1", "192.168.2.2")
+
+ #
+ # IPv6 part
+ #
+ out = tgen.gears["r1"].vtysh_cmd("show interface r1-eth0 json", True)
+ r1_r2_link_address = out["r1-eth0"]["ipAddresses"][1]["address"].split('/')[0]
+ out = tgen.gears["r1"].vtysh_cmd("show interface r1-eth1 json", True)
+ r1_r3_link_address = out["r1-eth1"]["ipAddresses"][1]["address"].split('/')[0]
+ out = tgen.gears["r2"].vtysh_cmd("show interface r2-eth0 json", True)
+ r2_link_address = out["r2-eth0"]["ipAddresses"][1]["address"].split('/')[0]
+ out = tgen.gears["r3"].vtysh_cmd("show interface r3-eth0 json", True)
+ r3_link_address = out["r3-eth0"]["ipAddresses"][1]["address"].split('/')[0]
+
+ expect_pim_peer("r1", "ipv6", "r1-eth0", r2_link_address)
+ expect_pim_peer("r2", "ipv6", "r2-eth0", r1_r2_link_address)
+ expect_pim_peer("r1", "ipv6", "r1-eth1", r3_link_address)
+
+
+def test_igmp_group_limit():
+ "Test IGMP group limits."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["r1"].vtysh_cmd("""
+ configure terminal
+ interface r1-eth2
+ ip igmp max-groups 4
+ """)
+ app_helper.run("h1", ["224.0.100.1", "h1-eth0"])
+ app_helper.run("h1", ["224.0.100.2", "h1-eth0"])
+ app_helper.run("h1", ["224.0.100.3", "h1-eth0"])
+ app_helper.run("h1", ["224.0.100.4", "h1-eth0"])
+ app_helper.run("h1", ["224.0.100.5", "h1-eth0"])
+ app_helper.run("h1", ["224.0.100.6", "h1-eth0"])
+
+ def expect_igmp_group_count():
+ igmp_groups = tgen.gears["r1"].vtysh_cmd("show ip igmp groups json", isjson=True)
+ try:
+ return len(igmp_groups["r1-eth2"]["groups"])
+ except KeyError:
+ return 0
+
+ topotest.run_and_expect(expect_igmp_group_count, 4, count=10, wait=2)
+
+ # Cleanup
+ app_helper.stop_host("h1")
+ tgen.gears["r1"].vtysh_cmd("""
+ configure terminal
+ interface r1-eth2
+ no ip igmp max-groups 4
+ exit
+ clear ip igmp interfaces
+ """)
+
+
+def test_igmp_group_source_limit():
+ "Test IGMP source limits."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["r1"].vtysh_cmd("""
+ configure terminal
+ interface r1-eth2
+ ip igmp max-sources 4
+ exit
+ """)
+
+ app_helper.run("h1", ["--source=192.168.100.10", "232.0.101.10", "h1-eth0"])
+ app_helper.run("h1", ["--source=192.168.100.11", "232.0.101.10", "h1-eth0"])
+ app_helper.run("h1", ["--source=192.168.100.12", "232.0.101.10", "h1-eth0"])
+ app_helper.run("h1", ["--source=192.168.100.13", "232.0.101.10", "h1-eth0"])
+ app_helper.run("h1", ["--source=192.168.100.14", "232.0.101.10", "h1-eth0"])
+ app_helper.run("h1", ["--source=192.168.100.15", "232.0.101.10", "h1-eth0"])
+ app_helper.run("h1", ["--source=192.168.100.16", "232.0.101.10", "h1-eth0"])
+
+ def expect_igmp_group_source_count():
+ igmp_sources = tgen.gears["r1"].vtysh_cmd("show ip igmp sources json", isjson=True)
+ try:
+ return len(igmp_sources["r1-eth2"]["232.0.101.10"]["sources"])
+ except KeyError:
+ return 0
+
+ topotest.run_and_expect(expect_igmp_group_source_count, 4, count=10, wait=2)
+
+ # Cleanup
+ tgen.gears["r1"].vtysh_cmd("""
+ configure terminal
+ interface r1-eth2
+ no ip igmp max-sources 4
+ exit
+ clear ip igmp interfaces
+ """)
+ app_helper.stop_host("h1")
+
+
+def test_mld_group_limit():
+ "Test MLD group limits."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["r1"].vtysh_cmd("""
+ configure terminal
+ interface r1-eth2
+ ipv6 mld max-groups 14
+ """)
+ app_helper.run("h1", ["FF05::100", "h1-eth0"])
+ app_helper.run("h1", ["FF05::101", "h1-eth0"])
+ app_helper.run("h1", ["FF05::102", "h1-eth0"])
+ app_helper.run("h1", ["FF05::103", "h1-eth0"])
+ app_helper.run("h1", ["FF05::104", "h1-eth0"])
+ app_helper.run("h1", ["FF05::105", "h1-eth0"])
+ app_helper.run("h1", ["FF05::106", "h1-eth0"])
+ app_helper.run("h1", ["FF05::107", "h1-eth0"])
+ app_helper.run("h1", ["FF05::108", "h1-eth0"])
+ app_helper.run("h1", ["FF05::109", "h1-eth0"])
+ app_helper.run("h1", ["FF05::110", "h1-eth0"])
+ app_helper.run("h1", ["FF05::111", "h1-eth0"])
+ app_helper.run("h1", ["FF05::112", "h1-eth0"])
+ app_helper.run("h1", ["FF05::113", "h1-eth0"])
+ app_helper.run("h1", ["FF05::114", "h1-eth0"])
+ app_helper.run("h1", ["FF05::115", "h1-eth0"])
+
+ def expect_mld_group_count():
+ mld_groups = tgen.gears["r1"].vtysh_cmd("show ipv6 mld groups json", isjson=True)
+ try:
+ return len(mld_groups["r1-eth2"]["groups"])
+ except KeyError:
+ return 0
+
+
+ topotest.run_and_expect(expect_mld_group_count, 14, count=10, wait=2)
+
+
+ # Cleanup
+ app_helper.stop_host("h1")
+ tgen.gears["r1"].vtysh_cmd("""
+ configure terminal
+ interface r1-eth2
+ no ipv6 mld max-groups 4
+ exit
+ clear ipv6 mld interfaces
+ """)
+
+
+def test_mld_group_source_limit():
+ "Test MLD source limits."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["r1"].vtysh_cmd("""
+ configure terminal
+ interface r1-eth2
+ ipv6 mld max-sources 4
+ exit
+ """)
+
+ app_helper.run("h1", ["--source=2001:db8:1::100", "FF35::100", "h1-eth0"])
+ app_helper.run("h1", ["--source=2001:db8:1::101", "FF35::100", "h1-eth0"])
+ app_helper.run("h1", ["--source=2001:db8:1::102", "FF35::100", "h1-eth0"])
+ app_helper.run("h1", ["--source=2001:db8:1::103", "FF35::100", "h1-eth0"])
+ app_helper.run("h1", ["--source=2001:db8:1::104", "FF35::100", "h1-eth0"])
+ app_helper.run("h1", ["--source=2001:db8:1::105", "FF35::100", "h1-eth0"])
+ app_helper.run("h1", ["--source=2001:db8:1::106", "FF35::100", "h1-eth0"])
+
+ def expect_mld_source_group_count():
+ mld_sources = tgen.gears["r1"].vtysh_cmd("show ipv6 mld joins json", isjson=True)
+ try:
+ return len(mld_sources["default"]["r1-eth2"]["ff35::100"].keys())
+ except KeyError:
+ return 0
+
+ topotest.run_and_expect(expect_mld_source_group_count, 4, count=10, wait=2)
+
+ # Cleanup
+ tgen.gears["r1"].vtysh_cmd("""
+ configure terminal
+ interface r1-eth2
+ no ipv6 mld max-sources 4
+ exit
+ clear ipv6 mld interfaces
+ """)
+ app_helper.stop_host("h1")
+
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/yang/frr-gmp.yang b/yang/frr-gmp.yang
index 5636010d91..26b19501f9 100644
--- a/yang/frr-gmp.yang
+++ b/yang/frr-gmp.yang
@@ -154,6 +154,20 @@ module frr-gmp {
"Enable IGMP proxy on the interface.";
}
+ leaf max-groups {
+ type uint32;
+ default "4294967295";
+ description
+ "Limit number of tracked IGMP group memberships on this interface.";
+ }
+
+ leaf max-sources {
+ type uint32;
+ default "4294967295";
+ description
+ "Limit number of tracked IGMPv3 sources on this interface.";
+ }
+
list join-group {
key "group-addr source-addr";
description
diff --git a/yang/frr-pim-rp.yang b/yang/frr-pim-rp.yang
index 5558b0888d..6146bd0890 100644
--- a/yang/frr-pim-rp.yang
+++ b/yang/frr-pim-rp.yang
@@ -61,13 +61,6 @@ module frr-pim-rp {
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.";
- revision 2017-03-09 {
- description
- "Initial revision.";
- reference
- "RFC XXXX: A YANG Data Model for PIM RP";
- }
-
revision 2024-09-26 {
description
"Add support for embedded RP.";
@@ -76,6 +69,13 @@ module frr-pim-rp {
Multicast Address";
}
+ revision 2017-03-09 {
+ description
+ "Initial revision.";
+ reference
+ "RFC XXXX: A YANG Data Model for PIM RP";
+ }
+
typedef plist-ref {
type string;
}
diff --git a/zebra/fpm_listener.c b/zebra/fpm_listener.c
index ed0842a3b1..140cfa77cf 100644
--- a/zebra/fpm_listener.c
+++ b/zebra/fpm_listener.c
@@ -189,7 +189,7 @@ read_fpm_msg(char *buf, size_t buf_len)
fprintf(stderr,
"Read %lu bytes but expected to read %lu bytes instead\n",
bytes_read, need_len);
- return NULL;
+ continue;
}
if (reading_full_msg)