diff options
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) |
