diff options
556 files changed, 32861 insertions, 9418 deletions
diff --git a/.gitignore b/.gitignore index 05ca0b1140..4e120dae85 100644 --- a/.gitignore +++ b/.gitignore @@ -110,3 +110,5 @@ refix .vscode .kitchen .emacs.desktop* + +/test-suite.log diff --git a/babeld/.gitignore b/babeld/.gitignore index fbdb90f677..71ef6786c7 100644 --- a/babeld/.gitignore +++ b/babeld/.gitignore @@ -4,5 +4,4 @@ !LICENCE !Makefile !subdir.am -!babeld.conf.sample !.gitignore diff --git a/babeld/babeld.conf.sample b/babeld/babeld.conf.sample deleted file mode 100644 index a77453a734..0000000000 --- a/babeld/babeld.conf.sample +++ /dev/null @@ -1,30 +0,0 @@ -debug babel common -!debug babel kernel -!debug babel filter -!debug babel timeout -!debug babel interface -!debug babel route -!debug babel all - -router babel -! network wlan0 -! network eth0 -! redistribute ipv4 kernel -! no redistribute ipv6 static - -! The defaults are fine for a wireless interface - -!interface wlan0 - -! A few optimisation tweaks are optional but recommended on a wired interface -! Disable link quality estimation, enable split horizon processing, and -! increase the hello and update intervals. - -!interface eth0 -! babel wired -! babel split-horizon -! babel hello-interval 12000 -! babel update-interval 36000 - -! log file /var/log/quagga/babeld.log -log stdout diff --git a/babeld/subdir.am b/babeld/subdir.am index 8e5b46350d..c9b6959fca 100644 --- a/babeld/subdir.am +++ b/babeld/subdir.am @@ -5,7 +5,6 @@ if BABELD noinst_LIBRARIES += babeld/libbabel.a sbin_PROGRAMS += babeld/babeld -dist_examples_DATA += babeld/babeld.conf.sample vtysh_scan += \ babeld/babel_interface.c \ babeld/babel_zebra.c \ diff --git a/bfdd/bfd.c b/bfdd/bfd.c index 234fc6d397..18f331e201 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -1941,6 +1941,14 @@ void bfd_sessions_remove_manual(void) hash_iterate(bfd_key_hash, _bfd_session_remove_manual, NULL); } +void bfd_profiles_remove(void) +{ + struct bfd_profile *bp; + + while ((bp = TAILQ_FIRST(&bplist)) != NULL) + bfd_profile_free(bp); +} + /* * Profile related hash functions. */ diff --git a/bfdd/bfd.h b/bfdd/bfd.h index a86c1bb9f3..d9d5d8cb5e 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -619,6 +619,7 @@ void bfd_session_free(struct bfd_session *bs); const struct bfd_session *bfd_session_next(const struct bfd_session *bs, bool mhop); void bfd_sessions_remove_manual(void); +void bfd_profiles_remove(void); /** * Set the BFD session echo state. diff --git a/bfdd/bfdd.conf.sample b/bfdd/bfdd.conf.sample deleted file mode 100644 index 9981e262bc..0000000000 --- a/bfdd/bfdd.conf.sample +++ /dev/null @@ -1,5 +0,0 @@ -password zebra -! -log stdout -! -line vty diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index ba80b2363f..6ec724d80c 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -273,11 +273,8 @@ DEFPY_YANG( void bfd_cli_show_shutdown(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - if (show_defaults) - vty_out(vty, " no shutdown\n"); - else - vty_out(vty, " %sshutdown\n", - yang_dnode_get_bool(dnode, NULL) ? "" : "no "); + vty_out(vty, " %sshutdown\n", + yang_dnode_get_bool(dnode, NULL) ? "" : "no "); } DEFPY_YANG( @@ -294,11 +291,8 @@ DEFPY_YANG( void bfd_cli_show_passive(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - if (show_defaults) - vty_out(vty, " no passive-mode\n"); - else - vty_out(vty, " %spassive-mode\n", - yang_dnode_get_bool(dnode, NULL) ? "" : "no "); + vty_out(vty, " %spassive-mode\n", + yang_dnode_get_bool(dnode, NULL) ? "" : "no "); } DEFPY_YANG( @@ -335,11 +329,7 @@ DEFPY_YANG( void bfd_cli_show_minimum_ttl(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - if (show_defaults) - vty_out(vty, " minimum-ttl 254\n"); - else - vty_out(vty, " minimum-ttl %s\n", - yang_dnode_get_string(dnode, NULL)); + vty_out(vty, " minimum-ttl %s\n", yang_dnode_get_string(dnode, NULL)); } DEFPY_YANG( @@ -356,12 +346,8 @@ DEFPY_YANG( void bfd_cli_show_mult(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - if (show_defaults) - vty_out(vty, " detect-multiplier %d\n", - BFD_DEFDETECTMULT); - else - vty_out(vty, " detect-multiplier %s\n", - yang_dnode_get_string(dnode, NULL)); + vty_out(vty, " detect-multiplier %s\n", + yang_dnode_get_string(dnode, NULL)); } DEFPY_YANG( @@ -382,15 +368,9 @@ DEFPY_YANG( void bfd_cli_show_rx(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - uint32_t value; + uint32_t value = yang_dnode_get_uint32(dnode, NULL); - if (show_defaults) - vty_out(vty, " receive-interval %d\n", - BFD_DEFREQUIREDMINRX); - else { - value = yang_dnode_get_uint32(dnode, NULL); - vty_out(vty, " receive-interval %u\n", value / 1000); - } + vty_out(vty, " receive-interval %u\n", value / 1000); } DEFPY_YANG( @@ -411,15 +391,9 @@ DEFPY_YANG( void bfd_cli_show_tx(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - uint32_t value; + uint32_t value = yang_dnode_get_uint32(dnode, NULL); - if (show_defaults) - vty_out(vty, " transmit-interval %d\n", - BFD_DEFDESIREDMINTX); - else { - value = yang_dnode_get_uint32(dnode, NULL); - vty_out(vty, " transmit-interval %u\n", value / 1000); - } + vty_out(vty, " transmit-interval %u\n", value / 1000); } DEFPY_YANG( @@ -445,11 +419,8 @@ DEFPY_YANG( void bfd_cli_show_echo(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - if (show_defaults) - vty_out(vty, " no echo-mode\n"); - else - vty_out(vty, " %secho-mode\n", - yang_dnode_get_bool(dnode, NULL) ? "" : "no "); + vty_out(vty, " %secho-mode\n", + yang_dnode_get_bool(dnode, NULL) ? "" : "no "); } DEFPY_YANG( @@ -498,15 +469,9 @@ DEFPY_YANG( void bfd_cli_show_desired_echo_transmission_interval(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - uint32_t value; + uint32_t value = yang_dnode_get_uint32(dnode, NULL); - if (show_defaults) - vty_out(vty, " echo transmit-interval %d\n", - BFD_DEF_DES_MIN_ECHO_TX); - else { - value = yang_dnode_get_uint32(dnode, NULL); - vty_out(vty, " echo transmit-interval %u\n", value / 1000); - } + vty_out(vty, " echo transmit-interval %u\n", value / 1000); } DEFPY_YANG( @@ -538,26 +503,19 @@ DEFPY_YANG( void bfd_cli_show_required_echo_receive_interval(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - uint32_t value; - - if (show_defaults) - vty_out(vty, " echo receive-interval %d\n", - BFD_DEF_REQ_MIN_ECHO_RX); - else { - value = yang_dnode_get_uint32(dnode, NULL); - if (value) - vty_out(vty, " echo receive-interval %u\n", - value / 1000); - else - vty_out(vty, " echo receive-interval disabled\n"); - } + uint32_t value = yang_dnode_get_uint32(dnode, NULL); + + if (value) + vty_out(vty, " echo receive-interval %u\n", value / 1000); + else + vty_out(vty, " echo receive-interval disabled\n"); } /* * Profile commands. */ DEFPY_YANG_NOSH(bfd_profile, bfd_profile_cmd, - "profile WORD$name", + "profile BFDPROF$name", BFD_PROFILE_STR BFD_PROFILE_NAME_STR) { diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c index 26bce4f357..b221b6d539 100644 --- a/bfdd/bfdd_nb_config.c +++ b/bfdd/bfdd_nb_config.c @@ -229,7 +229,15 @@ static int bfd_session_destroy(enum nb_event event, */ int bfdd_bfd_create(struct nb_cb_create_args *args) { - /* NOTHING */ + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* + * Set any non-NULL value to be able to call + * nb_running_unset_entry in bfdd_bfd_destroy. + */ + nb_running_set_entry(args->dnode, (void *)0x1); + return NB_OK; } @@ -245,7 +253,14 @@ int bfdd_bfd_destroy(struct nb_cb_destroy_args *args) return NB_OK; case NB_EV_APPLY: + /* + * We need to call this to unset pointers from + * the child nodes - sessions and profiles. + */ + nb_running_unset_entry(args->dnode); + bfd_sessions_remove_manual(); + bfd_profiles_remove(); break; case NB_EV_ABORT: diff --git a/bfdd/subdir.am b/bfdd/subdir.am index e572d4a3c0..8d35b933d7 100644 --- a/bfdd/subdir.am +++ b/bfdd/subdir.am @@ -5,7 +5,6 @@ if BFDD noinst_LIBRARIES += bfdd/libbfd.a sbin_PROGRAMS += bfdd/bfdd -dist_examples_DATA += bfdd/bfdd.conf.sample vtysh_scan += bfdd/bfdd_vty.c vtysh_scan += bfdd/bfdd_cli.c vtysh_daemons += bfdd diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index f658b0d0f0..71e4b56a00 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -3734,7 +3734,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, struct aspath *aspath; int send_as4_path = 0; int send_as4_aggregator = 0; - int use32bit = (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) ? 1 : 0; + bool use32bit = CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV); if (!bgp) bgp = peer->bgp; diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 1b176f8716..a583581030 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -243,6 +243,15 @@ struct attr { */ #define ATTR_ES_PEER_ROUTER (1 << 4) + /* These two flags are only set on L3 routes installed in a + * VRF as a result of EVPN MAC-IP route + * XXX - while splitting up per-family attrs these need to be + * classified as non-EVPN + */ +#define ATTR_ES_L3_NHG_USE (1 << 5) +#define ATTR_ES_L3_NHG_ACTIVE (1 << 6) +#define ATTR_ES_L3_NHG (ATTR_ES_L3_NHG_USE | ATTR_ES_L3_NHG_ACTIVE) + /* route tag */ route_tag_t tag; diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index 11e9344d1c..958d7cf0ed 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -29,6 +29,7 @@ #include "thread.h" #include "buffer.h" #include "stream.h" +#include "vrf.h" #include "zclient.h" #include "bfd.h" #include "lib/json.h" @@ -40,667 +41,390 @@ #include "bgpd/bgp_debug.h" #include "bgpd/bgp_vty.h" +DEFINE_MTYPE_STATIC(BGPD, BFD_CONFIG, "BFD configuration data"); + extern struct zclient *zclient; -/* - * bgp_bfd_peer_group2peer_copy - Copy the BFD information from peer group - * template - * to peer. - */ -void bgp_bfd_peer_group2peer_copy(struct peer *conf, struct peer *peer) +static void bfd_session_status_update(struct bfd_session_params *bsp, + const struct bfd_session_status *bss, + void *arg) { - struct bfd_info *bfd_info; - struct bfd_info *conf_bfd_info; - - if (!conf->bfd_info) - return; - - conf_bfd_info = (struct bfd_info *)conf->bfd_info; - if (!peer->bfd_info) - peer->bfd_info = bfd_info_create(); - - bfd_info = (struct bfd_info *)peer->bfd_info; + struct peer *peer = arg; + + if (BGP_DEBUG(bfd, BFD_LIB)) + zlog_debug("%s: neighbor %s vrf %s(%u) bfd state %s -> %s", + __func__, peer->conf_if ? peer->conf_if : peer->host, + bfd_sess_vrf(bsp), bfd_sess_vrf_id(bsp), + bfd_get_status_str(bss->previous_state), + bfd_get_status_str(bss->state)); + + if (bss->state == BSS_DOWN && bss->previous_state == BSS_UP) { + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE) + && bfd_sess_cbit(bsp) && !bss->remote_cbit) { + if (BGP_DEBUG(bfd, BFD_LIB)) + zlog_info( + "%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared", + peer->host); + return; + } + peer->last_reset = PEER_DOWN_BFD_DOWN; + BGP_EVENT_ADD(peer, BGP_Stop); + } - /* Copy BFD parameter values */ - bfd_info->required_min_rx = conf_bfd_info->required_min_rx; - bfd_info->desired_min_tx = conf_bfd_info->desired_min_tx; - bfd_info->detect_mult = conf_bfd_info->detect_mult; - bfd_info->type = conf_bfd_info->type; + if (bss->state == BSS_UP && bss->previous_state != BSS_UP + && peer->status != Established) { + if (!BGP_PEER_START_SUPPRESSED(peer)) { + bgp_fsm_nht_update(peer, true); + BGP_EVENT_ADD(peer, BGP_Start); + } + } } -/* - * bgp_bfd_is_peer_multihop - returns whether BFD peer is multi-hop or single - * hop. - */ -bool bgp_bfd_is_peer_multihop(struct peer *peer) +void bgp_peer_config_apply(struct peer *p, struct peer_group *pg) { - struct bfd_info *bfd_info; + struct listnode *n; + struct peer *pn; + struct peer *gconfig; + + /* When called on a group, apply to all peers. */ + if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)) { + for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn)) + bgp_peer_config_apply(pn, pg); + return; + } - bfd_info = (struct bfd_info *)peer->bfd_info; + /* No group, just use current configuration. */ + if (pg == NULL || pg->conf->bfd_config == NULL) { + bfd_sess_set_timers(p->bfd_config->session, + p->bfd_config->detection_multiplier, + p->bfd_config->min_rx, + p->bfd_config->min_tx); + bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit); + bfd_sess_set_profile(p->bfd_config->session, + p->bfd_config->profile); + bfd_sess_install(p->bfd_config->session); + return; + } - if (!bfd_info) - return false; + /* + * Check if the group configuration was overwritten or apply group + * configuration. + */ + gconfig = pg->conf; + + /* + * If using default control plane independent configuration, + * then prefer group's (e.g. it means it wasn't manually configured). + */ + if (!p->bfd_config->cbit) + bfd_sess_set_cbit(p->bfd_config->session, + gconfig->bfd_config->cbit); + else + bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit); - if ((bfd_info->type == BFD_TYPE_MULTIHOP) - || ((peer->sort == BGP_PEER_IBGP) && !peer->shared_network) - || is_ebgp_multihop_configured(peer)) - return true; + /* If no profile was specified in peer, then use the group profile. */ + if (p->bfd_config->profile[0] == 0) + bfd_sess_set_profile(p->bfd_config->session, + gconfig->bfd_config->profile); else - return false; + bfd_sess_set_profile(p->bfd_config->session, + p->bfd_config->profile); + + /* If no specific timers were configured, then use the group timers. */ + if (p->bfd_config->detection_multiplier == BFD_DEF_DETECT_MULT + || p->bfd_config->min_rx == BFD_DEF_MIN_RX + || p->bfd_config->min_tx == BFD_DEF_MIN_TX) + bfd_sess_set_timers(p->bfd_config->session, + gconfig->bfd_config->detection_multiplier, + gconfig->bfd_config->min_rx, + gconfig->bfd_config->min_tx); + else + bfd_sess_set_timers(p->bfd_config->session, + p->bfd_config->detection_multiplier, + p->bfd_config->min_rx, + p->bfd_config->min_tx); + + bfd_sess_install(p->bfd_config->session); } -/* - * bgp_bfd_peer_sendmsg - Format and send a Peer register/Unregister - * command to Zebra to be forwarded to BFD - */ -static void bgp_bfd_peer_sendmsg(struct peer *peer, int command) +void bgp_peer_bfd_update_source(struct peer *p) { - struct bfd_session_arg arg = {}; - struct bfd_info *bfd_info; - int multihop; - vrf_id_t vrf_id; - size_t addrlen; - - /* - * XXX: some pointers are dangling during shutdown, so instead of - * trying to send a message during signal handlers lets just wait BGP - * to terminate zebra's connection and BFD will automatically find - * out that we are no longer expecting notifications. - * - * The pointer that is causing a crash here is `peer->nexthop.ifp`. - * That happens because at this point of the shutdown all interfaces are - * already `free()`d. - */ - if (bm->terminating) + struct bfd_session_params *session = p->bfd_config->session; + bool changed = false; + int family; + union { + struct in_addr v4; + struct in6_addr v6; + } src, dst; + + /* Nothing to do for groups. */ + if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)) return; - bfd_info = (struct bfd_info *)peer->bfd_info; + /* Update peer's source/destination addresses. */ + bfd_sess_addresses(session, &family, &src.v6, &dst.v6); + if (family == AF_INET) { + if ((p->su_local + && p->su_local->sin.sin_addr.s_addr != src.v4.s_addr) + || p->su.sin.sin_addr.s_addr != dst.v4.s_addr) { + if (BGP_DEBUG(bfd, BFD_LIB)) + zlog_debug( + "%s: address [%pI4->%pI4] to [%pI4->%pI4]", + __func__, &src.v4, &dst.v4, + p->su_local ? &p->su_local->sin.sin_addr + : &src.v4, + &p->su.sin.sin_addr); + + bfd_sess_set_ipv4_addrs( + session, + p->su_local ? &p->su_local->sin.sin_addr : NULL, + &p->su.sin.sin_addr); + changed = true; + } + } else { + if ((p->su_local + && memcmp(&p->su_local->sin6, &src.v6, sizeof(src.v6))) + || memcmp(&p->su.sin6, &dst.v6, sizeof(dst.v6))) { + if (BGP_DEBUG(bfd, BFD_LIB)) + zlog_debug( + "%s: address [%pI6->%pI6] to [%pI6->%pI6]", + __func__, &src.v6, &dst.v6, + p->su_local + ? &p->su_local->sin6.sin6_addr + : &src.v6, + &p->su.sin6.sin6_addr); + + bfd_sess_set_ipv6_addrs( + session, + p->su_local ? &p->su_local->sin6.sin6_addr + : NULL, + &p->su.sin6.sin6_addr); + changed = true; + } + } - vrf_id = peer->bgp->vrf_id; + /* Update interface. */ + if (p->nexthop.ifp && bfd_sess_interface(session) == NULL) { + if (BGP_DEBUG(bfd, BFD_LIB)) + zlog_debug("%s: interface none to %s", __func__, + p->nexthop.ifp->name); - if (command == ZEBRA_BFD_DEST_DEREGISTER) { - multihop = - CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_TYPE_MULTIHOP); - UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_TYPE_MULTIHOP); - } else { - multihop = bgp_bfd_is_peer_multihop(peer); - if ((command == ZEBRA_BFD_DEST_REGISTER) && multihop) - SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_TYPE_MULTIHOP); + bfd_sess_set_interface(session, p->nexthop.ifp->name); + changed = true; } - /* while graceful restart with fwd path preserved - * and bfd controlplane check not configured is not kept - * keep bfd independent controlplane bit set to 1 + + /* + * Update TTL. + * + * Two cases: + * - We detected that the peer is a hop away from us (remove multi hop). + * (this happens when `p->shared_network` is set to `true`) + * - eBGP multi hop / TTL security changed. */ - if (!CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GRACEFUL_RESTART) - && !CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GR_PRESERVE_FWD) - && !CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) - SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); - - /* Set all message arguments. */ - arg.family = peer->su.sa.sa_family; - addrlen = arg.family == AF_INET ? sizeof(struct in_addr) - : sizeof(struct in6_addr); - - if (arg.family == AF_INET) - memcpy(&arg.dst, &peer->su.sin.sin_addr, addrlen); - else - memcpy(&arg.dst, &peer->su.sin6.sin6_addr, addrlen); + if (!PEER_IS_MULTIHOP(p) && bfd_sess_hop_count(session) > 1) { + if (BGP_DEBUG(bfd, BFD_LIB)) + zlog_debug("%s: TTL %d to 1", __func__, + bfd_sess_hop_count(session)); - if (peer->su_local) { - if (arg.family == AF_INET) - memcpy(&arg.src, &peer->su_local->sin.sin_addr, - addrlen); - else - memcpy(&arg.src, &peer->su_local->sin6.sin6_addr, - addrlen); + bfd_sess_set_hop_count(session, 1); + changed = true; } + if (PEER_IS_MULTIHOP(p) && p->ttl != bfd_sess_hop_count(session)) { + if (BGP_DEBUG(bfd, BFD_LIB)) + zlog_debug("%s: TTL %d to %d", __func__, + bfd_sess_hop_count(session), p->ttl); - if (peer->nexthop.ifp) { - arg.ifnamelen = strlen(peer->nexthop.ifp->name); - strlcpy(arg.ifname, peer->nexthop.ifp->name, - sizeof(arg.ifname)); + bfd_sess_set_hop_count(session, p->ttl); + changed = true; } - if (bfd_info->profile[0]) { - arg.profilelen = strlen(bfd_info->profile); - strlcpy(arg.profile, bfd_info->profile, sizeof(arg.profile)); + /* Update VRF. */ + if (bfd_sess_vrf_id(session) != p->bgp->vrf_id) { + if (BGP_DEBUG(bfd, BFD_LIB)) + zlog_debug( + "%s: VRF %s(%d) to %s(%d)", __func__, + bfd_sess_vrf(session), bfd_sess_vrf_id(session), + vrf_id_to_name(p->bgp->vrf_id), p->bgp->vrf_id); + + bfd_sess_set_vrf(session, p->bgp->vrf_id); + changed = true; } - arg.set_flag = 1; - arg.mhop = multihop; - arg.ttl = peer->ttl; - arg.vrf_id = vrf_id; - arg.command = command; - arg.bfd_info = bfd_info; - arg.min_tx = bfd_info->desired_min_tx; - arg.min_rx = bfd_info->required_min_rx; - arg.detection_multiplier = bfd_info->detect_mult; - arg.cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); - - /* Send message. */ - zclient_bfd_command(zclient, &arg); + if (changed) + bfd_sess_install(session); } -/* - * bgp_bfd_register_peer - register a peer with BFD through zebra - * for monitoring the peer rechahability. +/** + * Reset BFD configuration data structure to its defaults settings. */ -void bgp_bfd_register_peer(struct peer *peer) +static void bgp_peer_bfd_reset(struct peer *p) { - struct bfd_info *bfd_info; - - if (!peer->bfd_info) - return; - bfd_info = (struct bfd_info *)peer->bfd_info; - - /* Check if BFD is enabled and peer has already been registered with BFD - */ - if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG)) - return; - - bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_REGISTER); + /* Set defaults. */ + p->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT; + p->bfd_config->min_rx = BFD_DEF_MIN_RX; + p->bfd_config->min_tx = BFD_DEF_MIN_TX; + p->bfd_config->cbit = false; + p->bfd_config->profile[0] = 0; } -/** - * bgp_bfd_deregister_peer - deregister a peer with BFD through zebra - * for stopping the monitoring of the peer - * rechahability. - */ -void bgp_bfd_deregister_peer(struct peer *peer) +void bgp_peer_configure_bfd(struct peer *p, bool manual) { - struct bfd_info *bfd_info; + /* Groups should not call this. */ + assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)); - if (!peer->bfd_info) - return; - bfd_info = (struct bfd_info *)peer->bfd_info; + /* Already configured, skip it. */ + if (p->bfd_config) { + /* If manually active update flag. */ + if (!p->bfd_config->manual) + p->bfd_config->manual = manual; - /* Check if BFD is eanbled and peer has not been registered */ - if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG)) return; + } - bfd_info->status = BFD_STATUS_DOWN; - bfd_info->last_update = bgp_clock(); - - bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_DEREGISTER); -} - -/* - * bgp_bfd_update_peer - update peer with BFD with new BFD paramters - * through zebra. - */ -static void bgp_bfd_update_peer(struct peer *peer) -{ - struct bfd_info *bfd_info; + /* Allocate memory for configuration overrides. */ + p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config)); + p->bfd_config->manual = manual; - if (!peer->bfd_info) - return; - bfd_info = (struct bfd_info *)peer->bfd_info; + /* Create new session and assign callback. */ + p->bfd_config->session = bfd_sess_new(bfd_session_status_update, p); + bgp_peer_bfd_reset(p); - /* Check if the peer has been registered with BFD*/ - if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG)) - return; + /* Configure session with basic BGP peer data. */ + if (p->su.sa.sa_family == AF_INET) + bfd_sess_set_ipv4_addrs(p->bfd_config->session, + p->su_local ? &p->su_local->sin.sin_addr + : NULL, + &p->su.sin.sin_addr); + else + bfd_sess_set_ipv6_addrs( + p->bfd_config->session, + p->su_local ? &p->su_local->sin6.sin6_addr : NULL, + &p->su.sin6.sin6_addr); - bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_UPDATE); -} + bfd_sess_set_vrf(p->bfd_config->session, p->bgp->vrf_id); + bfd_sess_set_hop_count(p->bfd_config->session, + PEER_IS_MULTIHOP(p) ? p->ttl : 1); -/** - * bgp_bfd_reset_peer - reinitialise bfd - * ensures that bfd state machine is restarted - * to be synced with remote bfd - */ -void bgp_bfd_reset_peer(struct peer *peer) -{ - if (!peer->bfd_info) - return; + if (p->nexthop.ifp) + bfd_sess_set_interface(p->bfd_config->session, + p->nexthop.ifp->name); - bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_REGISTER); + bfd_sess_enable(p->bfd_config->session, true); } -/* - * bgp_bfd_update_type - update session type with BFD through zebra. - */ -static void bgp_bfd_update_type(struct peer *peer) +static void bgp_peer_remove_bfd(struct peer *p) { - struct bfd_info *bfd_info; - int multihop; - - if (!peer->bfd_info) - return; - bfd_info = (struct bfd_info *)peer->bfd_info; + /* Groups should not call this. */ + assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)); - /* Check if the peer has been registered with BFD*/ - if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG)) + /* + * Peer configuration was removed, however we must check if there + * is still a group configuration to keep this running. + */ + if (p->group && p->group->conf->bfd_config) { + p->bfd_config->manual = false; + bgp_peer_bfd_reset(p); + bgp_peer_config_apply(p, p->group); return; - - if (bfd_info->type == BFD_TYPE_NOT_CONFIGURED) { - multihop = bgp_bfd_is_peer_multihop(peer); - if ((multihop - && !CHECK_FLAG(bfd_info->flags, - BFD_FLAG_BFD_TYPE_MULTIHOP)) - || (!multihop && CHECK_FLAG(bfd_info->flags, - BFD_FLAG_BFD_TYPE_MULTIHOP))) { - bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_DEREGISTER); - bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_REGISTER); - } - } else { - if ((bfd_info->type == BFD_TYPE_MULTIHOP - && !CHECK_FLAG(bfd_info->flags, - BFD_FLAG_BFD_TYPE_MULTIHOP)) - || (bfd_info->type == BFD_TYPE_SINGLEHOP - && CHECK_FLAG(bfd_info->flags, - BFD_FLAG_BFD_TYPE_MULTIHOP))) { - bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_DEREGISTER); - bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_REGISTER); - } } -} - -/* - * bgp_bfd_dest_replay - Replay all the peers that have BFD enabled - * to zebra - */ -static int bgp_bfd_dest_replay(ZAPI_CALLBACK_ARGS) -{ - struct listnode *mnode, *node, *nnode; - struct bgp *bgp; - struct peer *peer; - - if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("Zebra: BFD Dest replay request"); - - /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); - - /* Replay the peer, if BFD is enabled in BGP */ - for (ALL_LIST_ELEMENTS_RO(bm->bgp, mnode, bgp)) - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - bgp_bfd_update_peer(peer); - } - - return 0; + bfd_sess_free(&p->bfd_config->session); + XFREE(MTYPE_BFD_CONFIG, p->bfd_config); } -/* - * bgp_bfd_peer_status_update - Update the BFD status if it has changed. Bring - * down the peer if the BFD session went down from - * * up. - */ -static void bgp_bfd_peer_status_update(struct peer *peer, int status, - int remote_cbit) +static void bgp_group_configure_bfd(struct peer *p) { - struct bfd_info *bfd_info; - int old_status; + struct listnode *n; + struct peer *pn; - bfd_info = (struct bfd_info *)peer->bfd_info; + /* Peers should not call this. */ + assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)); - if (bfd_info->status == status) + /* Already allocated: do nothing. */ + if (p->bfd_config) return; - old_status = bfd_info->status; - BFD_SET_CLIENT_STATUS(bfd_info->status, status); + p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config)); - bfd_info->last_update = bgp_clock(); + /* Set defaults. */ + p->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT; + p->bfd_config->min_rx = BFD_DEF_MIN_RX; + p->bfd_config->min_tx = BFD_DEF_MIN_TX; - if (status != old_status) { - if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)) - zlog_debug("[%s]: BFD %s", peer->host, - bfd_get_status_str(status)); - } - if ((status == BFD_STATUS_DOWN) && (old_status == BFD_STATUS_UP)) { - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE) && - CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE) && - !remote_cbit) { - zlog_info("%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared", - peer->host); - return; - } - peer->last_reset = PEER_DOWN_BFD_DOWN; - BGP_EVENT_ADD(peer, BGP_Stop); - } - if ((status == BFD_STATUS_UP) && (old_status == BFD_STATUS_DOWN) - && peer->status != Established) { - if (!BGP_PEER_START_SUPPRESSED(peer)) { - bgp_fsm_nht_update(peer, true); - BGP_EVENT_ADD(peer, BGP_Start); - } - } + for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn)) + bgp_peer_configure_bfd(pn, false); } -/* - * bgp_bfd_dest_update - Find the peer for which the BFD status - * has changed and bring down the peer - * connectivity if the BFD session went down. - */ -static int bgp_bfd_dest_update(ZAPI_CALLBACK_ARGS) +static void bgp_group_remove_bfd(struct peer *p) { - struct interface *ifp; - struct prefix dp; - struct prefix sp; - int status; - int remote_cbit; + struct listnode *n; + struct peer *pn; - ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status, - &remote_cbit, vrf_id); + /* Peers should not call this. */ + assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)); - if (BGP_DEBUG(zebra, ZEBRA)) { - struct vrf *vrf; + /* Already freed: do nothing. */ + if (p->bfd_config == NULL) + return; - vrf = vrf_lookup_by_id(vrf_id); + /* Free configuration and point to `NULL`. */ + XFREE(MTYPE_BFD_CONFIG, p->bfd_config); - if (ifp) - zlog_debug( - "Zebra: vrf %s(%u) interface %s bfd destination %pFX %s %s", - VRF_LOGNAME(vrf), vrf_id, ifp->name, &dp, - bfd_get_status_str(status), - remote_cbit ? "(cbit on)" : ""); + /* Now that it is `NULL` recalculate configuration for all peers. */ + for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn)) { + if (pn->bfd_config->manual) + bgp_peer_config_apply(pn, NULL); else - zlog_debug( - "Zebra: vrf %s(%u) source %pFX bfd destination %pFX %s %s", - VRF_LOGNAME(vrf), vrf_id, &sp, &dp, - bfd_get_status_str(status), - remote_cbit ? "(cbit on)" : ""); - } - - /* Bring the peer down if BFD is enabled in BGP */ - { - struct listnode *mnode, *node, *nnode; - struct bgp *bgp; - struct peer *peer; - - for (ALL_LIST_ELEMENTS_RO(bm->bgp, mnode, bgp)) - for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { - if (!peer->bfd_info) - continue; - - if ((dp.family == AF_INET) - && (peer->su.sa.sa_family == AF_INET)) { - if (dp.u.prefix4.s_addr - != peer->su.sin.sin_addr.s_addr) - continue; - } else if ((dp.family == AF_INET6) - && (peer->su.sa.sa_family - == AF_INET6)) { - if (memcmp(&dp.u.prefix6, - &peer->su.sin6.sin6_addr, - sizeof(struct in6_addr))) - continue; - } else - continue; - - if (ifp && (ifp == peer->nexthop.ifp)) { - bgp_bfd_peer_status_update(peer, - status, - remote_cbit); - } else { - if (!peer->su_local) - continue; - - if ((sp.family == AF_INET) - && (peer->su_local->sa.sa_family - == AF_INET)) { - if (sp.u.prefix4.s_addr - != peer->su_local->sin - .sin_addr.s_addr) - continue; - } else if ((sp.family == AF_INET6) - && (peer->su_local->sa - .sa_family - == AF_INET6)) { - if (memcmp(&sp.u.prefix6, - &peer->su_local->sin6 - .sin6_addr, - sizeof(struct - in6_addr))) - continue; - } else - continue; - - if ((vrf_id != VRF_DEFAULT) - && (peer->bgp->vrf_id != vrf_id)) - continue; - - bgp_bfd_peer_status_update(peer, - status, - remote_cbit); - } - } - } - - return 0; -} - -/* - * bgp_bfd_peer_param_set - Set the configured BFD paramter values for peer. - */ -static int bgp_bfd_peer_param_set(struct peer *peer, uint32_t min_rx, - uint32_t min_tx, uint8_t detect_mult, - int defaults) -{ - struct bfd_info *bi; - struct peer_group *group; - struct listnode *node, *nnode; - int command = 0; - - bfd_set_param((struct bfd_info **)&(peer->bfd_info), min_rx, min_tx, - detect_mult, NULL, defaults, &command); - - /* This command overrides profile if it was previously applied. */ - bi = peer->bfd_info; - bi->profile[0] = 0; - - if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - command = 0; - bfd_set_param((struct bfd_info **)&(peer->bfd_info), - min_rx, min_tx, detect_mult, NULL, - defaults, &command); - - /* - * This command overrides profile if it was previously - * applied. - */ - bi = peer->bfd_info; - bi->profile[0] = 0; - - if ((peer->status == Established) - && (command == ZEBRA_BFD_DEST_REGISTER)) - bgp_bfd_register_peer(peer); - else if (command == ZEBRA_BFD_DEST_UPDATE) - bgp_bfd_update_peer(peer); - } - } else { - if ((peer->status == Established) - && (command == ZEBRA_BFD_DEST_REGISTER)) - bgp_bfd_register_peer(peer); - else if (command == ZEBRA_BFD_DEST_UPDATE) - bgp_bfd_update_peer(peer); - } - return 0; -} - -/* - * bgp_bfd_peer_param_unset - Delete the configured BFD paramter values for - * peer. - */ -static int bgp_bfd_peer_param_unset(struct peer *peer) -{ - struct peer_group *group; - struct listnode *node, *nnode; - - if (!peer->bfd_info) - return 0; - - if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - bfd_info_free(&(peer->bfd_info)); - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - bgp_bfd_deregister_peer(peer); - bfd_info_free(&(peer->bfd_info)); - } - } else { - bgp_bfd_deregister_peer(peer); - bfd_info_free(&(peer->bfd_info)); + bgp_peer_remove_bfd(pn); } - return 0; } -/* - * bgp_bfd_peer_param_type_set - set the BFD session type (multihop or - * singlehop) - */ -static int bgp_bfd_peer_param_type_set(struct peer *peer, - enum bfd_sess_type type) +void bgp_peer_remove_bfd_config(struct peer *p) { - struct peer_group *group; - struct listnode *node, *nnode; - int command = 0; - struct bfd_info *bfd_info; - - if (!peer->bfd_info) - bfd_set_param((struct bfd_info **)&(peer->bfd_info), - BFD_DEF_MIN_RX, BFD_DEF_MIN_TX, - BFD_DEF_DETECT_MULT, NULL, 1, &command); - - bfd_info = (struct bfd_info *)peer->bfd_info; - bfd_info->type = type; - - if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - command = 0; - if (!peer->bfd_info) - bfd_set_param( - (struct bfd_info **)&(peer->bfd_info), - BFD_DEF_MIN_RX, BFD_DEF_MIN_TX, - BFD_DEF_DETECT_MULT, NULL, 1, &command); - - bfd_info = (struct bfd_info *)peer->bfd_info; - bfd_info->type = type; - - if (peer->status == Established) { - if (command == ZEBRA_BFD_DEST_REGISTER) - bgp_bfd_register_peer(peer); - else - bgp_bfd_update_type(peer); - } - } - } else { - if (peer->status == Established) { - if (command == ZEBRA_BFD_DEST_REGISTER) - bgp_bfd_register_peer(peer); - else - bgp_bfd_update_type(peer); - } - } - - return 0; -} - -#if HAVE_BFDD > 0 -/** - * Set peer BFD profile configuration. - */ -static int bgp_bfd_peer_set_profile(struct peer *peer, const char *profile) -{ - struct peer_group *group; - struct listnode *node, *nnode; - int command = 0; - struct bfd_info *bfd_info; - - bfd_set_param((struct bfd_info **)&(peer->bfd_info), BFD_DEF_MIN_RX, - BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, NULL, 1, &command); - - bfd_info = (struct bfd_info *)peer->bfd_info; - - /* If profile was specified, then copy string. */ - if (profile) - strlcpy(bfd_info->profile, profile, sizeof(bfd_info->profile)); - else /* Otherwise just initialize it empty. */ - bfd_info->profile[0] = 0; - - if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - command = 0; - bfd_set_param((struct bfd_info **)&(peer->bfd_info), - BFD_DEF_MIN_RX, BFD_DEF_MIN_TX, - BFD_DEF_DETECT_MULT, NULL, 1, &command); - - bfd_info = (struct bfd_info *)peer->bfd_info; - - /* If profile was specified, then copy string. */ - if (profile) - strlcpy(bfd_info->profile, profile, - sizeof(bfd_info->profile)); - else /* Otherwise just initialize it empty. */ - bfd_info->profile[0] = 0; - - if (peer->status == Established - && command == ZEBRA_BFD_DEST_REGISTER) - bgp_bfd_register_peer(peer); - else if (command == ZEBRA_BFD_DEST_UPDATE) - bgp_bfd_update_peer(peer); - } - } else { - if (peer->status == Established - && command == ZEBRA_BFD_DEST_REGISTER) - bgp_bfd_register_peer(peer); - else if (command == ZEBRA_BFD_DEST_UPDATE) - bgp_bfd_update_peer(peer); - } - - return 0; + if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)) + bgp_group_remove_bfd(p); + else + bgp_peer_remove_bfd(p); } -#endif /* * bgp_bfd_peer_config_write - Write the peer BFD configuration. */ -void bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer, char *addr) +void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer, + const char *addr) { - struct bfd_info *bfd_info; - - if (!peer->bfd_info) - return; - - bfd_info = (struct bfd_info *)peer->bfd_info; - - if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG)) + /* + * Always show group BFD configuration, but peer only when explicitly + * configured. + */ + if ((!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) + && peer->bfd_config->manual) + || CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { #if HAVE_BFDD > 0 vty_out(vty, " neighbor %s bfd\n", addr); #else vty_out(vty, " neighbor %s bfd %d %d %d\n", addr, - bfd_info->detect_mult, bfd_info->required_min_rx, - bfd_info->desired_min_tx); + peer->bfd_config->detection_multiplier, + peer->bfd_config->min_rx, peer->bfd_config->min_tx); #endif /* HAVE_BFDD */ - - if (bfd_info->type != BFD_TYPE_NOT_CONFIGURED) - vty_out(vty, " neighbor %s bfd %s\n", addr, - (bfd_info->type == BFD_TYPE_MULTIHOP) ? "multihop" - : "singlehop"); - - if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG) - && (bfd_info->type == BFD_TYPE_NOT_CONFIGURED)) { - vty_out(vty, " neighbor %s bfd", addr); - if (bfd_info->profile[0]) - vty_out(vty, " profile %s", bfd_info->profile); - vty_out(vty, "\n"); } - if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) - vty_out(vty, " neighbor %s bfd check-control-plane-failure\n", addr); + if (peer->bfd_config->profile[0]) + vty_out(vty, " neighbor %s bfd profile %s\n", addr, + peer->bfd_config->profile); + + if (peer->bfd_config->cbit) + vty_out(vty, " neighbor %s bfd check-control-plane-failure\n", + addr); } /* * bgp_bfd_show_info - Show the peer BFD information. */ -void bgp_bfd_show_info(struct vty *vty, struct peer *peer, bool use_json, +void bgp_bfd_show_info(struct vty *vty, const struct peer *peer, json_object *json_neigh) { - bfd_show_info(vty, (struct bfd_info *)peer->bfd_info, - bgp_bfd_is_peer_multihop(peer), 0, use_json, json_neigh); + bfd_sess_show(vty, json_neigh, peer->bfd_config->session); } DEFUN (neighbor_bfd, @@ -712,16 +436,17 @@ DEFUN (neighbor_bfd, { int idx_peer = 1; struct peer *peer; - int ret; peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); if (!peer) return CMD_WARNING_CONFIG_FAILED; - ret = bgp_bfd_peer_param_set(peer, BFD_DEF_MIN_RX, BFD_DEF_MIN_TX, - BFD_DEF_DETECT_MULT, 1); - if (ret != 0) - return bgp_vty_return(vty, ret); + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + bgp_group_configure_bfd(peer); + else + bgp_peer_configure_bfd(peer, true); + + bgp_peer_config_apply(peer, peer->group); return CMD_SUCCESS; } @@ -745,89 +470,30 @@ DEFUN( int idx_number_1 = 3; int idx_number_2 = 4; int idx_number_3 = 5; + long detection_multiplier, min_rx, min_tx; struct peer *peer; - uint32_t rx_val; - uint32_t tx_val; - uint8_t dm_val; - int ret; peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); if (!peer) return CMD_WARNING_CONFIG_FAILED; - if ((ret = bfd_validate_param( - vty, argv[idx_number_1]->arg, argv[idx_number_2]->arg, - argv[idx_number_3]->arg, &dm_val, &rx_val, &tx_val)) - != CMD_SUCCESS) - return ret; - - ret = bgp_bfd_peer_param_set(peer, rx_val, tx_val, dm_val, 0); - if (ret != 0) - return bgp_vty_return(vty, ret); - - return CMD_SUCCESS; -} - -DEFUN_HIDDEN (neighbor_bfd_type, - neighbor_bfd_type_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> bfd <multihop|singlehop>", - NEIGHBOR_STR - NEIGHBOR_ADDR_STR2 - "Enables BFD support\n" - "Multihop session\n" - "Single hop session\n") -{ - int idx_peer = 1; - int idx_hop = 3; - struct peer *peer; - enum bfd_sess_type type; - int ret; - - peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); - if (!peer) - return CMD_WARNING_CONFIG_FAILED; + detection_multiplier = strtol(argv[idx_number_1]->arg, NULL, 10); + min_rx = strtol(argv[idx_number_2]->arg, NULL, 10); + min_tx = strtol(argv[idx_number_3]->arg, NULL, 10); - if (strmatch(argv[idx_hop]->text, "singlehop")) - type = BFD_TYPE_SINGLEHOP; - else if (strmatch(argv[idx_hop]->text, "multihop")) - type = BFD_TYPE_MULTIHOP; + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + bgp_group_configure_bfd(peer); else - return CMD_WARNING_CONFIG_FAILED; - - ret = bgp_bfd_peer_param_type_set(peer, type); - if (ret != 0) - return bgp_vty_return(vty, ret); + bgp_peer_configure_bfd(peer, true); - return CMD_SUCCESS; -} - -static int bgp_bfd_set_check_controlplane_failure_peer(struct vty *vty, struct peer *peer, - const char *no) -{ - struct bfd_info *bfd_info; + peer->bfd_config->detection_multiplier = detection_multiplier; + peer->bfd_config->min_rx = min_rx; + peer->bfd_config->min_tx = min_tx; + bgp_peer_config_apply(peer, peer->group); - if (!peer->bfd_info) { - if (no) - return CMD_SUCCESS; - vty_out(vty, "%% Specify bfd command first\n"); - return CMD_WARNING_CONFIG_FAILED; - } - bfd_info = (struct bfd_info *)peer->bfd_info; - if (!no) { - if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) { - SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE); - bgp_bfd_update_peer(peer); - } - } else { - if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) { - UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE); - bgp_bfd_update_peer(peer); - } - } return CMD_SUCCESS; } - DEFUN (neighbor_bfd_check_controlplane_failure, neighbor_bfd_check_controlplane_failure_cmd, "[no] neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure", @@ -840,9 +506,6 @@ DEFUN (neighbor_bfd_check_controlplane_failure, const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL; int idx_peer = 0; struct peer *peer; - struct peer_group *group; - struct listnode *node, *nnode; - int ret = CMD_SUCCESS; if (no) idx_peer = 2; @@ -853,19 +516,16 @@ DEFUN (neighbor_bfd_check_controlplane_failure, vty_out(vty, "%% Specify remote-as or peer-group commands first\n"); return CMD_WARNING_CONFIG_FAILED; } - if (!peer->bfd_info) { - if (no) - return CMD_SUCCESS; - vty_out(vty, "%% Specify bfd command first\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - group = peer->group; - for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) - ret = bgp_bfd_set_check_controlplane_failure_peer(vty, peer, no); - } else - ret = bgp_bfd_set_check_controlplane_failure_peer(vty, peer, no); - return ret; + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + bgp_group_configure_bfd(peer); + else + bgp_peer_configure_bfd(peer, true); + + peer->bfd_config->cbit = no == NULL; + bgp_peer_config_apply(peer, peer->group); + + return CMD_SUCCESS; } DEFUN (no_neighbor_bfd, @@ -888,44 +548,15 @@ DEFUN (no_neighbor_bfd, { int idx_peer = 2; struct peer *peer; - int ret; peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); if (!peer) return CMD_WARNING_CONFIG_FAILED; - ret = bgp_bfd_peer_param_unset(peer); - if (ret != 0) - return bgp_vty_return(vty, ret); - - return CMD_SUCCESS; -} - - -DEFUN_HIDDEN (no_neighbor_bfd_type, - no_neighbor_bfd_type_cmd, - "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd <multihop|singlehop>", - NO_STR - NEIGHBOR_STR - NEIGHBOR_ADDR_STR2 - "Disables BFD support\n" - "Multihop session\n" - "Singlehop session\n") -{ - int idx_peer = 2; - struct peer *peer; - int ret; - - peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); - if (!peer) - return CMD_WARNING_CONFIG_FAILED; - - if (!peer->bfd_info) - return 0; - - ret = bgp_bfd_peer_param_type_set(peer, BFD_TYPE_NOT_CONFIGURED); - if (ret != 0) - return bgp_vty_return(vty, ret); + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + bgp_group_remove_bfd(peer); + else + bgp_peer_remove_bfd(peer); return CMD_SUCCESS; } @@ -941,15 +572,19 @@ DEFUN(neighbor_bfd_profile, neighbor_bfd_profile_cmd, { int idx_peer = 1, idx_prof = 4; struct peer *peer; - int ret; peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); if (!peer) return CMD_WARNING_CONFIG_FAILED; - ret = bgp_bfd_peer_set_profile(peer, argv[idx_prof]->arg); - if (ret != 0) - return bgp_vty_return(vty, ret); + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + bgp_group_configure_bfd(peer); + else + bgp_peer_configure_bfd(peer, true); + + strlcpy(peer->bfd_config->profile, argv[idx_prof]->arg, + sizeof(peer->bfd_config->profile)); + bgp_peer_config_apply(peer, peer->group); return CMD_SUCCESS; } @@ -965,38 +600,33 @@ DEFUN(no_neighbor_bfd_profile, no_neighbor_bfd_profile_cmd, { int idx_peer = 2; struct peer *peer; - int ret; peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); if (!peer) return CMD_WARNING_CONFIG_FAILED; - if (!peer->bfd_info) - return 0; + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + bgp_group_configure_bfd(peer); + else + bgp_peer_configure_bfd(peer, true); - ret = bgp_bfd_peer_set_profile(peer, NULL); - if (ret != 0) - return bgp_vty_return(vty, ret); + peer->bfd_config->profile[0] = 0; + bgp_peer_config_apply(peer, peer->group); return CMD_SUCCESS; } #endif /* HAVE_BFDD */ -void bgp_bfd_init(void) +void bgp_bfd_init(struct thread_master *tm) { - bfd_gbl_init(); - /* Initialize BFD client functions */ - zclient->interface_bfd_dest_update = bgp_bfd_dest_update; - zclient->bfd_dest_replay = bgp_bfd_dest_replay; + bfd_protocol_integration_init(zclient, tm); /* "neighbor bfd" commands. */ install_element(BGP_NODE, &neighbor_bfd_cmd); install_element(BGP_NODE, &neighbor_bfd_param_cmd); - install_element(BGP_NODE, &neighbor_bfd_type_cmd); install_element(BGP_NODE, &neighbor_bfd_check_controlplane_failure_cmd); install_element(BGP_NODE, &no_neighbor_bfd_cmd); - install_element(BGP_NODE, &no_neighbor_bfd_type_cmd); #if HAVE_BFDD > 0 install_element(BGP_NODE, &neighbor_bfd_profile_cmd); diff --git a/bgpd/bgp_bfd.h b/bgpd/bgp_bfd.h index f2fa959b45..9dca48a437 100644 --- a/bgpd/bgp_bfd.h +++ b/bgpd/bgp_bfd.h @@ -23,22 +23,58 @@ #ifndef _QUAGGA_BGP_BFD_H #define _QUAGGA_BGP_BFD_H -extern void bgp_bfd_init(void); +#define PEER_IS_MULTIHOP(peer) \ + ((((peer)->sort == BGP_PEER_IBGP) && !(peer)->shared_network) \ + || is_ebgp_multihop_configured((peer))) -extern void bgp_bfd_peer_group2peer_copy(struct peer *conf, struct peer *peer); +extern void bgp_bfd_init(struct thread_master *tm); -extern void bgp_bfd_register_peer(struct peer *peer); +extern void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer, + const char *addr); -extern void bgp_bfd_deregister_peer(struct peer *peer); +/** + * Show BFD information helper. + * + * \param vty the VTY pointer. + * \param peer the BGP configuration pointer. + * \param use_json unused. + * \param json_neigh JSON object when called as JSON command. + */ +extern void bgp_bfd_show_info(struct vty *vty, const struct peer *peer, + json_object *json_neigh); -extern void bgp_bfd_reset_peer(struct peer *peer); +/** + * When called on a group it applies configuration to all peers in that group, + * otherwise just applies the configuration to a single peer. + * + * This function should be called when configuration changes either on group + * or peer. + * + * \param p the BGP peer pointer. + * \param pg the BGP group to copy configuration from (it is usually + * `p->group` exception when copying new group configuration + * see `peer_group2peer_config_copy` function case). + */ +extern void bgp_peer_config_apply(struct peer *p, struct peer_group *pg); -extern void bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer, - char *addr); +/** + * Allocates and configure BFD session for peer. If it is already configured, + * then it does nothing. + * + * Always call `bgp_peer_config_apply` afterwards if you need the changes + * immediately applied. + */ +extern void bgp_peer_configure_bfd(struct peer *p, bool manual); -extern void bgp_bfd_show_info(struct vty *vty, struct peer *peer, bool use_json, - json_object *json_neigh); +/** + * Removes BFD configuration from either peer or peer group. + */ +extern void bgp_peer_remove_bfd_config(struct peer *p); -extern bool bgp_bfd_is_peer_multihop(struct peer *peer); +/** + * Special function to handle the case of changing source address. This + * happens when the peer/group is configured with `neigbor X update-source Y`. + */ +extern void bgp_peer_bfd_update_source(struct peer *p); #endif /* _QUAGGA_BGP_BFD_H */ diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 3afa6eaf09..ce1b7b552b 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -21,6 +21,7 @@ #include <zebra.h> #include <lib/version.h> +#include "lib/bfd.h" #include "lib/printfrr.h" #include "prefix.h" #include "linklist.h" @@ -67,6 +68,7 @@ unsigned long conf_bgp_debug_labelpool; unsigned long conf_bgp_debug_pbr; unsigned long conf_bgp_debug_graceful_restart; unsigned long conf_bgp_debug_evpn_mh; +unsigned long conf_bgp_debug_bfd; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_neighbor_events; @@ -86,6 +88,7 @@ unsigned long term_bgp_debug_labelpool; unsigned long term_bgp_debug_pbr; unsigned long term_bgp_debug_graceful_restart; unsigned long term_bgp_debug_evpn_mh; +unsigned long term_bgp_debug_bfd; struct list *bgp_debug_neighbor_events_peers = NULL; struct list *bgp_debug_keepalive_peers = NULL; @@ -2093,6 +2096,31 @@ DEFUN (no_debug_bgp_labelpool, return CMD_SUCCESS; } +DEFPY(debug_bgp_bfd, debug_bgp_bfd_cmd, + "[no] debug bgp bfd", + NO_STR + DEBUG_STR + BGP_STR + "Bidirection Forwarding Detection\n") +{ + if (vty->node == CONFIG_NODE) { + if (no) { + DEBUG_OFF(bfd, BFD_LIB); + bfd_protocol_integration_set_debug(false); + } else { + DEBUG_ON(bfd, BFD_LIB); + bfd_protocol_integration_set_debug(true); + } + } else { + if (no) + TERM_DEBUG_OFF(bfd, BFD_LIB); + else + TERM_DEBUG_ON(bfd, BFD_LIB); + } + + return CMD_SUCCESS; +} + DEFUN (no_debug_bgp, no_debug_bgp_cmd, "no debug bgp", @@ -2136,6 +2164,7 @@ DEFUN (no_debug_bgp, TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART); TERM_DEBUG_OFF(evpn_mh, EVPN_MH_ES); TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT); + TERM_DEBUG_OFF(bfd, BFD_LIB); vty_out(vty, "All possible debugging has been turned off\n"); @@ -2225,6 +2254,9 @@ DEFUN_NOSH (show_debugging_bgp, if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) vty_out(vty, " BGP EVPN-MH route debugging is on\n"); + if (BGP_DEBUG(bfd, BFD_LIB)) + vty_out(vty, " BGP BFD library debugging is on\n"); + vty_out(vty, "\n"); return CMD_SUCCESS; } @@ -2350,6 +2382,11 @@ static int bgp_config_write_debug(struct vty *vty) write++; } + if (CONF_BGP_DEBUG(bfd, BFD_LIB)) { + vty_out(vty, "debug bgp bfd\n"); + write++; + } + return write; } @@ -2478,6 +2515,10 @@ void bgp_debug_init(void) install_element(ENABLE_NODE, &debug_bgp_evpn_mh_cmd); install_element(CONFIG_NODE, &debug_bgp_evpn_mh_cmd); + + /* debug bgp bfd */ + install_element(ENABLE_NODE, &debug_bgp_bfd_cmd); + install_element(CONFIG_NODE, &debug_bgp_bfd_cmd); } /* Return true if this prefix is on the per_prefix_list of prefixes to debug diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index f16cfee4f2..fa8da1c345 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -78,6 +78,7 @@ extern unsigned long conf_bgp_debug_labelpool; extern unsigned long conf_bgp_debug_pbr; extern unsigned long conf_bgp_debug_graceful_restart; extern unsigned long conf_bgp_debug_evpn_mh; +extern unsigned long conf_bgp_debug_bfd; extern unsigned long term_bgp_debug_as4; extern unsigned long term_bgp_debug_neighbor_events; @@ -95,6 +96,7 @@ extern unsigned long term_bgp_debug_labelpool; extern unsigned long term_bgp_debug_pbr; extern unsigned long term_bgp_debug_graceful_restart; extern unsigned long term_bgp_debug_evpn_mh; +extern unsigned long term_bgp_debug_bfd; extern struct list *bgp_debug_neighbor_events_peers; extern struct list *bgp_debug_keepalive_peers; @@ -139,6 +141,8 @@ struct bgp_debug_filter { #define BGP_DEBUG_GRACEFUL_RESTART 0x01 +#define BGP_DEBUG_BFD_LIB 0x01 + #define CONF_DEBUG_ON(a, b) (conf_bgp_debug_ ## a |= (BGP_DEBUG_ ## b)) #define CONF_DEBUG_OFF(a, b) (conf_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b)) diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index ec71264081..2d4fea413a 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -65,11 +65,6 @@ DEFINE_QOBJ_TYPE(bgp_evpn_es); * Static function declarations */ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn); -static void bgp_evpn_update_type2_route_entry(struct bgp *bgp, - struct bgpevpn *vpn, - struct bgp_dest *dest, - struct bgp_path_info *local_pi, - const char *caller); static struct in_addr zero_vtep_ip; /* @@ -1602,8 +1597,8 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, } } - /* MAC-IP routes in the VNI route table are linked to the - * destination ES + /* local MAC-IP routes in the VNI table are linked to + * the destination ES */ if (route_change && vpn_rt && (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)) @@ -1669,6 +1664,18 @@ static void evpn_cleanup_local_non_best_route(struct bgp *bgp, evpn_zebra_reinstall_best_route(bgp, vpn, dest); } +static inline bool bgp_evpn_route_add_l3_ecomm_ok(struct bgpevpn *vpn, + const struct prefix_evpn *p, + esi_t *esi) +{ + return p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE + && (is_evpn_prefix_ipaddr_v4(p) + || !IN6_IS_ADDR_LINKLOCAL( + &p->prefix.macip_addr.ip.ipaddr_v6)) + && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) + && bgpevpn_get_l3vni(vpn) && bgp_evpn_es_add_l3_ecomm_ok(esi); +} + /* * Create or update EVPN route (of type based on prefix) for specified VNI * and schedule for processing. @@ -1738,12 +1745,8 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, * IPv4 or IPv6 global addresses and we're advertising L3VNI with * these routes. */ - if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE && - (is_evpn_prefix_ipaddr_v4(p) || - !IN6_IS_ADDR_LINKLOCAL(&p->prefix.macip_addr.ip.ipaddr_v6)) && - CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) && - bgpevpn_get_l3vni(vpn)) - add_l3_ecomm = 1; + add_l3_ecomm = bgp_evpn_route_add_l3_ecomm_ok( + vpn, p, (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL); /* Set up extended community. */ build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm); @@ -1930,11 +1933,10 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, return 0; } -static void bgp_evpn_update_type2_route_entry(struct bgp *bgp, - struct bgpevpn *vpn, - struct bgp_dest *dest, - struct bgp_path_info *local_pi, - const char *caller) +void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, + struct bgp_dest *dest, + struct bgp_path_info *local_pi, + const char *caller) { afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; @@ -1977,12 +1979,9 @@ static void bgp_evpn_update_type2_route_entry(struct bgp *bgp, /* Add L3 VNI RTs and RMAC for non IPv6 link-local if * using L3 VNI for type-2 routes also. */ - if ((is_evpn_prefix_ipaddr_v4(evp) || - !IN6_IS_ADDR_LINKLOCAL( - &evp->prefix.macip_addr.ip.ipaddr_v6)) && - CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) && - bgpevpn_get_l3vni(vpn)) - add_l3_ecomm = 1; + add_l3_ecomm = bgp_evpn_route_add_l3_ecomm_ok( + vpn, evp, + (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL); /* Set up extended community. */ build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm); @@ -2235,6 +2234,8 @@ int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) int ret; struct prefix_evpn p; + update_type1_routes_for_evi(bgp, vpn); + /* Update and advertise the type-3 route (only one) followed by the * locally learnt type-2 routes (MACIP) - for this VNI. * @@ -2377,6 +2378,8 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, afi_t afi = 0; safi_t safi = 0; bool new_pi = false; + bool use_l3nhg = false; + bool is_l3nhg_active = false; memset(pp, 0, sizeof(struct prefix)); ip_prefix_from_evpn_prefix(evp, pp); @@ -2412,6 +2415,13 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, else attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + bgp_evpn_es_vrf_use_nhg(bgp_vrf, &parent_pi->attr->esi, &use_l3nhg, + &is_l3nhg_active, NULL); + if (use_l3nhg) + attr.es_flags |= ATTR_ES_L3_NHG_USE; + if (is_l3nhg_active) + attr.es_flags |= ATTR_ES_L3_NHG_ACTIVE; + /* Check if route entry is already present. */ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (pi->extra @@ -2452,6 +2462,9 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* as it is an importation, change nexthop */ bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF); + /* Link path to evpn nexthop */ + bgp_evpn_path_nh_add(bgp_vrf, pi); + bgp_aggregate_increment(bgp_vrf, bgp_dest_get_prefix(dest), pi, afi, safi); @@ -2485,6 +2498,8 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, struct attr *attr_new; int ret; struct prefix_evpn ad_evp; + bool old_local_es = false; + bool new_local_es; /* EAD prefix in the global table doesn't include the VTEP-IP so * we need to create a different copy for the VNI @@ -2507,6 +2522,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* Create an info */ pi = bgp_create_evpn_bgp_path_info(parent_pi, dest, parent_pi->attr); + new_local_es = bgp_evpn_attr_is_local_es(pi->attr); } else { if (attrhash_cmp(pi->attr, parent_pi->attr) && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) { @@ -2525,17 +2541,29 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop)) SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED); + old_local_es = bgp_evpn_attr_is_local_es(pi->attr); + new_local_es = bgp_evpn_attr_is_local_es(attr_new); + /* If ESI is different or if its type has changed we + * need to reinstall the path in zebra + */ + if ((old_local_es != new_local_es) + || memcmp(&pi->attr->esi, &attr_new->esi, + sizeof(attr_new->esi))) { + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("VNI %d path %pFX chg to %s es", + vpn->vni, &pi->net->p, + new_local_es ? "local" + : "non-local"); + bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED); + } + /* Unintern existing, set to new. */ bgp_attr_unintern(&pi->attr); pi->attr = attr_new; pi->uptime = bgp_clock(); } - /* MAC-IP routes in the VNI table are linked to the destination ES */ - if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) - bgp_evpn_path_es_link(pi, vpn->vni, - bgp_evpn_attr_get_esi(pi->attr)); - /* Perform route selection and update zebra, if required. */ ret = evpn_route_select_install(bgp, vpn, dest); @@ -2545,10 +2573,9 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, * from sync-path to remote-path) */ local_pi = bgp_evpn_route_get_local_path(bgp, dest); - if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr)) + if (local_pi && (old_local_es || new_local_es)) bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi, - __func__); - + __func__); bgp_dest_unlock_node(dest); return ret; @@ -2617,6 +2644,9 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, /* Mark entry for deletion */ bgp_path_info_delete(dest, pi); + /* Unlink path to evpn nexthop */ + bgp_evpn_path_nh_del(bgp_vrf, pi); + /* Perform route selection and update zebra, if required. */ bgp_process(bgp_vrf, dest, afi, safi); @@ -2851,11 +2881,11 @@ static int bgp_evpn_route_rmac_self_check(struct bgp *bgp_vrf, /* don't import hosts that are locally attached */ static inline bool -bgp_evpn_skip_vrf_import_of_local_es(const struct prefix_evpn *evp, +bgp_evpn_skip_vrf_import_of_local_es(struct bgp *bgp_vrf, + const struct prefix_evpn *evp, struct bgp_path_info *pi, int install) { esi_t *esi; - struct in_addr nh; if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) { esi = bgp_evpn_attr_get_esi(pi->attr); @@ -2873,29 +2903,51 @@ bgp_evpn_skip_vrf_import_of_local_es(const struct prefix_evpn *evp, } return true; } + } + return false; +} - /* Don't import routes with ES as destination if the nexthop - * has not been advertised via the EAD-ES - */ - if (pi->attr) - nh = pi->attr->nexthop; +/* + * Install or uninstall a mac-ip route in the provided vrf if + * there is a rt match + */ +int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf, + struct bgp_path_info *pi, + int install) +{ + int ret = 0; + const struct prefix_evpn *evp = + (const struct prefix_evpn *)bgp_dest_get_prefix(pi->net); + + /* Consider "valid" remote routes applicable for + * this VRF. + */ + if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID) + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_NORMAL)) + return 0; + + if (is_route_matching_for_vrf(bgp_vrf, pi)) { + if (bgp_evpn_route_rmac_self_check(bgp_vrf, evp, pi)) + return 0; + + /* don't import hosts that are locally attached */ + if (install + && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi, + install)) + ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi); else - nh.s_addr = INADDR_ANY; - if (install && !bgp_evpn_es_is_vtep_active(esi, nh)) { - if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) { - char esi_buf[ESI_STR_LEN]; + ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp, + pi); - zlog_debug( - "vrf %s of evpn prefix %pFX skipped, nh %pI4 inactive in es %s", - install ? "import" : "unimport", evp, - &nh, - esi_to_str(esi, esi_buf, - sizeof(esi_buf))); - } - return true; - } + if (ret) + flog_err(EC_BGP_EVPN_FAIL, + "Failed to %s EVPN %pFX route in VRF %s", + install ? "install" : "uninstall", evp, + vrf_id_to_name(bgp_vrf->vrf_id)); } - return false; + + return ret; } /* @@ -2947,46 +2999,10 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install) for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { - /* Consider "valid" remote routes applicable for - * this VRF. - */ - if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID) - && pi->type == ZEBRA_ROUTE_BGP - && pi->sub_type == BGP_ROUTE_NORMAL)) - continue; - - /* don't import hosts that are locally attached - */ - if (bgp_evpn_skip_vrf_import_of_local_es( - evp, pi, install)) - continue; - - if (is_route_matching_for_vrf(bgp_vrf, pi)) { - if (bgp_evpn_route_rmac_self_check( - bgp_vrf, evp, pi)) - continue; - - if (install) - ret = install_evpn_route_entry_in_vrf( - bgp_vrf, evp, pi); - else - ret = uninstall_evpn_route_entry_in_vrf( - bgp_vrf, evp, pi); - - if (ret) { - flog_err( - EC_BGP_EVPN_FAIL, - "Failed to %s EVPN %pFX route in VRF %s", - install ? "install" - : "uninstall", - evp, - vrf_id_to_name( - bgp_vrf->vrf_id)); - bgp_dest_unlock_node(rd_dest); - bgp_dest_unlock_node(dest); - return ret; - } - } + ret = bgp_evpn_route_entry_install_if_vrf_match( + bgp_vrf, pi, install); + if (ret) + return ret; } } } @@ -3136,7 +3152,7 @@ static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) return ret; ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_AD_ROUTE, - 1); + 0); if (ret) return ret; @@ -3167,14 +3183,13 @@ static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi, || is_evpn_prefix_ipaddr_v6(evp))) return 0; - /* don't import hosts that are locally attached */ - if (bgp_evpn_skip_vrf_import_of_local_es(evp, pi, install)) - return 0; - for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) { int ret; - if (install) + /* don't import hosts that are locally attached */ + if (install + && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi, + install)) ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi); else ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp, @@ -3289,6 +3304,13 @@ static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi, if (sub_type != ECOMMUNITY_ROUTE_TARGET) continue; + /* non-local MAC-IP routes in the global route table are linked + * to the destination ES + */ + if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) + bgp_evpn_path_es_link(pi, 0, + bgp_evpn_attr_get_esi(pi->attr)); + /* * macip routes (type-2) are imported into VNI and VRF tables. * IMET route is imported into VNI table. @@ -3368,6 +3390,18 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, true, true); } +void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import) +{ + struct bgp *bgp_evpn; + + bgp_evpn = bgp_get_evpn(); + if (!bgp_evpn) + return; + + install_uninstall_evpn_route(bgp_evpn, AFI_L2VPN, SAFI_EVPN, + &pi->net->p, pi, import); +} + /* Import the pi into vrf routing tables */ void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import) { @@ -3721,7 +3755,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, if (attr) { STREAM_GET(&attr->esi, pkt, sizeof(esi_t)); - if (bgp_evpn_is_esi_local(&attr->esi)) + if (bgp_evpn_is_esi_local_and_non_bypass(&attr->esi)) attr->es_flags |= ATTR_ES_IS_LOCAL; else attr->es_flags &= ~ATTR_ES_IS_LOCAL; @@ -5774,11 +5808,14 @@ void bgp_evpn_init(struct bgp *bgp) /* Default BUM handling is to do head-end replication. */ bgp->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL; + + bgp_evpn_nh_init(bgp); } void bgp_evpn_vrf_delete(struct bgp *bgp_vrf) { bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); + bgp_evpn_nh_finish(bgp_vrf); } /* diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 29d3d2c62f..83a6dd84c8 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -206,5 +206,4 @@ extern void bgp_evpn_init(struct bgp *bgp); extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx); extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx); extern void update_advertise_vrf_routes(struct bgp *bgp_vrf); - #endif /* _QUAGGA_BGP_EVPN_H */ diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 123c46f12f..868238ebdd 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -64,17 +64,20 @@ static void bgp_evpn_es_vtep_del(struct bgp *bgp, struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr); static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es); static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es); -static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi); +static struct bgp_evpn_es_evi * +bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi); static uint32_t bgp_evpn_es_get_active_vtep_cnt(struct bgp_evpn_es *es); static void bgp_evpn_l3nhg_update_on_vtep_chg(struct bgp_evpn_es *es); static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi); static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller); -static void -bgp_evpn_es_path_update_on_vtep_chg(struct bgp_evpn_es_vtep *es_vtep, - bool active); +static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info); +static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es, + bool is_local); esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; static int bgp_evpn_run_consistency_checks(struct thread *t); +static void bgp_evpn_path_nh_info_free(struct bgp_path_evpn_nh_info *nh_info); +static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info); /****************************************************************************** * per-ES (Ethernet Segment) routing table @@ -970,6 +973,48 @@ static int bgp_evpn_type1_route_update(struct bgp *bgp, return 0; } +/* + * This function is called when the export RT for a VNI changes. + * Update all type-1 local routes for this VNI from VNI/ES tables and the global + * table and advertise these routes to peers. + */ + +void update_type1_routes_for_evi(struct bgp *bgp, struct bgpevpn *vpn) +{ + struct prefix_evpn p; + struct bgp_evpn_es *es; + struct bgp_evpn_es_evi *es_evi; + struct bgp_evpn_es_evi *es_evi_next; + + RB_FOREACH_SAFE(es_evi, bgp_es_evi_rb_head, + &vpn->es_evi_rb_tree, es_evi_next) { + es = es_evi->es; + + /* Update EAD-ES */ + if (CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) { + build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, + &es->esi, es->originator_ip); + if (bgp_evpn_type1_route_update(bgp, es, NULL, &p)) + flog_err(EC_BGP_EVPN_ROUTE_CREATE, + "%u: EAD-ES route update failure for ESI %s VNI %u", + bgp->vrf_id, es->esi_str, + es_evi->vpn->vni); + } + + /* Update EAD-EVI */ + if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) { + build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG, + &es->esi, es->originator_ip); + if (bgp_evpn_type1_route_update(bgp, es, es_evi->vpn, + &p)) + flog_err(EC_BGP_EVPN_ROUTE_DELETE, + "%u: EAD-EVI route update failure for ESI %s VNI %u", + bgp->vrf_id, es->esi_str, + es_evi->vpn->vni); + } + } +} + /* Delete local Type-1 route */ static int bgp_evpn_type1_es_route_delete(struct bgp *bgp, struct bgp_evpn_es *es, struct prefix_evpn *p) @@ -1249,8 +1294,6 @@ static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp, * removed. */ bgp_evpn_l3nhg_update_on_vtep_chg(es_vtep->es); - bgp_evpn_es_path_update_on_vtep_chg(es_vtep, new_active); - /* queue up the es for background consistency checks */ bgp_evpn_es_cons_checks_pend_add(es_vtep->es); } @@ -1326,59 +1369,60 @@ static void bgp_evpn_es_vtep_del(struct bgp *bgp, bgp_evpn_es_vtep_do_del(bgp, es_vtep, esr); } -bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh) -{ - struct bgp_evpn_es *es; - struct bgp_evpn_es_vtep *es_vtep; - struct listnode *node = NULL; - bool rc = false; - - if (!memcmp(esi, zero_esi, sizeof(*esi)) || !nh.s_addr) - return true; - - es = bgp_evpn_es_find(esi); - if (!es) - return false; - - for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { - if (es_vtep->vtep_ip.s_addr == nh.s_addr) { - if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE)) - rc = true; - break; - } - } - return rc; -} - /********************** ES MAC-IP paths ************************************* - * MAC-IP routes in the VNI routing table are linked to the destination - * ES for efficient updates on ES changes (such as VTEP add/del). + * 1. Local MAC-IP routes in the VNI routing table are linked to the + * destination ES (macip_evi_path_list) for efficient updates on ES oper + * state changes. + * 2. Non-local MAC-IP routes in the global routing table are linked to + * the detination for efficient updates on - + * a. VTEP add/del - this results in a L3NHG update. + * b. ES-VRF add/del - this may result in the host route being migrated to + * L3NHG or vice versa (flat multipath list). ****************************************************************************/ -void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info) +static void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info) { bgp_evpn_path_es_unlink(es_info); XFREE(MTYPE_BGP_EVPN_PATH_ES_INFO, es_info); } +void bgp_evpn_path_mh_info_free(struct bgp_path_mh_info *mh_info) +{ + if (mh_info->es_info) + bgp_evpn_path_es_info_free(mh_info->es_info); + if (mh_info->nh_info) + bgp_evpn_path_nh_info_free(mh_info->nh_info); + XFREE(MTYPE_BGP_EVPN_PATH_MH_INFO, mh_info); +} + static struct bgp_path_es_info * bgp_evpn_path_es_info_new(struct bgp_path_info *pi, vni_t vni) { struct bgp_path_info_extra *e; + struct bgp_path_mh_info *mh_info; + struct bgp_path_es_info *es_info; e = bgp_path_info_extra_get(pi); + /* If mh_info doesn't exist allocate it */ + mh_info = e->mh_info; + if (!mh_info) + e->mh_info = mh_info = XCALLOC(MTYPE_BGP_EVPN_PATH_MH_INFO, + sizeof(struct bgp_path_mh_info)); + /* If es_info doesn't exist allocate it */ - if (!e->es_info) { - e->es_info = XCALLOC(MTYPE_BGP_EVPN_PATH_ES_INFO, - sizeof(struct bgp_path_es_info)); - e->es_info->pi = pi; - e->es_info->vni = vni; + es_info = mh_info->es_info; + if (!es_info) { + mh_info->es_info = es_info = + XCALLOC(MTYPE_BGP_EVPN_PATH_ES_INFO, + sizeof(struct bgp_path_es_info)); + es_info->vni = vni; + es_info->pi = pi; } - return e->es_info; + return es_info; } -void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info) +static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info) { struct bgp_evpn_es *es = es_info->es; struct bgp_path_info *pi; @@ -1391,7 +1435,13 @@ void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info) zlog_debug("vni %u path %pFX unlinked from es %s", es_info->vni, &pi->net->p, es->esi_str); - list_delete_node(es->macip_path_list, &es_info->es_listnode); + if (es_info->vni) + list_delete_node(es->macip_evi_path_list, + &es_info->es_listnode); + else + list_delete_node(es->macip_global_path_list, + &es_info->es_listnode); + es_info->es = NULL; /* if there are no other references against the ES it @@ -1408,9 +1458,11 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) { struct bgp_path_es_info *es_info; struct bgp_evpn_es *es; - struct bgp *bgp_evpn = bgp_get_evpn(); + struct bgp *bgp_evpn; - es_info = pi->extra ? pi->extra->es_info : NULL; + es_info = (pi->extra && pi->extra->mh_info) + ? pi->extra->mh_info->es_info + : NULL; /* if the esi is zero just unlink the path from the old es */ if (!esi || !memcmp(esi, zero_esi, sizeof(*esi))) { if (es_info) @@ -1418,6 +1470,7 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) return; } + bgp_evpn = bgp_get_evpn(); if (!bgp_evpn) return; @@ -1444,43 +1497,59 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi) /* link mac-ip path to the new destination ES */ es_info->es = es; listnode_init(&es_info->es_listnode, es_info); - listnode_add(es->macip_path_list, &es_info->es_listnode); + if (es_info->vni) + listnode_add(es->macip_evi_path_list, &es_info->es_listnode); + else + listnode_add(es->macip_global_path_list, &es_info->es_listnode); +} + +static bool bgp_evpn_is_macip_path(struct bgp_path_info *pi) +{ + struct prefix_evpn *evp; + + /* Only MAC-IP routes need to be linked (MAC-only routes can be + * skipped) as these lists are maintained for managing + * host routes in the tenant VRF + */ + evp = (struct prefix_evpn *)&pi->net->p; + return is_evpn_prefix_ipaddr_v4(evp) || is_evpn_prefix_ipaddr_v6(evp); } +/* When a remote ES is added to a VRF, routes using that as + * a destination need to be migrated to a L3NHG or viceversa. + * This is done indirectly by re-attempting an install of the + * route in the associated VRFs. As a part of the VRF install use + * of l3 NHG is evaluated and this results in the + * attr.es_flag ATTR_ES_USE_L3_NHG being set or cleared. + */ static void -bgp_evpn_es_path_update_on_vtep_chg(struct bgp_evpn_es_vtep *es_vtep, - bool active) +bgp_evpn_es_path_update_on_es_vrf_chg(struct bgp_evpn_es_vrf *es_vrf, + const char *reason) { struct listnode *node; struct bgp_path_es_info *es_info; struct bgp_path_info *pi; - struct bgp_path_info *parent_pi; - struct bgp_evpn_es *es = es_vtep->es; + struct bgp_evpn_es *es = es_vrf->es; + + if (!bgp_mh_info->host_routes_use_l3nhg) + return; if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) - zlog_debug("update paths linked to es %s on vtep chg", - es->esi_str); + zlog_debug("update paths linked to es %s on es-vrf %s %s", + es->esi_str, es_vrf->bgp_vrf->name, reason); - for (ALL_LIST_ELEMENTS_RO(es->macip_path_list, node, es_info)) { + for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) { pi = es_info->pi; - if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID)) - continue; - - if (pi->sub_type != BGP_ROUTE_IMPORTED) - continue; - - parent_pi = pi->extra ? pi->extra->parent : NULL; - if (!parent_pi || !parent_pi->attr) - continue; - if (es_vtep->vtep_ip.s_addr != parent_pi->attr->nexthop.s_addr) + if (!bgp_evpn_is_macip_path(pi)) continue; if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) zlog_debug( - "update path %pFX linked to es %s on vtep chg", - &parent_pi->net->p, es->esi_str); - bgp_evpn_import_route_in_vrfs(parent_pi, active ? 1 : 0); + "update path %pFX linked to es %s on vrf chg", + &pi->net->p, es->esi_str); + bgp_evpn_route_entry_install_if_vrf_match(es_vrf->bgp_vrf, pi, + 1); } } @@ -1537,8 +1606,10 @@ static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi) listset_app_node_mem(es->es_vrf_list); /* Initialise the route list used for efficient event handling */ - es->macip_path_list = list_new(); - listset_app_node_mem(es->macip_path_list); + es->macip_evi_path_list = list_new(); + listset_app_node_mem(es->macip_evi_path_list); + es->macip_global_path_list = list_new(); + listset_app_node_mem(es->macip_global_path_list); QOBJ_REG(es, bgp_evpn_es); @@ -1552,7 +1623,8 @@ static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi) static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller) { if ((es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) - || listcount(es->macip_path_list)) + || listcount(es->macip_evi_path_list) + || listcount(es->macip_global_path_list)) return; if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) @@ -1562,7 +1634,8 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller) list_delete(&es->es_evi_list); list_delete(&es->es_vrf_list); list_delete(&es->es_vtep_list); - list_delete(&es->macip_path_list); + list_delete(&es->macip_evi_path_list); + list_delete(&es->macip_global_path_list); bgp_table_unlock(es->route_table); /* remove the entry from various databases */ @@ -1573,15 +1646,25 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller) XFREE(MTYPE_BGP_EVPN_ES, es); } +static inline bool bgp_evpn_is_es_local_and_non_bypass(struct bgp_evpn_es *es) +{ + return (es->flags & BGP_EVPNES_LOCAL) + && !(es->flags & BGP_EVPNES_BYPASS); +} + /* init local info associated with the ES */ static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es) { char buf[BGP_EVPN_PREFIX_RD_LEN]; + bool old_is_local; + bool is_local; if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL)) return; + old_is_local = bgp_evpn_is_es_local_and_non_bypass(es); SET_FLAG(es->flags, BGP_EVPNES_LOCAL); + listnode_init(&es->es_listnode, es); listnode_add(bgp_mh_info->local_es_list, &es->es_listnode); @@ -1591,16 +1674,28 @@ static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es) es->prd.prefixlen = 64; snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, es->rd_id); (void)str2prefix_rd(buf, &es->prd); + + is_local = bgp_evpn_is_es_local_and_non_bypass(es); + if (old_is_local != is_local) + bgp_evpn_mac_update_on_es_local_chg(es, is_local); } /* clear any local info associated with the ES */ -static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es) +static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es, bool finish) { + bool old_is_local; + bool is_local; + if (!CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL)) return; + old_is_local = bgp_evpn_is_es_local_and_non_bypass(es); UNSET_FLAG(es->flags, BGP_EVPNES_LOCAL); + is_local = bgp_evpn_is_es_local_and_non_bypass(es); + if (!finish && (old_is_local != is_local)) + bgp_evpn_mac_update_on_es_local_chg(es, is_local); + /* remove from the ES local list */ list_delete_node(bgp_mh_info->local_es_list, &es->es_listnode); @@ -1622,10 +1717,127 @@ static void bgp_evpn_es_remote_info_re_eval(struct bgp_evpn_es *es) } } -static inline bool bgp_evpn_local_es_is_active(struct bgp_evpn_es *es) +/* If ES is present and local it needs to be active/oper-up for + * including L3 EC + */ +bool bgp_evpn_es_add_l3_ecomm_ok(esi_t *esi) { - return (es->flags & BGP_EVPNES_OPER_UP) - && !(es->flags & BGP_EVPNES_BYPASS); + struct bgp_evpn_es *es; + + if (!esi || !bgp_mh_info->suppress_l3_ecomm_on_inactive_es) + return true; + + es = bgp_evpn_es_find(esi); + + return (!es || !(es->flags & BGP_EVPNES_LOCAL) + || bgp_evpn_local_es_is_active(es)); +} + +static bool bgp_evpn_is_valid_local_path(struct bgp_path_info *pi) +{ + return (CHECK_FLAG(pi->flags, BGP_PATH_VALID) + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_STATIC); +} + +/* Update all local MAC-IP routes in the VNI routing table associated + * with the ES. When the ES is down the routes are advertised without + * the L3 extcomm + */ +static void bgp_evpn_mac_update_on_es_oper_chg(struct bgp_evpn_es *es) +{ + struct listnode *node; + struct bgp_path_es_info *es_info; + struct bgp_path_info *pi; + struct bgp *bgp; + struct bgpevpn *vpn; + + if (!bgp_mh_info->suppress_l3_ecomm_on_inactive_es) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("update paths linked to es %s on oper chg", + es->esi_str); + + bgp = bgp_get_evpn(); + for (ALL_LIST_ELEMENTS_RO(es->macip_evi_path_list, node, es_info)) { + pi = es_info->pi; + + if (!bgp_evpn_is_valid_local_path(pi)) + continue; + + if (!bgp_evpn_is_macip_path(pi)) + continue; + + vpn = bgp_evpn_lookup_vni(bgp, es_info->vni); + if (!vpn) + continue; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug( + "update path %d %pFX linked to es %s on oper chg", + es_info->vni, &pi->net->p, es->esi_str); + + bgp_evpn_update_type2_route_entry(bgp, vpn, pi->net, pi, + __func__); + } +} + +static bool bgp_evpn_is_valid_bgp_path(struct bgp_path_info *pi) +{ + return (CHECK_FLAG(pi->flags, BGP_PATH_VALID) + && pi->type == ZEBRA_ROUTE_BGP + && pi->sub_type == BGP_ROUTE_NORMAL); +} + +/* If an ES is no longer local (or becomes local) we need to re-install + * paths using that ES as destination. This is needed as the criteria + * for best path selection has changed. + */ +static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es, + bool is_local) +{ + struct listnode *node; + struct bgp_path_es_info *es_info; + struct bgp_path_info *pi; + bool tmp_local; + struct attr *attr_new; + struct attr attr_tmp; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("update paths linked to es %s on chg to %s", + es->esi_str, is_local ? "local" : "non-local"); + + for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) { + pi = es_info->pi; + + /* Consider "valid" remote routes */ + if (!bgp_evpn_is_valid_bgp_path(pi)) + continue; + + if (!pi->attr) + continue; + + tmp_local = !!(pi->attr->es_flags & ATTR_ES_IS_LOCAL); + if (tmp_local == is_local) + continue; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug( + "update path %pFX linked to es %s on chg to %s", + &pi->net->p, es->esi_str, + is_local ? "local" : "non-local"); + + attr_tmp = *pi->attr; + if (is_local) + attr_tmp.es_flags |= ATTR_ES_IS_LOCAL; + else + attr_tmp.es_flags &= ~ATTR_ES_IS_LOCAL; + attr_new = bgp_attr_intern(&attr_tmp); + bgp_attr_unintern(&pi->attr); + pi->attr = attr_new; + bgp_evpn_import_type2_route(pi, 1); + } } static void bgp_evpn_local_es_deactivate(struct bgp *bgp, @@ -1657,6 +1869,8 @@ static void bgp_evpn_local_es_deactivate(struct bgp *bgp, "%u failed to delete type-1 route for ESI %s", bgp->vrf_id, es->esi_str); } + + bgp_evpn_mac_update_on_es_oper_chg(es); } /* Process ES link oper-down by withdrawing ES-EAD and ESR */ @@ -1704,6 +1918,8 @@ static void bgp_evpn_local_es_activate(struct bgp *bgp, struct bgp_evpn_es *es, es->originator_ip); (void)bgp_evpn_type1_route_update(bgp, es, NULL, &p); } + + bgp_evpn_mac_update_on_es_oper_chg(es); } /* Process ES link oper-up by generating ES-EAD and ESR */ @@ -1738,11 +1954,14 @@ static void bgp_evpn_local_es_bypass_update(struct bgp *bgp, bool old_bypass = !!(es->flags & BGP_EVPNES_BYPASS); bool old_active; bool new_active; + bool old_is_local; + bool is_local; if (bypass == old_bypass) return; old_active = bgp_evpn_local_es_is_active(es); + old_is_local = bgp_evpn_is_es_local_and_non_bypass(es); if (bypass) SET_FLAG(es->flags, BGP_EVPNES_BYPASS); else @@ -1759,6 +1978,10 @@ static void bgp_evpn_local_es_bypass_update(struct bgp *bgp, else bgp_evpn_local_es_deactivate(bgp, es); } + + is_local = bgp_evpn_is_es_local_and_non_bypass(es); + if (old_is_local != is_local) + bgp_evpn_mac_update_on_es_local_chg(es, is_local); } static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es) @@ -1783,16 +2006,17 @@ static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es) /* Clear local info associated with the ES and free it up if there is * no remote reference */ - bgp_evpn_es_local_info_clear(es); + bgp_evpn_es_local_info_clear(es, false); } -bool bgp_evpn_is_esi_local(esi_t *esi) +bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi) { struct bgp_evpn_es *es = NULL; /* Lookup ESI hash - should exist. */ es = bgp_evpn_es_find(esi); - return es ? !!(es->flags & BGP_EVPNES_LOCAL) : false; + + return es && bgp_evpn_is_es_local_and_non_bypass(es); } int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi) @@ -2072,7 +2296,9 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, json_object_int_add(json, "vrfCount", listcount(es->es_vrf_list)); json_object_int_add(json, "macipPathCount", - listcount(es->macip_path_list)); + listcount(es->macip_evi_path_list)); + json_object_int_add(json, "macipGlobalPathCount", + listcount(es->macip_global_path_list)); json_object_int_add(json, "inconsistentVniVtepCount", es->incons_evi_vtep_cnt); if (listcount(es->es_vtep_list)) { @@ -2120,8 +2346,10 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, vty_out(vty, " Remote VNI Count: %d\n", es->remote_es_evi_cnt); vty_out(vty, " VRF Count: %d\n", listcount(es->es_vrf_list)); - vty_out(vty, " MACIP Path Count: %d\n", - listcount(es->macip_path_list)); + vty_out(vty, " MACIP EVI Path Count: %d\n", + listcount(es->macip_evi_path_list)); + vty_out(vty, " MACIP Global Path Count: %d\n", + listcount(es->macip_global_path_list)); vty_out(vty, " Inconsistent VNI VTEP Count: %d\n", es->incons_evi_vtep_cnt); if (es->inconsistencies) { @@ -2352,6 +2580,8 @@ static void bgp_evpn_l3nhg_deactivate(struct bgp_evpn_es_vrf *es_vrf) es_vrf->nhg_id); bgp_evpn_l3nhg_zebra_del(es_vrf); es_vrf->flags &= ~BGP_EVPNES_VRF_NHG_ACTIVE; + /* MAC-IPs can now be installed via the L3NHG */ + bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "l3nhg-deactivate"); } static void bgp_evpn_l3nhg_activate(struct bgp_evpn_es_vrf *es_vrf, bool update) @@ -2370,6 +2600,8 @@ static void bgp_evpn_l3nhg_activate(struct bgp_evpn_es_vrf *es_vrf, bool update) es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id, es_vrf->nhg_id); es_vrf->flags |= BGP_EVPNES_VRF_NHG_ACTIVE; + /* MAC-IPs can now be installed via the L3NHG */ + bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "l3nhg_activate"); } bgp_evpn_l3nhg_zebra_add(es_vrf); @@ -2446,6 +2678,11 @@ static struct bgp_evpn_es_vrf *bgp_evpn_es_vrf_create(struct bgp_evpn_es *es, bgp_vrf->vrf_id, es_vrf->nhg_id, es_vrf->v6_nhg_id); bgp_evpn_l3nhg_activate(es_vrf, false /* update */); + /* update paths in the VRF that may already be associated with + * this destination ES + */ + bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "es-vrf-create"); + return es_vrf; } @@ -2474,6 +2711,11 @@ static void bgp_evpn_es_vrf_delete(struct bgp_evpn_es_vrf *es_vrf) /* remove from the VRF-ESI rb tree */ RB_REMOVE(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, es_vrf); + /* update paths in the VRF that may already be associated with + * this destination ES + */ + bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "es-vrf-delete"); + XFREE(MTYPE_BGP_EVPN_ES_VRF, es_vrf); } @@ -2556,22 +2798,56 @@ void bgp_evpn_es_evi_vrf_ref(struct bgpevpn *vpn) bgp_evpn_es_vrf_ref(es_evi, vpn->bgp_vrf); } +/* 1. If ES-VRF is not present install the host route with the exploded/flat + * multi-path list. + * 2. If ES-VRF is present - + * - if L3NHG has not been activated for the ES-VRF (this could be because + * all the PEs attached to the VRF are down) do not install the route + * in zebra. + * - if L3NHG has been activated install the route via that L3NHG + */ +void bgp_evpn_es_vrf_use_nhg(struct bgp *bgp_vrf, esi_t *esi, bool *use_l3nhg, + bool *is_l3nhg_active, + struct bgp_evpn_es_vrf **es_vrf_p) +{ + struct bgp_evpn_es *es; + struct bgp_evpn_es_vrf *es_vrf; + + if (!bgp_mh_info->host_routes_use_l3nhg) + return; + + es = bgp_evpn_es_find(esi); + if (!es) + return; + + es_vrf = bgp_evpn_es_vrf_find(es, bgp_vrf); + if (!es_vrf) + return; + + *use_l3nhg = true; + if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE) + *is_l3nhg_active = true; + if (es_vrf_p) + *es_vrf_p = es_vrf; +} + /* returns false if legacy-exploded mp needs to be used for route install */ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, uint32_t *nhg_p) { esi_t *esi; - struct bgp_evpn_es *es; - struct bgp_evpn_es_vrf *es_vrf; + struct bgp_evpn_es_vrf *es_vrf = NULL; struct bgp_path_info *parent_pi; struct bgp_node *rn; struct prefix_evpn *evp; struct bgp_path_info *mpinfo; + bool use_l3nhg = false; + bool is_l3nhg_active = false; *nhg_p = 0; - /* L3NHG support is disabled, use legacy-exploded multipath */ - if (!bgp_mh_info->host_routes_use_l3nhg) + /* we don't support NHG for routes leaked from another VRF yet */ + if (pi->extra && pi->extra->bgp_orig) return false; parent_pi = get_route_parent_evpn(pi); @@ -2591,15 +2867,17 @@ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, if (!memcmp(esi, zero_esi, sizeof(*esi))) return false; - /* if the ES-VRF is not setup or if the NHG has not been installed - * we cannot install the route yet, return a 0-NHG to indicate - * that + bgp_evpn_es_vrf_use_nhg(bgp_vrf, esi, &use_l3nhg, &is_l3nhg_active, + &es_vrf); + + /* L3NHG support is disabled, use legacy-exploded multipath */ + if (!use_l3nhg) + return false; + + /* if the NHG has not been installed we cannot install the route yet, + * return a 0-NHG to indicate that */ - es = bgp_evpn_es_find(esi); - if (!es) - return true; - es_vrf = bgp_evpn_es_vrf_find(es, bgp_vrf); - if (!es_vrf || !(es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE)) + if (!is_l3nhg_active) return true; /* this needs to be set the v6NHG if v6route */ @@ -2610,7 +2888,7 @@ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, for (mpinfo = bgp_path_info_mpath_next(pi); mpinfo; mpinfo = bgp_path_info_mpath_next(mpinfo)) { - /* if any of the paths of have a different ESI we can't use + /* if any of the paths have a different ESI we can't use * the NHG associated with the ES. fallback to legacy-exploded * multipath */ @@ -2946,7 +3224,8 @@ static struct bgp_evpn_es_evi *bgp_evpn_es_evi_new(struct bgp_evpn_es *es, /* remove the ES-EVI from the per-L2-VNI and per-ES tables and free * up the memory. */ -static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi) +static struct bgp_evpn_es_evi * +bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi) { struct bgp_evpn_es *es = es_evi->es; struct bgpevpn *vpn = es_evi->vpn; @@ -2955,7 +3234,7 @@ static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi) * reference */ if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | BGP_EVPNES_EVI_REMOTE)) - return; + return es_evi; bgp_evpn_es_vrf_deref(es_evi); @@ -2970,6 +3249,8 @@ static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi) /* remove from the VNI-ESI rb tree */ XFREE(MTYPE_BGP_EVPN_ES_EVI, es_evi); + + return NULL; } /* init local info associated with the ES-EVI */ @@ -2986,17 +3267,18 @@ static void bgp_evpn_es_evi_local_info_set(struct bgp_evpn_es_evi *es_evi) } /* clear any local info associated with the ES-EVI */ -static void bgp_evpn_es_evi_local_info_clear(struct bgp_evpn_es_evi *es_evi) +static struct bgp_evpn_es_evi * +bgp_evpn_es_evi_local_info_clear(struct bgp_evpn_es_evi *es_evi) { struct bgpevpn *vpn = es_evi->vpn; if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) - return; + return es_evi; UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL); list_delete_node(vpn->local_es_evi_list, &es_evi->l2vni_listnode); - bgp_evpn_es_evi_free(es_evi); + return bgp_evpn_es_evi_free(es_evi); } /* eval remote info associated with the ES */ @@ -3026,14 +3308,15 @@ static void bgp_evpn_es_evi_remote_info_re_eval(struct bgp_evpn_es_evi *es_evi) } } -static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi) +static struct bgp_evpn_es_evi * +bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi) { struct prefix_evpn p; struct bgp_evpn_es *es = es_evi->es; struct bgp *bgp; if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)) - return; + return es_evi; if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) zlog_debug("del local es %s evi %u", @@ -3067,8 +3350,7 @@ static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi) } } - bgp_evpn_es_evi_local_info_clear(es_evi); - + return bgp_evpn_es_evi_local_info_clear(es_evi); } int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni) @@ -3284,6 +3566,30 @@ int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn, return 0; } +/* If a VNI is being deleted we need to force del all remote VTEPs */ +static void bgp_evpn_remote_es_evi_flush(struct bgp_evpn_es_evi *es_evi) +{ + struct listnode *node = NULL; + struct listnode *nnode = NULL; + struct bgp_evpn_es_evi_vtep *evi_vtep; + struct bgp *bgp; + + bgp = bgp_get_evpn(); + if (!bgp) + return; + + /* delete all VTEPs */ + for (ALL_LIST_ELEMENTS(es_evi->es_evi_vtep_list, node, nnode, + evi_vtep)) { + evi_vtep->flags &= ~(BGP_EVPN_EVI_VTEP_EAD_PER_ES + | BGP_EVPN_EVI_VTEP_EAD_PER_EVI); + bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep); + bgp_evpn_es_evi_vtep_free(evi_vtep); + } + /* delete the EVI */ + bgp_evpn_es_evi_remote_info_re_eval(es_evi); +} + /* Initialize the ES tables maintained per-L2_VNI */ void bgp_evpn_vni_es_init(struct bgpevpn *vpn) { @@ -3303,7 +3609,9 @@ void bgp_evpn_vni_es_cleanup(struct bgpevpn *vpn) RB_FOREACH_SAFE(es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, es_evi_next) { - bgp_evpn_local_es_evi_do_del(es_evi); + es_evi = bgp_evpn_local_es_evi_do_del(es_evi); + if (es_evi) + bgp_evpn_remote_es_evi_flush(es_evi); } list_delete(&vpn->local_es_evi_list); @@ -3802,6 +4110,507 @@ static int bgp_evpn_run_consistency_checks(struct thread *t) return 0; } +/***************************************************************************** + * EVPN-Nexthop and RMAC management: nexthops associated with Type-2 routes + * that have an ES as destination are consolidated by BGP into a per-VRF + * nh->rmac mapping which is sent to zebra. Zebra installs the nexthop + * as a remote neigh/fdb entry with a dummy (type-1) prefix referencing it. + * + * This handling is needed because Type-2 routes with ES as dest use NHG + * that is setup using EAD routes (i.e. such NHGs do not include the + * RMAC info). + ****************************************************************************/ +static void bgp_evpn_nh_zebra_update_send(struct bgp_evpn_nh *nh, bool add) +{ + struct stream *s; + struct bgp *bgp_vrf = nh->bgp_vrf; + + /* Check socket. */ + if (!zclient || zclient->sock < 0) + return; + + /* Don't try to register if Zebra doesn't know of this instance. */ + if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp_vrf)) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("No zebra instance, not %s remote nh %s", + add ? "adding" : "deleting", nh->nh_str); + return; + } + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header( + s, add ? ZEBRA_EVPN_REMOTE_NH_ADD : ZEBRA_EVPN_REMOTE_NH_DEL, + bgp_vrf->vrf_id); + stream_putl(s, bgp_vrf->vrf_id); + stream_put(s, &nh->ip, sizeof(nh->ip)); + if (add) + stream_put(s, &nh->rmac, sizeof(nh->rmac)); + + stream_putw_at(s, 0, stream_get_endp(s)); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) { + if (add) + zlog_debug("evpn vrf %s nh %s rmac %pEA add to zebra", + nh->bgp_vrf->name, nh->nh_str, &nh->rmac); + else if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s del to zebra", + nh->bgp_vrf->name, nh->nh_str); + } + + zclient_send_message(zclient); +} + +static void bgp_evpn_nh_zebra_update(struct bgp_evpn_nh *nh, bool add) +{ + if (add && !is_zero_mac(&nh->rmac)) { + nh->flags |= BGP_EVPN_NH_READY_FOR_ZEBRA; + bgp_evpn_nh_zebra_update_send(nh, true); + } else { + if (!(nh->flags & BGP_EVPN_NH_READY_FOR_ZEBRA)) + return; + nh->flags &= ~BGP_EVPN_NH_READY_FOR_ZEBRA; + bgp_evpn_nh_zebra_update_send(nh, false); + } +} + +static void *bgp_evpn_nh_alloc(void *p) +{ + struct bgp_evpn_nh *tmp_n = p; + struct bgp_evpn_nh *n; + + n = XCALLOC(MTYPE_BGP_EVPN_NH, sizeof(struct bgp_evpn_nh)); + *n = *tmp_n; + + return ((void *)n); +} + +static struct bgp_evpn_nh *bgp_evpn_nh_find(struct bgp *bgp_vrf, + struct ipaddr *ip) +{ + struct bgp_evpn_nh tmp; + struct bgp_evpn_nh *n; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); + n = hash_lookup(bgp_vrf->evpn_nh_table, &tmp); + + return n; +} + +/* Add nexthop entry - implicitly created on first path reference */ +static struct bgp_evpn_nh *bgp_evpn_nh_add(struct bgp *bgp_vrf, + struct ipaddr *ip, + struct bgp_path_info *pi) +{ + struct bgp_evpn_nh tmp_n; + struct bgp_evpn_nh *n = NULL; + + memset(&tmp_n, 0, sizeof(struct bgp_evpn_nh)); + memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); + n = hash_get(bgp_vrf->evpn_nh_table, &tmp_n, bgp_evpn_nh_alloc); + ipaddr2str(ip, n->nh_str, sizeof(n->nh_str)); + n->bgp_vrf = bgp_vrf; + + n->pi_list = list_new(); + listset_app_node_mem(n->pi_list); + + /* Setup ref_pi when the nh is created */ + if (CHECK_FLAG(pi->flags, BGP_PATH_VALID) && pi->attr) { + n->ref_pi = pi; + memcpy(&n->rmac, &pi->attr->rmac, ETH_ALEN); + } + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s rmac %pEA add", n->bgp_vrf->name, + n->nh_str, &n->rmac); + bgp_evpn_nh_zebra_update(n, true); + return n; +} + +/* Delete nexthop entry if there are no paths referencing it */ +static void bgp_evpn_nh_del(struct bgp_evpn_nh *n) +{ + struct bgp_evpn_nh *tmp_n; + struct bgp *bgp_vrf = n->bgp_vrf; + + if (listcount(n->pi_list)) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s del to zebra", bgp_vrf->name, + n->nh_str); + + bgp_evpn_nh_zebra_update(n, false); + list_delete(&n->pi_list); + tmp_n = hash_release(bgp_vrf->evpn_nh_table, n); + XFREE(MTYPE_BGP_EVPN_NH, tmp_n); +} + +static unsigned int bgp_evpn_nh_hash_keymake(const void *p) +{ + const struct bgp_evpn_nh *n = p; + const struct ipaddr *ip = &n->ip; + + if (IS_IPADDR_V4(ip)) + return jhash_1word(ip->ipaddr_v4.s_addr, 0); + + return jhash2(ip->ipaddr_v6.s6_addr32, + array_size(ip->ipaddr_v6.s6_addr32), 0); +} + +static bool bgp_evpn_nh_cmp(const void *p1, const void *p2) +{ + const struct bgp_evpn_nh *n1 = p1; + const struct bgp_evpn_nh *n2 = p2; + + if (n1 == NULL && n2 == NULL) + return true; + + if (n1 == NULL || n2 == NULL) + return false; + + return (memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)) == 0); +} + +void bgp_evpn_nh_init(struct bgp *bgp_vrf) +{ + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh init", bgp_vrf->name); + bgp_vrf->evpn_nh_table = hash_create( + bgp_evpn_nh_hash_keymake, bgp_evpn_nh_cmp, "BGP EVPN NH table"); +} + +static void bgp_evpn_nh_flush_entry(struct bgp_evpn_nh *nh) +{ + struct listnode *node; + struct listnode *nnode; + struct bgp_path_evpn_nh_info *nh_info; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s flush", nh->bgp_vrf->name, + nh->nh_str); + + /* force flush paths */ + for (ALL_LIST_ELEMENTS(nh->pi_list, node, nnode, nh_info)) + bgp_evpn_path_nh_del(nh->bgp_vrf, nh_info->pi); +} + +static void bgp_evpn_nh_flush_cb(struct hash_bucket *bucket, void *ctxt) +{ + struct bgp_evpn_nh *nh = (struct bgp_evpn_nh *)bucket->data; + + bgp_evpn_nh_flush_entry(nh); +} + +void bgp_evpn_nh_finish(struct bgp *bgp_vrf) +{ + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh finish", bgp_vrf->name); + hash_iterate( + bgp_vrf->evpn_nh_table, + (void (*)(struct hash_bucket *, void *))bgp_evpn_nh_flush_cb, + NULL); + hash_free(bgp_vrf->evpn_nh_table); + bgp_vrf->evpn_nh_table = NULL; +} + +static void bgp_evpn_nh_update_ref_pi(struct bgp_evpn_nh *nh) +{ + struct listnode *node; + struct bgp_path_info *pi; + struct bgp_path_evpn_nh_info *nh_info; + + if (nh->ref_pi) + return; + + for (ALL_LIST_ELEMENTS_RO(nh->pi_list, node, nh_info)) { + pi = nh_info->pi; + if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID) || !pi->attr) + continue; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s ref_pi update", + nh->bgp_vrf->name, nh->nh_str); + nh->ref_pi = pi; + /* If we have a new pi copy rmac from it and update + * zebra if the new rmac is different + */ + if (memcmp(&nh->rmac, &nh->ref_pi->attr->rmac, ETH_ALEN)) { + memcpy(&nh->rmac, &nh->ref_pi->attr->rmac, ETH_ALEN); + bgp_evpn_nh_zebra_update(nh, true); + } + break; + } +} + +static void bgp_evpn_nh_clear_ref_pi(struct bgp_evpn_nh *nh, + struct bgp_path_info *pi) +{ + if (nh->ref_pi != pi) + return; + + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) + zlog_debug("evpn vrf %s nh %s ref_pi clear", nh->bgp_vrf->name, + nh->nh_str); + nh->ref_pi = NULL; + /* try to find another ref_pi */ + bgp_evpn_nh_update_ref_pi(nh); + /* couldn't find one - clear the old rmac and notify zebra */ + if (!nh->ref_pi) { + memset(&nh->rmac, 0, ETH_ALEN); + bgp_evpn_nh_zebra_update(nh, true); + } +} + +static void bgp_evpn_path_nh_info_free(struct bgp_path_evpn_nh_info *nh_info) +{ + bgp_evpn_path_nh_unlink(nh_info); + XFREE(MTYPE_BGP_EVPN_PATH_NH_INFO, nh_info); +} + +static struct bgp_path_evpn_nh_info * +bgp_evpn_path_nh_info_new(struct bgp_path_info *pi) +{ + struct bgp_path_info_extra *e; + struct bgp_path_mh_info *mh_info; + struct bgp_path_evpn_nh_info *nh_info; + + e = bgp_path_info_extra_get(pi); + + /* If mh_info doesn't exist allocate it */ + mh_info = e->mh_info; + if (!mh_info) + e->mh_info = mh_info = XCALLOC(MTYPE_BGP_EVPN_PATH_MH_INFO, + sizeof(struct bgp_path_mh_info)); + + /* If nh_info doesn't exist allocate it */ + nh_info = mh_info->nh_info; + if (!nh_info) { + mh_info->nh_info = nh_info = + XCALLOC(MTYPE_BGP_EVPN_PATH_NH_INFO, + sizeof(struct bgp_path_evpn_nh_info)); + nh_info->pi = pi; + } + + return nh_info; +} + +static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info) +{ + struct bgp_evpn_nh *nh = nh_info->nh; + struct bgp_path_info *pi; + char prefix_buf[PREFIX_STRLEN]; + + if (!nh) + return; + + pi = nh_info->pi; + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("path %s unlinked from nh %s %s", + pi->net ? prefix2str(&pi->net->p, prefix_buf, + sizeof(prefix_buf)) + : "", + nh->bgp_vrf->name, nh->nh_str); + + list_delete_node(nh->pi_list, &nh_info->nh_listnode); + + nh_info->nh = NULL; + + /* check if the ref_pi need to be updated */ + bgp_evpn_nh_clear_ref_pi(nh, pi); + + /* if there are no other references against the nh it + * needs to be freed + */ + bgp_evpn_nh_del(nh); + + /* Note we don't free the path nh_info on unlink; it will be freed up + * along with the path. + */ +} + +static void bgp_evpn_path_nh_link(struct bgp *bgp_vrf, struct bgp_path_info *pi) +{ + struct bgp_path_evpn_nh_info *nh_info; + struct bgp_evpn_nh *nh; + struct ipaddr ip; + + /* EVPN nexthop setup in bgp has been turned off */ + if (!bgp_mh_info->bgp_evpn_nh_setup) + return; + + if (!bgp_vrf->evpn_nh_table) { + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("path %pFX linked to vrf %s failed", + &pi->net->p, bgp_vrf->name); + return; + } + + nh_info = (pi->extra && pi->extra->mh_info) + ? pi->extra->mh_info->nh_info + : NULL; + + /* if NHG is not being used for this path we don't need to manage the + * nexthops in bgp (they are managed by zebra instead) + */ + if (!(pi->attr->es_flags & ATTR_ES_L3_NHG_USE)) { + if (nh_info) + bgp_evpn_path_nh_unlink(nh_info); + return; + } + + /* setup nh_info against the path if it doesn't aleady exist */ + if (!nh_info) + nh_info = bgp_evpn_path_nh_info_new(pi); + + /* find-create nh */ + memset(&ip, 0, sizeof(ip)); + if (pi->net->p.family == AF_INET6) { + SET_IPADDR_V6(&ip); + memcpy(&ip.ipaddr_v6, &pi->attr->mp_nexthop_global, + sizeof(ip.ipaddr_v6)); + } else { + SET_IPADDR_V4(&ip); + memcpy(&ip.ipaddr_v4, &pi->attr->nexthop, sizeof(ip.ipaddr_v4)); + } + + nh = bgp_evpn_nh_find(bgp_vrf, &ip); + if (!nh) + nh = bgp_evpn_nh_add(bgp_vrf, &ip, pi); + + /* dup check */ + if (nh_info->nh == nh) { + /* Check if any of the paths are now valid */ + bgp_evpn_nh_update_ref_pi(nh); + return; + } + + /* unlink old nh if any */ + bgp_evpn_path_nh_unlink(nh_info); + + if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) + zlog_debug("path %pFX linked to nh %s %s", &pi->net->p, + nh->bgp_vrf->name, nh->nh_str); + + /* link mac-ip path to the new nh */ + nh_info->nh = nh; + listnode_init(&nh_info->nh_listnode, nh_info); + listnode_add(nh->pi_list, &nh_info->nh_listnode); + /* If a new valid path got linked to the nh see if can get the rmac + * from it + */ + bgp_evpn_nh_update_ref_pi(nh); + if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) { + if (!nh->ref_pi) + zlog_debug( + "path %pFX linked to nh %s %s with no valid pi", + &pi->net->p, nh->bgp_vrf->name, nh->nh_str); + } +} + +void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi) +{ + struct bgp_path_evpn_nh_info *nh_info; + + nh_info = (pi->extra && pi->extra->mh_info) + ? pi->extra->mh_info->nh_info + : NULL; + + if (!nh_info) + return; + + bgp_evpn_path_nh_unlink(nh_info); +} + +void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi) +{ + bgp_evpn_path_nh_link(bgp_vrf, pi); +} + +static void bgp_evpn_nh_show_entry(struct bgp_evpn_nh *nh, struct vty *vty, + json_object *json_array) +{ + json_object *json = NULL; + char mac_buf[ETHER_ADDR_STRLEN]; + char prefix_buf[PREFIX_STRLEN]; + + if (json_array) + /* create a separate json object for each ES */ + json = json_object_new_object(); + + prefix_mac2str(&nh->rmac, mac_buf, sizeof(mac_buf)); + if (nh->ref_pi && nh->ref_pi->net) + prefix2str(&nh->ref_pi->net->p, prefix_buf, sizeof(prefix_buf)); + else + prefix_buf[0] = '\0'; + if (json) { + json_object_string_add(json, "vrf", nh->bgp_vrf->name); + json_object_string_add(json, "ip", nh->nh_str); + json_object_string_add(json, "rmac", mac_buf); + json_object_string_add(json, "basePath", prefix_buf); + json_object_int_add(json, "pathCount", listcount(nh->pi_list)); + } else { + vty_out(vty, "%-15s %-15s %-17s %-10d %s\n", nh->bgp_vrf->name, + nh->nh_str, mac_buf, listcount(nh->pi_list), + prefix_buf); + } + + /* add ES to the json array */ + if (json_array) + json_object_array_add(json_array, json); +} + +struct nh_show_ctx { + struct vty *vty; + json_object *json; +}; + +static void bgp_evpn_nh_show_hash_cb(struct hash_bucket *bucket, void *ctxt) +{ + struct bgp_evpn_nh *nh = (struct bgp_evpn_nh *)bucket->data; + struct nh_show_ctx *wctx = (struct nh_show_ctx *)ctxt; + + bgp_evpn_nh_show_entry(nh, wctx->vty, wctx->json); +} + +/* Display all evpn nexthops */ +void bgp_evpn_nh_show(struct vty *vty, bool uj) +{ + json_object *json_array = NULL; + struct bgp *bgp_vrf; + struct listnode *node; + struct nh_show_ctx wctx; + + if (uj) { + /* create an array of nexthops */ + json_array = json_object_new_array(); + } else { + vty_out(vty, "%-15s %-15s %-17s %-10s %s\n", "VRF", "IP", + "RMAC", "#Paths", "Base Path"); + } + + wctx.vty = vty; + wctx.json = json_array; + + /* walk through all vrfs */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) { + hash_iterate(bgp_vrf->evpn_nh_table, + (void (*)(struct hash_bucket *, + void *))bgp_evpn_nh_show_hash_cb, + &wctx); + } + + /* print the array of json-ESs */ + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json_array, JSON_C_TO_STRING_PRETTY)); + json_object_free(json_array); + } +} + /*****************************************************************************/ void bgp_evpn_mh_init(void) { @@ -3824,6 +4633,8 @@ void bgp_evpn_mh_init(void) bgp_mh_info->consistency_checking = true; bgp_mh_info->install_l3nhg = false; bgp_mh_info->host_routes_use_l3nhg = BGP_EVPN_MH_USE_ES_L3NHG_DEF; + bgp_mh_info->suppress_l3_ecomm_on_inactive_es = true; + bgp_mh_info->bgp_evpn_nh_setup = true; memset(&zero_esi_buf, 0, sizeof(esi_t)); } @@ -3838,7 +4649,7 @@ void bgp_evpn_mh_finish(void) RB_FOREACH_SAFE (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree, es_next) { - bgp_evpn_es_local_info_clear(es); + bgp_evpn_es_local_info_clear(es, true); } if (bgp_mh_info->t_cons_check) thread_cancel(&bgp_mh_info->t_cons_check); diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index 1053e3f022..c96de86871 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -105,8 +105,17 @@ struct bgp_evpn_es { /* List of MAC-IP VNI paths using this ES as destination - * element is bgp_path_info_extra->es_info + * Note: Only local/zebra-added MACIP paths in the VNI + * routing table are linked to this list */ - struct list *macip_path_list; + struct list *macip_evi_path_list; + + /* List of MAC-IP paths in the global routing table using this + * ES as destination - data is bgp_path_info_extra->es_info + * Note: Only non-local/imported MACIP paths in the global + * routing table are linked to this list + */ + struct list *macip_global_path_list; /* Number of remote VNIs referencing this ES */ uint32_t remote_es_evi_cnt; @@ -241,6 +250,26 @@ struct bgp_evpn_es_evi_vtep { struct bgp_evpn_es_vtep *es_vtep; }; +/* A nexthop is created when a path (imported from an EVPN type-2 route) + * is added to the VRF route table using that nexthop. + * It is added on first pi reference and removed on last pi deref. + */ +struct bgp_evpn_nh { + /* backpointer to the VRF */ + struct bgp *bgp_vrf; + /* nexthop/VTEP IP */ + struct ipaddr ip; + /* description for easy logging */ + char nh_str[INET6_ADDRSTRLEN]; + struct ethaddr rmac; + /* pi from which we are pulling the nh RMAC */ + struct bgp_path_info *ref_pi; + /* List of VRF paths using this nexthop */ + struct list *pi_list; + uint8_t flags; +#define BGP_EVPN_NH_READY_FOR_ZEBRA (1 << 0) +}; + /* multihoming information stored in bgp_master */ #define bgp_mh_info (bm->mh_info) struct bgp_evpn_mh_info { @@ -273,6 +302,12 @@ struct bgp_evpn_mh_info { /* Skip EAD-EVI advertisements by turning off this knob */ bool ead_evi_tx; #define BGP_EVPN_MH_EAD_EVI_TX_DEF true + /* If the Local ES is inactive we advertise the MAC-IP without the + * L3 ecomm + */ + bool suppress_l3_ecomm_on_inactive_es; + /* Setup EVPN PE nexthops and their RMAC in bgpd */ + bool bgp_evpn_nh_setup; }; /****************************************************************************/ @@ -330,11 +365,18 @@ static inline uint32_t bgp_evpn_attr_get_df_pref(struct attr *attr) return (attr) ? attr->df_pref : 0; } +static inline bool bgp_evpn_local_es_is_active(struct bgp_evpn_es *es) +{ + return (es->flags & BGP_EVPNES_OPER_UP) + && !(es->flags & BGP_EVPNES_BYPASS); +} + /****************************************************************************/ extern int bgp_evpn_es_route_install_uninstall(struct bgp *bgp, struct bgp_evpn_es *es, afi_t afi, safi_t safi, struct prefix_evpn *evp, struct bgp_path_info *pi, int install); +extern void update_type1_routes_for_evi(struct bgp *bgp, struct bgpevpn *vpn); int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi, struct attr *attr, uint8_t *pfx, int psize, uint32_t addpath_id); @@ -361,21 +403,28 @@ void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni, bool uj, bool detail); void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail); struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi); -extern bool bgp_evpn_is_esi_local(esi_t *esi); extern void bgp_evpn_vrf_es_init(struct bgp *bgp_vrf); +extern bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi); extern void bgp_evpn_es_vrf_deref(struct bgp_evpn_es_evi *es_evi); extern void bgp_evpn_es_vrf_ref(struct bgp_evpn_es_evi *es_evi, struct bgp *bgp_vrf); -extern void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info); -extern void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info); +extern void bgp_evpn_path_mh_info_free(struct bgp_path_mh_info *mh_info); extern void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi); -extern bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh); extern bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi, uint32_t *nhg_p); extern void bgp_evpn_es_vrf_show(struct vty *vty, bool uj, struct bgp_evpn_es *es); extern void bgp_evpn_es_vrf_show_esi(struct vty *vty, esi_t *esi, bool uj); extern void bgp_evpn_switch_ead_evi_rx(void); +extern bool bgp_evpn_es_add_l3_ecomm_ok(esi_t *esi); +extern void bgp_evpn_es_vrf_use_nhg(struct bgp *bgp_vrf, esi_t *esi, + bool *use_l3nhg, bool *is_l3nhg_active, + struct bgp_evpn_es_vrf **es_vrf_p); +extern void bgp_evpn_nh_init(struct bgp *bgp_vrf); +extern void bgp_evpn_nh_finish(struct bgp *bgp_vrf); +extern void bgp_evpn_nh_show(struct vty *vty, bool uj); +extern void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi); +extern void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi); #endif /* _FRR_BGP_EVPN_MH_H */ diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index ff4970af41..debed9f68b 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -631,4 +631,13 @@ bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi, const struct prefix_evpn *evp, struct prefix_rd *prd); extern void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import); +extern void bgp_evpn_update_type2_route_entry(struct bgp *bgp, + struct bgpevpn *vpn, + struct bgp_node *rn, + struct bgp_path_info *local_pi, + const char *caller); +extern int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf, + struct bgp_path_info *pi, + int install); +extern void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import); #endif /* _BGP_EVPN_PRIVATE_H */ diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index b101589a79..ed8a6a9506 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -688,7 +688,8 @@ static void show_esi_routes(struct bgp *bgp, /* Display all MAC-IP VNI routes linked to an ES */ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, - json_object *json, int detail) + json_object *json, int detail, + bool global_table) { struct bgp_node *rn; struct bgp_path_info *pi; @@ -709,11 +710,17 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, json_paths = json_object_new_array(); RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) { + struct list *es_list; if (esi && memcmp(esi, &es->esi, sizeof(*esi))) continue; - for (ALL_LIST_ELEMENTS_RO(es->macip_path_list, node, es_info)) { + if (global_table) + es_list = es->macip_global_path_list; + else + es_list = es->macip_evi_path_list; + + for (ALL_LIST_ELEMENTS_RO(es_list, node, es_info)) { json_object *json_path = NULL; pi = es_info->pi; @@ -734,9 +741,9 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, json_path = json_object_new_array(); if (detail) - route_vty_out_detail(vty, bgp, rn, pi, - AFI_L2VPN, SAFI_EVPN, - json_path); + route_vty_out_detail( + vty, bgp, rn, pi, AFI_L2VPN, SAFI_EVPN, + RPKI_NOT_BEING_USED, json_path); else route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN, json_path, false); @@ -758,6 +765,18 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi, } } +static void bgp_evpn_show_routes_mac_ip_evi_es(struct vty *vty, esi_t *esi, + json_object *json, int detail) +{ + return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, false); +} + +static void bgp_evpn_show_routes_mac_ip_global_es(struct vty *vty, esi_t *esi, + json_object *json, int detail) +{ + return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, true); +} + static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, struct vty *vty, struct in_addr vtep_ip, json_object *json, int detail) @@ -823,6 +842,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type, if (detail) route_vty_out_detail(vty, bgp, dest, pi, AFI_L2VPN, SAFI_EVPN, + RPKI_NOT_BEING_USED, json_path); else route_vty_out(vty, p, pi, 0, SAFI_EVPN, @@ -1377,33 +1397,43 @@ DEFUN(show_ip_bgp_l2vpn_evpn, "show [ip] bgp l2vpn evpn [json]", SHOW_STR IP_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR JSON_STR) { - return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL, 0, + return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL, + SHOW_DISPLAY_STANDARD, use_json(argc, argv)); } DEFUN(show_ip_bgp_l2vpn_evpn_rd, show_ip_bgp_l2vpn_evpn_rd_cmd, - "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN [json]", + "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> [json]", SHOW_STR IP_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR "Display information for a route distinguisher\n" - "VPN Route Distinguisher\n" JSON_STR) + "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" + JSON_STR) { int idx_ext_community = 0; int ret; struct prefix_rd prd; + int rd_all = 0; - argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); + argv_find(argv, argc, "all", &rd_all); + if (rd_all) + return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, + NULL, SHOW_DISPLAY_STANDARD, + use_json(argc, argv)); + argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; } - return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_normal, NULL, 0, + return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_normal, NULL, + SHOW_DISPLAY_STANDARD, use_json(argc, argv)); } @@ -1418,34 +1448,41 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_tags, "Display information about all EVPN NLRIs\n" "Display BGP tags for prefixes\n") { - return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL, 1, - 0); + return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL, + SHOW_DISPLAY_TAGS, 0); } DEFUN(show_ip_bgp_l2vpn_evpn_rd_tags, show_ip_bgp_l2vpn_evpn_rd_tags_cmd, - "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN tags", + "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> tags", SHOW_STR IP_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR "Display information for a route distinguisher\n" - "VPN Route Distinguisher\n" "Display BGP tags for prefixes\n") + "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" + "Display BGP tags for prefixes\n") { int idx_ext_community = 0; int ret; struct prefix_rd prd; + int rd_all = 0; - argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); + argv_find(argv, argc, "all", &rd_all); + if (rd_all) + return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, + NULL, SHOW_DISPLAY_TAGS, 0); + argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; } - return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_normal, NULL, 1, - 0); + return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_normal, NULL, + SHOW_DISPLAY_TAGS, 0); } DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_routes, @@ -1511,13 +1548,13 @@ DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_routes, return CMD_WARNING; } - return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, peer, 0, - uj); + return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, peer, + SHOW_DISPLAY_STANDARD, uj); } DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes, show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd, - "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors <A.B.C.D|X:X::X:X|WORD> routes [json]", + "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors <A.B.C.D|X:X::X:X|WORD> routes [json]", SHOW_STR IP_STR BGP_STR @@ -1525,6 +1562,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes, EVPN_HELP_STR "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" "Detailed information on TCP and BGP neighbor connections\n" "IPv4 Neighbor to display information about\n" "IPv6 Neighbor to display information about\n" @@ -1541,6 +1579,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes, afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; struct bgp *bgp = NULL; + int rd_all = 0; bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, &bgp, uj); @@ -1549,20 +1588,26 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes, return CMD_WARNING; } - argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); - ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); - if (!ret) { - if (uj) { - json_object *json_no = NULL; - json_no = json_object_new_object(); - json_object_string_add(json_no, "warning", - "Malformed Route Distinguisher"); - vty_out(vty, "%s\n", - json_object_to_json_string(json_no)); - json_object_free(json_no); - } else - vty_out(vty, "%% Malformed Route Distinguisher\n"); - return CMD_WARNING; + argv_find(argv, argc, "all", &rd_all); + if (!rd_all) { + argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", + &idx_ext_community); + ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); + if (!ret) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add( + json_no, "warning", + "Malformed Route Distinguisher"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, + "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } } /* neighbors <A.B.C.D|X:X::X:X|WORD> */ @@ -1599,8 +1644,13 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes, return CMD_WARNING; } - return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_neighbor, peer, 0, - uj); + + if (rd_all) + return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, + peer, SHOW_DISPLAY_STANDARD, uj); + else + return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_neighbor, + peer, SHOW_DISPLAY_STANDARD, uj); } DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes, @@ -1674,7 +1724,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes, DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes, show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd, - "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json]", + "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json]", SHOW_STR IP_STR BGP_STR @@ -1682,6 +1732,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes, EVPN_HELP_STR "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" "Detailed information on TCP and BGP neighbor connections\n" "IPv4 Neighbor to display information about\n" "IPv6 Neighbor to display information about\n" @@ -1698,6 +1749,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes, char *peerstr = NULL; afi_t afi = AFI_L2VPN; safi_t safi = SAFI_EVPN; + int rd_all = 0; if (uj) argc--; @@ -1712,8 +1764,6 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes, return CMD_WARNING; } - argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); - /* neighbors <A.B.C.D|X:X::X:X|WORD> */ argv_find(argv, argc, "neighbors", &idx); peerstr = argv[++idx]->arg; @@ -1748,19 +1798,29 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes, return CMD_WARNING; } - ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); - if (!ret) { - if (uj) { - json_object *json_no = NULL; - json_no = json_object_new_object(); - json_object_string_add(json_no, "warning", - "Malformed Route Distinguisher"); - vty_out(vty, "%s\n", - json_object_to_json_string(json_no)); - json_object_free(json_no); - } else - vty_out(vty, "%% Malformed Route Distinguisher\n"); - return CMD_WARNING; + argv_find(argv, argc, "all", &rd_all); + if (rd_all) + return show_adj_route_vpn(vty, peer, NULL, AFI_L2VPN, SAFI_EVPN, + uj); + else { + argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", + &idx_ext_community); + ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); + if (!ret) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add( + json_no, "warning", + "Malformed Route Distinguisher"); + vty_out(vty, "%s\n", + json_object_to_json_string(json_no)); + json_object_free(json_no); + } else + vty_out(vty, + "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } } return show_adj_route_vpn(vty, peer, &prd, AFI_L2VPN, SAFI_EVPN, uj); @@ -1785,7 +1845,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_overlay, DEFUN(show_ip_bgp_evpn_rd_overlay, show_ip_bgp_evpn_rd_overlay_cmd, - "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN overlay", + "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> overlay", SHOW_STR IP_STR BGP_STR @@ -1793,14 +1853,21 @@ DEFUN(show_ip_bgp_evpn_rd_overlay, EVPN_HELP_STR "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" "Display BGP Overlay Information for prefixes\n") { int idx_ext_community = 0; int ret; struct prefix_rd prd; + int rd_all = 0; - argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); + argv_find(argv, argc, "all", &rd_all); + if (rd_all) + return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, + NULL, SHOW_DISPLAY_OVERLAY, + use_json(argc, argv)); + argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community); ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); @@ -2367,7 +2434,8 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path); + route_vty_out_detail(vty, bgp, dest, pi, afi, safi, + RPKI_NOT_BEING_USED, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2436,7 +2504,8 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path); + route_vty_out_detail(vty, bgp, dest, pi, afi, safi, + RPKI_NOT_BEING_USED, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2541,7 +2610,8 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp, if (json) json_path = json_object_new_array(); - route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path); + route_vty_out_detail(vty, bgp, dest, pi, afi, safi, + RPKI_NOT_BEING_USED, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2651,7 +2721,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, pi, afi, safi, - json_path); + RPKI_NOT_BEING_USED, json_path); if (json) json_object_array_add(json_paths, json_path); @@ -2699,6 +2769,131 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp, } /* + * Display BGP EVPN routing table -- all RDs and MAC and/or IP + * (vty handler). Only matching type-2 routes will be displayed. + */ +static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp, + struct ethaddr *mac, struct ipaddr *ip, + json_object *json) +{ + struct bgp_dest *rd_dest; + struct bgp_table *table; + struct bgp_dest *dest; + struct bgp_path_info *pi; + afi_t afi = AFI_L2VPN; + safi_t safi = SAFI_EVPN; + uint32_t prefix_cnt, path_cnt; + prefix_cnt = path_cnt = 0; + + /* EVPN routing table is a 2-level table with the first level being + * the RD. We need to look in every RD we know about. + */ + for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest; + rd_dest = bgp_route_next(rd_dest)) { + json_object *json_paths = NULL; /* paths array for prefix */ + json_object *json_prefix = NULL; /* prefix within an RD */ + json_object *json_rd = NULL; /* holds all prefixes for RD */ + char rd_str[RD_ADDRSTRLEN]; + char prefix_str[BUFSIZ]; + int add_rd_to_json = 0; + struct prefix_evpn ep; + const struct prefix *rd_destp = bgp_dest_get_prefix(rd_dest); + + table = bgp_dest_get_bgp_table_info(rd_dest); + if (table == NULL) + continue; + + prefix_rd2str((struct prefix_rd *)rd_destp, rd_str, + sizeof(rd_str)); + + /* Construct an RT-2 from the user-supplied mac(ip), + * then search the l2vpn evpn table for it. + */ + build_evpn_type2_prefix(&ep, mac, ip); + dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, + (struct prefix *)&ep, + (struct prefix_rd *)rd_destp); + if (!dest) + continue; + + if (json) + json_rd = json_object_new_object(); + + const struct prefix *p = bgp_dest_get_prefix(dest); + + prefix2str(p, prefix_str, sizeof(prefix_str)); + + pi = bgp_dest_get_bgp_path_info(dest); + if (pi) { + /* RD header - per RD. */ + bgp_evpn_show_route_rd_header(vty, rd_dest, json_rd, + rd_str, RD_ADDRSTRLEN); + prefix_cnt++; + } + + if (json) { + json_prefix = json_object_new_object(); + json_paths = json_object_new_array(); + json_object_string_add(json_prefix, "prefix", + prefix_str); + json_object_int_add(json_prefix, "prefixLen", + p->prefixlen); + } else + /* Prefix and num paths displayed once per prefix. */ + route_vty_out_detail_header( + vty, bgp, dest, (struct prefix_rd *)rd_destp, + AFI_L2VPN, SAFI_EVPN, json_prefix); + + /* For EVPN, the prefix is displayed for each path (to + * fit in with code that already exists). + */ + for (; pi; pi = pi->next) { + json_object *json_path = NULL; + + add_rd_to_json = 1; + path_cnt++; + + if (json) + json_path = json_object_new_array(); + + route_vty_out_detail(vty, bgp, dest, pi, AFI_L2VPN, + SAFI_EVPN, RPKI_NOT_BEING_USED, + json_path); + + if (json) + json_object_array_add(json_paths, json_path); + else + vty_out(vty, "\n"); + } + + if (json) { + json_object_object_add(json_prefix, "paths", + json_paths); + json_object_object_add(json_rd, prefix_str, + json_prefix); + if (add_rd_to_json) + json_object_object_add(json, rd_str, json_rd); + else { + json_object_free(json_rd); + json_rd = NULL; + } + } + } + + if (json) { + json_object_int_add(json, "numPrefix", prefix_cnt); + json_object_int_add(json, "numPaths", path_cnt); + } else { + if (prefix_cnt == 0) { + vty_out(vty, "No Matching EVPN prefixes exist\n"); + } else { + vty_out(vty, "Displayed %u prefixes (%u paths)\n", + prefix_cnt, path_cnt); + } + } +} + +/* * Display BGP EVPN routing table - all routes (vty handler). * If 'type' is non-zero, only routes matching that type are shown. */ @@ -2810,6 +3005,7 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, */ for (; pi; pi = pi->next) { json_object *json_path = NULL; + path_cnt++; add_prefix_to_json = 1; add_rd_to_json = 1; @@ -2820,7 +3016,8 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, if (detail) { route_vty_out_detail( vty, bgp, dest, pi, AFI_L2VPN, - SAFI_EVPN, json_path); + SAFI_EVPN, RPKI_NOT_BEING_USED, + json_path); } else route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path, false); @@ -3255,12 +3452,6 @@ DEFUN (no_bgp_evpn_advertise_default_gw, if (!bgp) return CMD_WARNING; - if (!EVPN_ENABLED(bgp)) { - vty_out(vty, - "This command is only supported under the EVPN VRF\n"); - return CMD_WARNING; - } - evpn_unset_advertise_default_gw(bgp, NULL); return CMD_SUCCESS; @@ -3521,16 +3712,16 @@ DEFPY(bgp_evpn_advertise_svi_ip, if (!bgp) return CMD_WARNING; - if (!EVPN_ENABLED(bgp)) { - vty_out(vty, - "This command is only supported under EVPN VRF\n"); - return CMD_WARNING; - } - if (no) evpn_set_advertise_svi_macip(bgp, NULL, 0); - else + else { + if (!EVPN_ENABLED(bgp)) { + vty_out(vty, + "This command is only supported under EVPN VRF\n"); + return CMD_WARNING; + } evpn_set_advertise_svi_macip(bgp, NULL, 1); + } return CMD_SUCCESS; } @@ -4100,6 +4291,21 @@ DEFPY(show_bgp_l2vpn_evpn_es_vrf, show_bgp_l2vpn_evpn_es_vrf_cmd, return CMD_SUCCESS; } +DEFPY(show_bgp_l2vpn_evpn_nh, + show_bgp_l2vpn_evpn_nh_cmd, + "show bgp l2vpn evpn next-hops [json$uj]", + SHOW_STR + BGP_STR + L2VPN_HELP_STR + EVPN_HELP_STR + "Nexthops\n" + JSON_STR) +{ + bgp_evpn_nh_show(vty, uj); + + return CMD_SUCCESS; +} + /* * Display EVPN neighbor summary. */ @@ -4221,7 +4427,7 @@ DEFUN(show_bgp_l2vpn_evpn_route, */ DEFUN(show_bgp_l2vpn_evpn_route_rd, show_bgp_l2vpn_evpn_route_rd_cmd, - "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type "EVPN_TYPE_ALL_LIST"] [json]", + "show bgp l2vpn evpn route rd <ASN:NN_OR_IP-ADDRESS:NN|all> [type "EVPN_TYPE_ALL_LIST"] [json]", SHOW_STR BGP_STR L2VPN_HELP_STR @@ -4229,6 +4435,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd, EVPN_RT_HELP_STR EVPN_RT_DIST_HELP_STR EVPN_ASN_IP_HELP_STR + "All VPN Route Distinguishers\n" EVPN_TYPE_HELP_STR EVPN_TYPE_ALL_LIST_HELP_STR JSON_STR) @@ -4237,9 +4444,10 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd, int ret; struct prefix_rd prd; int type = 0; - int rd_idx = 0; bool uj = false; json_object *json = NULL; + int idx_ext_community = 0; + int rd_all = 0; bgp = bgp_get_evpn(); if (!bgp) @@ -4250,10 +4458,12 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd, if (uj) json = json_object_new_object(); - /* get the RD */ - if (argv_find(argv, argc, "rd", &rd_idx)) { - ret = str2prefix_rd(argv[rd_idx + 1]->arg, &prd); - + argv_find(argv, argc, "all", &rd_all); + if (!rd_all) { + /* get the RD */ + argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", + &idx_ext_community); + ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; @@ -4263,7 +4473,10 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd, if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0) return CMD_WARNING; - evpn_show_route_rd(vty, bgp, &prd, type, json); + if (rd_all) + evpn_show_all_routes(vty, bgp, type, json, 1); + else + evpn_show_route_rd(vty, bgp, &prd, type, json); if (uj) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -4279,7 +4492,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd, */ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, show_bgp_l2vpn_evpn_route_rd_macip_cmd, - "show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN mac WORD [ip WORD] [json]", + "show bgp l2vpn evpn route rd <ASN:NN_OR_IP-ADDRESS:NN|all> mac WORD [ip WORD] [json]", SHOW_STR BGP_STR L2VPN_HELP_STR @@ -4287,6 +4500,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, EVPN_RT_HELP_STR EVPN_RT_DIST_HELP_STR EVPN_ASN_IP_HELP_STR + "All VPN Route Distinguishers\n" "MAC\n" "MAC address (e.g., 00:e0:ec:20:12:62)\n" "IP\n" @@ -4298,11 +4512,12 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, struct prefix_rd prd; struct ethaddr mac; struct ipaddr ip; - int rd_idx = 0; + int idx_ext_community = 0; int mac_idx = 0; int ip_idx = 0; bool uj = false; json_object *json = NULL; + int rd_all = 0; memset(&mac, 0, sizeof(struct ethaddr)); memset(&ip, 0, sizeof(struct ipaddr)); @@ -4317,8 +4532,11 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, json = json_object_new_object(); /* get the prd */ - if (argv_find(argv, argc, "rd", &rd_idx)) { - ret = str2prefix_rd(argv[rd_idx + 1]->arg, &prd); + argv_find(argv, argc, "all", &rd_all); + if (!rd_all) { + argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", + &idx_ext_community); + ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); return CMD_WARNING; @@ -4341,7 +4559,10 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip, } } - evpn_show_route_rd_macip(vty, bgp, &prd, &mac, &ip, json); + if (rd_all) + evpn_show_route_rd_all_macip(vty, bgp, &mac, &ip, json); + else + evpn_show_route_rd_macip(vty, bgp, &prd, &mac, &ip, json); if (uj) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -4658,12 +4879,12 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni_all, } DEFPY_HIDDEN( - show_bgp_l2vpn_evpn_route_mac_ip_es, - show_bgp_l2vpn_evpn_route_mac_ip_es_cmd, - "show bgp l2vpn evpn route mac-ip-es [NAME$esi_str|detail$detail] [json$uj]", + show_bgp_l2vpn_evpn_route_mac_ip_evi_es, + show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd, + "show bgp l2vpn evpn route mac-ip-evi-es [NAME$esi_str|detail$detail] [json$uj]", SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR "EVPN route information\n" - "MAC IP routes linked to the ES\n" + "MAC IP routes in the EVI tables linked to the ES\n" "ES ID\n" "Detailed information\n" JSON_STR) { @@ -4683,7 +4904,44 @@ DEFPY_HIDDEN( if (uj) json = json_object_new_object(); - bgp_evpn_show_routes_mac_ip_es(vty, esi_p, json, !!detail); + bgp_evpn_show_routes_mac_ip_evi_es(vty, esi_p, json, !!detail); + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; +} + +DEFPY_HIDDEN( + show_bgp_l2vpn_evpn_route_mac_ip_global_es, + show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd, + "show bgp l2vpn evpn route mac-ip-global-es [NAME$esi_str|detail$detail] [json$uj]", + SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR + "EVPN route information\n" + "MAC IP routes in the global table linked to the ES\n" + "ES ID\n" + "Detailed information\n" JSON_STR) +{ + esi_t esi; + esi_t *esi_p; + json_object *json = NULL; + + if (esi_str) { + if (!str_to_esi(esi_str, &esi)) { + vty_out(vty, "%%Malformed ESI\n"); + return CMD_WARNING; + } + esi_p = &esi; + } else { + esi_p = NULL; + } + + if (uj) + json = json_object_new_object(); + bgp_evpn_show_routes_mac_ip_global_es(vty, esi_p, json, !!detail); if (uj) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -5957,6 +6215,7 @@ void bgp_ethernetvpn_init(void) install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_evi_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd); + install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd); @@ -5968,7 +6227,10 @@ void bgp_ethernetvpn_init(void) &show_bgp_l2vpn_evpn_route_vni_multicast_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd); - install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_mac_ip_es_cmd); + install_element(VIEW_NODE, + &show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd); + install_element(VIEW_NODE, + &show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd); install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd); diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index f099309f97..45a856a459 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -1215,8 +1215,9 @@ int bgp_stop(struct peer *peer) peer->nsf_af_count = 0; /* deregister peer */ - if (peer->last_reset == PEER_DOWN_UPDATE_SOURCE_CHANGE) - bgp_bfd_deregister_peer(peer); + if (peer->bfd_config + && peer->last_reset == PEER_DOWN_UPDATE_SOURCE_CHANGE) + bfd_sess_uninstall(peer->bfd_config->session); if (peer_dynamic_neighbor(peer) && !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) { @@ -2122,7 +2123,10 @@ static int bgp_establish(struct peer *peer) hash_release(peer->bgp->peerhash, peer); hash_get(peer->bgp->peerhash, peer, hash_alloc_intern); - bgp_bfd_reset_peer(peer); + /* Start BFD peer if not already running. */ + if (peer->bfd_config) + bgp_peer_bfd_update_source(peer); + return ret; } diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 6b3df87515..ea74a82cec 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -65,6 +65,7 @@ #include "bgpd/bgp_nb.h" #include "bgpd/bgp_evpn_mh.h" #include "bgpd/bgp_nht.h" +#include "bgpd/bgp_routemap_nb.h" #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -162,6 +163,9 @@ __attribute__((__noreturn__)) void sigint(void) assert(bm->terminating == false); bm->terminating = true; /* global flag that shutting down */ + /* Disable BFD events to avoid wasting processing. */ + bfd_protocol_integration_set_shutdown(true); + bgp_terminate(); bgp_exit(0); @@ -385,6 +389,7 @@ static const struct frr_yang_module_info *const bgpd_yang_modules[] = { &frr_route_map_info, &frr_routing_info, &frr_vrf_info, + &frr_bgp_route_map_info, }; FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT, diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 9a4eb5d3bd..36bdc05eb7 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -119,7 +119,10 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value"); DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_MH_INFO, "BGP EVPN MH Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP"); +DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_MH_INFO, "BGP EVPN PATH MH Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_ES_INFO, "BGP EVPN PATH ES Information"); +DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_NH_INFO, "BGP EVPN PATH NH Information"); +DEFINE_MTYPE(BGPD, BGP_EVPN_NH, "BGP EVPN Nexthop"); DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI_VTEP, "BGP EVPN ES-EVI VTEP"); DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI Information"); DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI, "BGP EVPN ES-per-EVI Information"); diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 7b839f1d4c..29923424e3 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -118,6 +118,9 @@ DECLARE_MTYPE(BGP_EVPN_ES_EVI); DECLARE_MTYPE(BGP_EVPN_ES_VRF); DECLARE_MTYPE(BGP_EVPN_ES_VTEP); DECLARE_MTYPE(BGP_EVPN_PATH_ES_INFO); +DECLARE_MTYPE(BGP_EVPN_PATH_MH_INFO); +DECLARE_MTYPE(BGP_EVPN_PATH_NH_INFO); +DECLARE_MTYPE(BGP_EVPN_NH); DECLARE_MTYPE(BGP_EVPN_ES_EVI_VTEP); DECLARE_MTYPE(BGP_EVPN); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index d9acda8bd0..62fed931f9 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1417,7 +1417,6 @@ void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, /* to */ struct bgp *bgp_vpn, /* from */ afi_t afi) { - struct prefix_rd prd; struct bgp_dest *pdest; safi_t safi = SAFI_MPLS_VPN; @@ -1428,16 +1427,10 @@ void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, /* to */ */ for (pdest = bgp_table_top(bgp_vpn->rib[afi][safi]); pdest; pdest = bgp_route_next(pdest)) { - const struct prefix *p = bgp_dest_get_prefix(pdest); struct bgp_table *table; struct bgp_dest *bn; struct bgp_path_info *bpi; - memset(&prd, 0, sizeof(prd)); - prd.family = AF_UNSPEC; - prd.prefixlen = 64; - memcpy(prd.val, &p->u.val, 8); - /* This is the per-RD table of prefixes */ table = bgp_dest_get_bgp_table_info(pdest); @@ -2043,7 +2036,7 @@ int bgp_show_mpls_vpn(struct vty *vty, afi_t afi, struct prefix_rd *prd, DEFUN (show_bgp_ip_vpn_all_rd, show_bgp_ip_vpn_all_rd_cmd, - "show bgp "BGP_AFI_CMD_STR" vpn all [rd ASN:NN_OR_IP-ADDRESS:NN] [json]", + "show bgp "BGP_AFI_CMD_STR" vpn all [rd <ASN:NN_OR_IP-ADDRESS:NN|all>] [json]", SHOW_STR BGP_STR BGP_VPNVX_HELP_STR @@ -2051,6 +2044,7 @@ DEFUN (show_bgp_ip_vpn_all_rd, "Display VPN NLRI specific information\n" "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" JSON_STR) { int ret; @@ -2059,7 +2053,9 @@ DEFUN (show_bgp_ip_vpn_all_rd, int idx = 0; if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) { - if (argv_find(argv, argc, "rd", &idx)) { + /* Constrain search if user supplies RD && RD != "all" */ + if (argv_find(argv, argc, "rd", &idx) + && strcmp(argv[idx + 1]->arg, "all")) { ret = str2prefix_rd(argv[idx + 1]->arg, &prd); if (!ret) { vty_out(vty, @@ -2080,26 +2076,28 @@ DEFUN (show_bgp_ip_vpn_all_rd, ALIAS(show_bgp_ip_vpn_all_rd, show_bgp_ip_vpn_rd_cmd, - "show bgp "BGP_AFI_CMD_STR" vpn rd ASN:NN_OR_IP-ADDRESS:NN [json]", + "show bgp "BGP_AFI_CMD_STR" vpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> [json]", SHOW_STR BGP_STR BGP_VPNVX_HELP_STR "Display VPN NLRI specific information\n" "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" JSON_STR) #ifdef KEEP_OLD_VPN_COMMANDS DEFUN (show_ip_bgp_vpn_rd, show_ip_bgp_vpn_rd_cmd, - "show ip bgp "BGP_AFI_CMD_STR" vpn rd ASN:NN_OR_IP-ADDRESS:NN", + "show ip bgp "BGP_AFI_CMD_STR" vpn rd <ASN:NN_OR_IP-ADDRESS:NN|all>", SHOW_STR IP_STR BGP_STR BGP_AFI_HELP_STR "Address Family modifier\n" "Display information for a route distinguisher\n" - "VPN Route Distinguisher\n") + "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n") { int idx_ext_community = argc - 1; int ret; @@ -2108,6 +2106,10 @@ DEFUN (show_ip_bgp_vpn_rd, int idx = 0; if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { + if (!strcmp(argv[idx_ext_community]->arg, "all")) + return bgp_show_mpls_vpn(vty, afi, NULL, + bgp_show_type_normal, NULL, 0, + 0); ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); @@ -2157,13 +2159,14 @@ DEFUN (show_ip_bgp_vpn_all_tags, DEFUN (show_ip_bgp_vpn_rd_tags, show_ip_bgp_vpn_rd_tags_cmd, - "show [ip] bgp <vpnv4|vpnv6> rd ASN:NN_OR_IP-ADDRESS:NN tags", + "show [ip] bgp <vpnv4|vpnv6> rd <ASN:NN_OR_IP-ADDRESS:NN|all> tags", SHOW_STR IP_STR BGP_STR BGP_VPNVX_HELP_STR "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" "Display BGP tags for prefixes\n") { int idx_ext_community = 5; @@ -2173,6 +2176,10 @@ DEFUN (show_ip_bgp_vpn_rd_tags, int idx = 0; if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { + if (!strcmp(argv[idx_ext_community]->arg, "all")) + return bgp_show_mpls_vpn(vty, afi, NULL, + bgp_show_type_normal, NULL, 1, + 0); ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); @@ -2247,13 +2254,14 @@ DEFUN (show_ip_bgp_vpn_all_neighbor_routes, DEFUN (show_ip_bgp_vpn_rd_neighbor_routes, show_ip_bgp_vpn_rd_neighbor_routes_cmd, - "show [ip] bgp <vpnv4|vpnv6> rd ASN:NN_OR_IP-ADDRESS:NN neighbors A.B.C.D routes [json]", + "show [ip] bgp <vpnv4|vpnv6> rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors A.B.C.D routes [json]", SHOW_STR IP_STR BGP_STR BGP_VPNVX_HELP_STR "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Display routes learned from neighbor\n" @@ -2265,26 +2273,32 @@ DEFUN (show_ip_bgp_vpn_rd_neighbor_routes, union sockunion su; struct peer *peer; struct prefix_rd prd; + bool prefix_rd_all = false; bool uj = use_json(argc, argv); afi_t afi; int idx = 0; if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { - ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); - if (!ret) { - if (uj) { - json_object *json_no = NULL; - json_no = json_object_new_object(); - json_object_string_add( - json_no, "warning", - "Malformed Route Distinguisher"); - vty_out(vty, "%s\n", - json_object_to_json_string(json_no)); - json_object_free(json_no); - } else - vty_out(vty, - "%% Malformed Route Distinguisher\n"); - return CMD_WARNING; + if (!strcmp(argv[idx_ext_community]->arg, "all")) + prefix_rd_all = true; + else { + ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); + if (!ret) { + if (uj) { + json_object *json_no = NULL; + json_no = json_object_new_object(); + json_object_string_add( + json_no, "warning", + "Malformed Route Distinguisher"); + vty_out(vty, "%s\n", + json_object_to_json_string( + json_no)); + json_object_free(json_no); + } else + vty_out(vty, + "%% Malformed Route Distinguisher\n"); + return CMD_WARNING; + } } ret = str2sockunion(argv[idx_ipv4]->arg, &su); @@ -2320,8 +2334,14 @@ DEFUN (show_ip_bgp_vpn_rd_neighbor_routes, return CMD_WARNING; } - return bgp_show_mpls_vpn(vty, afi, &prd, bgp_show_type_neighbor, - &su, 0, uj); + if (prefix_rd_all) + return bgp_show_mpls_vpn(vty, afi, NULL, + bgp_show_type_neighbor, &su, 0, + uj); + else + return bgp_show_mpls_vpn(vty, afi, &prd, + bgp_show_type_neighbor, &su, 0, + uj); } return CMD_SUCCESS; } @@ -2387,13 +2407,14 @@ DEFUN (show_ip_bgp_vpn_all_neighbor_advertised_routes, DEFUN (show_ip_bgp_vpn_rd_neighbor_advertised_routes, show_ip_bgp_vpn_rd_neighbor_advertised_routes_cmd, - "show [ip] bgp <vpnv4|vpnv6> rd ASN:NN_OR_IP-ADDRESS:NN neighbors A.B.C.D advertised-routes [json]", + "show [ip] bgp <vpnv4|vpnv6> rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors A.B.C.D advertised-routes [json]", SHOW_STR IP_STR BGP_STR BGP_VPNVX_HELP_STR "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" + "All VPN Route Distinguishers\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Display the routes advertised to a BGP neighbor\n" @@ -2442,6 +2463,9 @@ DEFUN (show_ip_bgp_vpn_rd_neighbor_advertised_routes, return CMD_WARNING; } + if (!strcmp(argv[idx_ext_community]->arg, "all")) + return show_adj_route_vpn(vty, peer, NULL, AFI_IP, + SAFI_MPLS_VPN, uj); ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd); if (!ret) { if (uj) { diff --git a/bgpd/bgp_mplsvpn_snmp.c b/bgpd/bgp_mplsvpn_snmp.c index b74cf37ac7..6f75856d54 100644 --- a/bgpd/bgp_mplsvpn_snmp.c +++ b/bgpd/bgp_mplsvpn_snmp.c @@ -1478,10 +1478,16 @@ static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[], oid_copy_str(&name[namelen], (*l3vpn_bgp)->name, vrf_name_len); oid_index = namelen + vrf_name_len; - name[oid_index++] = - v4 ? INETADDRESSTYPEIPV4 : INETADDRESSTYPEIPV6; - oid_copy_addr(&name[oid_index], &p->u.prefix4, - addr_len); + if (v4) { + name[oid_index++] = INETADDRESSTYPEIPV4; + oid_copy_in_addr(&name[oid_index], + &p->u.prefix4); + } else { + name[oid_index++] = INETADDRESSTYPEIPV6; + oid_copy_in6_addr(&name[oid_index], + &p->u.prefix6); + } + oid_index += addr_len; name[oid_index++] = p->prefixlen; name[oid_index++] = *policy >> 8; @@ -1493,9 +1499,8 @@ static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[], INETADDRESSTYPEUNKNOWN; else { name[oid_index++] = INETADDRESSTYPEIPV4; - oid_copy_addr(&name[oid_index], - &attr->nexthop, - sizeof(struct in_addr)); + oid_copy_in_addr(&name[oid_index], + &attr->nexthop); oid_index += sizeof(struct in_addr); } } else { @@ -1505,11 +1510,9 @@ static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[], INETADDRESSTYPEUNKNOWN; else { name[oid_index++] = INETADDRESSTYPEIPV6; - oid_copy_addr( + oid_copy_in6_addr( &name[oid_index], - (struct in_addr *)&attr - ->mp_nexthop_global, - sizeof(struct in6_addr)); + &attr->mp_nexthop_global); oid_index += sizeof(struct in6_addr); } } diff --git a/bgpd/bgp_nb.c b/bgpd/bgp_nb.c index f65a4be677..2547439499 100644 --- a/bgpd/bgp_nb.c +++ b/bgpd/bgp_nb.c @@ -7406,6 +7406,76 @@ const struct frr_yang_module_info frr_bgp_info = { } }, { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/rmap-import", + .cbs = { + .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_modify, + .destroy = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/rmap-export", + .cbs = { + .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_modify, + .destroy = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/plist-import", + .cbs = { + .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_modify, + .destroy = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/plist-export", + .cbs = { + .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_modify, + .destroy = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/access-list-import", + .cbs = { + .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_modify, + .destroy = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/access-list-export", + .cbs = { + .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_modify, + .destroy = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/as-path-filter-list-import", + .cbs = { + .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_modify, + .destroy = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/as-path-filter-list-export", + .cbs = { + .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_modify, + .destroy = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/unsuppress-map-import", + .cbs = { + .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_modify, + .destroy = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/unsuppress-map-export", + .cbs = { + .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_modify, + .destroy = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_destroy, + } + }, + { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-flowspec/route-reflector/route-reflector-client", .cbs = { .modify = bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_ipv4_flowspec_route_reflector_route_reflector_client_modify, @@ -10004,6 +10074,76 @@ const struct frr_yang_module_info frr_bgp_info = { } }, { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/rmap-import", + .cbs = { + .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_modify, + .destroy = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/rmap-export", + .cbs = { + .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_modify, + .destroy = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/plist-import", + .cbs = { + .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_modify, + .destroy = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/plist-export", + .cbs = { + .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_modify, + .destroy = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/access-list-import", + .cbs = { + .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_modify, + .destroy = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/access-list-export", + .cbs = { + .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_modify, + .destroy = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/as-path-filter-list-import", + .cbs = { + .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_modify, + .destroy = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/as-path-filter-list-export", + .cbs = { + .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_modify, + .destroy = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/unsuppress-map-import", + .cbs = { + .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_modify, + .destroy = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_destroy, + } + }, + { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/unsuppress-map-export", + .cbs = { + .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_modify, + .destroy = bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_destroy, + } + }, + { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-flowspec/route-reflector/route-reflector-client", .cbs = { .modify = bgp_peer_groups_peer_group_afi_safis_afi_safi_ipv4_flowspec_route_reflector_route_reflector_client_modify, diff --git a/bgpd/bgp_nb.h b/bgpd/bgp_nb.h index eb7725d3dd..57f379b6cc 100644 --- a/bgpd/bgp_nb.h +++ b/bgpd/bgp_nb.h @@ -3325,6 +3325,48 @@ int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_route_server struct nb_cb_modify_args *args); int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_soft_reconfiguration_modify( struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_modify( + struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_destroy( + struct nb_cb_destroy_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_modify( + struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_destroy( + struct nb_cb_destroy_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_modify( + struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_destroy( + struct nb_cb_destroy_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_modify( + struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_destroy( + struct nb_cb_destroy_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_destroy( + struct nb_cb_destroy_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_modify( + struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_destroy( + struct nb_cb_destroy_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_modify( + struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_destroy( + struct nb_cb_destroy_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_modify( + struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_destroy( + struct nb_cb_destroy_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_modify( + struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_destroy( + struct nb_cb_destroy_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_modify( + struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_destroy( + struct nb_cb_destroy_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_modify( + struct nb_cb_modify_args *args); +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_destroy( + struct nb_cb_destroy_args *args); int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_ipv4_flowspec_route_reflector_route_reflector_client_modify( struct nb_cb_modify_args *args); int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_ipv4_flowspec_route_server_route_server_client_modify( @@ -4529,6 +4571,46 @@ int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_route_server_route_ struct nb_cb_modify_args *args); int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_soft_reconfiguration_modify( struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_modify( + struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_destroy( + struct nb_cb_destroy_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_modify( + struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_destroy( + struct nb_cb_destroy_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_modify( + struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_destroy( + struct nb_cb_destroy_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_modify( + struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_destroy( + struct nb_cb_destroy_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_modify( + struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_destroy( + struct nb_cb_destroy_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_modify( + struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_destroy( + struct nb_cb_destroy_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_modify( + struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_destroy( + struct nb_cb_destroy_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_modify( + struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_destroy( + struct nb_cb_destroy_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_modify( + struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_destroy( + struct nb_cb_destroy_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_modify( + struct nb_cb_modify_args *args); +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_destroy( + struct nb_cb_destroy_args *args); int bgp_peer_groups_peer_group_afi_safis_afi_safi_ipv4_flowspec_route_reflector_route_reflector_client_modify( struct nb_cb_modify_args *args); int bgp_peer_groups_peer_group_afi_safis_afi_safi_ipv4_flowspec_route_server_route_server_client_modify( diff --git a/bgpd/bgp_nb_config.c b/bgpd/bgp_nb_config.c index 75be3c79fe..5a88bd08d9 100644 --- a/bgpd/bgp_nb_config.c +++ b/bgpd/bgp_nb_config.c @@ -158,8 +158,26 @@ int bgp_router_destroy(struct nb_cb_destroy_args *args) struct bgp *tmp_bgp; for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) { - if (tmp_bgp->inst_type - == BGP_INSTANCE_TYPE_VRF) { + if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) + continue; + if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_VRF_EXPORT) || + (bgp == bgp_get_evpn() && + (CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST) || + CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], + BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))) || + (tmp_bgp->vnihash && hashcount(tmp_bgp->vnihash))) { snprintf( args->errmsg, args->errmsg_len, "Cannot delete default BGP instance. Dependent VRF instances exist\n"); @@ -3146,8 +3164,7 @@ int bgp_neighbors_neighbor_neighbor_remote_as_remote_as_type_modify( return NB_OK; str2sockunion(peer_str, &su); - ret = peer_remote_as(bgp, &su, NULL, &as, as_type, AFI_IP, - SAFI_UNICAST); + ret = peer_remote_as(bgp, &su, NULL, &as, as_type); if (bgp_nb_errmsg_return(args->errmsg, args->errmsg_len, ret) < 0) return NB_ERR_INCONSISTENCY; @@ -3202,8 +3219,7 @@ int bgp_neighbors_neighbor_neighbor_remote_as_remote_as_modify( as = yang_dnode_get_uint32(args->dnode, NULL); str2sockunion(peer_str, &su); - ret = peer_remote_as(bgp, &su, NULL, &as, as_type, AFI_IP, - SAFI_UNICAST); + ret = peer_remote_as(bgp, &su, NULL, &as, as_type); if (bgp_nb_errmsg_return(args->errmsg, args->errmsg_len, ret) < 0) return NB_ERR_INCONSISTENCY; @@ -4370,8 +4386,7 @@ int bgp_neighbors_unnumbered_neighbor_create(struct nb_cb_create_args *args) "./neighbor-remote-as/remote-as"); } - if (peer_conf_interface_create(bgp, peer_str, AFI_IP, - SAFI_UNICAST, v6_only, + if (peer_conf_interface_create(bgp, peer_str, v6_only, peer_grp_str, as_type, as, args->errmsg, args->errmsg_len)) return NB_ERR_INCONSISTENCY; @@ -4440,9 +4455,9 @@ int bgp_neighbors_unnumbered_neighbor_v6only_modify( v6_only = yang_dnode_get_bool(args->dnode, NULL); - if (peer_conf_interface_create( - bgp, peer_str, AFI_IP, SAFI_UNICAST, v6_only, NULL, - AS_UNSPECIFIED, 0, args->errmsg, args->errmsg_len)) + if (peer_conf_interface_create(bgp, peer_str, v6_only, NULL, + AS_UNSPECIFIED, 0, args->errmsg, + args->errmsg_len)) return NB_ERR_INCONSISTENCY; break; @@ -5174,8 +5189,6 @@ void bgp_neighbors_unnumbered_neighbor_neighbor_remote_as_apply_finish( int ret; as_t as = 0; struct peer *peer = NULL; - afi_t afi = AFI_IP; - safi_t safi = SAFI_UNICAST; bgp = nb_running_get_entry(args->dnode, NULL, true); peer_str = yang_dnode_get_string(args->dnode, "../interface"); @@ -5185,7 +5198,7 @@ void bgp_neighbors_unnumbered_neighbor_neighbor_remote_as_apply_finish( peer = peer_lookup_by_conf_if(bgp, peer_str); - ret = peer_remote_as(bgp, NULL, peer_str, &as, as_type, afi, safi); + ret = peer_remote_as(bgp, NULL, peer_str, &as, as_type); if (ret < 0 && !peer) { snprintf(args->errmsg, args->errmsg_len, @@ -21835,8 +21848,7 @@ int bgp_neighbors_neighbor_afi_safis_afi_safi_ipv6_labeled_unicast_filter_config case NB_EV_PREPARE: case NB_EV_ABORT: case NB_EV_APPLY: - /* TODO: implement me. */ - break; + return bgp_neighbor_afi_safi_plist_modify(args, FILTER_IN); } return NB_OK; @@ -21850,8 +21862,7 @@ int bgp_neighbors_neighbor_afi_safis_afi_safi_ipv6_labeled_unicast_filter_config case NB_EV_PREPARE: case NB_EV_ABORT: case NB_EV_APPLY: - /* TODO: implement me. */ - break; + return bgp_neighbor_afi_safi_plist_destroy(args, FILTER_IN); } return NB_OK; @@ -22979,8 +22990,7 @@ int bgp_neighbors_neighbor_afi_safis_afi_safi_l3vpn_ipv4_unicast_filter_config_p case NB_EV_ABORT: return NB_OK; case NB_EV_APPLY: - bgp_neighbor_afi_safi_plist_modify(args, FILTER_IN); - break; + return bgp_neighbor_afi_safi_plist_modify(args, FILTER_IN); } return NB_OK; @@ -35526,6 +35536,350 @@ int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_soft_reconfi /* * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/rmap-import + */ +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return bgp_unnumbered_neighbor_afi_safi_rmap_modify(args, + RMAP_IN); + } + + return NB_OK; +} + +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return bgp_unnumbered_neighbor_afi_safi_rmap_destroy(args, + RMAP_IN); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/rmap-export + */ +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return bgp_unnumbered_neighbor_afi_safi_rmap_modify(args, + RMAP_OUT); + } + + return NB_OK; +} + +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return bgp_unnumbered_neighbor_afi_safi_rmap_destroy(args, + RMAP_OUT); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/plist-import + */ +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/plist-export + */ +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/access-list-import + */ +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/access-list-export + */ +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/as-path-filter-list-import + */ +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/as-path-filter-list-export + */ +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/unsuppress-map-import + */ +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn/filter-config/unsuppress-map-export + */ +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-flowspec/route-reflector/route-reflector-client */ int bgp_neighbors_unnumbered_neighbor_afi_safis_afi_safi_ipv4_flowspec_route_reflector_route_reflector_client_modify( @@ -47019,6 +47373,346 @@ int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_soft_reconfiguratio /* * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/rmap-import + */ +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return bgp_peer_group_afi_safi_rmap_modify(args, RMAP_IN); + } + + return NB_OK; +} + +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_import_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return bgp_peer_group_afi_safi_rmap_destroy(args, RMAP_IN); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/rmap-export + */ +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return bgp_peer_group_afi_safi_rmap_modify(args, RMAP_OUT); + } + + return NB_OK; +} + +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_rmap_export_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return bgp_peer_group_afi_safi_rmap_destroy(args, RMAP_OUT); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/plist-import + */ +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_import_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/plist-export + */ +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_plist_export_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/access-list-import + */ +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_import_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/access-list-export + */ +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_access_list_export_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/as-path-filter-list-import + */ +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_import_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/as-path-filter-list-export + */ +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_as_path_filter_list_export_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/unsuppress-map-import + */ +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_import_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn/filter-config/unsuppress-map-export + */ +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +int bgp_peer_groups_peer_group_afi_safis_afi_safi_l2vpn_evpn_filter_config_unsuppress_map_export_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} + +/* + * XPath: * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-bgp:bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-flowspec/route-reflector/route-reflector-client */ int bgp_peer_groups_peer_group_afi_safis_afi_safi_ipv4_flowspec_route_reflector_route_reflector_client_modify( @@ -47557,7 +48251,7 @@ int bgp_peer_groups_peer_group_afi_safis_afi_safi_ipv6_flowspec_filter_config_rm case NB_EV_ABORT: break; case NB_EV_APPLY: - return bgp_peer_group_afi_safi_plist_destroy(args, FILTER_IN); + return bgp_peer_group_afi_safi_rmap_destroy(args, RMAP_OUT); } return NB_OK; @@ -47591,7 +48285,7 @@ int bgp_peer_groups_peer_group_afi_safis_afi_safi_ipv6_flowspec_filter_config_pl case NB_EV_ABORT: break; case NB_EV_APPLY: - return bgp_peer_group_afi_safi_rmap_destroy(args, RMAP_OUT); + return bgp_peer_group_afi_safi_plist_destroy(args, FILTER_IN); } return NB_OK; diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 03ff27c7ca..4821ce8ddb 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -544,7 +544,7 @@ static int bgp_accept(struct thread *thread) peer1->host); peer = peer_create(&su, peer1->conf_if, peer1->bgp, peer1->local_as, - peer1->as, peer1->as_type, 0, 0, NULL); + peer1->as, peer1->as_type, NULL); hash_release(peer->bgp->peerhash, peer); hash_get(peer->bgp->peerhash, peer, hash_alloc_intern); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 0fea198cd2..9c8d7878c5 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -194,6 +194,16 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, bnc->srte_color, bnc->bgp->name_pretty, peer); } + } else { + if (BGP_DEBUG(nht, NHT)) { + char buf[PREFIX2STR_BUFFER]; + + zlog_debug( + "Found existing bnc %s(%s) flags 0x%x ifindex %d #paths %d peer %p", + bnc_str(bnc, buf, PREFIX2STR_BUFFER), + bnc->bgp->name_pretty, bnc->flags, bnc->ifindex, + bnc->path_count, bnc->nht_info); + } } if (is_bgp_static_route) { @@ -237,6 +247,11 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); } + if (peer && (bnc->ifindex != ifindex)) { + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); + bnc->ifindex = ifindex; + } if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW) { SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); SET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); @@ -506,6 +521,11 @@ static int bgp_nht_ifp_initial(struct thread *thread) if (!ifp) return 0; + if (BGP_DEBUG(nht, NHT)) + zlog_debug( + "Handle NHT initial update for Intf %s(%d) status %s", + ifp->name, ifp->ifindex, if_is_up(ifp) ? "up" : "down"); + if (if_is_up(ifp)) bgp_nht_ifp_up(ifp); else diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 124a477248..f56c866de5 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -71,6 +71,7 @@ #include "bgpd/bgp_mac.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_trace.h" +#include "bgpd/bgp_rpki.h" #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -250,8 +251,8 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra) if (e->aggr_suppressors) list_delete(&e->aggr_suppressors); - if (e->es_info) - bgp_evpn_path_es_info_free(e->es_info); + if (e->mh_info) + bgp_evpn_path_mh_info_free(e->mh_info); if ((*extra)->bgp_fs_iprule) list_delete(&((*extra)->bgp_fs_iprule)); @@ -565,6 +566,8 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, int internal_as_route; int confed_as_route; int ret = 0; + int igp_metric_ret = 0; + int peer_sort_ret = -1; char new_buf[PATH_ADDPATH_STR_BUFFER]; char exist_buf[PATH_ADDPATH_STR_BUFFER]; uint32_t new_mm_seq; @@ -971,7 +974,9 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, zlog_debug( "%s: %s wins over %s due to eBGP peer > iBGP peer", pfx_buf, new_buf, exist_buf); - return 1; + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX)) + return 1; + peer_sort_ret = 1; } if (exist_sort == BGP_PEER_EBGP @@ -981,7 +986,9 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, zlog_debug( "%s: %s loses to %s due to iBGP peer < eBGP peer", pfx_buf, new_buf, exist_buf); - return 0; + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX)) + return 0; + peer_sort_ret = 0; } /* 8. IGP metric check. */ @@ -993,19 +1000,19 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, existm = exist->extra->igpmetric; if (newm < existm) { - if (debug) + if (debug && peer_sort_ret < 0) zlog_debug( "%s: %s wins over %s due to IGP metric %u < %u", pfx_buf, new_buf, exist_buf, newm, existm); - ret = 1; + igp_metric_ret = 1; } if (newm > existm) { - if (debug) + if (debug && peer_sort_ret < 0) zlog_debug( "%s: %s loses to %s due to IGP metric %u > %u", pfx_buf, new_buf, exist_buf, newm, existm); - ret = 0; + igp_metric_ret = 0; } /* 9. Same IGP metric. Compare the cluster list length as @@ -1023,21 +1030,21 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, existm = BGP_CLUSTER_LIST_LENGTH(exist->attr); if (newm < existm) { - if (debug) + if (debug && peer_sort_ret < 0) zlog_debug( "%s: %s wins over %s due to CLUSTER_LIST length %u < %u", pfx_buf, new_buf, exist_buf, newm, existm); - ret = 1; + igp_metric_ret = 1; } if (newm > existm) { - if (debug) + if (debug && peer_sort_ret < 0) zlog_debug( "%s: %s loses to %s due to CLUSTER_LIST length %u > %u", pfx_buf, new_buf, exist_buf, newm, existm); - ret = 0; + igp_metric_ret = 0; } } } @@ -1051,7 +1058,10 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, zlog_debug( "%s: %s wins over %s due to confed-external peer > confed-internal peer", pfx_buf, new_buf, exist_buf); - return 1; + if (!CHECK_FLAG(bgp->flags, + BGP_FLAG_PEERTYPE_MULTIPATH_RELAX)) + return 1; + peer_sort_ret = 1; } if (exist_sort == BGP_PEER_CONFED @@ -1061,7 +1071,10 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, zlog_debug( "%s: %s loses to %s due to confed-internal peer < confed-external peer", pfx_buf, new_buf, exist_buf); - return 0; + if (!CHECK_FLAG(bgp->flags, + BGP_FLAG_PEERTYPE_MULTIPATH_RELAX)) + return 0; + peer_sort_ret = 0; } } @@ -1122,20 +1135,40 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new, * TODO: If unequal cost ibgp multipath is enabled we can * mark the paths as equal here instead of returning */ - if (debug) { - if (ret == 1) - zlog_debug( - "%s: %s wins over %s after IGP metric comparison", - pfx_buf, new_buf, exist_buf); - else - zlog_debug( - "%s: %s loses to %s after IGP metric comparison", - pfx_buf, new_buf, exist_buf); + + /* Prior to the addition of BGP_FLAG_PEERTYPE_MULTIPATH_RELAX, + * if either step 7 or 10 (peer type checks) yielded a winner, + * that result was returned immediately. Returning from step 10 + * ignored the return value computed in steps 8 and 9 (IGP + * metric checks). In order to preserve that behavior, if + * peer_sort_ret is set, return that rather than igp_metric_ret. + */ + ret = peer_sort_ret; + if (peer_sort_ret < 0) { + ret = igp_metric_ret; + if (debug) { + if (ret == 1) + zlog_debug( + "%s: %s wins over %s after IGP metric comparison", + pfx_buf, new_buf, exist_buf); + else + zlog_debug( + "%s: %s loses to %s after IGP metric comparison", + pfx_buf, new_buf, exist_buf); + } + *reason = bgp_path_selection_igp_metric; } - *reason = bgp_path_selection_igp_metric; return ret; } + /* + * At this point, the decision whether to set *paths_eq = 1 has been + * completed. If we deferred returning because of bestpath peer-type + * relax configuration, return now. + */ + if (peer_sort_ret >= 0) + return peer_sort_ret; + /* 12. If both paths are external, prefer the path that was received first (the oldest one). This step minimizes route-flap, since a newer path won't displace an older one, even if it was the @@ -7551,18 +7584,20 @@ static const char *bgp_origin2str(uint8_t origin) return "n/a"; } -static const char *bgp_rpki_validation2str(int v_state) +static const char *bgp_rpki_validation2str(enum rpki_states v_state) { switch (v_state) { - case 1: + case RPKI_NOT_BEING_USED: + return "not used"; + case RPKI_VALID: return "valid"; - case 2: + case RPKI_NOTFOUND: return "not found"; - case 3: + case RPKI_INVALID: return "invalid"; - default: - break; } + + assert(!"We should never get here this is a dev escape"); return "ERROR"; } @@ -8188,8 +8223,8 @@ void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type, } /* Static function to display route. */ -static void route_vty_out_route(const struct prefix *p, struct vty *vty, - json_object *json, bool wide) +static void route_vty_out_route(struct bgp_dest *dest, const struct prefix *p, + struct vty *vty, json_object *json, bool wide) { int len = 0; char buf[BUFSIZ]; @@ -8206,6 +8241,7 @@ static void route_vty_out_route(const struct prefix *p, struct vty *vty, json_object_int_add(json, "prefixLen", p->prefixlen); prefix2str(p, buf2, PREFIX_STRLEN); json_object_string_add(json, "network", buf2); + json_object_int_add(json, "version", dest->version); } } else if (p->family == AF_ETHERNET) { len = vty_out(vty, "%pFX", p); @@ -8230,6 +8266,7 @@ static void route_vty_out_route(const struct prefix *p, struct vty *vty, json_object_int_add(json, "prefixLen", p->prefixlen); prefix2str(p, buf2, PREFIX_STRLEN); json_object_string_add(json, "network", buf2); + json_object_int_add(json, "version", dest->version); } } @@ -8305,8 +8342,11 @@ bgp_path_selection_reason2str(enum bgp_path_selection_reason reason) /* Print the short form route status for a bgp_path_info */ static void route_vty_short_status_out(struct vty *vty, struct bgp_path_info *path, + const struct prefix *p, json_object *json_path) { + enum rpki_states rpki_state = RPKI_NOT_BEING_USED; + if (json_path) { /* Route status display. */ @@ -8352,6 +8392,17 @@ static void route_vty_short_status_out(struct vty *vty, return; } + /* RPKI validation state */ + rpki_state = + hook_call(bgp_rpki_prefix_status, path->peer, path->attr, p); + + if (rpki_state == RPKI_VALID) + vty_out(vty, "V"); + else if (rpki_state == RPKI_INVALID) + vty_out(vty, "I"); + else if (rpki_state == RPKI_NOTFOUND) + vty_out(vty, "N"); + /* Route status display. */ if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED)) vty_out(vty, "R"); @@ -8420,16 +8471,16 @@ void route_vty_out(struct vty *vty, const struct prefix *p, json_path = json_object_new_object(); /* short status lead text */ - route_vty_short_status_out(vty, path, json_path); + route_vty_short_status_out(vty, path, p, json_path); if (!json_paths) { /* print prefix and mask */ if (!display) - route_vty_out_route(p, vty, json_path, wide); + route_vty_out_route(path->net, p, vty, json_path, wide); else vty_out(vty, "%*s", (wide ? 45 : 17), " "); } else { - route_vty_out_route(p, vty, json_path, wide); + route_vty_out_route(path->net, p, vty, json_path, wide); } /* @@ -8856,15 +8907,17 @@ void route_vty_out(struct vty *vty, const struct prefix *p, if (safi == SAFI_EVPN) { struct bgp_path_es_info *path_es_info = NULL; - if (path->extra) - path_es_info = path->extra->es_info; - if (bgp_evpn_is_esi_valid(&attr->esi)) { /* XXX - add these params to the json out */ vty_out(vty, "%*s", 20, " "); vty_out(vty, "ESI:%s", esi_to_str(&attr->esi, esi_buf, sizeof(esi_buf))); + + if (path->extra && path->extra->mh_info) + path_es_info = + path->extra->mh_info->es_info; + if (path_es_info && path_es_info->es) vty_out(vty, " VNI: %u", path_es_info->vni); @@ -8887,9 +8940,9 @@ void route_vty_out(struct vty *vty, const struct prefix *p, } /* called from terminal list command */ -void route_vty_out_tmp(struct vty *vty, const struct prefix *p, - struct attr *attr, safi_t safi, bool use_json, - json_object *json_ar, bool wide) +void route_vty_out_tmp(struct vty *vty, struct bgp_dest *dest, + const struct prefix *p, struct attr *attr, safi_t safi, + bool use_json, json_object *json_ar, bool wide) { json_object *json_status = NULL; json_object *json_net = NULL; @@ -8921,7 +8974,7 @@ void route_vty_out_tmp(struct vty *vty, const struct prefix *p, json_object_string_add(json_net, "network", buff); } } else - route_vty_out_route(p, vty, NULL, wide); + route_vty_out_route(dest, p, vty, NULL, wide); /* Print attribute */ if (attr) { @@ -9064,12 +9117,12 @@ void route_vty_out_tag(struct vty *vty, const struct prefix *p, json_out = json_object_new_object(); /* short status lead text */ - route_vty_short_status_out(vty, path, json_out); + route_vty_short_status_out(vty, path, p, json_out); /* print prefix and mask */ if (json == NULL) { if (!display) - route_vty_out_route(p, vty, NULL, false); + route_vty_out_route(path->net, p, vty, NULL, false); else vty_out(vty, "%*s", 17, " "); } @@ -9167,11 +9220,11 @@ void route_vty_out_overlay(struct vty *vty, const struct prefix *p, } /* short status lead text */ - route_vty_short_status_out(vty, path, json_path); + route_vty_short_status_out(vty, path, p, json_path); /* print prefix and mask */ if (!display) - route_vty_out_route(p, vty, json_path, false); + route_vty_out_route(path->net, p, vty, json_path, false); else vty_out(vty, "%*s", 17, " "); @@ -9271,12 +9324,12 @@ static void damp_route_vty_out(struct vty *vty, const struct prefix *p, char timebuf[BGP_UPTIME_LEN]; /* short status lead text */ - route_vty_short_status_out(vty, path, json); + route_vty_short_status_out(vty, path, p, json); /* print prefix and mask */ if (!use_json) { if (!display) - route_vty_out_route(p, vty, NULL, false); + route_vty_out_route(path->net, p, vty, NULL, false); else vty_out(vty, "%*s", 17, " "); } @@ -9342,12 +9395,12 @@ static void flap_route_vty_out(struct vty *vty, const struct prefix *p, bdi = path->extra->damp_info; /* short status lead text */ - route_vty_short_status_out(vty, path, json); + route_vty_short_status_out(vty, path, p, json); /* print prefix and mask */ if (!use_json) { if (!display) - route_vty_out_route(p, vty, NULL, false); + route_vty_out_route(path->net, p, vty, NULL, false); else vty_out(vty, "%*s", 17, " "); } @@ -9547,9 +9600,10 @@ 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, struct bgp_path_info *path, - afi_t afi, safi_t safi, json_object *json_paths) +void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, + struct bgp_path_info *path, afi_t afi, safi_t safi, + enum rpki_states rpki_curr_state, + json_object *json_paths) { char buf[INET6_ADDRSTRLEN]; char buf1[BUFSIZ]; @@ -9580,7 +9634,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, int i; char *nexthop_hostname = bgp_nexthop_hostname(path->peer, path->nexthop); - int rpki_validation_state = 0; if (json_paths) { json_path = json_object_new_object(); @@ -9626,12 +9679,20 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, buf1, sizeof(buf1)); if (is_pi_family_evpn(parent_ri)) { vty_out(vty, - " Imported from %s:%pFX, VNI %s\n", + " Imported from %s:%pFX, VNI %s", buf1, (struct prefix_evpn *) bgp_dest_get_prefix( dest), tag_buf); + if (attr->es_flags & ATTR_ES_L3_NHG) + vty_out(vty, ", L3NHG %s", + (attr->es_flags + & ATTR_ES_L3_NHG_ACTIVE) + ? "active" + : "inactive"); + vty_out(vty, "\n"); + } else vty_out(vty, " Imported from %s:%pFX\n", @@ -10076,6 +10137,9 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, vty_out(vty, ", valid"); } + if (json_paths) + json_object_int_add(json_path, "version", bn->version); + if (path->peer != bgp->peer_self) { if (path->peer->as == path->peer->local_as) { if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) { @@ -10179,18 +10243,14 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, } } - const struct prefix *p = bgp_dest_get_prefix(bn); - if (p->family == AF_INET || p->family == AF_INET6) - rpki_validation_state = hook_call(bgp_rpki_prefix_status, - path->peer, path->attr, p); - if (rpki_validation_state) { + if (rpki_curr_state != RPKI_NOT_BEING_USED) { if (json_paths) json_object_string_add( json_path, "rpkiValidationState", - bgp_rpki_validation2str(rpki_validation_state)); + bgp_rpki_validation2str(rpki_curr_state)); else - vty_out(vty, ", validation-state: %s", - bgp_rpki_validation2str(rpki_validation_state)); + vty_out(vty, ", rpki validation-state: %s", + bgp_rpki_validation2str(rpki_curr_state)); } if (json_bestpath) @@ -10510,7 +10570,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, struct bgp_table *table, enum bgp_show_type type, void *output_arg, char *rd, int is_last, unsigned long *output_cum, unsigned long *total_cum, - unsigned long *json_header_depth, uint8_t show_flags) + unsigned long *json_header_depth, uint8_t show_flags, + enum rpki_states rpki_target_state) { struct bgp_path_info *pi; struct bgp_dest *dest; @@ -10559,6 +10620,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, /* Start processing of routes. */ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { const struct prefix *dest_p = bgp_dest_get_prefix(dest); + enum rpki_states rpki_curr_state = RPKI_NOT_BEING_USED; pi = bgp_dest_get_bgp_path_info(dest); if (pi == NULL) @@ -10572,6 +10634,25 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, for (; pi; pi = pi->next) { total_count++; + + if (type == bgp_show_type_prefix_version) { + uint32_t version = + strtoul(output_arg, NULL, 10); + if (dest->version < version) + continue; + } + + if (type == bgp_show_type_rpki) { + if (dest_p->family == AF_INET + || dest_p->family == AF_INET6) + rpki_curr_state = hook_call( + bgp_rpki_prefix_status, + pi->peer, pi->attr, dest_p); + if (rpki_target_state != RPKI_NOT_BEING_USED + && rpki_curr_state != rpki_target_state) + continue; + } + if (type == bgp_show_type_flap_statistics || type == bgp_show_type_flap_neighbor || type == bgp_show_type_dampend_paths @@ -10736,6 +10817,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, vty_out(vty, BGP_SHOW_SCODE_HEADER); vty_out(vty, BGP_SHOW_NCODE_HEADER); vty_out(vty, BGP_SHOW_OCODE_HEADER); + vty_out(vty, BGP_SHOW_RPKI_HEADER); if (type == bgp_show_type_dampend_paths || type == bgp_show_type_damp_neighbor) vty_out(vty, BGP_SHOW_DAMP_HEADER); @@ -10763,9 +10845,17 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, flap_route_vty_out(vty, dest_p, pi, display, AFI_IP, safi, use_json, json_paths); - else - route_vty_out(vty, dest_p, pi, display, safi, - json_paths, wide); + else { + if (CHECK_FLAG(show_flags, BGP_SHOW_OPT_DETAIL)) + route_vty_out_detail( + vty, bgp, dest, pi, + family2afi(dest_p->family), + safi, RPKI_NOT_BEING_USED, + json_paths); + else + route_vty_out(vty, dest_p, pi, display, + safi, json_paths, wide); + } display++; } @@ -10881,7 +10971,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, bgp_show_table(vty, bgp, safi, itable, type, output_arg, rd, next == NULL, &output_cum, &total_cum, &json_header_depth, - show_flags); + show_flags, RPKI_NOT_BEING_USED); if (next == NULL) show_msg = false; } @@ -10899,7 +10989,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, } static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, enum bgp_show_type type, void *output_arg, - uint8_t show_flags) + uint8_t show_flags, enum rpki_states rpki_target_state) { struct bgp_table *table; unsigned long json_header_depth = 0; @@ -10934,7 +11024,8 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, safi = SAFI_UNICAST; return bgp_show_table(vty, bgp, safi, table, type, output_arg, NULL, 1, - NULL, NULL, &json_header_depth, show_flags); + NULL, NULL, &json_header_depth, show_flags, + rpki_target_state); } static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, @@ -10968,7 +11059,7 @@ static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi, : bgp->name); } bgp_show(vty, bgp, afi, safi, bgp_show_type_normal, NULL, - show_flags); + show_flags, RPKI_NOT_BEING_USED); } if (use_json) @@ -11030,16 +11121,22 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, } } else { if (!json) { - vty_out(vty, "BGP routing table entry for %s%s%pFX\n", + vty_out(vty, + "BGP routing table entry for %s%s%pFX, version %" PRIu64 + "\n", ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) ? prefix_rd2str(prd, buf1, sizeof(buf1)) : ""), - safi == SAFI_MPLS_VPN ? ":" : "", p); + safi == SAFI_MPLS_VPN ? ":" : "", p, + dest->version); - } else + } else { json_object_string_add(json, "prefix", prefix2str(p, prefix_str, sizeof(prefix_str))); + json_object_int_add(json, "version", dest->version); + + } } if (has_valid_label) { @@ -11187,15 +11284,25 @@ static void bgp_show_path_info(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) + int *display, enum rpki_states rpki_target_state) { struct bgp_path_info *pi; int header = 1; char rdbuf[RD_ADDRSTRLEN]; json_object *json_header = NULL; json_object *json_paths = NULL; + const struct prefix *p = bgp_dest_get_prefix(bgp_node); for (pi = bgp_dest_get_bgp_path_info(bgp_node); pi; pi = pi->next) { + enum rpki_states rpki_curr_state = RPKI_NOT_BEING_USED; + + if (p->family == AF_INET || p->family == AF_INET6) + rpki_curr_state = hook_call(bgp_rpki_prefix_status, + pi->peer, pi->attr, p); + + if (rpki_target_state != RPKI_NOT_BEING_USED + && rpki_curr_state != rpki_target_state) + continue; if (json && !json_paths) { /* Instantiate json_paths only if path is valid */ @@ -11221,9 +11328,8 @@ static void bgp_show_path_info(struct prefix_rd *pfx_rd, || (pathtype == BGP_PATH_SHOW_MULTIPATH && (CHECK_FLAG(pi->flags, BGP_PATH_MULTIPATH) || CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)))) - route_vty_out_detail(vty, bgp, bgp_node, - pi, AFI_IP, safi, - json_paths); + route_vty_out_detail(vty, bgp, bgp_node, pi, AFI_IP, + safi, rpki_curr_state, json_paths); } if (json && json_paths) { @@ -11238,6 +11344,7 @@ static void bgp_show_path_info(struct prefix_rd *pfx_rd, 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) { @@ -11285,7 +11392,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi, json, pathtype, - &display); + &display, rpki_target_state); bgp_dest_unlock_node(rm); } @@ -11344,7 +11451,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty, bgp, afi, safi, json, pathtype, - &display); + &display, rpki_target_state); bgp_dest_unlock_node(rm); } @@ -11371,7 +11478,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, || dest_p->prefixlen == match.prefixlen) { bgp_show_path_info(NULL, dest, vty, bgp, afi, safi, json, pathtype, - &display); + &display, rpki_target_state); } bgp_dest_unlock_node(dest); @@ -11397,7 +11504,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp, 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, - bool use_json) + enum rpki_states rpki_target_state, bool use_json) { if (!bgp) { bgp = bgp_get_default(); @@ -11415,8 +11522,8 @@ static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str, safi = SAFI_UNICAST; return bgp_show_route_in_table(vty, bgp, bgp->rib[afi][safi], ip_str, - afi, safi, prd, prefix_check, pathtype, - use_json); + afi, safi, rpki_target_state, prd, + prefix_check, pathtype, use_json); } static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc, @@ -11458,9 +11565,9 @@ static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc, } ret = bgp_show(vty, bgp, afi, safi, - (exact ? bgp_show_type_lcommunity_exact - : bgp_show_type_lcommunity), - lcom, show_flags); + (exact ? bgp_show_type_lcommunity_exact + : bgp_show_type_lcommunity), + lcom, show_flags, RPKI_NOT_BEING_USED); lcommunity_free(&lcom); return ret; @@ -11488,7 +11595,7 @@ static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp, return bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_lcommunity_list_exact : bgp_show_type_lcommunity_list), - list, show_flags); + list, show_flags, RPKI_NOT_BEING_USED); } DEFUN (show_ip_bgp_large_community_list, @@ -11570,7 +11677,8 @@ DEFUN (show_ip_bgp_large_community, exact_match, afi, safi, uj); } else return bgp_show(vty, bgp, afi, safi, - bgp_show_type_lcommunity_all, NULL, show_flags); + bgp_show_type_lcommunity_all, NULL, show_flags, + RPKI_NOT_BEING_USED); } static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi, @@ -11818,7 +11926,9 @@ DEFPY (show_ip_bgp_json, |accept-own|accept-own-nexthop|route-filter-v6\ |route-filter-v4|route-filter-translated-v6\ |route-filter-translated-v4] [exact-match]\ - ] [json$uj | wide$wide]", + |rpki <invalid|valid|notfound>\ + |version (1-4294967295)\ + ] [json$uj [detail$detail] | wide$wide]", SHOW_STR IP_STR BGP_STR @@ -11847,7 +11957,14 @@ DEFPY (show_ip_bgp_json, "RT translated VPNv6 route filtering (well-known community)\n" "RT translated VPNv4 route filtering (well-known community)\n" "Exact match of the communities\n" + "RPKI route types\n" + "A valid path as determined by rpki\n" + "A invalid path as determined by rpki\n" + "A path that has no rpki data\n" + "Display prefixes with matching version numbers\n" + "Version number and above\n" JSON_STR + "Display detailed version of JSON output\n" "Increase table width for longer prefixes\n") { afi_t afi = AFI_IP6; @@ -11857,15 +11974,19 @@ DEFPY (show_ip_bgp_json, int idx = 0; int exact_match = 0; char *community = NULL; + char *prefix_version = NULL; bool first = true; uint8_t show_flags = 0; - + enum rpki_states rpki_target_state = RPKI_NOT_BEING_USED; if (uj) { argc--; SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); } + if (detail) + SET_FLAG(show_flags, BGP_SHOW_OPT_DETAIL); + /* [<ipv4|ipv6> [all]] */ if (all) { SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL); @@ -11916,15 +12037,33 @@ DEFPY (show_ip_bgp_json, sh_type = bgp_show_type_community_all; } + if (argv_find(argv, argc, "rpki", &idx)) { + sh_type = bgp_show_type_rpki; + if (argv_find(argv, argc, "valid", &idx)) + rpki_target_state = RPKI_VALID; + else if (argv_find(argv, argc, "invalid", &idx)) + rpki_target_state = RPKI_INVALID; + } + + /* Display prefixes with matching version numbers */ + if (argv_find(argv, argc, "version", &idx)) { + sh_type = bgp_show_type_prefix_version; + prefix_version = argv[idx + 1]->arg; + } + if (!all) { /* show bgp: AFI_IP6, show ip bgp: AFI_IP */ if (community) return bgp_show_community(vty, bgp, community, exact_match, afi, safi, show_flags); + else if (prefix_version) + return bgp_show(vty, bgp, afi, safi, sh_type, + prefix_version, show_flags, + rpki_target_state); else return bgp_show(vty, bgp, afi, safi, sh_type, NULL, - show_flags); + show_flags, rpki_target_state); } else { /* show <ip> bgp ipv4 all: AFI_IP, show <ip> bgp ipv6 all: * AFI_IP6 */ @@ -11959,9 +12098,15 @@ DEFPY (show_ip_bgp_json, bgp_show_community(vty, bgp, community, exact_match, afi, safi, show_flags); + else if (prefix_version) + return bgp_show(vty, bgp, afi, safi, + sh_type, prefix_version, + show_flags, + rpki_target_state); else bgp_show(vty, bgp, afi, safi, sh_type, - NULL, show_flags); + NULL, show_flags, + rpki_target_state); if (uj) vty_out(vty, "}\n"); } @@ -11990,9 +12135,15 @@ DEFPY (show_ip_bgp_json, bgp_show_community(vty, bgp, community, exact_match, afi, safi, show_flags); + else if (prefix_version) + return bgp_show(vty, bgp, afi, safi, + sh_type, prefix_version, + show_flags, + rpki_target_state); else bgp_show(vty, bgp, afi, safi, sh_type, - NULL, show_flags); + NULL, show_flags, + rpki_target_state); if (uj) vty_out(vty, "}\n"); } @@ -12005,7 +12156,7 @@ DEFPY (show_ip_bgp_json, 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>] [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> [<bestpath|multipath>] [rpki <valid|invalid|notfound>] [json]", SHOW_STR IP_STR BGP_STR @@ -12018,6 +12169,10 @@ DEFUN (show_ip_bgp_route, "IPv6 prefix\n" "Display only the bestpath\n" "Display only multipaths\n" + "Display only paths that match the specified rpki state\n" + "A valid path as determined by rpki\n" + "A invalid path as determined by rpki\n" + "A path that has no rpki data\n" JSON_STR) { int prefix_check = 0; @@ -12074,7 +12229,7 @@ DEFUN (show_ip_bgp_route, path_type = BGP_PATH_SHOW_ALL; return bgp_show_route(vty, bgp, prefix, afi, safi, NULL, prefix_check, - path_type, uj); + path_type, RPKI_NOT_BEING_USED, uj); } DEFUN (show_ip_bgp_regexp, @@ -12169,7 +12324,8 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr, return CMD_WARNING; } - rc = bgp_show(vty, bgp, afi, safi, type, regex, show_flags); + rc = bgp_show(vty, bgp, afi, safi, type, regex, show_flags, + RPKI_NOT_BEING_USED); bgp_regex_free(regex); return rc; } @@ -12188,7 +12344,8 @@ static int bgp_show_prefix_list(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, plist, show_flags); + return bgp_show(vty, bgp, afi, safi, type, plist, show_flags, + RPKI_NOT_BEING_USED); } static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp, @@ -12205,7 +12362,8 @@ static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, as_list, show_flags); + return bgp_show(vty, bgp, afi, safi, type, as_list, show_flags, + RPKI_NOT_BEING_USED); } static int bgp_show_route_map(struct vty *vty, struct bgp *bgp, @@ -12221,7 +12379,8 @@ static int bgp_show_route_map(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - return bgp_show(vty, bgp, afi, safi, type, rmap, show_flags); + return bgp_show(vty, bgp, afi, safi, type, rmap, show_flags, + RPKI_NOT_BEING_USED); } static int bgp_show_community(struct vty *vty, struct bgp *bgp, @@ -12240,7 +12399,7 @@ static int bgp_show_community(struct vty *vty, struct bgp *bgp, ret = bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_community_exact : bgp_show_type_community), - com, show_flags); + com, show_flags, RPKI_NOT_BEING_USED); community_free(&com); return ret; @@ -12262,7 +12421,7 @@ static int bgp_show_community_list(struct vty *vty, struct bgp *bgp, return bgp_show(vty, bgp, afi, safi, (exact ? bgp_show_type_community_list_exact : bgp_show_type_community_list), - list, show_flags); + list, show_flags, RPKI_NOT_BEING_USED); } static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, @@ -12281,7 +12440,8 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp, return CMD_WARNING; } - ret = bgp_show(vty, bgp, afi, safi, type, p, show_flags); + ret = bgp_show(vty, bgp, afi, safi, type, p, show_flags, + RPKI_NOT_BEING_USED); prefix_free(&p); return ret; } @@ -12969,7 +13129,8 @@ DEFUN (show_ip_bgp_vpn_all_route_prefix, } return bgp_show_route(vty, bgp, network, AFI_IP, SAFI_MPLS_VPN, NULL, 0, - BGP_PATH_SHOW_ALL, use_json(argc, argv)); + BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED, + use_json(argc, argv)); } #endif /* KEEP_OLD_VPN_COMMANDS */ @@ -13003,7 +13164,7 @@ DEFUN (show_bgp_l2vpn_evpn_route_prefix, } return bgp_show_route(vty, NULL, network, AFI_L2VPN, SAFI_EVPN, NULL, prefix_check, BGP_PATH_SHOW_ALL, - use_json(argc, argv)); + RPKI_NOT_BEING_USED, use_json(argc, argv)); } static void show_adj_route_header(struct vty *vty, struct bgp *bgp, @@ -13045,6 +13206,7 @@ static void show_adj_route_header(struct vty *vty, struct bgp *bgp, vty_out(vty, BGP_SHOW_SCODE_HEADER); vty_out(vty, BGP_SHOW_NCODE_HEADER); vty_out(vty, BGP_SHOW_OCODE_HEADER); + vty_out(vty, BGP_SHOW_RPKI_HEADER); } *header1 = 0; } @@ -13131,6 +13293,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, vty_out(vty, BGP_SHOW_SCODE_HEADER); vty_out(vty, BGP_SHOW_NCODE_HEADER); vty_out(vty, BGP_SHOW_OCODE_HEADER); + vty_out(vty, BGP_SHOW_RPKI_HEADER); vty_out(vty, "Originating default network %s\n\n", (afi == AFI_IP) ? "0.0.0.0/0" : "::/0"); @@ -13191,7 +13354,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, && (route_filtered || ret == RMAP_DENY)) (*filtered_count)++; - route_vty_out_tmp(vty, rn_p, &attr, safi, + route_vty_out_tmp(vty, dest, rn_p, &attr, safi, use_json, json_ar, wide); bgp_attr_undup(&attr, ain->attr); (*output_count)++; @@ -13233,8 +13396,8 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, } } route_vty_out_tmp( - vty, rn_p, &attr, safi, - use_json, json_ar, + vty, dest, rn_p, &attr, + safi, use_json, json_ar, wide); (*output_count)++; } else { @@ -13258,7 +13421,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) continue; - route_vty_out_tmp(vty, + route_vty_out_tmp(vty, dest, bgp_dest_get_prefix(dest), pi->attr, safi, use_json, json_ar, wide); @@ -13723,7 +13886,8 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer, if (safi == SAFI_LABELED_UNICAST) safi = SAFI_UNICAST; - return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, show_flags); + return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, show_flags, + RPKI_NOT_BEING_USED); } DEFUN (show_ip_bgp_flowspec_routes_detailed, @@ -13756,7 +13920,7 @@ DEFUN (show_ip_bgp_flowspec_routes_detailed, return CMD_WARNING; return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, - show_flags); + show_flags, RPKI_NOT_BEING_USED); } DEFUN (show_ip_bgp_neighbor_routes, @@ -13824,13 +13988,14 @@ struct bgp_distance { DEFUN (show_bgp_afi_vpn_rd_route, show_bgp_afi_vpn_rd_route_cmd, - "show bgp "BGP_AFI_CMD_STR" vpn rd ASN:NN_OR_IP-ADDRESS:NN <A.B.C.D/M|X:X::X:X/M> [json]", + "show bgp "BGP_AFI_CMD_STR" vpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> <A.B.C.D/M|X:X::X:X/M> [json]", SHOW_STR BGP_STR BGP_AFI_HELP_STR "Address Family modifier\n" "Display information for a route distinguisher\n" "Route Distinguisher\n" + "All Route Distinguishers\n" "Network in the BGP routing table to display\n" "Network in the BGP routing table to display\n" JSON_STR) @@ -13845,6 +14010,12 @@ DEFUN (show_bgp_afi_vpn_rd_route, return CMD_WARNING; } + 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)); + ret = str2prefix_rd(argv[5]->arg, &prd); if (!ret) { vty_out(vty, "%% Malformed Route Distinguisher\n"); @@ -13852,7 +14023,8 @@ DEFUN (show_bgp_afi_vpn_rd_route, } return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, &prd, - 0, BGP_PATH_SHOW_ALL, use_json(argc, argv)); + 0, BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED, + use_json(argc, argv)); } static struct bgp_distance *bgp_distance_new(void) diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 1dec99f085..e58213a688 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -28,6 +28,7 @@ #include "nexthop.h" #include "bgp_table.h" #include "bgp_addpath_types.h" +#include "bgp_rpki.h" struct bgp_nexthop_cache; struct bgp_route_evpn; @@ -56,6 +57,8 @@ enum bgp_show_type { bgp_show_type_dampend_paths, bgp_show_type_damp_neighbor, bgp_show_type_detail, + bgp_show_type_rpki, + bgp_show_type_prefix_version, }; enum bgp_show_adj_route_type { @@ -70,8 +73,11 @@ enum bgp_show_adj_route_type { "Status codes: s suppressed, d damped, " \ "h history, * valid, > best, = multipath,\n" \ " i internal, r RIB-failure, S Stale, R Removed\n" -#define BGP_SHOW_OCODE_HEADER "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n" +#define BGP_SHOW_OCODE_HEADER \ + "Origin codes: i - IGP, e - EGP, ? - incomplete\n" #define BGP_SHOW_NCODE_HEADER "Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self\n" +#define BGP_SHOW_RPKI_HEADER \ + "RPKI validation codes: V valid, I invalid, N Not found\n\n" #define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n" #define BGP_SHOW_HEADER_WIDE " Network Next Hop Metric LocPrf Weight Path\n" @@ -102,7 +108,9 @@ enum bgp_show_adj_route_type { #define BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE -15 #define BGP_NLRI_PARSE_ERROR -32 -/* MAC-IP/type-2 path_info in the VNI routing table is linked to the +/* 1. local MAC-IP/type-2 paths in the VNI routing table are linked to the + * destination ES + * 2. remote MAC-IP paths in the global routing table are linked to the * destination ES */ struct bgp_path_es_info { @@ -113,6 +121,27 @@ struct bgp_path_es_info { struct bgp_evpn_es *es; /* memory used for linking the path to the destination ES */ struct listnode es_listnode; + uint8_t flags; +/* Path is linked to the VNI list */ +#define BGP_EVPN_PATH_ES_INFO_VNI_LIST (1 << 0) +/* Path is linked to the global list */ +#define BGP_EVPN_PATH_ES_INFO_GLOBAL_LIST (1 << 1) +}; + +/* IP paths imported into the VRF from an EVPN route source + * are linked to the nexthop/VTEP IP + */ +struct bgp_path_evpn_nh_info { + /* back pointer to the route */ + struct bgp_path_info *pi; + struct bgp_evpn_nh *nh; + /* memory used for linking the path to the nexthop */ + struct listnode nh_listnode; +}; + +struct bgp_path_mh_info { + struct bgp_path_es_info *es_info; + struct bgp_path_evpn_nh_info *nh_info; }; /* Ancillary information to struct bgp_path_info, @@ -202,7 +231,7 @@ struct bgp_path_info_extra { /* presence of FS pbr iprule based entry */ struct list *bgp_fs_iprule; /* Destination Ethernet Segment links for EVPN MH */ - struct bgp_path_es_info *es_info; + struct bgp_path_mh_info *mh_info; }; struct bgp_path_info { @@ -560,6 +589,7 @@ DECLARE_HOOK(bgp_process, #define BGP_SHOW_OPT_AFI_IP6 (1 << 4) #define BGP_SHOW_OPT_ESTABLISHED (1 << 5) #define BGP_SHOW_OPT_FAILED (1 << 6) +#define BGP_SHOW_OPT_DETAIL (1 << 7) /* Prototypes. */ extern void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi, @@ -689,9 +719,10 @@ extern void route_vty_out(struct vty *vty, const struct prefix *p, extern void route_vty_out_tag(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, safi_t safi, json_object *json); -extern void route_vty_out_tmp(struct vty *vty, const struct prefix *p, - struct attr *attr, safi_t safi, bool use_json, - json_object *json_ar, bool wide); +extern void route_vty_out_tmp(struct vty *vty, struct bgp_dest *dest, + const struct prefix *p, struct attr *attr, + safi_t safi, bool use_json, json_object *json_ar, + bool wide); extern void route_vty_out_overlay(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, json_object *json); @@ -740,7 +771,8 @@ 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, struct bgp_path_info *path, afi_t afi, - safi_t safi, json_object *json_paths); + safi_t safi, enum rpki_states, + json_object *json_paths); extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, struct bgp_table *table, struct prefix_rd *prd, enum bgp_show_type type, void *output_arg, diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index b7f3289ffc..7692bb7ffe 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -40,6 +40,7 @@ #include "queue.h" #include "frrstr.h" #include "network.h" +#include "lib/northbound_cli.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" @@ -3429,81 +3430,6 @@ static const struct route_map_rule_cmd route_set_originator_id_cmd = { route_set_originator_id_free, }; -/* Add bgp route map rule. */ -static int bgp_route_match_add(struct vty *vty, const char *command, - const char *arg, route_map_event_t type) -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - int retval = CMD_SUCCESS; - enum rmap_compile_rets ret; - - ret = route_map_add_match(index, command, arg, type); - switch (ret) { - case RMAP_RULE_MISSING: - vty_out(vty, "%% BGP Can't find rule.\n"); - retval = CMD_WARNING_CONFIG_FAILED; - break; - case RMAP_COMPILE_ERROR: - vty_out(vty, "%% BGP Argument is malformed.\n"); - retval = CMD_WARNING_CONFIG_FAILED; - break; - case RMAP_COMPILE_SUCCESS: - /* - * Intentionally doing nothing here. - */ - break; - } - - return retval; -} - -/* Delete bgp route map rule. */ -static int bgp_route_match_delete(struct vty *vty, const char *command, - const char *arg, route_map_event_t type) -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - enum rmap_compile_rets ret; - int retval = CMD_SUCCESS; - char *dep_name = NULL; - const char *tmpstr; - char *rmap_name = NULL; - - if (type != RMAP_EVENT_MATCH_DELETED) { - /* ignore the mundane, the types without any dependency */ - if (arg == NULL) { - if ((tmpstr = route_map_get_match_arg(index, command)) - != NULL) - dep_name = - XSTRDUP(MTYPE_ROUTE_MAP_RULE, tmpstr); - } else { - dep_name = XSTRDUP(MTYPE_ROUTE_MAP_RULE, arg); - } - rmap_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, index->map->name); - } - - ret = route_map_delete_match(index, command, dep_name, type); - switch (ret) { - case RMAP_RULE_MISSING: - vty_out(vty, "%% BGP Can't find rule.\n"); - retval = CMD_WARNING_CONFIG_FAILED; - break; - case RMAP_COMPILE_ERROR: - vty_out(vty, "%% BGP Argument is malformed.\n"); - retval = CMD_WARNING_CONFIG_FAILED; - break; - case RMAP_COMPILE_SUCCESS: - /* - * Nothing to do here - */ - break; - } - - XFREE(MTYPE_ROUTE_MAP_RULE, dep_name); - XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); - - return retval; -} - /* * This is the workhorse routine for processing in/out routemap * modifications. @@ -3914,29 +3840,40 @@ static void bgp_route_map_event(const char *rmap_name) route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_ADDED); } -DEFUN (match_mac_address, - match_mac_address_cmd, - "match mac address WORD", - MATCH_STR - "mac address\n" - "Match address of route\n" - "MAC Access-list name\n") +DEFUN_YANG (match_mac_address, + match_mac_address_cmd, + "match mac address WORD", + MATCH_STR + "mac address\n" + "Match address of route\n" + "MAC Access-list name\n") { - return bgp_route_match_add(vty, "mac address", argv[3]->arg, - RMAP_EVENT_FILTER_ADDED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:mac-address-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[3]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_mac_address, - no_match_mac_address_cmd, - "no match mac address WORD", - NO_STR - MATCH_STR - "mac\n" - "Match address of route\n" - "MAC acess-list name\n") +DEFUN_YANG (no_match_mac_address, + no_match_mac_address_cmd, + "no match mac address WORD", + NO_STR + MATCH_STR + "mac\n" + "Match address of route\n" + "MAC acess-list name\n") { - return bgp_route_match_delete(vty, "mac address", argv[4]->arg, - RMAP_EVENT_FILTER_DELETED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:mac-address-list']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } /* @@ -3963,241 +3900,377 @@ static const char *parse_evpn_rt_type(const char *num_rt_type) return num_rt_type; } -DEFUN (match_evpn_route_type, - match_evpn_route_type_cmd, - "match evpn route-type <macip|2|multicast|3|prefix|5>", - MATCH_STR - EVPN_HELP_STR - EVPN_TYPE_HELP_STR - EVPN_TYPE_2_HELP_STR - EVPN_TYPE_2_HELP_STR - EVPN_TYPE_3_HELP_STR - EVPN_TYPE_3_HELP_STR - EVPN_TYPE_5_HELP_STR - EVPN_TYPE_5_HELP_STR) -{ - return bgp_route_match_add(vty, "evpn route-type", - parse_evpn_rt_type(argv[3]->arg), - RMAP_EVENT_MATCH_ADDED); -} - -DEFUN (no_match_evpn_route_type, - no_match_evpn_route_type_cmd, - "no match evpn route-type <macip|2|multicast|3|prefix|5>", - NO_STR - MATCH_STR - EVPN_HELP_STR - EVPN_TYPE_HELP_STR - EVPN_TYPE_2_HELP_STR - EVPN_TYPE_2_HELP_STR - EVPN_TYPE_3_HELP_STR - EVPN_TYPE_3_HELP_STR - EVPN_TYPE_5_HELP_STR - EVPN_TYPE_5_HELP_STR) +DEFUN_YANG (match_evpn_route_type, + match_evpn_route_type_cmd, + "match evpn route-type <macip|2|multicast|3|prefix|5>", + MATCH_STR + EVPN_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_2_HELP_STR + EVPN_TYPE_2_HELP_STR + EVPN_TYPE_3_HELP_STR + EVPN_TYPE_3_HELP_STR + EVPN_TYPE_5_HELP_STR + EVPN_TYPE_5_HELP_STR) { - return bgp_route_match_delete(vty, "evpn route-type", - parse_evpn_rt_type(argv[4]->arg), - RMAP_EVENT_MATCH_DELETED); -} + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:evpn-route-type']"; + char xpath_value[XPATH_MAXLEN]; + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:evpn-route-type", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + parse_evpn_rt_type(argv[3]->arg)); -DEFUN (match_evpn_vni, - match_evpn_vni_cmd, - "match evpn vni " CMD_VNI_RANGE, - MATCH_STR - EVPN_HELP_STR - "Match VNI\n" - "VNI ID\n") + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_match_evpn_route_type, + no_match_evpn_route_type_cmd, + "no match evpn route-type <macip|2|multicast|3|prefix|5>", + NO_STR + MATCH_STR + EVPN_HELP_STR + EVPN_TYPE_HELP_STR + EVPN_TYPE_2_HELP_STR + EVPN_TYPE_2_HELP_STR + EVPN_TYPE_3_HELP_STR + EVPN_TYPE_3_HELP_STR + EVPN_TYPE_5_HELP_STR + EVPN_TYPE_5_HELP_STR) { - return bgp_route_match_add(vty, "evpn vni", argv[3]->arg, - RMAP_EVENT_MATCH_ADDED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:evpn-route-type']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_evpn_vni, - no_match_evpn_vni_cmd, - "no match evpn vni " CMD_VNI_RANGE, - NO_STR - MATCH_STR - EVPN_HELP_STR - "Match VNI\n" - "VNI ID\n") + +DEFUN_YANG (match_evpn_vni, + match_evpn_vni_cmd, + "match evpn vni " CMD_VNI_RANGE, + MATCH_STR + EVPN_HELP_STR + "Match VNI\n" + "VNI ID\n") +{ + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:evpn-vni']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:evpn-vni", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[3]->arg); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_match_evpn_vni, + no_match_evpn_vni_cmd, + "no match evpn vni " CMD_VNI_RANGE, + NO_STR + MATCH_STR + EVPN_HELP_STR + "Match VNI\n" + "VNI ID\n") { - return bgp_route_match_delete(vty, "evpn vni", argv[4]->arg, - RMAP_EVENT_MATCH_DELETED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:evpn-vni']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:evpn-vni", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, argv[3]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_evpn_default_route, - match_evpn_default_route_cmd, - "match evpn default-route", - MATCH_STR - EVPN_HELP_STR - "default EVPN type-5 route\n") +DEFUN_YANG (match_evpn_default_route, + match_evpn_default_route_cmd, + "match evpn default-route", + MATCH_STR + EVPN_HELP_STR + "default EVPN type-5 route\n") { - return bgp_route_match_add(vty, "evpn default-route", NULL, - RMAP_EVENT_MATCH_ADDED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:evpn-default-route']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:evpn-default-route", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, NULL); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_evpn_default_route, - no_match_evpn_default_route_cmd, - "no match evpn default-route", - NO_STR - MATCH_STR - EVPN_HELP_STR - "default EVPN type-5 route\n") +DEFUN_YANG (no_match_evpn_default_route, + no_match_evpn_default_route_cmd, + "no match evpn default-route", + NO_STR + MATCH_STR + EVPN_HELP_STR + "default EVPN type-5 route\n") { - return bgp_route_match_delete(vty, "evpn default-route", NULL, - RMAP_EVENT_MATCH_DELETED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:evpn-default-route']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_evpn_rd, - match_evpn_rd_cmd, - "match evpn rd ASN:NN_OR_IP-ADDRESS:NN", - MATCH_STR - EVPN_HELP_STR - "Route Distinguisher\n" - "ASN:XX or A.B.C.D:XX\n") +DEFUN_YANG (match_evpn_rd, + match_evpn_rd_cmd, + "match evpn rd ASN:NN_OR_IP-ADDRESS:NN", + MATCH_STR + EVPN_HELP_STR + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n") { - return bgp_route_match_add(vty, "evpn rd", argv[3]->arg, - RMAP_EVENT_MATCH_ADDED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:evpn-rd']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:route-distinguisher", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[3]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_evpn_rd, - no_match_evpn_rd_cmd, - "no match evpn rd ASN:NN_OR_IP-ADDRESS:NN", - NO_STR - MATCH_STR - EVPN_HELP_STR - "Route Distinguisher\n" - "ASN:XX or A.B.C.D:XX\n") +DEFUN_YANG (no_match_evpn_rd, + no_match_evpn_rd_cmd, + "no match evpn rd ASN:NN_OR_IP-ADDRESS:NN", + NO_STR + MATCH_STR + EVPN_HELP_STR + "Route Distinguisher\n" + "ASN:XX or A.B.C.D:XX\n") { - return bgp_route_match_delete(vty, "evpn rd", argv[4]->arg, - RMAP_EVENT_MATCH_DELETED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:evpn-rd']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFPY(match_vrl_source_vrf, +DEFPY_YANG(match_vrl_source_vrf, match_vrl_source_vrf_cmd, "match source-vrf NAME$vrf_name", MATCH_STR "source vrf\n" "The VRF name\n") { - return bgp_route_match_add(vty, "source-vrf", vrf_name, - RMAP_EVENT_MATCH_ADDED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:source-vrf']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:source-vrf", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, vrf_name); + + return nb_cli_apply_changes(vty, NULL); } -DEFPY(no_match_vrl_source_vrf, +DEFPY_YANG(no_match_vrl_source_vrf, no_match_vrl_source_vrf_cmd, "no match source-vrf NAME$vrf_name", - NO_STR - MATCH_STR + NO_STR MATCH_STR "source vrf\n" "The VRF name\n") { - return bgp_route_match_delete(vty, "source-vrf", vrf_name, - RMAP_EVENT_MATCH_DELETED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:source-vrf']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_peer, +DEFPY_YANG (match_peer, match_peer_cmd, - "match peer <A.B.C.D|X:X::X:X|WORD>", + "match peer <A.B.C.D$addrv4|X:X::X:X$addrv6|WORD$intf>", MATCH_STR "Match peer address\n" "IP address of peer\n" "IPv6 address of peer\n" "Interface name of peer\n") { - int idx_ip = 2; - return bgp_route_match_add(vty, "peer", argv[idx_ip]->arg, - RMAP_EVENT_MATCH_ADDED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:peer']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + if (addrv4_str) { + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:peer-ipv4-address", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + addrv4_str); + } else if (addrv6_str) { + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:peer-ipv6-address", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + addrv6_str); + } else { + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:peer-interface", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, intf); + } + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_peer_local, - match_peer_local_cmd, - "match peer local", - MATCH_STR - "Match peer address\n" - "Static or Redistributed routes\n") +DEFUN_YANG (match_peer_local, + match_peer_local_cmd, + "match peer local", + MATCH_STR + "Match peer address\n" + "Static or Redistributed routes\n") { - return bgp_route_match_add(vty, "peer", "local", - RMAP_EVENT_MATCH_DELETED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:peer']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:peer-local", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true"); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_peer, - no_match_peer_cmd, - "no match peer [<local|A.B.C.D|X:X::X:X|WORD>]", - NO_STR - MATCH_STR - "Match peer address\n" - "Static or Redistributed routes\n" - "IP address of peer\n" - "IPv6 address of peer\n" - "Interface name of peer\n") +DEFUN_YANG (no_match_peer, + no_match_peer_cmd, + "no match peer [<local|A.B.C.D|X:X::X:X|WORD>]", + NO_STR + MATCH_STR + "Match peer address\n" + "Static or Redistributed routes\n" + "IP address of peer\n" + "IPv6 address of peer\n" + "Interface name of peer\n") { - int idx_peer = 3; + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:peer']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - if (argc <= idx_peer) - return bgp_route_match_delete(vty, "peer", NULL, - RMAP_EVENT_MATCH_DELETED); - return bgp_route_match_delete(vty, "peer", argv[idx_peer]->arg, - RMAP_EVENT_MATCH_DELETED); + return nb_cli_apply_changes(vty, NULL); } #ifdef HAVE_SCRIPTING -DEFUN (match_script, - match_script_cmd, - "[no] match script WORD", - NO_STR - MATCH_STR - "Execute script to determine match\n" - "The script name to run, without .lua; e.g. 'myroutemap' to run myroutemap.lua\n") +DEFUN_YANG (match_script, + match_script_cmd, + "[no] match script WORD", + NO_STR + MATCH_STR + "Execute script to determine match\n" + "The script name to run, without .lua; e.g. 'myroutemap' to run myroutemap.lua\n") { bool no = strmatch(argv[0]->text, "no"); int i = 0; argv_find(argv, argc, "WORD", &i); const char *script = argv[i]->arg; + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-script']"; + char xpath_value[XPATH_MAXLEN]; if (no) { - return bgp_route_match_delete(vty, "script", script, - RMAP_EVENT_FILTER_DELETED); - } else { - return bgp_route_match_add(vty, "script", script, - RMAP_EVENT_FILTER_ADDED); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:script", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, + script); + + return nb_cli_apply_changes(vty, NULL); } + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:script", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + script); + + return nb_cli_apply_changes(vty, NULL); } #endif /* HAVE_SCRIPTING */ /* match probability */ -DEFUN (match_probability, - match_probability_cmd, - "match probability (0-100)", - MATCH_STR - "Match portion of routes defined by percentage value\n" - "Percentage of routes\n") +DEFUN_YANG (match_probability, + match_probability_cmd, + "match probability (0-100)", + MATCH_STR + "Match portion of routes defined by percentage value\n" + "Percentage of routes\n") { int idx_number = 2; - return bgp_route_match_add(vty, "probability", argv[idx_number]->arg, - RMAP_EVENT_MATCH_ADDED); + + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:probability']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:probability", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_number]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_probability, - no_match_probability_cmd, - "no match probability [(1-99)]", - NO_STR - MATCH_STR - "Match portion of routes defined by percentage value\n" - "Percentage of routes\n") +DEFUN_YANG (no_match_probability, + no_match_probability_cmd, + "no match probability [(1-99)]", + NO_STR + MATCH_STR + "Match portion of routes defined by percentage value\n" + "Percentage of routes\n") { int idx_number = 3; + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:probability']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + if (argc <= idx_number) - return bgp_route_match_delete(vty, "probability", NULL, - RMAP_EVENT_MATCH_DELETED); - return bgp_route_match_delete(vty, "probability", argv[idx_number]->arg, - RMAP_EVENT_MATCH_DELETED); + return nb_cli_apply_changes(vty, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:probability", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, + argv[idx_number]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_ip_route_source, +DEFPY_YANG (match_ip_route_source, match_ip_route_source_cmd, "match ip route-source <(1-199)|(1300-2699)|WORD>", MATCH_STR @@ -4207,102 +4280,134 @@ DEFUN (match_ip_route_source, "IP access-list number (expanded range)\n" "IP standard access-list name\n") { + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:ip-route-source']"; + char xpath_value[XPATH_MAXLEN + 32]; int idx_acl = 3; - return bgp_route_match_add(vty, "ip route-source", argv[idx_acl]->arg, - RMAP_EVENT_FILTER_ADDED); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:list-name", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_acl]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_ip_route_source, - no_match_ip_route_source_cmd, - "no match ip route-source [<(1-199)|(1300-2699)|WORD>]", - NO_STR - MATCH_STR - IP_STR - "Match advertising source address of route\n" - "IP access-list number\n" - "IP access-list number (expanded range)\n" - "IP standard access-list name\n") +DEFUN_YANG (no_match_ip_route_source, + no_match_ip_route_source_cmd, + "no match ip route-source [<(1-199)|(1300-2699)|WORD>]", + NO_STR + MATCH_STR + IP_STR + "Match advertising source address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP standard access-list name\n") { - int idx_number = 4; - if (argc <= idx_number) - return bgp_route_match_delete(vty, "ip route-source", NULL, - RMAP_EVENT_FILTER_DELETED); - return bgp_route_match_delete(vty, "ip route-source", - argv[idx_number]->arg, - RMAP_EVENT_FILTER_DELETED); -} + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:ip-route-source']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} -DEFUN (match_ip_route_source_prefix_list, - match_ip_route_source_prefix_list_cmd, - "match ip route-source prefix-list WORD", - MATCH_STR - IP_STR - "Match advertising source address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") +DEFUN_YANG (match_ip_route_source_prefix_list, + match_ip_route_source_prefix_list_cmd, + "match ip route-source prefix-list WORD", + MATCH_STR + IP_STR + "Match advertising source address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") { int idx_word = 4; - return bgp_route_match_add(vty, "ip route-source prefix-list", - argv[idx_word]->arg, RMAP_EVENT_PLIST_ADDED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:ip-route-source-prefix-list']"; + char xpath_value[XPATH_MAXLEN + 32]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_word]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_ip_route_source_prefix_list, - no_match_ip_route_source_prefix_list_cmd, - "no match ip route-source prefix-list [WORD]", - NO_STR - MATCH_STR - IP_STR - "Match advertising source address of route\n" - "Match entries of prefix-lists\n" - "IP prefix-list name\n") +DEFUN_YANG (no_match_ip_route_source_prefix_list, + no_match_ip_route_source_prefix_list_cmd, + "no match ip route-source prefix-list [WORD]", + NO_STR + MATCH_STR + IP_STR + "Match advertising source address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") { - int idx_word = 5; - if (argc <= idx_word) - return bgp_route_match_delete(vty, - "ip route-source prefix-list", - NULL, RMAP_EVENT_PLIST_DELETED); - return bgp_route_match_delete(vty, "ip route-source prefix-list", - argv[idx_word]->arg, - RMAP_EVENT_PLIST_DELETED); -} + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:ip-route-source-prefix-list']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} -DEFUN (match_local_pref, - match_local_pref_cmd, - "match local-preference (0-4294967295)", - MATCH_STR - "Match local-preference of route\n" - "Metric value\n") +DEFUN_YANG (match_local_pref, + match_local_pref_cmd, + "match local-preference (0-4294967295)", + MATCH_STR + "Match local-preference of route\n" + "Metric value\n") { int idx_number = 2; - return bgp_route_match_add(vty, "local-preference", - argv[idx_number]->arg, - RMAP_EVENT_MATCH_ADDED); + + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-local-preference']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:local-preference", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_number]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_local_pref, - no_match_local_pref_cmd, - "no match local-preference [(0-4294967295)]", - NO_STR - MATCH_STR - "Match local preference of route\n" - "Local preference value\n") +DEFUN_YANG (no_match_local_pref, + no_match_local_pref_cmd, + "no match local-preference [(0-4294967295)]", + NO_STR + MATCH_STR + "Match local preference of route\n" + "Local preference value\n") { int idx_localpref = 3; + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-local-preference']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + if (argc <= idx_localpref) - return bgp_route_match_delete(vty, "local-preference", NULL, - RMAP_EVENT_MATCH_DELETED); - return bgp_route_match_delete(vty, "local-preference", - argv[idx_localpref]->arg, - RMAP_EVENT_MATCH_DELETED); + return nb_cli_apply_changes(vty, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:local-preference", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, + argv[idx_localpref]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_community, +DEFPY_YANG (match_community, match_community_cmd, "match community <(1-99)|(100-500)|WORD> [exact-match]", MATCH_STR @@ -4312,478 +4417,608 @@ DEFUN (match_community, "Community-list name\n" "Do exact matching of communities\n") { + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-community']"; + char xpath_value[XPATH_MAXLEN]; + char xpath_match[XPATH_MAXLEN]; int idx_comm_list = 2; - int ret; - char *argstr; - size_t argstr_len; - if (argc == 4) { - argstr_len = strlen(argv[idx_comm_list]->arg) - + strlen("exact-match") + 2; - argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, argstr_len); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(argstr, argstr_len, "%s exact-match", - argv[idx_comm_list]->arg); - } else - argstr = argv[idx_comm_list]->arg; - - ret = bgp_route_match_add(vty, "community", argstr, - RMAP_EVENT_CLIST_ADDED); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[idx_comm_list]->arg); - if (argstr != argv[idx_comm_list]->arg) - XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr); - - return ret; -} - -DEFUN (no_match_community, - no_match_community_cmd, - "no match community [<(1-99)|(100-500)|WORD> [exact-match]]", - NO_STR - MATCH_STR - "Match BGP community list\n" - "Community-list number (standard)\n" - "Community-list number (expanded)\n" - "Community-list name\n" - "Do exact matching of communities\n") -{ - return bgp_route_match_delete(vty, "community", NULL, - RMAP_EVENT_CLIST_DELETED); -} + if (argc == 4) { + snprintf( + xpath_match, sizeof(xpath_match), + "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match", + xpath); + nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY, + "true"); + } else { + snprintf( + xpath_match, sizeof(xpath_match), + "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match", + xpath); + nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY, + "false"); + } -DEFUN (match_lcommunity, - match_lcommunity_cmd, - "match large-community <(1-99)|(100-500)|WORD> [exact-match]", - MATCH_STR - "Match BGP large community list\n" - "Large Community-list number (standard)\n" - "Large Community-list number (expanded)\n" - "Large Community-list name\n" - "Do exact matching of communities\n") -{ + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_match_community, + no_match_community_cmd, + "no match community [<(1-99)|(100-500)|WORD> [exact-match]]", + NO_STR + MATCH_STR + "Match BGP community list\n" + "Community-list number (standard)\n" + "Community-list number (expanded)\n" + "Community-list name\n" + "Do exact matching of communities\n") +{ + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-community']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG (match_lcommunity, + match_lcommunity_cmd, + "match large-community <(1-99)|(100-500)|WORD> [exact-match]", + MATCH_STR + "Match BGP large community list\n" + "Large Community-list number (standard)\n" + "Large Community-list number (expanded)\n" + "Large Community-list name\n" + "Do exact matching of communities\n") +{ + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-large-community']"; + char xpath_value[XPATH_MAXLEN]; + char xpath_match[XPATH_MAXLEN]; int idx_lcomm_list = 2; - int ret; - char *argstr; - size_t argstr_len; - if (argc == 4) { - argstr_len = strlen(argv[idx_lcomm_list]->arg) - + strlen("exact-match") + 2; - argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, argstr_len); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(argstr, argstr_len, "%s exact-match", - argv[idx_lcomm_list]->arg); - } else - argstr = argv[idx_lcomm_list]->arg; + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[idx_lcomm_list]->arg); - ret = bgp_route_match_add(vty, "large-community", argstr, - RMAP_EVENT_LLIST_ADDED); - if (argstr != argv[idx_lcomm_list]->arg) - XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr); + if (argc == 4) { + snprintf( + xpath_match, sizeof(xpath_match), + "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match", + xpath); + nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY, + "true"); + } else { + snprintf( + xpath_match, sizeof(xpath_match), + "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match", + xpath); + nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY, + "false"); + } - return ret; + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_lcommunity, - no_match_lcommunity_cmd, - "no match large-community [<(1-99)|(100-500)|WORD> [exact-match]]", - NO_STR - MATCH_STR - "Match BGP large community list\n" - "Large Community-list number (standard)\n" - "Large Community-list number (expanded)\n" - "Large Community-list name\n" - "Do exact matching of communities\n") +DEFUN_YANG (no_match_lcommunity, + no_match_lcommunity_cmd, + "no match large-community [<(1-99)|(100-500)|WORD> [exact-match]]", + NO_STR + MATCH_STR + "Match BGP large community list\n" + "Large Community-list number (standard)\n" + "Large Community-list number (expanded)\n" + "Large Community-list name\n" + "Do exact matching of communities\n") { - return bgp_route_match_delete(vty, "large-community", NULL, - RMAP_EVENT_LLIST_DELETED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-large-community']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_ecommunity, - match_ecommunity_cmd, - "match extcommunity <(1-99)|(100-500)|WORD>", - MATCH_STR - "Match BGP/VPN extended community list\n" - "Extended community-list number (standard)\n" - "Extended community-list number (expanded)\n" - "Extended community-list name\n") +DEFPY_YANG (match_ecommunity, + match_ecommunity_cmd, + "match extcommunity <(1-99)|(100-500)|WORD>", + MATCH_STR + "Match BGP/VPN extended community list\n" + "Extended community-list number (standard)\n" + "Extended community-list number (expanded)\n" + "Extended community-list name\n") { + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-extcommunity']"; + char xpath_value[XPATH_MAXLEN]; int idx_comm_list = 2; - return bgp_route_match_add(vty, "extcommunity", - argv[idx_comm_list]->arg, - RMAP_EVENT_ECLIST_ADDED); + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[idx_comm_list]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_ecommunity, - no_match_ecommunity_cmd, - "no match extcommunity [<(1-99)|(100-500)|WORD>]", - NO_STR - MATCH_STR - "Match BGP/VPN extended community list\n" - "Extended community-list number (standard)\n" - "Extended community-list number (expanded)\n" - "Extended community-list name\n") +DEFUN_YANG (no_match_ecommunity, + no_match_ecommunity_cmd, + "no match extcommunity [<(1-99)|(100-500)|WORD>]", + NO_STR + MATCH_STR + "Match BGP/VPN extended community list\n" + "Extended community-list number (standard)\n" + "Extended community-list number (expanded)\n" + "Extended community-list name\n") { - return bgp_route_match_delete(vty, "extcommunity", NULL, - RMAP_EVENT_ECLIST_DELETED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-extcommunity']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_aspath, - match_aspath_cmd, - "match as-path WORD", - MATCH_STR - "Match BGP AS path list\n" - "AS path access-list name\n") +DEFUN_YANG (match_aspath, + match_aspath_cmd, + "match as-path WORD", + MATCH_STR + "Match BGP AS path list\n" + "AS path access-list name\n") { int idx_word = 2; - return bgp_route_match_add(vty, "as-path", argv[idx_word]->arg, - RMAP_EVENT_ASLIST_ADDED); + + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:as-path-list']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:list-name", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_word]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_aspath, - no_match_aspath_cmd, - "no match as-path [WORD]", - NO_STR - MATCH_STR - "Match BGP AS path list\n" - "AS path access-list name\n") +DEFUN_YANG (no_match_aspath, + no_match_aspath_cmd, + "no match as-path [WORD]", + NO_STR + MATCH_STR + "Match BGP AS path list\n" + "AS path access-list name\n") { - return bgp_route_match_delete(vty, "as-path", NULL, - RMAP_EVENT_ASLIST_DELETED); -} + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:as-path-list']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); -DEFUN (match_origin, - match_origin_cmd, - "match origin <egp|igp|incomplete>", - MATCH_STR - "BGP origin code\n" - "remote EGP\n" - "local IGP\n" - "unknown heritage\n") + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (match_origin, + match_origin_cmd, + "match origin <egp|igp|incomplete>", + MATCH_STR + "BGP origin code\n" + "remote EGP\n" + "local IGP\n" + "unknown heritage\n") { int idx_origin = 2; + const char *origin_type; + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-origin']"; + char xpath_value[XPATH_MAXLEN]; + if (strncmp(argv[idx_origin]->arg, "igp", 2) == 0) - return bgp_route_match_add(vty, "origin", "igp", - RMAP_EVENT_MATCH_ADDED); - if (strncmp(argv[idx_origin]->arg, "egp", 1) == 0) - return bgp_route_match_add(vty, "origin", "egp", - RMAP_EVENT_MATCH_ADDED); - if (strncmp(argv[idx_origin]->arg, "incomplete", 2) == 0) - return bgp_route_match_add(vty, "origin", "incomplete", - RMAP_EVENT_MATCH_ADDED); + origin_type = "igp"; + else if (strncmp(argv[idx_origin]->arg, "egp", 1) == 0) + origin_type = "egp"; + else if (strncmp(argv[idx_origin]->arg, "incomplete", 2) == 0) + origin_type = "incomplete"; + else { + vty_out(vty, "%% Invalid match origin type\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:origin", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, origin_type); - vty_out(vty, "%% Invalid match origin type\n"); - return CMD_WARNING_CONFIG_FAILED; + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_origin, - no_match_origin_cmd, - "no match origin [<egp|igp|incomplete>]", - NO_STR - MATCH_STR - "BGP origin code\n" - "remote EGP\n" - "local IGP\n" - "unknown heritage\n") +DEFUN_YANG (no_match_origin, + no_match_origin_cmd, + "no match origin [<egp|igp|incomplete>]", + NO_STR + MATCH_STR + "BGP origin code\n" + "remote EGP\n" + "local IGP\n" + "unknown heritage\n") { - return bgp_route_match_delete(vty, "origin", NULL, - RMAP_EVENT_MATCH_DELETED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:match-origin']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_table_id, - set_table_id_cmd, - "set table (1-4294967295)", - SET_STR - "export route to non-main kernel table\n" - "Kernel routing table id\n") +DEFUN_YANG (set_table_id, + set_table_id_cmd, + "set table (1-4294967295)", + SET_STR + "export route to non-main kernel table\n" + "Kernel routing table id\n") { - int idx_id = 2; - - VTY_DECLVAR_CONTEXT(route_map_index, index); - - return generic_set_add(vty, index, "table", argv[idx_id]->arg); + int idx_number = 2; + const char *xpath = "./set-action[action='frr-bgp-route-map:table']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:table", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_number]->arg); + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_table_id, + no_set_table_id_cmd, + "no set table", + NO_STR + SET_STR + "export route to non-main kernel table\n") +{ + const char *xpath = "./set-action[action='frr-bgp-route-map:table']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (set_ip_nexthop_peer, + set_ip_nexthop_peer_cmd, + "[no] set ip next-hop peer-address", + NO_STR + SET_STR + IP_STR + "Next hop address\n" + "Use peer address (for BGP only)\n") +{ + char xpath_value[XPATH_MAXLEN]; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-ipv4-nexthop']"; + + if (strmatch(argv[0]->text, "no")) + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + else { + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:ipv4-nexthop", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + "peer-address"); + } + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_table_id, - no_set_table_id_cmd, - "no set table", - NO_STR - SET_STR - "export route to non-main kernel table\n") +DEFUN_YANG (set_ip_nexthop_unchanged, + set_ip_nexthop_unchanged_cmd, + "[no] set ip next-hop unchanged", + NO_STR + SET_STR + IP_STR + "Next hop address\n" + "Don't modify existing Next hop address\n") { - VTY_DECLVAR_CONTEXT(route_map_index, index); + char xpath_value[XPATH_MAXLEN]; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-ipv4-nexthop']"; - return generic_set_delete(vty, index, "table", NULL); + if (strmatch(argv[0]->text, "no")) + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + else { + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:ipv4-nexthop", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + "unchanged"); + } + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_ip_nexthop_peer, - set_ip_nexthop_peer_cmd, - "[no] set ip next-hop peer-address", - NO_STR - SET_STR - IP_STR - "Next hop address\n" - "Use peer address (for BGP only)\n") +DEFUN_YANG (set_distance, + set_distance_cmd, + "set distance (0-255)", + SET_STR + "BGP Administrative Distance to use\n" + "Distance value\n") { - int (*func)(struct vty *, struct route_map_index *, const char *, - const char *) = strmatch(argv[0]->text, "no") - ? generic_set_delete - : generic_set_add; + int idx_number = 2; + const char *xpath = "./set-action[action='frr-bgp-route-map:distance']"; + char xpath_value[XPATH_MAXLEN]; - return func(vty, VTY_GET_CONTEXT(route_map_index), "ip next-hop", - "peer-address"); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:distance", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_number]->arg); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_ip_nexthop_unchanged, - set_ip_nexthop_unchanged_cmd, - "[no] set ip next-hop unchanged", - NO_STR - SET_STR - IP_STR - "Next hop address\n" - "Don't modify existing Next hop address\n") +DEFUN_YANG (no_set_distance, + no_set_distance_cmd, + "no set distance [(0-255)]", + NO_STR SET_STR + "BGP Administrative Distance to use\n" + "Distance value\n") { - int (*func)(struct vty *, struct route_map_index *, const char *, - const char *) = strmatch(argv[0]->text, "no") - ? generic_set_delete - : generic_set_add; + const char *xpath = "./set-action[action='frr-bgp-route-map:distance']"; - return func(vty, VTY_GET_CONTEXT(route_map_index), "ip next-hop", - "unchanged"); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_distance, - set_distance_cmd, - "set distance (0-255)", - SET_STR - "BGP Administrative Distance to use\n" - "Distance value\n") +DEFUN_YANG (set_local_pref, + set_local_pref_cmd, + "set local-preference WORD", + SET_STR + "BGP local preference path attribute\n" + "Preference value (0-4294967295)\n") { int idx_number = 2; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-local-preference']"; + char xpath_value[XPATH_MAXLEN]; - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "distance", argv[idx_number]->arg); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:local-pref", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_number]->arg); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_distance, - no_set_distance_cmd, - "no set distance [(0-255)]", - NO_STR SET_STR - "BGP Administrative Distance to use\n" - "Distance value\n") +DEFUN_YANG (no_set_local_pref, + no_set_local_pref_cmd, + "no set local-preference [WORD]", + NO_STR + SET_STR + "BGP local preference path attribute\n" + "Preference value (0-4294967295)\n") { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "distance", NULL); + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-local-preference']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_local_pref, - set_local_pref_cmd, - "set local-preference WORD", - SET_STR - "BGP local preference path attribute\n" - "Preference value (0-4294967295)\n") +DEFUN_YANG (set_weight, + set_weight_cmd, + "set weight (0-4294967295)", + SET_STR + "BGP weight for routing table\n" + "Weight value\n") { int idx_number = 2; - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "local-preference", argv[idx_number]->arg); -} - + const char *xpath = "./set-action[action='frr-bgp-route-map:weight']"; + char xpath_value[XPATH_MAXLEN]; -DEFUN (no_set_local_pref, - no_set_local_pref_cmd, - "no set local-preference [WORD]", - NO_STR - SET_STR - "BGP local preference path attribute\n" - "Preference value (0-4294967295)\n") -{ - int idx_localpref = 3; - if (argc <= idx_localpref) - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "local-preference", NULL); - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "local-preference", argv[idx_localpref]->arg); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:weight", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_number]->arg); + return nb_cli_apply_changes(vty, NULL); } - -DEFUN (set_weight, - set_weight_cmd, - "set weight (0-4294967295)", - SET_STR - "BGP weight for routing table\n" - "Weight value\n") +DEFUN_YANG (no_set_weight, + no_set_weight_cmd, + "no set weight [(0-4294967295)]", + NO_STR + SET_STR + "BGP weight for routing table\n" + "Weight value\n") { - int idx_number = 2; - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), "weight", - argv[idx_number]->arg); -} + const char *xpath = "./set-action[action='frr-bgp-route-map:weight']"; - -DEFUN (no_set_weight, - no_set_weight_cmd, - "no set weight [(0-4294967295)]", - NO_STR - SET_STR - "BGP weight for routing table\n" - "Weight value\n") -{ - int idx_weight = 3; - if (argc <= idx_weight) - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "weight", NULL); - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "weight", argv[idx_weight]->arg); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_label_index, - set_label_index_cmd, - "set label-index (0-1048560)", - SET_STR - "Label index to associate with the prefix\n" - "Label index value\n") +DEFUN_YANG (set_label_index, + set_label_index_cmd, + "set label-index (0-1048560)", + SET_STR + "Label index to associate with the prefix\n" + "Label index value\n") { int idx_number = 2; - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "label-index", argv[idx_number]->arg); + const char *xpath = + "./set-action[action='frr-bgp-route-map:label-index']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:label-index", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_number]->arg); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_label_index, - no_set_label_index_cmd, - "no set label-index [(0-1048560)]", - NO_STR - SET_STR - "Label index to associate with the prefix\n" - "Label index value\n") +DEFUN_YANG (no_set_label_index, + no_set_label_index_cmd, + "no set label-index [(0-1048560)]", + NO_STR + SET_STR + "Label index to associate with the prefix\n" + "Label index value\n") { - int idx_label_index = 3; - if (argc <= idx_label_index) - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "label-index", NULL); - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "label-index", argv[idx_label_index]->arg); + const char *xpath = + "./set-action[action='frr-bgp-route-map:label-index']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_aspath_prepend_asn, - set_aspath_prepend_asn_cmd, - "set as-path prepend (1-4294967295)...", - SET_STR - "Transform BGP AS_PATH attribute\n" - "Prepend to the as-path\n" - "AS number\n") +DEFUN_YANG (set_aspath_prepend_asn, + set_aspath_prepend_asn_cmd, + "set as-path prepend (1-4294967295)...", + SET_STR + "Transform BGP AS_PATH attribute\n" + "Prepend to the as-path\n" + "AS number\n") { int idx_asn = 3; int ret; char *str; str = argv_concat(argv, argc, idx_asn); - ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "as-path prepend", str); - XFREE(MTYPE_TMP, str); + const char *xpath = + "./set-action[action='frr-bgp-route-map:as-path-prepend']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:prepend-as-path", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str); + ret = nb_cli_apply_changes(vty, NULL); + XFREE(MTYPE_TMP, str); return ret; } -DEFUN (set_aspath_prepend_lastas, - set_aspath_prepend_lastas_cmd, - "set as-path prepend last-as (1-10)", - SET_STR - "Transform BGP AS_PATH attribute\n" - "Prepend to the as-path\n" - "Use the peer's AS-number\n" - "Number of times to insert\n") +DEFUN_YANG (set_aspath_prepend_lastas, + set_aspath_prepend_lastas_cmd, + "set as-path prepend last-as (1-10)", + SET_STR + "Transform BGP AS_PATH attribute\n" + "Prepend to the as-path\n" + "Use the peer's AS-number\n" + "Number of times to insert\n") { - return set_aspath_prepend_asn(self, vty, argc, argv); + int idx_num = 4; + + const char *xpath = + "./set-action[action='frr-bgp-route-map:as-path-prepend']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:last-as", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_num]->arg); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_aspath_prepend, - no_set_aspath_prepend_cmd, - "no set as-path prepend [(1-4294967295)]", - NO_STR - SET_STR - "Transform BGP AS_PATH attribute\n" - "Prepend to the as-path\n" - "AS number\n") +DEFUN_YANG (no_set_aspath_prepend, + no_set_aspath_prepend_cmd, + "no set as-path prepend [(1-4294967295)]", + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Prepend to the as-path\n" + "AS number\n") { - int idx_asn = 4; - int ret; - char *str; + const char *xpath = + "./set-action[action='frr-bgp-route-map:as-path-prepend']"; - str = argv_concat(argv, argc, idx_asn); - ret = generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "as-path prepend", str); - XFREE(MTYPE_TMP, str); - return ret; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_aspath_prepend_lastas, - no_set_aspath_prepend_lastas_cmd, - "no set as-path prepend last-as [(1-10)]", - NO_STR - SET_STR - "Transform BGP AS_PATH attribute\n" - "Prepend to the as-path\n" - "Use the peers AS-number\n" - "Number of times to insert\n") +DEFUN_YANG (no_set_aspath_prepend_lastas, + no_set_aspath_prepend_lastas_cmd, + "no set as-path prepend last-as [(1-10)]", + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Prepend to the as-path\n" + "Use the peers AS-number\n" + "Number of times to insert\n") { - return no_set_aspath_prepend(self, vty, argc, argv); + const char *xpath = + "./set-action[action='frr-bgp-route-map:as-path-prepend']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_aspath_exclude, - set_aspath_exclude_cmd, - "set as-path exclude (1-4294967295)...", - SET_STR - "Transform BGP AS-path attribute\n" - "Exclude from the as-path\n" - "AS number\n") +DEFUN_YANG (set_aspath_exclude, + set_aspath_exclude_cmd, + "set as-path exclude (1-4294967295)...", + SET_STR + "Transform BGP AS-path attribute\n" + "Exclude from the as-path\n" + "AS number\n") { int idx_asn = 3; int ret; char *str; str = argv_concat(argv, argc, idx_asn); - ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "as-path exclude", str); + + const char *xpath = + "./set-action[action='frr-bgp-route-map:as-path-exclude']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:exclude-as-path", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str); + ret = nb_cli_apply_changes(vty, NULL); XFREE(MTYPE_TMP, str); return ret; } -DEFUN (no_set_aspath_exclude, - no_set_aspath_exclude_cmd, - "no set as-path exclude (1-4294967295)...", - NO_STR - SET_STR - "Transform BGP AS_PATH attribute\n" - "Exclude from the as-path\n" - "AS number\n") +DEFUN_YANG (no_set_aspath_exclude, + no_set_aspath_exclude_cmd, + "no set as-path exclude (1-4294967295)...", + NO_STR + SET_STR + "Transform BGP AS_PATH attribute\n" + "Exclude from the as-path\n" + "AS number\n") { - int idx_asn = 4; - int ret; - char *str; + const char *xpath = + "./set-action[action='frr-bgp-route-map:as-path-exclude']"; - str = argv_concat(argv, argc, idx_asn); - ret = generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "as-path exclude", str); - XFREE(MTYPE_TMP, str); - return ret; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -ALIAS(no_set_aspath_exclude, no_set_aspath_exclude_all_cmd, - "no set as-path exclude", - NO_STR SET_STR - "Transform BGP AS_PATH attribute\n" - "Exclude from the as-path\n") +ALIAS_YANG (no_set_aspath_exclude, no_set_aspath_exclude_all_cmd, + "no set as-path exclude", + NO_STR SET_STR + "Transform BGP AS_PATH attribute\n" + "Exclude from the as-path\n") -DEFUN (set_community, - set_community_cmd, - "set community AA:NN...", - SET_STR - "BGP community attribute\n" - COMMUNITY_VAL_STR) +DEFUN_YANG (set_community, + set_community_cmd, + "set community AA:NN...", + SET_STR + "BGP community attribute\n" + COMMUNITY_VAL_STR) { int idx_aa_nn = 2; int i; @@ -4792,9 +5027,18 @@ DEFUN (set_community, struct buffer *b; struct community *com = NULL; char *str; - char *argstr; + char *argstr = NULL; int ret; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-community']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:community-string", + xpath); + b = buffer_new(1024); for (i = idx_aa_nn; i < argc; i++) { @@ -4870,50 +5114,61 @@ DEFUN (set_community, argstr = XCALLOC(MTYPE_TMP, argstr_sz); strlcpy(argstr, str, argstr_sz); strlcat(argstr, " additive", argstr_sz); - ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "community", argstr); - XFREE(MTYPE_TMP, argstr); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argstr); } else - ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "community", str); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str); + + ret = nb_cli_apply_changes(vty, NULL); + if (argstr) + XFREE(MTYPE_TMP, argstr); community_free(&com); return ret; } -DEFUN (set_community_none, - set_community_none_cmd, - "set community none", - SET_STR - "BGP community attribute\n" - "No community attribute\n") +DEFUN_YANG (set_community_none, + set_community_none_cmd, + "set community none", + SET_STR + "BGP community attribute\n" + "No community attribute\n") { - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "community", "none"); + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-community']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:community-none", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true"); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_community, - no_set_community_cmd, - "no set community AA:NN...", - NO_STR - SET_STR - "BGP community attribute\n" - COMMUNITY_VAL_STR) +DEFUN_YANG (no_set_community, + no_set_community_cmd, + "no set community AA:NN...", + NO_STR + SET_STR + "BGP community attribute\n" + COMMUNITY_VAL_STR) { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "community", NULL); -} + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-community']"; -ALIAS (no_set_community, - no_set_community_short_cmd, - "no set community", - NO_STR - SET_STR - "BGP community attribute\n") + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} +ALIAS_YANG (no_set_community, + no_set_community_short_cmd, + "no set community", + NO_STR + SET_STR + "BGP community attribute\n") -DEFUN (set_community_delete, +DEFPY_YANG (set_community_delete, set_community_delete_cmd, "set comm-list <(1-99)|(100-500)|WORD> delete", SET_STR @@ -4923,93 +5178,124 @@ DEFUN (set_community_delete, "Community-list name\n" "Delete matching communities\n") { + const char *xpath = + "./set-action[action='frr-bgp-route-map:comm-list-delete']"; + char xpath_value[XPATH_MAXLEN]; int idx_comm_list = 2; - char *args; - args = argv_concat(argv, argc, idx_comm_list); - generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), "comm-list", - args); - XFREE(MTYPE_TMP, args); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:comm-list-name", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_comm_list]->arg); + + return nb_cli_apply_changes(vty, NULL); - return CMD_SUCCESS; } -DEFUN (no_set_community_delete, - no_set_community_delete_cmd, - "no set comm-list [<(1-99)|(100-500)|WORD> delete]", - NO_STR - SET_STR - "set BGP community list (for deletion)\n" - "Community-list number (standard)\n" - "Community-list number (expanded)\n" - "Community-list name\n" - "Delete matching communities\n") +DEFUN_YANG (no_set_community_delete, + no_set_community_delete_cmd, + "no set comm-list [<(1-99)|(100-500)|WORD> delete]", + NO_STR + SET_STR + "set BGP community list (for deletion)\n" + "Community-list number (standard)\n" + "Community-list number (expanded)\n" + "Community-list name\n" + "Delete matching communities\n") { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "comm-list", NULL); + const char *xpath = + "./set-action[action='frr-bgp-route-map:comm-list-delete']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_lcommunity, - set_lcommunity_cmd, - "set large-community AA:BB:CC...", - SET_STR - "BGP large community attribute\n" - "Large Community number in aa:bb:cc format or additive\n") +DEFUN_YANG (set_lcommunity, + set_lcommunity_cmd, + "set large-community AA:BB:CC...", + SET_STR + "BGP large community attribute\n" + "Large Community number in aa:bb:cc format or additive\n") { - int ret; char *str; + int ret; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-large-community']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:large-community-string", + xpath); str = argv_concat(argv, argc, 2); - ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "large-community", str); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str); + ret = nb_cli_apply_changes(vty, NULL); XFREE(MTYPE_TMP, str); - return ret; } -DEFUN (set_lcommunity_none, - set_lcommunity_none_cmd, - "set large-community none", - SET_STR - "BGP large community attribute\n" - "No large community attribute\n") +DEFUN_YANG (set_lcommunity_none, + set_lcommunity_none_cmd, + "set large-community none", + SET_STR + "BGP large community attribute\n" + "No large community attribute\n") { - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "large-community", "none"); + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-large-community']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:large-community-none", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true"); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_lcommunity, - no_set_lcommunity_cmd, - "no set large-community none", - NO_STR - SET_STR - "BGP large community attribute\n" - "No community attribute\n") +DEFUN_YANG (no_set_lcommunity, + no_set_lcommunity_cmd, + "no set large-community none", + NO_STR + SET_STR + "BGP large community attribute\n" + "No community attribute\n") { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "large-community", NULL); + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-large-community']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_lcommunity1, - no_set_lcommunity1_cmd, - "no set large-community AA:BB:CC...", - NO_STR - SET_STR - "BGP large community attribute\n" - "Large community in AA:BB:CC... format or additive\n") +DEFUN_YANG (no_set_lcommunity1, + no_set_lcommunity1_cmd, + "no set large-community AA:BB:CC...", + NO_STR + SET_STR + "BGP large community attribute\n" + "Large community in AA:BB:CC... format or additive\n") { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "large-community", NULL); + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-large-community']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -ALIAS (no_set_lcommunity1, - no_set_lcommunity1_short_cmd, - "no set large-community", - NO_STR - SET_STR - "BGP large community attribute\n") +ALIAS_YANG (no_set_lcommunity1, + no_set_lcommunity1_short_cmd, + "no set large-community", + NO_STR + SET_STR + "BGP large community attribute\n") -DEFUN (set_lcommunity_delete, +DEFPY_YANG (set_lcommunity_delete, set_lcommunity_delete_cmd, "set large-comm-list <(1-99)|(100-500)|WORD> delete", SET_STR @@ -5019,336 +5305,401 @@ DEFUN (set_lcommunity_delete, "Large Community-list name\n" "Delete matching large communities\n") { + const char *xpath = + "./set-action[action='frr-bgp-route-map:large-comm-list-delete']"; + char xpath_value[XPATH_MAXLEN]; int idx_lcomm_list = 2; - char *args; - args = argv_concat(argv, argc, idx_lcomm_list); - generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "large-comm-list", args); - XFREE(MTYPE_TMP, args); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - return CMD_SUCCESS; + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:comm-list-name", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_lcomm_list]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_lcommunity_delete, - no_set_lcommunity_delete_cmd, - "no set large-comm-list <(1-99)|(100-500)|WORD> [delete]", - NO_STR - SET_STR - "set BGP large community list (for deletion)\n" - "Large Community-list number (standard)\n" - "Large Communitly-list number (expanded)\n" - "Large Community-list name\n" - "Delete matching large communities\n") +DEFUN_YANG (no_set_lcommunity_delete, + no_set_lcommunity_delete_cmd, + "no set large-comm-list <(1-99)|(100-500)|WORD> [delete]", + NO_STR + SET_STR + "set BGP large community list (for deletion)\n" + "Large Community-list number (standard)\n" + "Large Communitly-list number (expanded)\n" + "Large Community-list name\n" + "Delete matching large communities\n") { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "large-comm-list", NULL); + const char *xpath = + "./set-action[action='frr-bgp-route-map:large-comm-list-delete']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -ALIAS (no_set_lcommunity_delete, - no_set_lcommunity_delete_short_cmd, - "no set large-comm-list", - NO_STR - SET_STR - "set BGP large community list (for deletion)\n") +ALIAS_YANG (no_set_lcommunity_delete, + no_set_lcommunity_delete_short_cmd, + "no set large-comm-list", + NO_STR + SET_STR + "set BGP large community list (for deletion)\n") -DEFUN (set_ecommunity_rt, - set_ecommunity_rt_cmd, - "set extcommunity rt ASN:NN_OR_IP-ADDRESS:NN...", - SET_STR - "BGP extended community attribute\n" - "Route Target extended community\n" - "VPN extended community\n") +DEFUN_YANG (set_ecommunity_rt, + set_ecommunity_rt_cmd, + "set extcommunity rt ASN:NN_OR_IP-ADDRESS:NN...", + SET_STR + "BGP extended community attribute\n" + "Route Target extended community\n" + "VPN extended community\n") { int idx_asn_nn = 3; - int ret; char *str; + int ret; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-rt']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:extcommunity-rt", xpath); str = argv_concat(argv, argc, idx_asn_nn); - ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "extcommunity rt", str); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str); + ret = nb_cli_apply_changes(vty, NULL); XFREE(MTYPE_TMP, str); - return ret; } -DEFUN (no_set_ecommunity_rt, - no_set_ecommunity_rt_cmd, - "no set extcommunity rt ASN:NN_OR_IP-ADDRESS:NN...", - NO_STR - SET_STR - "BGP extended community attribute\n" - "Route Target extended community\n" - "VPN extended community\n") -{ - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "extcommunity rt", NULL); -} - -ALIAS (no_set_ecommunity_rt, - no_set_ecommunity_rt_short_cmd, - "no set extcommunity rt", - NO_STR - SET_STR - "BGP extended community attribute\n" - "Route Target extended community\n") - -DEFUN (set_ecommunity_soo, - set_ecommunity_soo_cmd, - "set extcommunity soo ASN:NN_OR_IP-ADDRESS:NN...", - SET_STR - "BGP extended community attribute\n" - "Site-of-Origin extended community\n" - "VPN extended community\n") +DEFUN_YANG (no_set_ecommunity_rt, + no_set_ecommunity_rt_cmd, + "no set extcommunity rt ASN:NN_OR_IP-ADDRESS:NN...", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Route Target extended community\n" + "VPN extended community\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-rt']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS_YANG (no_set_ecommunity_rt, + no_set_ecommunity_rt_short_cmd, + "no set extcommunity rt", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Route Target extended community\n") + +DEFUN_YANG (set_ecommunity_soo, + set_ecommunity_soo_cmd, + "set extcommunity soo ASN:NN_OR_IP-ADDRESS:NN...", + SET_STR + "BGP extended community attribute\n" + "Site-of-Origin extended community\n" + "VPN extended community\n") { int idx_asn_nn = 3; - int ret; char *str; + int ret; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-soo']"; + char xpath_value[XPATH_MAXLEN]; + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:extcommunity-soo", + xpath); str = argv_concat(argv, argc, idx_asn_nn); - ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "extcommunity soo", str); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str); + ret = nb_cli_apply_changes(vty, NULL); XFREE(MTYPE_TMP, str); return ret; } - -DEFUN (no_set_ecommunity_soo, - no_set_ecommunity_soo_cmd, - "no set extcommunity soo ASN:NN_OR_IP-ADDRESS:NN...", - NO_STR - SET_STR - "BGP extended community attribute\n" - "Site-of-Origin extended community\n" - "VPN extended community\n") +DEFUN_YANG (no_set_ecommunity_soo, + no_set_ecommunity_soo_cmd, + "no set extcommunity soo ASN:NN_OR_IP-ADDRESS:NN...", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Site-of-Origin extended community\n" + "VPN extended community\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-soo']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS_YANG (no_set_ecommunity_soo, + no_set_ecommunity_soo_short_cmd, + "no set extcommunity soo", + NO_STR + SET_STR + "GP extended community attribute\n" + "Site-of-Origin extended community\n") + +DEFUN_YANG (set_ecommunity_lb, + set_ecommunity_lb_cmd, + "set extcommunity bandwidth <(1-25600)|cumulative|num-multipaths> [non-transitive]", + SET_STR + "BGP extended community attribute\n" + "Link bandwidth extended community\n" + "Bandwidth value in Mbps\n" + "Cumulative bandwidth of all multipaths (outbound-only)\n" + "Internally computed bandwidth based on number of multipaths (outbound-only)\n" + "Attribute is set as non-transitive\n") { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "extcommunity soo", NULL); -} - -ALIAS (no_set_ecommunity_soo, - no_set_ecommunity_soo_short_cmd, - "no set extcommunity soo", - NO_STR - SET_STR - "GP extended community attribute\n" - "Site-of-Origin extended community\n") + int idx_lb = 3; + int idx_non_transitive = 4; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-lb']"; + char xpath_lb_type[XPATH_MAXLEN]; + char xpath_bandwidth[XPATH_MAXLEN]; + char xpath_non_transitive[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_lb_type, sizeof(xpath_lb_type), + "%s/rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type", + xpath); + snprintf(xpath_bandwidth, sizeof(xpath_bandwidth), + "%s/rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth", + xpath); + snprintf(xpath_non_transitive, sizeof(xpath_non_transitive), + "%s/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific", + xpath); + + if ((strcmp(argv[idx_lb]->arg, "cumulative")) == 0) + nb_cli_enqueue_change(vty, xpath_lb_type, NB_OP_MODIFY, + "cumulative-bandwidth"); + else if ((strcmp(argv[idx_lb]->arg, "num-multipaths")) == 0) + nb_cli_enqueue_change(vty, xpath_lb_type, NB_OP_MODIFY, + "computed-bandwidth"); + else { + nb_cli_enqueue_change(vty, xpath_lb_type, NB_OP_MODIFY, + "explicit-bandwidth"); + nb_cli_enqueue_change(vty, xpath_bandwidth, NB_OP_MODIFY, + argv[idx_lb]->arg); + } -DEFUN (set_ecommunity_lb, - set_ecommunity_lb_cmd, - "set extcommunity bandwidth <(1-25600)|cumulative|num-multipaths> [non-transitive]", - SET_STR - "BGP extended community attribute\n" - "Link bandwidth extended community\n" - "Bandwidth value in Mbps\n" - "Cumulative bandwidth of all multipaths (outbound-only)\n" - "Internally computed bandwidth based on number of multipaths (outbound-only)\n" - "Attribute is set as non-transitive\n") + if (argv[idx_non_transitive]) + nb_cli_enqueue_change(vty, xpath_non_transitive, NB_OP_MODIFY, + "true"); + else + nb_cli_enqueue_change(vty, xpath_non_transitive, NB_OP_MODIFY, + "false"); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_ecommunity_lb, + no_set_ecommunity_lb_cmd, + "no set extcommunity bandwidth <(1-25600)|cumulative|num-multipaths> [non-transitive]", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Link bandwidth extended community\n" + "Bandwidth value in Mbps\n" + "Cumulative bandwidth of all multipaths (outbound-only)\n" + "Internally computed bandwidth based on number of multipaths (outbound-only)\n" + "Attribute is set as non-transitive\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-lb']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS_YANG (no_set_ecommunity_lb, + no_set_ecommunity_lb_short_cmd, + "no set extcommunity bandwidth", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Link bandwidth extended community\n") + +DEFUN_YANG (set_origin, + set_origin_cmd, + "set origin <egp|igp|incomplete>", + SET_STR + "BGP origin code\n" + "remote EGP\n" + "local IGP\n" + "unknown heritage\n") { - int idx_lb = 3; - int ret; - char *str; + int idx_origin = 2; + const char *origin_type; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-origin']"; + char xpath_value[XPATH_MAXLEN]; - str = argv_concat(argv, argc, idx_lb); - ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "extcommunity bandwidth", str); - XFREE(MTYPE_TMP, str); - return ret; -} + if (strncmp(argv[idx_origin]->arg, "igp", 2) == 0) + origin_type = "igp"; + else if (strncmp(argv[idx_origin]->arg, "egp", 1) == 0) + origin_type = "egp"; + else if (strncmp(argv[idx_origin]->arg, "incomplete", 2) == 0) + origin_type = "incomplete"; + else { + vty_out(vty, "%% Invalid match origin type\n"); + return CMD_WARNING_CONFIG_FAILED; + } + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:origin", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, origin_type); -DEFUN (no_set_ecommunity_lb, - no_set_ecommunity_lb_cmd, - "no set extcommunity bandwidth <(1-25600)|cumulative|num-multipaths> [non-transitive]", - NO_STR - SET_STR - "BGP extended community attribute\n" - "Link bandwidth extended community\n" - "Bandwidth value in Mbps\n" - "Cumulative bandwidth of all multipaths (outbound-only)\n" - "Internally computed bandwidth based on number of multipaths (outbound-only)\n" - "Attribute is set as non-transitive\n") -{ - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "extcommunity bandwidth", NULL); + return nb_cli_apply_changes(vty, NULL); } -ALIAS (no_set_ecommunity_lb, - no_set_ecommunity_lb_short_cmd, - "no set extcommunity bandwidth", - NO_STR - SET_STR - "BGP extended community attribute\n" - "Link bandwidth extended community\n") - -DEFUN (set_origin, - set_origin_cmd, - "set origin <egp|igp|incomplete>", - SET_STR - "BGP origin code\n" - "remote EGP\n" - "local IGP\n" - "unknown heritage\n") +DEFUN_YANG (no_set_origin, + no_set_origin_cmd, + "no set origin [<egp|igp|incomplete>]", + NO_STR + SET_STR + "BGP origin code\n" + "remote EGP\n" + "local IGP\n" + "unknown heritage\n") { - int idx_origin = 2; - if (strncmp(argv[idx_origin]->arg, "igp", 2) == 0) - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "origin", "igp"); - if (strncmp(argv[idx_origin]->arg, "egp", 1) == 0) - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "origin", "egp"); - if (strncmp(argv[idx_origin]->arg, "incomplete", 2) == 0) - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "origin", "incomplete"); + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-origin']"; - vty_out(vty, "%% Invalid set origin type\n"); - return CMD_WARNING_CONFIG_FAILED; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } - -DEFUN (no_set_origin, - no_set_origin_cmd, - "no set origin [<egp|igp|incomplete>]", - NO_STR - SET_STR - "BGP origin code\n" - "remote EGP\n" - "local IGP\n" - "unknown heritage\n") +DEFUN_YANG (set_atomic_aggregate, + set_atomic_aggregate_cmd, + "set atomic-aggregate", + SET_STR + "BGP atomic aggregate attribute\n" ) { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "origin", NULL); -} + const char *xpath = + "./set-action[action='frr-bgp-route-map:atomic-aggregate']"; + char xpath_value[XPATH_MAXLEN]; + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:atomic-aggregate", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, NULL); -DEFUN (set_atomic_aggregate, - set_atomic_aggregate_cmd, - "set atomic-aggregate", - SET_STR - "BGP atomic aggregate attribute\n" ) -{ - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "atomic-aggregate", NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_atomic_aggregate, - no_set_atomic_aggregate_cmd, - "no set atomic-aggregate", - NO_STR - SET_STR - "BGP atomic aggregate attribute\n" ) +DEFUN_YANG (no_set_atomic_aggregate, + no_set_atomic_aggregate_cmd, + "no set atomic-aggregate", + NO_STR + SET_STR + "BGP atomic aggregate attribute\n" ) { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "atomic-aggregate", NULL); + const char *xpath = + "./set-action[action='frr-bgp-route-map:atomic-aggregate']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_aggregator_as, - set_aggregator_as_cmd, - "set aggregator as (1-4294967295) A.B.C.D", - SET_STR - "BGP aggregator attribute\n" - "AS number of aggregator\n" - "AS number\n" - "IP address of aggregator\n") +DEFUN_YANG (set_aggregator_as, + set_aggregator_as_cmd, + "set aggregator as (1-4294967295) A.B.C.D", + SET_STR + "BGP aggregator attribute\n" + "AS number of aggregator\n" + "AS number\n" + "IP address of aggregator\n") { int idx_number = 3; int idx_ipv4 = 4; - int ret; - struct in_addr address; - char *argstr; - size_t argstr_len; + char xpath_asn[XPATH_MAXLEN]; + char xpath_addr[XPATH_MAXLEN]; + const char *xpath = + "./set-action[action='frr-bgp-route-map:aggregator']"; - ret = inet_aton(argv[idx_ipv4]->arg, &address); - if (ret == 0) { - vty_out(vty, "Aggregator IP address is invalid\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - argstr_len = - strlen(argv[idx_number]->arg) + strlen(argv[idx_ipv4]->arg) + 2; - argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, argstr_len); - - snprintf(argstr, argstr_len, "%s %s", argv[idx_number]->arg, - argv[idx_ipv4]->arg); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - ret = generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "aggregator as", argstr); + snprintf( + xpath_asn, sizeof(xpath_asn), + "%s/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-asn", + xpath); + nb_cli_enqueue_change(vty, xpath_asn, NB_OP_MODIFY, + argv[idx_number]->arg); - XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr); + snprintf( + xpath_addr, sizeof(xpath_addr), + "%s/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address", + xpath); + nb_cli_enqueue_change(vty, xpath_addr, NB_OP_MODIFY, + argv[idx_ipv4]->arg); - return ret; + return nb_cli_apply_changes(vty, NULL); } - -DEFUN (no_set_aggregator_as, - no_set_aggregator_as_cmd, - "no set aggregator as [(1-4294967295) A.B.C.D]", - NO_STR - SET_STR - "BGP aggregator attribute\n" - "AS number of aggregator\n" - "AS number\n" - "IP address of aggregator\n") +DEFUN_YANG (no_set_aggregator_as, + no_set_aggregator_as_cmd, + "no set aggregator as [(1-4294967295) A.B.C.D]", + NO_STR + SET_STR + "BGP aggregator attribute\n" + "AS number of aggregator\n" + "AS number\n" + "IP address of aggregator\n") { - int idx_asn = 4; - int idx_ip = 5; - int ret; - struct in_addr address; - char *argstr; - size_t argstr_len; - - if (argc <= idx_asn) - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "aggregator as", NULL); - - ret = inet_aton(argv[idx_ip]->arg, &address); - if (ret == 0) { - vty_out(vty, "Aggregator IP address is invalid\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - argstr_len = strlen(argv[idx_asn]->arg) + strlen(argv[idx_ip]->arg) + 2; - argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, argstr_len); + const char *xpath = + "./set-action[action='frr-bgp-route-map:aggregator']"; - snprintf(argstr, argstr_len, "%s %s", argv[idx_asn]->arg, - argv[idx_ip]->arg); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} - ret = generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "aggregator as", argstr); +DEFUN_YANG (match_ipv6_next_hop, + match_ipv6_next_hop_cmd, + "match ipv6 next-hop X:X::X:X", + MATCH_STR + IPV6_STR + "Match IPv6 next-hop address of route\n" + "IPv6 address of next hop\n") +{ + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:ipv6-nexthop']"; + char xpath_value[XPATH_MAXLEN]; - XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:ipv6-address", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[3]->arg); - return ret; + return nb_cli_apply_changes(vty, NULL); } -DEFUN (match_ipv6_next_hop, - match_ipv6_next_hop_cmd, - "match ipv6 next-hop X:X::X:X", - MATCH_STR - IPV6_STR - "Match IPv6 next-hop address of route\n" - "IPv6 address of next hop\n") +DEFUN_YANG (no_match_ipv6_next_hop, + no_match_ipv6_next_hop_cmd, + "no match ipv6 next-hop X:X::X:X", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 next-hop address of route\n" + "IPv6 address of next hop\n") { - int idx_ipv6 = 3; - return bgp_route_match_add(vty, "ipv6 next-hop", argv[idx_ipv6]->arg, - RMAP_EVENT_MATCH_ADDED); -} + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:ipv6-nexthop']"; -DEFUN (no_match_ipv6_next_hop, - no_match_ipv6_next_hop_cmd, - "no match ipv6 next-hop X:X::X:X", - NO_STR - MATCH_STR - IPV6_STR - "Match IPv6 next-hop address of route\n" - "IPv6 address of next hop\n") -{ - int idx_ipv6 = 4; - return bgp_route_match_delete(vty, "ipv6 next-hop", argv[idx_ipv6]->arg, - RMAP_EVENT_MATCH_DELETED); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFPY (match_ipv4_next_hop, +DEFPY_YANG (match_ipv4_next_hop, match_ipv4_next_hop_cmd, "match ip next-hop address A.B.C.D", MATCH_STR @@ -5357,13 +5708,20 @@ DEFPY (match_ipv4_next_hop, "IP address\n" "IP address of next-hop\n") { - int idx_ipv4 = 4; + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:ipv4-nexthop']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:ipv4-address", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg); - return bgp_route_match_add(vty, "ip next-hop address", - argv[idx_ipv4]->arg, RMAP_EVENT_MATCH_ADDED); + return nb_cli_apply_changes(vty, NULL); } -DEFPY (no_match_ipv4_next_hop, +DEFPY_YANG (no_match_ipv4_next_hop, no_match_ipv4_next_hop_cmd, "no match ip next-hop address [A.B.C.D]", NO_STR @@ -5373,264 +5731,312 @@ DEFPY (no_match_ipv4_next_hop, "IP address\n" "IP address of next-hop\n") { - return bgp_route_match_delete(vty, "ip next-hop address", NULL, - RMAP_EVENT_MATCH_DELETED); + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:ipv4-nexthop']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_ipv6_nexthop_peer, - set_ipv6_nexthop_peer_cmd, - "set ipv6 next-hop peer-address", - SET_STR - IPV6_STR - "Next hop address\n" - "Use peer address (for BGP only)\n") +DEFUN_YANG (set_ipv6_nexthop_peer, + set_ipv6_nexthop_peer_cmd, + "set ipv6 next-hop peer-address", + SET_STR + IPV6_STR + "Next hop address\n" + "Use peer address (for BGP only)\n") { - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 next-hop peer-address", NULL); + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv6-peer-address']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:preference", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true"); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_ipv6_nexthop_peer, - no_set_ipv6_nexthop_peer_cmd, - "no set ipv6 next-hop peer-address", - NO_STR - SET_STR - IPV6_STR - "IPv6 next-hop address\n" - "Use peer address (for BGP only)\n") +DEFUN_YANG (no_set_ipv6_nexthop_peer, + no_set_ipv6_nexthop_peer_cmd, + "no set ipv6 next-hop peer-address", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "Use peer address (for BGP only)\n") { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 next-hop peer-address", NULL); + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv6-peer-address']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_ipv6_nexthop_prefer_global, - set_ipv6_nexthop_prefer_global_cmd, - "set ipv6 next-hop prefer-global", - SET_STR - IPV6_STR - "IPv6 next-hop address\n" - "Prefer global over link-local if both exist\n") +DEFUN_YANG (set_ipv6_nexthop_prefer_global, + set_ipv6_nexthop_prefer_global_cmd, + "set ipv6 next-hop prefer-global", + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "Prefer global over link-local if both exist\n") { - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 next-hop prefer-global", NULL); - ; + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv6-prefer-global']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:preference", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true"); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_ipv6_nexthop_prefer_global, - no_set_ipv6_nexthop_prefer_global_cmd, - "no set ipv6 next-hop prefer-global", - NO_STR - SET_STR - IPV6_STR - "IPv6 next-hop address\n" - "Prefer global over link-local if both exist\n") +DEFUN_YANG (no_set_ipv6_nexthop_prefer_global, + no_set_ipv6_nexthop_prefer_global_cmd, + "no set ipv6 next-hop prefer-global", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "Prefer global over link-local if both exist\n") { - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 next-hop prefer-global", NULL); + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv6-prefer-global']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (set_ipv6_nexthop_global, - set_ipv6_nexthop_global_cmd, - "set ipv6 next-hop global X:X::X:X", - SET_STR - IPV6_STR - "IPv6 next-hop address\n" - "IPv6 global address\n" - "IPv6 address of next hop\n") +DEFUN_YANG (set_ipv6_nexthop_global, + set_ipv6_nexthop_global_cmd, + "set ipv6 next-hop global X:X::X:X", + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 global address\n" + "IPv6 address of next hop\n") { int idx_ipv6 = 4; - struct in6_addr addr; - int ret; + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv6-nexthop-global']"; + char xpath_value[XPATH_MAXLEN]; - ret = inet_pton(AF_INET6, argv[idx_ipv6]->arg, &addr); - if (!ret) { - vty_out(vty, "%% Malformed nexthop address\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (IN6_IS_ADDR_UNSPECIFIED(&addr) || IN6_IS_ADDR_LOOPBACK(&addr) - || IN6_IS_ADDR_MULTICAST(&addr) || IN6_IS_ADDR_LINKLOCAL(&addr)) { - vty_out(vty, "%% Invalid global nexthop address\n"); - return CMD_WARNING_CONFIG_FAILED; - } + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:ipv6-address", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_ipv6]->arg); - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 next-hop global", argv[idx_ipv6]->arg); + return nb_cli_apply_changes(vty, NULL); } - -DEFUN (no_set_ipv6_nexthop_global, - no_set_ipv6_nexthop_global_cmd, - "no set ipv6 next-hop global X:X::X:X", - NO_STR - SET_STR - IPV6_STR - "IPv6 next-hop address\n" - "IPv6 global address\n" - "IPv6 address of next hop\n") +DEFUN_YANG (no_set_ipv6_nexthop_global, + no_set_ipv6_nexthop_global_cmd, + "no set ipv6 next-hop global X:X::X:X", + NO_STR + SET_STR + IPV6_STR + "IPv6 next-hop address\n" + "IPv6 global address\n" + "IPv6 address of next hop\n") { - int idx_ipv6 = 5; - if (argc <= idx_ipv6) - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 next-hop global", NULL); - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 next-hop global", argv[idx_ipv6]->arg); + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv6-nexthop-global']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } #ifdef KEEP_OLD_VPN_COMMANDS -DEFUN (set_vpn_nexthop, - set_vpn_nexthop_cmd, - "set <vpnv4 next-hop A.B.C.D|vpnv6 next-hop X:X::X:X>", - SET_STR - "VPNv4 information\n" - "VPN next-hop address\n" - "IP address of next hop\n" - "VPNv6 information\n" - "VPN next-hop address\n" - "IPv6 address of next hop\n") +DEFUN_YANG (set_vpn_nexthop, + set_vpn_nexthop_cmd, + "set <vpnv4 next-hop A.B.C.D|vpnv6 next-hop X:X::X:X>", + SET_STR + "VPNv4 information\n" + "VPN next-hop address\n" + "IP address of next hop\n" + "VPNv6 information\n" + "VPN next-hop address\n" + "IPv6 address of next hop\n") { int idx_ip = 3; afi_t afi; int idx = 0; + char xpath_value[XPATH_MAXLEN]; if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { - if (afi == AFI_IP) - return generic_set_add( - vty, VTY_GET_CONTEXT(route_map_index), - "ipv4 vpn next-hop", argv[idx_ip]->arg); - else - return generic_set_add( - vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 vpn next-hop", argv[idx_ip]->arg); + if (afi == AFI_IP) { + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv4-vpn-address']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:ipv4-address", + xpath); + } else { + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv6-vpn-address']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:ipv6-address", + xpath); + } + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_ip]->arg); + + return nb_cli_apply_changes(vty, NULL); } + return CMD_SUCCESS; } -DEFUN (no_set_vpn_nexthop, - no_set_vpn_nexthop_cmd, - "no set <vpnv4 next-hop A.B.C.D|vpnv6 next-hop X:X::X:X>", - NO_STR - SET_STR - "VPNv4 information\n" - "VPN next-hop address\n" - "IP address of next hop\n" - "VPNv6 information\n" - "VPN next-hop address\n" - "IPv6 address of next hop\n") +DEFUN_YANG (no_set_vpn_nexthop, + no_set_vpn_nexthop_cmd, + "no set <vpnv4 next-hop A.B.C.D|vpnv6 next-hop X:X::X:X>", + NO_STR + SET_STR + "VPNv4 information\n" + "VPN next-hop address\n" + "IP address of next hop\n" + "VPNv6 information\n" + "VPN next-hop address\n" + "IPv6 address of next hop\n") { - int idx_ip = 4; - char *arg; afi_t afi; int idx = 0; - if (argc <= idx_ip) - arg = NULL; - else - arg = argv[idx_ip]->arg; if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) { - if (afi == AFI_IP) - return generic_set_delete( - vty, VTY_GET_CONTEXT(route_map_index), - "ipv4 vpn next-hop", arg); - else - return generic_set_delete( - vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 vpn next-hop", argv[idx_ip]->arg); + if (afi == AFI_IP) { + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv4-vpn-address']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + } else { + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv6-vpn-address']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + } + return nb_cli_apply_changes(vty, NULL); } return CMD_SUCCESS; } #endif /* KEEP_OLD_VPN_COMMANDS */ -DEFUN (set_ipx_vpn_nexthop, - set_ipx_vpn_nexthop_cmd, - "set <ipv4|ipv6> vpn next-hop <A.B.C.D|X:X::X:X>", - SET_STR - "IPv4 information\n" - "IPv6 information\n" - "VPN information\n" - "VPN next-hop address\n" - "IP address of next hop\n" - "IPv6 address of next hop\n") +DEFUN_YANG (set_ipx_vpn_nexthop, + set_ipx_vpn_nexthop_cmd, + "set <ipv4|ipv6> vpn next-hop <A.B.C.D|X:X::X:X>", + SET_STR + "IPv4 information\n" + "IPv6 information\n" + "VPN information\n" + "VPN next-hop address\n" + "IP address of next hop\n" + "IPv6 address of next hop\n") { int idx_ip = 4; afi_t afi; int idx = 0; + char xpath_value[XPATH_MAXLEN]; if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) { - if (afi == AFI_IP) - return generic_set_add( - vty, VTY_GET_CONTEXT(route_map_index), - "ipv4 vpn next-hop", argv[idx_ip]->arg); - else - return generic_set_add( - vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 vpn next-hop", argv[idx_ip]->arg); + if (afi == AFI_IP) { + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv4-vpn-address']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:ipv4-address", + xpath); + } else { + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv6-vpn-address']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:ipv6-address", + xpath); + } + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_ip]->arg); + return nb_cli_apply_changes(vty, NULL); } return CMD_SUCCESS; } -DEFUN (no_set_ipx_vpn_nexthop, - no_set_ipx_vpn_nexthop_cmd, - "no set <ipv4|ipv6> vpn next-hop [<A.B.C.D|X:X::X:X>]", - NO_STR - SET_STR - "IPv4 information\n" - "IPv6 information\n" - "VPN information\n" - "VPN next-hop address\n" - "IP address of next hop\n" - "IPv6 address of next hop\n") -{ - int idx_ip = 5; - char *arg; +DEFUN_YANG (no_set_ipx_vpn_nexthop, + no_set_ipx_vpn_nexthop_cmd, + "no set <ipv4|ipv6> vpn next-hop [<A.B.C.D|X:X::X:X>]", + NO_STR + SET_STR + "IPv4 information\n" + "IPv6 information\n" + "VPN information\n" + "VPN next-hop address\n" + "IP address of next hop\n" + "IPv6 address of next hop\n") +{ afi_t afi; int idx = 0; - if (argc <= idx_ip) - arg = NULL; - else - arg = argv[idx_ip]->arg; if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) { - if (afi == AFI_IP) - return generic_set_delete( - vty, VTY_GET_CONTEXT(route_map_index), - "ipv4 vpn next-hop", arg); - else - return generic_set_delete( - vty, VTY_GET_CONTEXT(route_map_index), - "ipv6 vpn next-hop", arg); + if (afi == AFI_IP) { + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv4-vpn-address']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + } else { + const char *xpath = + "./set-action[action='frr-bgp-route-map:ipv6-vpn-address']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + } + return nb_cli_apply_changes(vty, NULL); } return CMD_SUCCESS; } -DEFUN (set_originator_id, - set_originator_id_cmd, - "set originator-id A.B.C.D", - SET_STR - "BGP originator ID attribute\n" - "IP address of originator\n") +DEFUN_YANG (set_originator_id, + set_originator_id_cmd, + "set originator-id A.B.C.D", + SET_STR + "BGP originator ID attribute\n" + "IP address of originator\n") { int idx_ipv4 = 2; - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "originator-id", argv[idx_ipv4]->arg); -} + const char *xpath = + "./set-action[action='frr-bgp-route-map:originator-id']"; + char xpath_value[XPATH_MAXLEN]; + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:originator-id", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_ipv4]->arg); -DEFUN (no_set_originator_id, - no_set_originator_id_cmd, - "no set originator-id [A.B.C.D]", - NO_STR - SET_STR - "BGP originator ID attribute\n" - "IP address of originator\n") + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_originator_id, + no_set_originator_id_cmd, + "no set originator-id [A.B.C.D]", + NO_STR + SET_STR + "BGP originator ID attribute\n" + "IP address of originator\n") { - int idx = 0; - char *arg = - argv_find(argv, argc, "A.B.C.D", &idx) ? argv[idx]->arg : NULL; + const char *xpath = + "./set-action[action='frr-bgp-route-map:originator-id']"; - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "originator-id", arg); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } - /* Initialization of route map. */ void bgp_route_map_init(void) { diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c new file mode 100644 index 0000000000..fc59122184 --- /dev/null +++ b/bgpd/bgp_routemap_nb.c @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_routemap_nb.h" + +/* clang-format off */ +const struct frr_yang_module_info frr_bgp_route_map_info = { + .name = "frr-bgp-route-map", + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:local-preference", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_local_preference_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:script", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_script_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_script_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:origin", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_origin_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_origin_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:rpki", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_rpki_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:probability", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_probability_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-vrf", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv4-address", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-interface", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv6-address", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-local", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_peer_local_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_peer_local_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:list-name", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_list_name_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_list_name_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-default-route", + .cbs = { + .create = lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_create, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-vni", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-route-type", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:route-distinguisher", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list", + .cbs = { + .apply_finish = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:ipv4-address", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:ipv6-address", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:distance", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_distance_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_distance_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-rt", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv4-address", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_ipv4_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv4-nexthop", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv6-address", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:preference", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_preference_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_preference_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:label-index", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_label_index_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_label_index_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:local-pref", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_local_pref_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_local_pref_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:weight", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_weight_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_weight_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:origin", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_origin_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_origin_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:originator-id", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_originator_id_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_originator_id_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:table", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_table_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_table_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:atomic-aggregate", + .cbs = { + .create = lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_create, + .destroy = lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:prepend-as-path", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:last-as", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_last_as_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_last_as_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:exclude-as-path", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:community-none", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_community_none_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_community_none_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:community-string", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_community_string_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_community_string_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:large-community-none", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_large_community_none_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_large_community_none_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:large-community-string", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_large_community_string_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_large_community_string_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator", + .cbs = { + .apply_finish = lib_route_map_entry_set_action_rmap_set_action_aggregator_finish, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-asn", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:comm-list-name", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_comm_list_name_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_comm_list_name_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb", + .cbs = { + .apply_finish = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h new file mode 100644 index 0000000000..a15f521513 --- /dev/null +++ b/bgpd/bgp_routemap_nb.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_BGP_ROUTEMAP_NB_H_ +#define _FRR_BGP_ROUTEMAP_NB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct frr_yang_module_info frr_bgp_route_map_info; + +/* prototypes */ +int lib_route_map_entry_match_condition_rmap_match_condition_local_preference_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_script_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_script_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_origin_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_origin_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_rpki_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_probability_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_peer_local_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_peer_local_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_extended_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_extended_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_list_name_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_list_name_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_create(struct nb_cb_create_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy(struct nb_cb_destroy_args *args); +void lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish(struct nb_cb_apply_finish_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_distance_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_preference_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_preference_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_label_index_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_label_index_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_local_pref_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_local_pref_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_weight_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_weight_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_origin_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_origin_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_originator_id_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_originator_id_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_table_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_table_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_create(struct nb_cb_create_args *args); +int lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_last_as_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_last_as_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_community_none_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_community_none_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_community_string_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_community_string_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_large_community_none_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_large_community_none_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_large_community_string_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_large_community_string_destroy(struct nb_cb_destroy_args *args); +void lib_route_map_entry_set_action_rmap_set_action_aggregator_finish(struct nb_cb_apply_finish_args *args); +int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_extended_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_extended_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_comm_list_name_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_comm_list_name_destroy(struct nb_cb_destroy_args *args); +void lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish(struct nb_cb_apply_finish_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(struct nb_cb_destroy_args *args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c new file mode 100644 index 0000000000..ec6284273e --- /dev/null +++ b/bgpd/bgp_routemap_nb_config.c @@ -0,0 +1,2637 @@ +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_routemap_nb.h" + +/* Add bgp route map rule. */ +static int bgp_route_match_add(struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type, + char *errmsg, size_t errmsg_len) +{ + int retval = CMD_SUCCESS; + enum rmap_compile_rets ret; + + ret = route_map_add_match(index, command, arg, type); + switch (ret) { + case RMAP_RULE_MISSING: + snprintf(errmsg, errmsg_len, "%% BGP Can't find rule."); + retval = CMD_WARNING_CONFIG_FAILED; + break; + case RMAP_COMPILE_ERROR: + snprintf(errmsg, errmsg_len, "%% BGP Argument is malformed."); + retval = CMD_WARNING_CONFIG_FAILED; + break; + case RMAP_COMPILE_SUCCESS: + /* + * Intentionally doing nothing here. + */ + break; + } + + return retval; +} + +/* Delete bgp route map rule. */ +static int bgp_route_match_delete(struct route_map_index *index, + const char *command, const char *arg, + route_map_event_t type, + char *errmsg, size_t errmsg_len) +{ + enum rmap_compile_rets ret; + int retval = CMD_SUCCESS; + char *dep_name = NULL; + const char *tmpstr; + char *rmap_name = NULL; + + if (type != RMAP_EVENT_MATCH_DELETED) { + /* ignore the mundane, the types without any dependency */ + if (arg == NULL) { + if ((tmpstr = route_map_get_match_arg(index, command)) + != NULL) + dep_name = + XSTRDUP(MTYPE_ROUTE_MAP_RULE, tmpstr); + } else { + dep_name = XSTRDUP(MTYPE_ROUTE_MAP_RULE, arg); + } + rmap_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, index->map->name); + } + + ret = route_map_delete_match(index, command, dep_name, type); + switch (ret) { + case RMAP_RULE_MISSING: + snprintf(errmsg, errmsg_len, "%% BGP Can't find rule."); + retval = CMD_WARNING_CONFIG_FAILED; + break; + case RMAP_COMPILE_ERROR: + snprintf(errmsg, errmsg_len, + "%% BGP Argument is malformed."); + retval = CMD_WARNING_CONFIG_FAILED; + break; + case RMAP_COMPILE_SUCCESS: + /* + * Nothing to do here + */ + break; + } + + XFREE(MTYPE_ROUTE_MAP_RULE, dep_name); + XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name); + + return retval; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:local-preference + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_local_preference_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *local_pref; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + local_pref = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "local-preference"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "local-preference", + local_pref, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:script + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_script_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *script; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + script = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "script"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "script", + script, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_script_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:origin + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_origin_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *origin; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + origin = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "origin"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "origin", origin, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_origin_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:rpki + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_rpki_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *rpki; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + rpki = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "rpki"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "rpki", rpki, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:probability + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_probability_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *probability; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + probability = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "probability"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "probability", + probability, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-vrf + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *vrf; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + vrf = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "source-vrf"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "source-vrf", vrf, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv4-address + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *peer; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + peer = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "peer"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "peer", peer, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-interface + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *peer; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + peer = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "peer"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "peer", peer, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv6-address + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *peer; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + peer = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "peer"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "peer", peer, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-local + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_peer_local_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + bool value; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + value = yang_dnode_get_bool(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "peer"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + if (value) { + ret = bgp_route_match_add(rhc->rhc_rmi, "peer", + "local", + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_peer_local_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:list-name + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_list_name_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *list_name; + enum rmap_compile_rets ret = RMAP_COMPILE_SUCCESS; + const char *condition; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + list_name = yang_dnode_get_string(args->dnode, NULL); + condition = yang_dnode_get_string(args->dnode, + "../../frr-route-map:condition"); + + if (IS_MATCH_AS_LIST(condition)) { + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "as-path"; + rhc->rhc_event = RMAP_EVENT_ASLIST_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "as-path", + list_name, RMAP_EVENT_ASLIST_ADDED, + args->errmsg, args->errmsg_len); + } else if (IS_MATCH_MAC_LIST(condition)) { + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "mac address"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, + "mac address", + list_name, + RMAP_EVENT_FILTER_ADDED, + args->errmsg, args->errmsg_len); + } else if (IS_MATCH_ROUTE_SRC(condition)) { + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "ip route-source"; + rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, + "ip route-source", + list_name, RMAP_EVENT_FILTER_ADDED, + args->errmsg, args->errmsg_len); + } else if (IS_MATCH_ROUTE_SRC_PL(condition)) { + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "ip route-source prefix-list"; + rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, + "ip route-source prefix-list", + list_name, RMAP_EVENT_PLIST_ADDED, + args->errmsg, args->errmsg_len); + } + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_list_name_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-default-route + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_create( + struct nb_cb_create_args *args) +{ + struct routemap_hook_context *rhc; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "evpn default-route"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "evpn default-route", + NULL, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-vni + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *vni; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + vni = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "evpn vni"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "evpn vni", vni, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-route-type + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "evpn route-type"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "evpn route-type", + type, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:route-distinguisher + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *rd; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + rd = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "evpn rd"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "evpn rd", rd, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath = /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list + */ +void +lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish( + struct nb_cb_apply_finish_args *args) +{ + struct routemap_hook_context *rhc; + const char *value; + bool exact_match = false; + char *argstr; + const char *condition; + route_map_event_t event; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + value = yang_dnode_get_string(args->dnode, "./comm-list-name"); + + if (yang_dnode_exists(args->dnode, "./comm-list-name-exact-match")) + exact_match = yang_dnode_get_bool( + args->dnode, "./comm-list-name-exact-match"); + + if (exact_match) { + argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, + strlen(value) + strlen("exact-match") + 2); + + snprintf(argstr, (strlen(value) + strlen("exact-match") + 2), + "%s exact-match", value); + } else + argstr = (char *)value; + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + + condition = yang_dnode_get_string(args->dnode, + "../../frr-route-map:condition"); + if (IS_MATCH_COMMUNITY(condition)) { + rhc->rhc_rule = "community"; + event = RMAP_EVENT_CLIST_ADDED; + rhc->rhc_event = RMAP_EVENT_CLIST_DELETED; + } else if (IS_MATCH_LCOMMUNITY(condition)) { + rhc->rhc_rule = "large-community"; + event = RMAP_EVENT_LLIST_ADDED; + rhc->rhc_event = RMAP_EVENT_LLIST_DELETED; + } else { + rhc->rhc_rule = "extcommunity"; + event = RMAP_EVENT_ECLIST_ADDED; + rhc->rhc_event = RMAP_EVENT_ECLIST_DELETED; + } + + bgp_route_match_add(rhc->rhc_rmi, rhc->rhc_rule, argstr, event, + args->errmsg, args->errmsg_len); + + if (argstr != value) + XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr); +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; + +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:ipv4-address + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *peer; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + peer = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "ip next-hop address"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, rhc->rhc_rule, + peer, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:ipv6-address + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *peer; + enum rmap_compile_rets ret; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + peer = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "ipv6 next-hop"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, rhc->rhc_rule, + peer, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:distance + */ +int lib_route_map_entry_set_action_rmap_set_action_distance_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "distance"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "distance", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_distance_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-rt + */ +int +lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "extcommunity rt"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "extcommunity rt", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo + */ +int +lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "extcommunity soo"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "extcommunity soo", + type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv4-address + */ +int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *addr; + int rv = CMD_SUCCESS; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + addr = yang_dnode_get_string(args->dnode, NULL); + + rhc->rhc_shook = generic_set_delete; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + rhc->rhc_rule = "ipv4 vpn next-hop"; + + rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, addr, + args->errmsg, args->errmsg_len); + + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv4-nexthop + */ +int lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "ip next-hop"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, type, + args->errmsg, args->errmsg_len); + + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv6-address + */ +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *addr; + int rv = CMD_SUCCESS; + const char *action = NULL; + struct in6_addr i6a; + + action = yang_dnode_get_string(args->dnode, + "../../frr-route-map:action"); + switch (args->event) { + case NB_EV_VALIDATE: + if (action && IS_SET_IPV6_NH_GLOBAL(action)) { + yang_dnode_get_ipv6(&i6a, args->dnode, NULL); + if (IN6_IS_ADDR_UNSPECIFIED(&i6a) + || IN6_IS_ADDR_LOOPBACK(&i6a) + || IN6_IS_ADDR_MULTICAST(&i6a) + || IN6_IS_ADDR_LINKLOCAL(&i6a)) + return NB_ERR_VALIDATION; + } + /* FALLTHROUGH */ + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + break; + } + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + addr = yang_dnode_get_string(args->dnode, NULL); + + rhc->rhc_shook = generic_set_delete; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + if (IS_SET_IPV6_NH_GLOBAL(action)) + /* Set destroy information. */ + rhc->rhc_rule = "ipv6 next-hop global"; + else + rhc->rhc_rule = "ipv6 vpn next-hop"; + + rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, addr, + args->errmsg, args->errmsg_len); + + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:preference + */ +int lib_route_map_entry_set_action_rmap_set_action_preference_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + int rv = CMD_SUCCESS; + const char *action = NULL; + bool value; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + value = yang_dnode_get_bool(args->dnode, NULL); + + rhc->rhc_shook = generic_set_delete; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + action = yang_dnode_get_string(args->dnode, + "../../frr-route-map:action"); + + if (value) { + if (IS_SET_IPV6_PEER_ADDR(action)) + /* Set destroy information. */ + rhc->rhc_rule = "ipv6 next-hop peer-address"; + else + rhc->rhc_rule = "ipv6 next-hop prefer-global"; + + rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, + NULL, + args->errmsg, args->errmsg_len); + } + + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_preference_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:label-index + */ +int lib_route_map_entry_set_action_rmap_set_action_label_index_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "label-index"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "label-index", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_label_index_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:local-pref + */ +int lib_route_map_entry_set_action_rmap_set_action_local_pref_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "local-preference"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "local-preference", + type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_local_pref_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:weight + */ +int lib_route_map_entry_set_action_rmap_set_action_weight_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "weight"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "weight", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_weight_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:origin + */ +int lib_route_map_entry_set_action_rmap_set_action_origin_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "origin"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "origin", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_origin_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:originator-id + */ +int lib_route_map_entry_set_action_rmap_set_action_originator_id_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "originator-id"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "originator-id", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_originator_id_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:table + */ +int lib_route_map_entry_set_action_rmap_set_action_table_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "table"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "table", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_table_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:atomic-aggregate + */ +int +lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_create( + struct nb_cb_create_args *args) +{ + struct routemap_hook_context *rhc; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "atomic-aggregate"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, NULL, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:prepend-as-path + */ +int +lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "as-path prepend"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "as-path prepend", + type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:last-as + */ +int lib_route_map_entry_set_action_rmap_set_action_last_as_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *value; + char *argstr; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + value = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "as-path prepend"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, + strlen(value) + strlen("last-as") + 2); + + snprintf(argstr, (strlen(value) + strlen("last-as") + 2), + "last-as %s", value); + + rv = generic_set_add(rhc->rhc_rmi, "as-path prepend", + argstr, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr); + return NB_ERR_INCONSISTENCY; + } + + XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr); + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_last_as_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:exclude-as-path + */ +int +lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "as-path exclude"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "as-path exclude", + type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:community-none + */ +int lib_route_map_entry_set_action_rmap_set_action_community_none_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + bool none = false; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + none = yang_dnode_get_bool(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "community"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + if (none) { + rv = generic_set_add(rhc->rhc_rmi, "community", + "none", + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + return NB_OK; + } + + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_community_none_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:community-string + */ +int +lib_route_map_entry_set_action_rmap_set_action_community_string_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "community"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "community", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_community_string_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:large-community-none + */ +int +lib_route_map_entry_set_action_rmap_set_action_large_community_none_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + bool none = false; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + none = yang_dnode_get_bool(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "large-community"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + if (none) { + rv = generic_set_add(rhc->rhc_rmi, + "large-community", + "none", + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + return NB_OK; + } + + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_large_community_none_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:large-community-string + */ +int +lib_route_map_entry_set_action_rmap_set_action_large_community_string_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "large-community"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "large-community", + type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_large_community_string_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * xpath = + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator + */ +void lib_route_map_entry_set_action_rmap_set_action_aggregator_finish( + struct nb_cb_apply_finish_args *args) +{ + struct routemap_hook_context *rhc; + const char *asn; + const char *addr; + char *argstr; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + asn = yang_dnode_get_string(args->dnode, "./aggregator-asn"); + addr = yang_dnode_get_string(args->dnode, "./aggregator-address"); + + argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, + strlen(asn) + strlen(addr) + 2); + + snprintf(argstr, (strlen(asn) + strlen(addr) + 2), "%s %s", asn, addr); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "aggregator as"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, argstr, + args->errmsg, args->errmsg_len); + XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr); +} +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-asn + */ +int +lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address + */ +int +lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_modify( + struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + break; + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:comm-list-name + */ +int lib_route_map_entry_set_action_rmap_set_action_comm_list_name_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *value; + const char *action; + int rv = CMD_SUCCESS; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + value = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + + action = yang_dnode_get_string(args->dnode, + "../../frr-route-map:action"); + if (IS_SET_COMM_LIST_DEL(action)) + rhc->rhc_rule = "comm-list"; + else + rhc->rhc_rule = "large-comm-list"; + + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, value, + args->errmsg, args->errmsg_len); + + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_comm_list_name_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +enum e_community_lb_type { + EXPLICIT_BANDWIDTH, + CUMULATIVE_BANDWIDTH, + COMPUTED_BANDWIDTH +}; + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb + */ +void +lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish( + struct nb_cb_apply_finish_args *args) +{ + struct routemap_hook_context *rhc; + int lb_type; + char str[VTY_BUFSIZ]; + uint16_t bandwidth; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + lb_type = yang_dnode_get_enum(args->dnode, "./lb-type"); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "extcommunity bandwidth"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + switch (lb_type) { + case EXPLICIT_BANDWIDTH: + bandwidth = yang_dnode_get_uint16(args->dnode, "./bandwidth"); + snprintf(str, sizeof(str), "%d", bandwidth); + break; + case CUMULATIVE_BANDWIDTH: + snprintf(str, sizeof(str), "%s", "cumulative"); + break; + case COMPUTED_BANDWIDTH: + snprintf(str, sizeof(str), "%s", "num-multipaths"); + } + + if (yang_dnode_get_bool(args->dnode, "./two-octet-as-specific")) + strlcat(str, " non-transitive", sizeof(str)); + + generic_set_add(rhc->rhc_rmi, + "extcommunity bandwidth", str, + args->errmsg, args->errmsg_len); +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type + */ +int +lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify( + struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth + */ +int +lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_modify( + struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific + */ +int +lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify( + struct nb_cb_modify_args *args) +{ + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 9344384956..bb85ad393d 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -47,6 +47,9 @@ #include "bgpd/bgp_attr.h" #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_rpki.h" +#include "northbound_cli.h" + #include "lib/network.h" #include "lib/thread.h" #ifndef VTYSH_EXTRACT_PL @@ -63,10 +66,6 @@ DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server"); DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group"); -#define RPKI_VALID 1 -#define RPKI_NOTFOUND 2 -#define RPKI_INVALID 3 - #define POLLING_PERIOD_DEFAULT 3600 #define EXPIRE_INTERVAL_DEFAULT 7200 #define RETRY_INTERVAL_DEFAULT 600 @@ -1166,7 +1165,7 @@ DEFUN (show_rpki_prefix_table, return CMD_SUCCESS; } -DEFPY(show_rpki_as_number, show_rpki_as_number_cmd, +DEFPY (show_rpki_as_number, show_rpki_as_number_cmd, "show rpki as-number (1-4294967295)$by_asn", SHOW_STR RPKI_OUTPUT_STRING "Lookup by ASN in prefix table\n" @@ -1359,7 +1358,7 @@ DEFUN (no_debug_rpki, return CMD_SUCCESS; } -DEFUN (match_rpki, +DEFUN_YANG (match_rpki, match_rpki_cmd, "match rpki <valid|invalid|notfound>", MATCH_STR @@ -1368,27 +1367,19 @@ DEFUN (match_rpki, "Invalid prefix\n" "Prefix not found\n") { - VTY_DECLVAR_CONTEXT(route_map_index, index); - enum rmap_compile_rets ret; - - ret = route_map_add_match(index, "rpki", argv[2]->arg, - RMAP_EVENT_MATCH_ADDED); - switch (ret) { - case RMAP_RULE_MISSING: - vty_out(vty, "%% BGP Can't find rule.\n"); - return CMD_WARNING_CONFIG_FAILED; - case RMAP_COMPILE_ERROR: - vty_out(vty, "%% BGP Argument is malformed.\n"); - return CMD_WARNING_CONFIG_FAILED; - case RMAP_COMPILE_SUCCESS: - return CMD_SUCCESS; - break; - } + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:rpki']"; + char xpath_value[XPATH_MAXLEN]; - return CMD_SUCCESS; + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:rpki", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[2]->arg); + + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_match_rpki, +DEFUN_YANG (no_match_rpki, no_match_rpki_cmd, "no match rpki <valid|invalid|notfound>", NO_STR @@ -1398,26 +1389,11 @@ DEFUN (no_match_rpki, "Invalid prefix\n" "Prefix not found\n") { - VTY_DECLVAR_CONTEXT(route_map_index, index); - enum rmap_compile_rets ret; - - ret = route_map_delete_match(index, "rpki", argv[3]->arg, - RMAP_EVENT_MATCH_DELETED); - switch (ret) { - case RMAP_RULE_MISSING: - vty_out(vty, "%% BGP Can't find rule.\n"); - return CMD_WARNING_CONFIG_FAILED; - break; - case RMAP_COMPILE_ERROR: - vty_out(vty, "%% BGP Argument is malformed.\n"); - return CMD_WARNING_CONFIG_FAILED; - break; - case RMAP_COMPILE_SUCCESS: - return CMD_SUCCESS; - break; - } + const char *xpath = + "./match-condition[condition='frr-bgp-route-map:rpki']"; - return CMD_SUCCESS; + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + return nb_cli_apply_changes(vty, NULL); } static void install_cli_commands(void) diff --git a/eigrpd/eigrp_memory.h b/bgpd/bgp_rpki.h index bf1eff8751..4dd4b4a2b2 100644 --- a/eigrpd/eigrp_memory.h +++ b/bgpd/bgp_rpki.h @@ -1,6 +1,8 @@ -/* eigrpd memory type declarations - * - * Copyright (C) 2017 Donald Sharp +/* + * bgp_rpki code + * Copyright (C) 2021 NVIDIA Corporation and Mellanox Technologies, LTD + * All Rights Reserved + * Donald Sharp * * This file is part of FRR. * @@ -18,26 +20,14 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifndef __BGP_RPKI_H__ +#define __BGP_RPKI_H__ -#ifndef _FRR_EIGRP_MEMORY_H -#define _FRR_EIGRP_MEMORY_H - -#include "memory.h" - -DECLARE_MGROUP(EIGRPD); -DECLARE_MTYPE(EIGRP_TOP); -DECLARE_MTYPE(EIGRP_IF); -DECLARE_MTYPE(EIGRP_NEIGHBOR); -DECLARE_MTYPE(EIGRP_IF_PARAMS); -DECLARE_MTYPE(EIGRP_IF_INFO); -DECLARE_MTYPE(EIGRP_FIFO); -DECLARE_MTYPE(EIGRP_PACKET); -DECLARE_MTYPE(EIGRP_IPV4_INT_TLV); -DECLARE_MTYPE(EIGRP_SEQ_TLV); -DECLARE_MTYPE(EIGRP_AUTH_TLV); -DECLARE_MTYPE(EIGRP_AUTH_SHA256_TLV); -DECLARE_MTYPE(EIGRP_PREFIX_DESCRIPTOR); -DECLARE_MTYPE(EIGRP_ROUTE_DESCRIPTOR); -DECLARE_MTYPE(EIGRP_FSM_MSG); +enum rpki_states { + RPKI_NOT_BEING_USED, + RPKI_VALID, + RPKI_NOTFOUND, + RPKI_INVALID +}; -#endif /* _FRR_EIGRP_MEMORY_H */ +#endif diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index bc26314b50..3afdbea908 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -435,7 +435,7 @@ static struct peer *bgpPeerTable_lookup(struct variable *v, oid name[], if (peer == NULL) return NULL; - oid_copy_addr(name + namelen, addr, sizeof(struct in_addr)); + oid_copy_in_addr(name + namelen, addr); *length = sizeof(struct in_addr) + namelen; return peer; @@ -767,14 +767,12 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[], v->namelen + BGP_PATHATTR_ENTRY_OFFSET; offset = name + v->namelen; - oid_copy_addr(offset, &rn_p->u.prefix4, - IN_ADDR_SIZE); + oid_copy_in_addr(offset, &rn_p->u.prefix4); offset += IN_ADDR_SIZE; *offset = rn_p->prefixlen; offset++; - oid_copy_addr(offset, - &min->peer->su.sin.sin_addr, - IN_ADDR_SIZE); + oid_copy_in_addr(offset, + &min->peer->su.sin.sin_addr); addr->prefix = rn_p->u.prefix4; addr->prefixlen = rn_p->prefixlen; @@ -868,7 +866,7 @@ static int bgpTrapEstablished(struct peer *peer) if (ret == 0) return 0; - oid_copy_addr(index, &addr, IN_ADDR_SIZE); + oid_copy_in_addr(index, &addr); smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid, array_size(bgp_trap_oid), bgp_oid, @@ -887,7 +885,7 @@ static int bgpTrapBackwardTransition(struct peer *peer) if (ret == 0) return 0; - oid_copy_addr(index, &addr, IN_ADDR_SIZE); + oid_copy_in_addr(index, &addr); smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid, array_size(bgp_trap_oid), bgp_oid, diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index 7e3aa2a48a..833bdec2ed 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -201,18 +201,17 @@ struct bgp_node *bgp_table_subtree_lookup(const struct bgp_table *table, } printfrr_ext_autoreg_p("BD", printfrr_bd) -static ssize_t printfrr_bd(char *buf, size_t bsz, const char *fmt, - int prec, const void *ptr) +static ssize_t printfrr_bd(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) { const struct bgp_dest *dest = ptr; - const struct prefix *p; + const struct prefix *p = bgp_dest_get_prefix(dest); + char cbuf[PREFIX_STRLEN]; - if (dest) { - p = bgp_dest_get_prefix(dest); - prefix2str(p, buf, bsz); - } else { - strlcpy(buf, "NULL", bsz); - } + if (!dest) + return bputs(buf, "(null)"); - return 2; + /* need to get the real length even if buffer too small */ + prefix2str(p, cbuf, sizeof(cbuf)); + return bputs(buf, cbuf); } diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index fb64f010f9..bb0c95e32f 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -267,7 +267,7 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp, } if ((flags & UPDWALK_FLAGS_ADVQUEUE) && adj->adv && adj->adv->baa) { - route_vty_out_tmp(vty, dest_p, + route_vty_out_tmp(vty, dest, dest_p, adj->adv->baa->attr, SUBGRP_SAFI(subgrp), 0, NULL, false); @@ -275,7 +275,7 @@ static void subgrp_show_adjq_vty(struct update_subgroup *subgrp, } if ((flags & UPDWALK_FLAGS_ADVERTISED) && adj->attr) { - route_vty_out_tmp(vty, dest_p, + route_vty_out_tmp(vty, dest, dest_p, adj->attr, SUBGRP_SAFI(subgrp), 0, NULL, false); diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c index cb459ae13c..8d2cffbb47 100644 --- a/bgpd/bgp_vpn.c +++ b/bgpd/bgp_vpn.c @@ -229,8 +229,9 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer, } rd_header = 0; } - route_vty_out_tmp(vty, bgp_dest_get_prefix(rm), attr, - safi, use_json, json_routes, false); + route_vty_out_tmp(vty, rm, bgp_dest_get_prefix(rm), + attr, safi, use_json, json_routes, + false); output_count++; } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 6a44303339..1e465d2620 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -1324,16 +1324,19 @@ DEFUN_YANG_NOSH(router_bgp, as_t as; struct bgp *bgp; const char *name = NULL; - char as_str[12] = {'\0'}; enum bgp_instance_type inst_type; char base_xpath[XPATH_MAXLEN]; + const struct lyd_node *bgp_glb_dnode; // "router bgp" without an ASN if (argc == 2) { // Pending: Make VRF option available for ASN less config - bgp = bgp_get_default(); + snprintf(base_xpath, sizeof(base_xpath), FRR_BGP_GLOBAL_XPATH, + "frr-bgp:bgp", "bgp", VRF_DEFAULT_NAME); - if (bgp == NULL) { + bgp_glb_dnode = yang_dnode_get(vty->candidate_config->dnode, + base_xpath); + if (!bgp_glb_dnode) { vty_out(vty, "%% No BGP process is configured\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -1343,31 +1346,19 @@ DEFUN_YANG_NOSH(router_bgp, return CMD_WARNING_CONFIG_FAILED; } - snprintf(base_xpath, sizeof(base_xpath), FRR_BGP_GLOBAL_XPATH, - "frr-bgp:bgp", "bgp", VRF_DEFAULT_NAME); - - nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); - snprintf(as_str, 12, "%d", bgp->as); - nb_cli_enqueue_change(vty, "./global/local-as", NB_OP_MODIFY, - as_str); - if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) { - nb_cli_enqueue_change(vty, - "./global/instance-type-view", - NB_OP_MODIFY, "true"); - } + as = yang_dnode_get_uint32(bgp_glb_dnode, "./global/local-as"); - nb_cli_pending_commit_check(vty); - ret = nb_cli_apply_changes(vty, base_xpath); - if (ret == CMD_SUCCESS) { - VTY_PUSH_XPATH(BGP_NODE, base_xpath); + VTY_PUSH_XPATH(BGP_NODE, base_xpath); - /* - * For backward compatibility with old commands we still - * need to use the qobj infrastructure. - */ + /* + * For backward compatibility with old commands we still + * need to use the qobj infrastructure. + */ + bgp = bgp_lookup(as, NULL); + if (bgp) VTY_PUSH_CONTEXT(BGP_NODE, bgp); - } - return ret; + + return CMD_SUCCESS; } // "router bgp X" @@ -1399,7 +1390,6 @@ DEFUN_YANG_NOSH(router_bgp, NB_OP_MODIFY, "true"); } - nb_cli_pending_commit_check(vty); ret = nb_cli_apply_changes(vty, base_xpath); if (ret == CMD_SUCCESS) { VTY_PUSH_XPATH(BGP_NODE, base_xpath); @@ -1408,6 +1398,7 @@ DEFUN_YANG_NOSH(router_bgp, * For backward compatibility with old commands we still * need to use the qobj infrastructure. */ + nb_cli_pending_commit_check(vty); bgp = bgp_lookup(as, name); if (bgp) VTY_PUSH_CONTEXT(BGP_NODE, bgp); @@ -1423,10 +1414,7 @@ DEFUN_YANG(no_router_bgp, "no router bgp [(1-4294967295)$instasn [<view|vrf> VIEWVRFNAME]]", NO_STR ROUTER_STR BGP_STR AS_STR BGP_INSTANCE_HELP_STR) { - int idx_asn = 3; int idx_vrf = 5; - as_t as = 0; - struct bgp *bgp; const char *name = NULL; char base_xpath[XPATH_MAXLEN]; const struct lyd_node *bgp_glb_dnode; @@ -1448,72 +1436,15 @@ DEFUN_YANG(no_router_bgp, vty_out(vty, "%% Please specify ASN and VRF\n"); return CMD_WARNING_CONFIG_FAILED; } - - /* tcli mode bgp would not be set until apply stage. */ - bgp = nb_running_get_entry(bgp_glb_dnode, NULL, false); - if (!bgp) - return CMD_SUCCESS; - - if (bgp->l3vni) { - vty_out(vty, "%% Please unconfigure l3vni %u", - bgp->l3vni); - return CMD_WARNING_CONFIG_FAILED; - } } else { - as = strtoul(argv[idx_asn]->arg, NULL, 10); - if (argc > 4) name = argv[idx_vrf]->arg; + else + name = VRF_DEFAULT_NAME; - /* Lookup bgp structure. */ - bgp = bgp_lookup(as, name); - if (!bgp) { - vty_out(vty, "%% Can't find BGP instance\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - if (bgp->l3vni) { - vty_out(vty, "%% Please unconfigure l3vni %u\n", - bgp->l3vni); - return CMD_WARNING_CONFIG_FAILED; - } - - /* Cannot delete default instance if vrf instances exist */ - if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { - struct listnode *node; - struct bgp *tmp_bgp; - - for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) { - if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) - continue; - if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_VRF_EXPORT) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST], - BGP_CONFIG_VRF_TO_VRF_EXPORT) || - (bgp == bgp_get_evpn() && - (CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))) || - (tmp_bgp->vnihash && hashcount(tmp_bgp->vnihash))) { - vty_out(vty, - "%% Cannot delete default BGP instance. Dependent VRF instances exist\n"); - return CMD_WARNING_CONFIG_FAILED; - } - } - } + snprintf(base_xpath, sizeof(base_xpath), FRR_BGP_GLOBAL_XPATH, + "frr-bgp:bgp", "bgp", name); } - snprintf(base_xpath, sizeof(base_xpath), FRR_BGP_GLOBAL_XPATH, - "frr-bgp:bgp", "bgp", - bgp->name ? bgp->name : VRF_DEFAULT_NAME); nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); @@ -3571,6 +3502,37 @@ DEFUN_YANG (no_bgp_bestpath_aspath_multipath_relax, return nb_cli_apply_changes(vty, NULL); } +/* "bgp bestpath peer-type multipath-relax" configuration. */ +DEFUN(bgp_bestpath_peer_type_multipath_relax, + bgp_bestpath_peer_type_multipath_relax_cmd, + "bgp bestpath peer-type multipath-relax", + BGP_STR + "Change the default bestpath selection\n" + "Peer type\n" + "Allow load sharing across routes learned from different peer types\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + SET_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX); + bgp_recalculate_all_bestpaths(bgp); + + return CMD_SUCCESS; +} + +DEFUN(no_bgp_bestpath_peer_type_multipath_relax, + no_bgp_bestpath_peer_type_multipath_relax_cmd, + "no bgp bestpath peer-type multipath-relax", + NO_STR BGP_STR + "Change the default bestpath selection\n" + "Peer type\n" + "Allow load sharing across routes learned from different peer types\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + UNSET_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX); + bgp_recalculate_all_bestpaths(bgp); + + return CMD_SUCCESS; +} + /* "bgp log-neighbor-changes" configuration. */ DEFUN_YANG(bgp_log_neighbor_changes, bgp_log_neighbor_changes_cmd, @@ -3727,6 +3689,29 @@ DEFPY (no_bgp_bestpath_bw, return CMD_SUCCESS; } +/* "no bgp default ipv6-unicast". */ +DEFUN(no_bgp_default_ipv6_unicast, no_bgp_default_ipv6_unicast_cmd, + "no bgp default ipv6-unicast", NO_STR + "BGP specific commands\n" + "Configure BGP defaults\n" + "Activate ipv6-unicast for a peer by default\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + UNSET_FLAG(bgp->flags, BGP_FLAG_DEFAULT_IPV6); + return CMD_SUCCESS; +} + +DEFUN(bgp_default_ipv6_unicast, bgp_default_ipv6_unicast_cmd, + "bgp default ipv6-unicast", + "BGP specific commands\n" + "Configure BGP defaults\n" + "Activate ipv6-unicast for a peer by default\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + SET_FLAG(bgp->flags, BGP_FLAG_DEFAULT_IPV6); + return CMD_SUCCESS; +} + /* "no bgp default ipv4-unicast". */ DEFUN (no_bgp_default_ipv4_unicast, no_bgp_default_ipv4_unicast_cmd, @@ -4005,7 +3990,7 @@ void cli_show_router_global_neighbor_config(struct vty *vty, DEFUN_YANG(bgp_listen_limit, bgp_listen_limit_cmd, - "bgp listen limit (1-5000)", + "bgp listen limit (1-65535)", "BGP specific commands\n" "BGP Dynamic Neighbors listen commands\n" "Maximum number of BGP Dynamic Neighbors that can be created\n" @@ -4022,7 +4007,7 @@ DEFUN_YANG(bgp_listen_limit, DEFUN_YANG(no_bgp_listen_limit, no_bgp_listen_limit_cmd, - "no bgp listen limit [(1-5000)]", + "no bgp listen limit [(1-65535)]", NO_STR "BGP specific commands\n" "BGP Dynamic Neighbors listen commands\n" @@ -4368,10 +4353,10 @@ DEFUN_YANG(neighbor_remote_as, return nb_cli_apply_changes(vty, base_xpath); } -int peer_conf_interface_create(struct bgp *bgp, const char *conf_if, afi_t afi, - safi_t safi, bool v6only, - const char *peer_group_name, int as_type, - as_t as, char *errmsg, size_t errmsg_len) +int peer_conf_interface_create(struct bgp *bgp, const char *conf_if, + bool v6only, const char *peer_group_name, + int as_type, as_t as, char *errmsg, + size_t errmsg_len) { struct peer *peer; struct peer_group *group; @@ -4388,16 +4373,10 @@ int peer_conf_interface_create(struct bgp *bgp, const char *conf_if, afi_t afi, peer = peer_lookup_by_conf_if(bgp, conf_if); if (peer) { if (as_type != AS_UNSPECIFIED) - ret = peer_remote_as(bgp, NULL, conf_if, &as, as_type, - afi, safi); + ret = peer_remote_as(bgp, NULL, conf_if, &as, as_type); } else { - if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_DEFAULT_IPV4) - && afi == AFI_IP && safi == SAFI_UNICAST) - peer = peer_create(NULL, conf_if, bgp, bgp->as, as, - as_type, 0, 0, NULL); - else - peer = peer_create(NULL, conf_if, bgp, bgp->as, as, - as_type, afi, safi, NULL); + peer = peer_create(NULL, conf_if, bgp, bgp->as, as, as_type, + NULL); if (!peer) { snprintf(errmsg, errmsg_len, @@ -6139,10 +6118,28 @@ DEFUN_YANG (neighbor_send_community, "Send Community attribute to this neighbor\n") { int idx_peer = 1; + char *peer_str = argv[idx_peer]->arg; + char base_xpath[XPATH_MAXLEN]; + char af_xpath[XPATH_MAXLEN]; + char std_xpath[XPATH_MAXLEN]; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); - return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty), - bgp_node_safi(vty), - PEER_FLAG_SEND_COMMUNITY); + snprintf(af_xpath, sizeof(af_xpath), FRR_BGP_AF_XPATH, + yang_afi_safi_value2identity(afi, safi)); + + if (peer_and_group_lookup_nb(vty, peer_str, base_xpath, + sizeof(base_xpath), af_xpath) + < 0) + return CMD_WARNING_CONFIG_FAILED; + + snprintf(std_xpath, sizeof(std_xpath), + "./%s/send-community/send-community", + bgp_afi_safi_get_container_str(afi, safi)); + + nb_cli_enqueue_change(vty, std_xpath, NB_OP_MODIFY, "true"); + + return nb_cli_apply_changes(vty, base_xpath); } ALIAS_HIDDEN(neighbor_send_community, neighbor_send_community_hidden_cmd, @@ -6159,10 +6156,28 @@ DEFUN_YANG (no_neighbor_send_community, "Send Community attribute to this neighbor\n") { int idx_peer = 2; + char *peer_str = argv[idx_peer]->arg; + char base_xpath[XPATH_MAXLEN]; + char af_xpath[XPATH_MAXLEN]; + char std_xpath[XPATH_MAXLEN]; + afi_t afi = bgp_node_afi(vty); + safi_t safi = bgp_node_safi(vty); - return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg, - bgp_node_afi(vty), bgp_node_safi(vty), - PEER_FLAG_SEND_COMMUNITY); + snprintf(af_xpath, sizeof(af_xpath), FRR_BGP_AF_XPATH, + yang_afi_safi_value2identity(afi, safi)); + + if (peer_and_group_lookup_nb(vty, peer_str, base_xpath, + sizeof(base_xpath), af_xpath) + < 0) + return CMD_WARNING_CONFIG_FAILED; + + snprintf(std_xpath, sizeof(std_xpath), + "./%s/send-community/send-community", + bgp_afi_safi_get_container_str(afi, safi)); + + nb_cli_enqueue_change(vty, std_xpath, NB_OP_MODIFY, "false"); + + return nb_cli_apply_changes(vty, base_xpath); } ALIAS_HIDDEN(no_neighbor_send_community, no_neighbor_send_community_hidden_cmd, @@ -10443,6 +10458,9 @@ static void bgp_show_bestpath_json(struct bgp *bgp, json_object *json) } else json_object_string_add(bestpath, "multiPathRelax", "false"); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX)) + json_object_boolean_true_add(bestpath, "peerTypeRelax"); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID)) json_object_string_add(bestpath, "compareRouterId", "true"); if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED) @@ -14358,7 +14376,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, vty_out(vty, "\n"); /* BFD information. */ - bgp_bfd_show_info(vty, p, use_json, json_neigh); + if (p->bfd_config) + bgp_bfd_show_info(vty, p, json_neigh); if (use_json) { if (p->conf_if) /* Configured interface name. */ @@ -16765,11 +16784,8 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, peer->rtt_expected, peer->rtt_keepalive_conf); /* bfd */ - if (peer->bfd_info) { - if (!peer_group_active(peer) || !g_peer->bfd_info) { - bgp_bfd_peer_config_write(vty, peer, addr); - } - } + if (peer->bfd_config) + bgp_bfd_peer_config_write(vty, peer, addr); /* password */ if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD)) @@ -16966,18 +16982,36 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, } } else { if (peer->afc[afi][safi]) { - if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { - if (CHECK_FLAG(bgp->flags, - BGP_FLAG_NO_DEFAULT_IPV4)) { + if ((afi == AFI_IP || afi == AFI_IP6) + && safi == SAFI_UNICAST) { + if (afi == AFI_IP + && CHECK_FLAG(bgp->flags, + BGP_FLAG_NO_DEFAULT_IPV4)) { + vty_out(vty, " neighbor %s activate\n", + addr); + } else if (afi == AFI_IP6 + && !CHECK_FLAG( + bgp->flags, + BGP_FLAG_DEFAULT_IPV6)) { vty_out(vty, " neighbor %s activate\n", addr); } - } else + } else { vty_out(vty, " neighbor %s activate\n", addr); + } } else { - if ((afi == AFI_IP) && (safi == SAFI_UNICAST)) { - if (!CHECK_FLAG(bgp->flags, - BGP_FLAG_NO_DEFAULT_IPV4)) { + if ((afi == AFI_IP || afi == AFI_IP6) + && safi == SAFI_UNICAST) { + if (afi == AFI_IP + && !CHECK_FLAG(bgp->flags, + BGP_FLAG_NO_DEFAULT_IPV4)) { + vty_out(vty, + " no neighbor %s activate\n", + addr); + } else if (afi == AFI_IP6 + && CHECK_FLAG( + bgp->flags, + BGP_FLAG_DEFAULT_IPV6)) { vty_out(vty, " no neighbor %s activate\n", addr); @@ -17411,6 +17445,10 @@ int bgp_config_write(struct vty *vty) if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_DEFAULT_IPV4)) vty_out(vty, " no bgp default ipv4-unicast\n"); + /* BGP default ipv6-unicast. */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DEFAULT_IPV6)) + vty_out(vty, " bgp default ipv6-unicast\n"); + /* BGP default local-preference. */ if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF) vty_out(vty, " bgp default local-preference %u\n", @@ -17581,6 +17619,10 @@ int bgp_config_write(struct vty *vty) vty_out(vty, "\n"); } + if (CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX)) + vty_out(vty, + " bgp bestpath peer-type multipath-relax\n"); + /* Link bandwidth handling. */ if (bgp->lb_handling == BGP_LINK_BW_IGNORE_BW) vty_out(vty, " bgp bestpath bandwidth ignore\n"); @@ -18065,6 +18107,11 @@ void bgp_vty_init(void) install_element(BGP_NODE, &bgp_bestpath_aspath_multipath_relax_cmd); install_element(BGP_NODE, &no_bgp_bestpath_aspath_multipath_relax_cmd); + /* "bgp bestpath peer-type multipath-relax" commands */ + install_element(BGP_NODE, &bgp_bestpath_peer_type_multipath_relax_cmd); + install_element(BGP_NODE, + &no_bgp_bestpath_peer_type_multipath_relax_cmd); + /* "bgp log-neighbor-changes" commands */ install_element(BGP_NODE, &bgp_log_neighbor_changes_cmd); install_element(BGP_NODE, &no_bgp_log_neighbor_changes_cmd); @@ -18081,6 +18128,10 @@ void bgp_vty_init(void) install_element(BGP_NODE, &no_bgp_default_ipv4_unicast_cmd); install_element(BGP_NODE, &bgp_default_ipv4_unicast_cmd); + /* "no bgp default ipv6-unicast" commands. */ + install_element(BGP_NODE, &no_bgp_default_ipv6_unicast_cmd); + install_element(BGP_NODE, &bgp_default_ipv6_unicast_cmd); + /* "bgp network import-check" commands. */ install_element(BGP_NODE, &bgp_network_import_check_cmd); install_element(BGP_NODE, &bgp_network_import_check_exact_cmd); diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 85619dd074..251bdc3fe3 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -205,9 +205,9 @@ extern int peer_local_interface_cfg(struct bgp *bgp, const char *ip_str, const char *str, char *errmsg, size_t errmsg_len); extern int peer_conf_interface_create(struct bgp *bgp, const char *conf_if, - afi_t afi, safi_t safi, bool v6only, - const char *peer_group_name, int as_type, - as_t as, char *errmsg, size_t errmsg_len); + bool v6only, const char *peer_group_name, + int as_type, as_t as, char *errmsg, + size_t errmsg_len); extern int peer_flag_modify_nb(struct bgp *bgp, const char *ip_str, struct peer *peer, uint32_t flag, bool set, char *errmsg, size_t errmsg_len); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index afdd5123fb..d1912db01f 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -283,20 +283,9 @@ static int bgp_ifp_down(struct interface *ifp) if (!CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) { for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { -#if defined(HAVE_CUMULUS) - /* Take down directly connected EBGP peers as well as - * 1-hop BFD - * tracked (directly connected) IBGP peers. - */ - if ((peer->ttl != BGP_DEFAULT_TTL) - && (peer->gtsm_hops != BGP_GTSM_HOPS_CONNECTED) - && (!peer->bfd_info - || bgp_bfd_is_peer_multihop(peer))) -#else - /* Take down directly connected EBGP peers */ + /* Take down directly connected peers. */ if ((peer->ttl != BGP_DEFAULT_TTL) && (peer->gtsm_hops != BGP_GTSM_HOPS_CONNECTED)) -#endif continue; if (ifp == peer->nexthop.ifp) { @@ -1180,6 +1169,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, int nh_family; unsigned int valid_nh_count = 0; int has_valid_label = 0; + bool allow_recursion = false; uint8_t distance; struct peer *peer; struct bgp_path_info *mpinfo; @@ -1257,7 +1247,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, || CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) || CHECK_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) - SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); + allow_recursion = true; if (info->attr->rmap_table_id) { SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID); @@ -1383,6 +1373,15 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, if (!nh_updated) continue; + /* Allow recursion if it is a multipath group with both + * eBGP and iBGP paths. + */ + if (!allow_recursion + && CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX) + && (mpinfo->peer->sort == BGP_PEER_IBGP + || mpinfo->peer->sort == BGP_PEER_CONFED)) + allow_recursion = true; + if (mpinfo->extra && bgp_is_valid_label(&mpinfo->extra->label[0]) && !CHECK_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE)) { @@ -1411,6 +1410,9 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, memcpy(api.opaque.data, aspath->str, api.opaque.length); } + if (allow_recursion) + SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); + /* * When we create an aggregate route we must also * install a Null0 route in the RIB, so overwrite diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 10532f0915..d37b9fa48c 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1161,7 +1161,9 @@ static void peer_free(struct peer *peer) XFREE(MTYPE_PEER_CONF_IF, peer->conf_if); - bfd_info_free(&(peer->bfd_info)); + /* Remove BFD configuration. */ + if (peer->bfd_config) + bgp_peer_remove_bfd_config(peer); FOREACH_AFI_SAFI (afi, safi) bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE); @@ -1468,8 +1470,10 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) { paf = peer_src->peer_af_array[afidx]; - if (paf != NULL) - peer_af_create(peer_dst, paf->afi, paf->safi); + if (paf != NULL) { + if (!peer_af_find(peer_dst, paf->afi, paf->safi)) + peer_af_create(peer_dst, paf->afi, paf->safi); + } } /* update-source apply */ @@ -1683,12 +1687,13 @@ void bgp_recalculate_all_bestpaths(struct bgp *bgp) */ struct peer *peer_create(union sockunion *su, const char *conf_if, struct bgp *bgp, as_t local_as, as_t remote_as, - int as_type, afi_t afi, safi_t safi, - struct peer_group *group) + int as_type, struct peer_group *group) { int active; struct peer *peer; char buf[SU_ADDRSTRLEN]; + afi_t afi; + safi_t safi; peer = peer_new(bgp); if (conf_if) { @@ -1747,9 +1752,23 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE); - if (afi && safi) { - peer->afc[afi][safi] = 1; - peer_af_create(peer, afi, safi); + /* If address family is IPv4 and `bgp default ipv4-unicast` (default), + * then activate the neighbor for this AF. + * If address family is IPv6 and `bgp default ipv6-unicast` + * (non-default), then activate the neighbor for this AF. + */ + FOREACH_AFI_SAFI (afi, safi) { + if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST) { + if ((afi == AFI_IP + && !CHECK_FLAG(bgp->flags, + BGP_FLAG_NO_DEFAULT_IPV4)) + || (afi == AFI_IP6 + && CHECK_FLAG(bgp->flags, + BGP_FLAG_DEFAULT_IPV6))) { + peer->afc[afi][safi] = 1; + peer_af_create(peer, afi, safi); + } + } } /* auto shutdown if configured */ @@ -1878,7 +1897,7 @@ void peer_as_change(struct peer *peer, as_t as, int as_specified) /* If peer does not exist, create new one. If peer already exists, set AS number to the peer. */ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if, - as_t *as, int as_type, afi_t afi, safi_t safi) + as_t *as, int as_type) { struct peer *peer; as_t local_as; @@ -1946,16 +1965,7 @@ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if, else local_as = bgp->as; - /* If this is IPv4 unicast configuration and "no bgp default - ipv4-unicast" is specified. */ - - if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_DEFAULT_IPV4) - && afi == AFI_IP && safi == SAFI_UNICAST) - peer_create(su, conf_if, bgp, local_as, *as, as_type, 0, - 0, NULL); - else - peer_create(su, conf_if, bgp, local_as, *as, as_type, - afi, safi, NULL); + peer_create(su, conf_if, bgp, local_as, *as, as_type, NULL); } return 0; @@ -2386,7 +2396,9 @@ int peer_delete(struct peer *peer) SET_FLAG(peer->flags, PEER_FLAG_DELETE); - bgp_bfd_deregister_peer(peer); + /* Remove BFD settings. */ + if (peer->bfd_config) + bgp_peer_remove_bfd_config(peer); /* Delete peer route flap dampening configuration. This needs to happen * before removing the peer from peer groups. @@ -2562,6 +2574,8 @@ struct peer_group *peer_group_get(struct bgp *bgp, const char *name) group->conf = peer_new(bgp); if (!CHECK_FLAG(bgp->flags, BGP_FLAG_NO_DEFAULT_IPV4)) group->conf->afc[AFI_IP][SAFI_UNICAST] = 1; + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DEFAULT_IPV6)) + group->conf->afc[AFI_IP6][SAFI_UNICAST] = 1; XFREE(MTYPE_BGP_PEER_HOST, group->conf->host); group->conf->host = XSTRDUP(MTYPE_BGP_PEER_HOST, name); group->conf->group = group; @@ -2668,7 +2682,11 @@ static void peer_group2peer_config_copy(struct peer_group *group, /* Update GR flags for the peer. */ bgp_peer_gr_flags_update(peer); - bgp_bfd_peer_group2peer_copy(conf, peer); + /* Apply BFD settings from group to peer if it exists. */ + if (conf->bfd_config) { + bgp_peer_configure_bfd(peer, false); + bgp_peer_config_apply(peer, group); + } } /* Peer group's remote AS configuration. */ @@ -2758,7 +2776,8 @@ int peer_group_delete(struct peer_group *group) XFREE(MTYPE_PEER_GROUP_HOST, group->name); group->name = NULL; - bfd_info_free(&(group->conf->bfd_info)); + if (group->conf->bfd_config) + bgp_peer_remove_bfd_config(group->conf); group->conf->group = NULL; peer_delete(group->conf); @@ -2996,7 +3015,7 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, } peer = peer_create(su, NULL, bgp, bgp->as, group->conf->as, - group->conf->as_type, 0, 0, group); + group->conf->as_type, group); peer = peer_lock(peer); /* group->peer list reference */ listnode_add(group->peer, peer); @@ -3008,7 +3027,10 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, FOREACH_AFI_SAFI (afi, safi) { if (group->conf->afc[afi][safi]) { peer->afc[afi][safi] = 1; - peer_af_create(peer, afi, safi); + + if (!peer_af_find(peer, afi, safi)) + peer_af_create(peer, afi, safi); + peer_group2peer_config_copy_af(group, peer, afi, safi); } else if (peer->afc[afi][safi]) @@ -3807,7 +3829,7 @@ struct peer *peer_create_bind_dynamic_neighbor(struct bgp *bgp, /* Create peer first; we've already checked group config is valid. */ peer = peer_create(su, NULL, bgp, bgp->as, group->conf->as, - group->conf->as_type, 0, 0, group); + group->conf->as_type, group); if (!peer) return NULL; @@ -7687,7 +7709,7 @@ void bgp_init(unsigned short instance) bgp_clist = community_list_init(); /* BFD init */ - bgp_bfd_init(); + bgp_bfd_init(bm->master); bgp_lp_vty_init(); diff --git a/bgpd/bgpd.conf.sample b/bgpd/bgpd.conf.sample deleted file mode 100644 index 1fb4f1600b..0000000000 --- a/bgpd/bgpd.conf.sample +++ /dev/null @@ -1,31 +0,0 @@ -! -*- bgp -*- -! -! BGPd sample configuration file -! -! $Id: bgpd.conf.sample,v 1.1 2002/12/13 20:15:29 paul Exp $ -! -hostname bgpd -password zebra -!enable password please-set-at-here -! -! -router bgp 7675 -! bgp router-id 10.0.0.1 -! network 10.0.0.0/8 -! neighbor 10.0.0.2 remote-as 7675 -! neighbor 10.0.0.2 ebgp-multihop -! -! address-family ipv4 unicast -! neighbor 10.0.0.2 route-map set-nexthop out -! neighbor 10.0.0.2 next-hop-self -! exit-address-family -! -! access-list all permit any -! -!route-map set-nexthop permit 10 -! match ip address all -! set ip next-hop 10.0.0.1 -! -!log file bgpd.log -! -log stdout diff --git a/bgpd/bgpd.conf.vnc.sample b/bgpd/bgpd.conf.vnc.sample deleted file mode 100644 index a8a2dc5fa0..0000000000 --- a/bgpd/bgpd.conf.vnc.sample +++ /dev/null @@ -1,91 +0,0 @@ -hostname H192.1.1.1 -password zebra -#enable password zebra -log stdout notifications -log monitor notifications -#debug bgp - -line vty -exec-timeout 1000 -exit - - -router bgp 64512 - - # Must set a router-id if no zebra (default 0.0.0.0) - bgp router-id 192.1.1.1 - - neighbor 192.1.1.2 remote-as 64512 - neighbor 192.1.1.2 description H192.1.1.2 - neighbor 192.1.1.2 update-source 192.1.1.1 - neighbor 192.1.1.2 advertisement-interval 1 - - neighbor 192.1.1.3 remote-as 64512 - neighbor 192.1.1.3 description H192.1.1.3 - neighbor 192.1.1.3 update-source 192.1.1.1 - neighbor 192.1.1.3 advertisement-interval 1 - - address-family ipv4 unicast - no neighbor 192.1.1.2 activate - no neighbor 192.1.1.3 activate - - address-family vpnv4 - neighbor 192.1.1.2 activate - neighbor 192.1.1.3 activate - exit-address-family - - address-family vpnv6 - neighbor 192.1.1.2 activate - neighbor 192.1.1.3 activate - exit-address-family - - vnc defaults - rd auto:vn:5226 - response-lifetime 45 - rt both 1000:1 1000:2 - exit-vnc - - vnc nve-group group1 - prefix vn 172.16.0.0/16 - exit-vnc - - vnc nve-group red - prefix vn 10.0.0.0/8 - rd auto:vn:10 - rt both 1000:10 - exit-vnc - - vnc nve-group blue - prefix vn 20.0.0.0/8 - rd auto:vn:20 - rt both 1000:20 - exit-vnc - - vnc nve-group green - prefix vn 30.0.0.0/8 - rd auto:vn:20 - rt both 1000:30 - exit-vnc - - vnc nve-group rfc4291v6c - prefix vn ::ac10:0/112 - rd auto:vn:5227 - rt both 2000:1 - exit-vnc - - vnc nve-group rfc4291v6m - prefix vn ::ffff:ac10:0/112 - rd auto:vn:5528 - rt both 3000:1 - exit-vnc - - vnc nve-group rfc6052v6 - prefix vn 64:ff9b::ac10:0/112 - rd auto:vn:5529 - rt both 4000:1 - exit-vnc - -exit - - - diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 52c9491ff2..51134dc8c5 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -45,6 +45,8 @@ #include "bgp_nexthop.h" #include "bgp_damp.h" +#include "lib/bfd.h" + #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -477,6 +479,8 @@ struct bgp { #define BGP_FLAG_SHUTDOWN (1 << 27) #define BGP_FLAG_SUPPRESS_FIB_PENDING (1 << 28) #define BGP_FLAG_SUPPRESS_DUPLICATES (1 << 29) +#define BGP_FLAG_DEFAULT_IPV6 (1 << 30) +#define BGP_FLAG_PEERTYPE_MULTIPATH_RELAX (1 << 31) enum global_mode GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE] [BGP_GLOBAL_GR_EVENT_CMD]; @@ -661,6 +665,9 @@ struct bgp { /* RB tree of ES-VRFs */ struct bgp_es_vrf_rb_head es_vrf_rb_tree; + /* Hash table of EVPN nexthops maintained per-tenant-VRF */ + struct hash *evpn_nh_table; + /* vrf flags */ uint32_t vrf_flags; #define BGP_VRF_AUTO (1 << 0) @@ -1557,8 +1564,29 @@ struct peer { #define PEER_RMAP_TYPE_EXPORT (1U << 7) /* neighbor route-map export */ #define PEER_RMAP_TYPE_AGGREGATE (1U << 8) /* aggregate-address route-map */ - /* peer specific BFD information */ - struct bfd_info *bfd_info; + /** Peer overwrite configuration. */ + struct bfd_session_config { + /** + * Manual configuration bit. + * + * This flag only makes sense for real peers (and not groups), + * it keeps track if the user explicitly configured BFD for a + * peer. + */ + bool manual; + /** Control Plane Independent. */ + bool cbit; + /** Detection multiplier. */ + uint8_t detection_multiplier; + /** Minimum required RX interval. */ + uint32_t min_rx; + /** Minimum required TX interval. */ + uint32_t min_tx; + /** Profile name. */ + char profile[BFD_PROFILE_NAME_LEN]; + /** Peer BFD session */ + struct bfd_session_params *session; + } * bfd_config; /* hostname and domainname advertised by host */ char *hostname; @@ -1796,7 +1824,7 @@ struct bgp_nlri { /* BGP Dynamic Neighbors feature */ #define BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT 100 #define BGP_DYNAMIC_NEIGHBORS_LIMIT_MIN 1 -#define BGP_DYNAMIC_NEIGHBORS_LIMIT_MAX 5000 +#define BGP_DYNAMIC_NEIGHBORS_LIMIT_MAX 65535 /* Flag for peer_clear_soft(). */ enum bgp_clear_type { @@ -1922,8 +1950,7 @@ extern bool peer_active(struct peer *); extern bool peer_active_nego(struct peer *); extern void bgp_recalculate_all_bestpaths(struct bgp *bgp); extern struct peer *peer_create(union sockunion *, const char *, struct bgp *, - as_t, as_t, int, afi_t, safi_t, - struct peer_group *); + as_t, as_t, int, struct peer_group *); extern struct peer *peer_create_accept(struct bgp *); extern void peer_xfer_config(struct peer *dst, struct peer *src); extern char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json, @@ -1991,7 +2018,7 @@ extern bool bgp_update_delay_configured(struct bgp *); extern int bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi); extern void peer_as_change(struct peer *, as_t, int); extern int peer_remote_as(struct bgp *, union sockunion *, const char *, as_t *, - int, afi_t, safi_t); + int); extern int peer_group_remote_as(struct bgp *, const char *, as_t *, int); extern int peer_delete(struct peer *peer); extern void peer_notify_unconfig(struct peer *peer); diff --git a/bgpd/subdir.am b/bgpd/subdir.am index 3991f7d1ed..07e71ba601 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -6,11 +6,6 @@ if BGPD noinst_LIBRARIES += bgpd/libbgp.a sbin_PROGRAMS += bgpd/bgpd noinst_PROGRAMS += bgpd/bgp_btoa -dist_examples_DATA += \ - bgpd/bgpd.conf.sample \ - bgpd/bgpd.conf.sample2 \ - bgpd/bgpd.conf.vnc.sample \ - # end vtysh_scan += \ bgpd/bgp_bfd.c \ bgpd/bgp_debug.c \ @@ -96,6 +91,8 @@ bgpd_libbgp_a_SOURCES = \ bgpd/bgp_regex.c \ bgpd/bgp_route.c \ bgpd/bgp_routemap.c \ + bgpd/bgp_routemap_nb.c \ + bgpd/bgp_routemap_nb_config.c \ bgpd/bgp_script.c \ bgpd/bgp_table.c \ bgpd/bgp_updgrp.c \ @@ -176,7 +173,9 @@ noinst_HEADERS += \ bgpd/bgp_pbr.h \ bgpd/bgp_rd.h \ bgpd/bgp_regex.h \ + bgpd/bgp_rpki.h \ bgpd/bgp_route.h \ + bgpd/bgp_routemap_nb.h \ bgpd/bgp_script.h \ bgpd/bgp_table.h \ bgpd/bgp_updgrp.h \ @@ -254,4 +253,6 @@ nodist_bgpd_bgpd_SOURCES = \ yang/frr-bgp-bmp.yang.c \ yang/frr-bgp-rpki.yang.c \ yang/frr-deviations-bgp-datacenter.yang.c \ + yang/frr-bgp-filter.yang.c \ + yang/frr-bgp-route-map.yang.c \ # end diff --git a/configure.ac b/configure.ac index 44d68f4845..f9516e559f 100755 --- a/configure.ac +++ b/configure.ac @@ -288,11 +288,17 @@ if test "$enable_clang_coverage" = "yes"; then fi if test "$enable_scripting" = "yes"; then - AX_PROG_LUA([5.3]) - AX_LUA_HEADERS + AX_PROG_LUA([5.3], [5.4], [], [ + AC_MSG_ERROR([Lua 5.3 is required to build with Lua support. No other version is supported.]) + ]) + AX_LUA_HEADERS([], [ + AC_MSG_ERROR([Lua 5.3 headers are required to build with Lua support. No other version is supported.]) + ]) AX_LUA_LIBS([ - AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting]) - LIBS="$LIBS $LUA_LIB" + AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting]) + LIBS="$LIBS $LUA_LIB" + ], [ + AC_MSG_ERROR([Lua 5.3 libraries are required to build with Lua support. No other version is supported.]) ]) fi diff --git a/debian/frr.dirs b/debian/frr.dirs index 9e592e370c..e3832d10a1 100644 --- a/debian/frr.dirs +++ b/debian/frr.dirs @@ -2,7 +2,6 @@ etc/frr/ etc/iproute2/rt_protos.d/ etc/logrotate.d/ usr/share/doc/frr/ -usr/share/doc/frr/examples/ usr/share/lintian/overrides/ usr/share/yang/ var/log/frr/ diff --git a/debian/frr.install b/debian/frr.install index 9972b579f0..48263222f8 100644 --- a/debian/frr.install +++ b/debian/frr.install @@ -1,5 +1,6 @@ debian/frr.conf usr/lib/tmpfiles.d etc/ +tools/etc/frr/frr.conf etc/frr/ tools/frr-reload usr/lib/frr/ usr/bin/mtracebis usr/bin/vtysh @@ -16,6 +17,5 @@ usr/lib/frr/*.sh usr/lib/frr/*d usr/lib/frr/watchfrr usr/lib/frr/zebra -usr/share/doc/frr/examples usr/share/man/ usr/share/yang/ diff --git a/debian/rules b/debian/rules index 25ae04261d..93d0cdb2a0 100755 --- a/debian/rules +++ b/debian/rules @@ -43,7 +43,6 @@ export PYTHON=python3 override_dh_auto_configure: $(shell dpkg-buildflags --export=sh); \ dh_auto_configure -- \ - --enable-exampledir=/usr/share/doc/frr/examples/ \ --localstatedir=/var/run/frr \ --sbindir=/usr/lib/frr \ --sysconfdir=/etc/frr \ @@ -92,8 +91,6 @@ endif cp -r tools/etc/* debian/tmp/etc/ -rm debian/tmp/etc/frr/daemons.conf - sed -e 's#^!log file #!log file /var/log/frr/#' -i debian/tmp/usr/share/doc/frr/examples/*sample* - # drop dev-only files find debian/tmp -name '*.la' -o -name '*.a' -o -name 'lib*.so' | xargs rm -f rm -rf debian/tmp/usr/include diff --git a/doc/developer/_static/overrides.css b/doc/developer/_static/overrides.css index 1d702bb6e9..302b8d6bd7 100644 --- a/doc/developer/_static/overrides.css +++ b/doc/developer/_static/overrides.css @@ -214,6 +214,22 @@ pre { .highlight .na { color: var(--primary-2); } .highlight .nv { color: var(--complement-0); } +.rst-content code.frrfmtout { + background-color: var(--secondary-1-9); + border-color: var(--secondary-1-1); + font-size:100%; +} +.rst-content code.frrfmtout::before { + content: "⇒ \""; +} +.rst-content code.frrfmtout::after { + content: "\""; +} +.rst-content code.frrfmtout span { + color: var(--secondary-1-4); + font-size:100%; +} + strong { font-weight:500; } diff --git a/doc/developer/building-frr-for-alpine.rst b/doc/developer/building-frr-for-alpine.rst index f88fc7bfdc..68e58c9d76 100644 --- a/doc/developer/building-frr-for-alpine.rst +++ b/doc/developer/building-frr-for-alpine.rst @@ -85,8 +85,6 @@ startup. To configure by hand: docker exec -it frr /bin/sh vi /etc/frr/daemons - cp /etc/frr/zebra.conf.sample /etc/frr/zebra.conf - vi /etc/frr/zebra.conf /etc/init.d/frr start Or, to configure the daemons using /etc/frr from a host volume, put the diff --git a/doc/developer/building-frr-for-ubuntu2004.rst b/doc/developer/building-frr-for-ubuntu2004.rst index ffc05a6841..58d72e2891 100644 --- a/doc/developer/building-frr-for-ubuntu2004.rst +++ b/doc/developer/building-frr-for-ubuntu2004.rst @@ -27,7 +27,7 @@ ubuntu apt repositories; in order to install it: .. code-block:: shell - curl https://bootstrap.pypa.io/2.7/get-pip.py --output get-pip.py + curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py sudo python2 ./get-pip.py # And verify the installation diff --git a/doc/developer/conf.py b/doc/developer/conf.py index f4bb65ec79..20265f4aad 100644 --- a/doc/developer/conf.py +++ b/doc/developer/conf.py @@ -17,6 +17,8 @@ import os import re import pygments from sphinx.highlighting import lexers +from sphinx.util import logging +logger = logging.getLogger(__name__) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -362,11 +364,37 @@ texinfo_documents = [ with open("../extra/frrlexer.py", "rb") as lex: frrlexerpy = lex.read() +frrfmt_re = re.compile(r'^\s*%(?P<spec>[^\s]+)\s+\((?P<types>.*)\)\s*$') + +def parse_frrfmt(env, text, node): + from sphinx import addnodes + + m = frrfmt_re.match(text) + if not m: + logger.warning('could not parse frrfmt:: %r' % (text), location=node) + node += addnodes.desc_name(text, text) + return text + + spec, types = m.group('spec'), m.group('types') + + node += addnodes.desc_sig_operator('%', '%') + node += addnodes.desc_name(spec + ' ', spec + ' ') + plist = addnodes.desc_parameterlist() + for typ in types.split(','): + typ = typ.strip() + plist += addnodes.desc_parameter(typ, typ) + node += plist + return '%' + spec + # custom extensions here def setup(app): # object type for FRR CLI commands, can be extended to document parent CLI # node later on app.add_object_type("clicmd", "clicmd") + + # printfrr extensions + app.add_object_type("frrfmt", "frrfmt", parse_node=parse_frrfmt) + # css overrides for HTML theme app.add_stylesheet("overrides.css") # load Pygments lexer for FRR config syntax diff --git a/doc/developer/link-state.rst b/doc/developer/link-state.rst index f1fc52966b..1cbaf27ffe 100644 --- a/doc/developer/link-state.rst +++ b/doc/developer/link-state.rst @@ -81,26 +81,47 @@ corresponds to a Link State information conveyed by the routing protocol. Functions ^^^^^^^^^ -A set of functions is provided to create, delete and compare Link State Node: +A set of functions is provided to create, delete and compare Link State +Node, Atribute and Prefix: .. c:function:: struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr router_id, struct in6_addr router6_id) -.. c:function:: voidls_node_del(struct ls_node *node) -.. c:function:: int ls_node_same(struct ls_node *n1, struct ls_node *n2) +.. c:function:: struct ls_attributes *ls_attributes_new(struct ls_node_id adv, struct in_addr local, struct in6_addr local6, uint32_t local_id) +.. c:function:: struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p) -and Link State Attributes: + Create respectively a new Link State Node, Attribute or Prefix. + Structure is dynamically allocated. Link State Node ID (adv) is mandatory + and: -.. c:function:: struct ls_attributes *ls_attributes_new(struct ls_node_id adv, struct in_addr local, struct in6_addr local6, uint32_t local_id) + - at least one of IPv4 or IPv6 must be provided for the router ID + (router_id or router6_id) for Node + - at least one of local, local6 or local_id must be provided for Attribute + - prefix is mandatory for Link State Prefix. + +.. c:function:: void ls_node_del(struct ls_node *node) .. c:function:: void ls_attributes_del(struct ls_attributes *attr) +.. c:function:: void ls_prefix_del(struct ls_prefix *pref) + + Remove, respectively Link State Node, Attributes or Prefix. + Data structure is freed. + +.. c:function:: void ls_attributes_srlg_del(struct ls_attributes *attr) + + Remove SRLGs attribute if defined. Data structure is freed. + +.. c:function:: int ls_node_same(struct ls_node *n1, struct ls_node *n2) .. c:function:: int ls_attributes_same(struct ls_attributes *a1, struct ls_attributes *a2) +.. c:function:: int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix*p2) + + Check, respectively if two Link State Nodes, Attributes or Prefix are equal. + Note that these routines have the same return value sense as '==' (which is + different from a comparison). -The low level API doesn't provide any particular functions for the Link State -Prefix structure as this latter is simpler to manipulate. Link State TED -------------- This is the high level API that provides functions to create, update, delete a -Link State Database to from a Traffic Engineering Database (TED). +Link State Database to build a Traffic Engineering Database (TED). Data Structures ^^^^^^^^^^^^^^^ @@ -143,35 +164,143 @@ A unique Key is used to identify both Vertices and Edges within the Graph. .. c:type:: struct ls_prefix .. c:type:: struct ls_ted +TED stores Vertex, Edge and Subnet elements with a RB Tree structure. +The Vertex key corresponds to the Router ID for OSPF and ISO System ID for +IS-IS. The Edge key corresponds to the IPv4 address, the lowest 64 bits of +the IPv6 address or the combination of the local & remote ID of the interface. +The Subnet key corresponds to the Prefix address (v4 or v6). -Functions -^^^^^^^^^ +An additional status for Vertex, Edge and Subnet allows to determine the state +of the element in the TED: UNSET, NEW, UPDATE, DELETE, SYNC, ORPHAN. Normal +state is SYNC. NEW, UPDATE and DELETE are temporary state when element is +processed. UNSET is normally never used and ORPHAN serves to identify elements +that must be remove when TED is cleaning. + +Vertex, Edges and Subnets management functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. c:function:: struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node) +.. c:function:: struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes) +.. c:function:: struct ls_subnet *ls_subnet_add(struct ls_ted *ted, struct ls_prefix *pref) + + Add, respectively new Vertex, Edge or Subnet to the Link State Datebase. + Vertex, Edge or Subnet are created from, respectively the Link State Node, + Attribute or Prefix structure. Data structure are dynamically allocated. + .. c:function:: struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node) +.. c:function:: struct ls_edge *ls_edge_update(struct ls_ted *ted, struct ls_attributes *attributes) +.. c:function:: struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref) + + Update, respectively Vertex, Edge or Subnet with, respectively the Link + State Node, Attribute or Prefix. A new data structure is created if no one + corresponds to the Link State Node, Attribute or Prefix. If element already + exists in the TED, its associated Link State information is replaced by the + new one if there are different and the old associated Link State information + is deleted and memory freed. + .. c:function:: void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex) +.. c:function:: void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex) +.. c:function:: void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge) +.. c:function:: void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge) +.. c:function:: void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet) +.. c:function:: void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet) + + Delete, respectively Link State Vertex, Edge or Subnet. Data structure are + freed but not the associated Link State information with the simple `_del()` + form of the function while the `_del_all()` version freed also associated + Link State information. TED is not modified if Vertex, Edge or Subnet is + NULL or not found in the Data Base. Note that references between Vertices, + Edges and Subnets are removed first. + .. c:function:: struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key) .. c:function:: struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted, struct ls_node_id id) -.. c:function:: int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2) -.. c:function:: struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes) -.. c:function:: struct ls_edge *ls_edge_update(struct ls_ted *ted, struct ls_attributes *attributes) -.. c:function:: void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge) + Find Vertex in the TED by its unique key or its Link State Node ID. + Return Vertex if found, NULL otherwise. + .. c:function:: struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key) .. c:function:: struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted, struct ls_attributes *attributes); .. c:function:: struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted, struct ls_attributes *attributes); -.. c:function:: struct ls_subnet *ls_subnet_add(struct ls_ted *ted, struct ls_prefix *pref) -.. c:function:: void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet) + Find Edge in the Link State Data Base by its key, source or distination + (local IPv4 or IPv6 address or local ID) informations of the Link State + Attributes. Return Edge if found, NULL otherwise. + .. c:function:: struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix) + Find Subnet in the Link State Data Base by its key, i.e. the associated + prefix. Return Subnet if found, NULL otherwise. + +.. c:function:: int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2) +.. c:function:: int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2) +.. c:function:: int ls_subnet_same(struct ls_subnet *s1, struct ls_subnet *s2) + + Check, respectively if two Vertices, Edges or Subnets are equal. + Note that these routines has the same return value sense as '==' + (which is different from a comparison). + + +TED management functions +^^^^^^^^^^^^^^^^^^^^^^^^ + +Some helpers functions have been also provided to ease TED management: + .. c:function:: struct ls_ted *ls_ted_new(const uint32_t key, char *name, uint32_t asn) + + Create a new Link State Data Base. Key must be different from 0. + Name could be NULL and AS number equal to 0 if unknown. + .. c:function:: void ls_ted_del(struct ls_ted *ted) +.. c:function:: void ls_ted_del_all(struct ls_ted *ted) + + Delete existing Link State Data Base. Vertices, Edges, and Subnets are not + removed with ls_ted_del() function while they are with ls_ted_del_all(). + .. c:function:: void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, struct ls_edge *edge) + + Connect Source and Destination Vertices by given Edge. Only non NULL source + and destination vertices are connected. + .. c:function:: void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source) .. c:function:: void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, bool source) + + Connect / Disconnect Link State Edge to the Link State Vertex which could be + a Source (source = true) or a Destination (source = false) Vertex. + .. c:function:: void ls_disconnect_edge(struct ls_edge *edge) + Disconnect Link State Edge from both Source and Destination Vertex. + Note that Edge is not removed but its status is marked as ORPHAN. + +.. c:function:: void ls_vertex_clean(struct ls_ted *ted, struct ls_vertex *vertex, struct zclient *zclient) + + Clean Vertex structure by removing all Edges and Subnets marked as ORPHAN + from this vertex. Corresponding Link State Update message is sent if zclient + parameter is not NULL. Note that associated Link State Attribute and Prefix + are also removed and memory freed. + +.. c:function:: void ls_ted_clean(struct ls_ted *ted) + + Clean Link State Data Base by removing all Vertices, Edges and SubNets + marked as ORPHAN. Note that associated Link State Node, Attributes and + Prefix are removed too. + +.. c:function:: void ls_show_vertex(struct ls_vertex *vertex, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_edge(struct ls_edeg *edge, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_vertices(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_edges(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_subnets(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_ted(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose) + + Respectively, show Vertex, Edge, Subnet provided as parameter, all Vertices, + all Edges, all Subnets and the whole TED if not specified. Output could be + more detailed with verbose parameter for VTY output. If both JSON and VTY + output are specified, JSON takes precedence over VTY. + +.. c:function:: void ls_dump_ted(struct ls_ted *ted) + + Dump TED information to the current logging output. Link State Messages ------------------- @@ -198,8 +327,8 @@ Figure 1 below, illustrates the ZAPI Opaque message exchange between a message sequences are as follows: - First, both *Producer* and *Consumer* must register to their respective ZAPI - Opaque Message. **Link State Sync** for the *Producer* in order to receive - Database synchronisation request from a *Consumer*. **Link State Update** for + Opaque Message: **Link State Sync** for the *Producer* in order to receive + Database synchronisation request from a *Consumer*, **Link State Update** for the *Consumer* in order to received any Link State update from a *Producer*. These register messages are stored by Zebra to determine to which daemon it should redistribute the ZAPI messages it receives. @@ -245,22 +374,22 @@ message sequences are as follows: | | Request LS Sync | v \ | Request LS Sync |<----------------------------| | |<-----------------------------| | Synchronistation - | LS DB Sync | | Phase - |----------------------------->| LS DB Sync | | + | LS DB Update | | Phase + |----------------------------->| LS DB Update | | | |---------------------------->| | - | LS DB Sync (cont'd) | | | - |----------------------------->| LS DB Sync (cont'd) | | + | LS DB Update (cont'd) | | | + |----------------------------->| LS DB Update (cont'd) | | | . |---------------------------->| | | . | . | | | . | . | | - | LS DB Sync (end) | . | | - |----------------------------->| LS DB Sync (end) | | + | LS DB Update (end) | . | | + |----------------------------->| LS DB Update (end) | | | |---------------------------->| | | | | / : : : : : : - | LS Update | | \ - |----------------------------->| LS Update | | + | LS DB Update | | \ + |----------------------------->| LS DB Update | | | |---------------------------->| Update Phase | | | | : : : / @@ -269,7 +398,7 @@ message sequences are as follows: | | Unregister LS Update | | | |<----------------------------| Deregister Phase | | | | - | LS Update | | | + | LS DB Update | | | |----------------------------->| | | | | | / | | | @@ -305,10 +434,65 @@ Opaque Link State type at once. Functions ^^^^^^^^^ +.. c:function:: int ls_register(struct zclient *zclient, bool server) +.. c:function:: int ls_unregister(struct zclient *zclient, bool server) + + Register / Unregister daemon to received ZAPI Link State Opaque messages. + Server must be set to true for *Producer* and to false for *Consumer*. + +.. c:function:: int ls_request_sync(struct zclient *zclient) + + Request initial Synchronisation to collect the whole Link State Database. + .. c:function:: struct ls_message *ls_parse_msg(struct stream *s) + + Parse Link State Message from stream. Used this function once receiving a + new ZAPI Opaque message of type Link State. + +.. c:function:: void ls_delete_msg(struct ls_message *msg) + + Delete existing message. Data structure is freed. + .. c:function:: int ls_send_msg(struct zclient *zclient, struct ls_message *msg, struct zapi_opaque_reg_info *dst) + + Send Link State Message as new ZAPI Opaque message of type Link State. + If destination is not NULL, message is sent as Unicast otherwise it is + broadcast to all registered daemon. + .. c:function:: struct ls_message *ls_vertex2msg(struct ls_message *msg, struct ls_vertex *vertex) .. c:function:: struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge) .. c:function:: struct ls_message *ls_subnet2msg(struct ls_message *msg, struct ls_subnet *subnet) + + Create respectively a new Link State Message from a Link State Vertex, Edge + or Subnet. If Link State Message is NULL, a new data structure is + dynamically allocated. Note that the Vertex, Edge and Subnet status is used + to determine the corresponding Link State Message event: ADD, UPDATE, + DELETE, SYNC. + +.. c:function:: int ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg) +.. c:function:: int ls_msg2edge(struct ls_ted *ted, struct ls_message *msg) +.. c:function:: int ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg) + + Convert Link State Message respectively in Vertex, Edge or Subnet and + update the Link State Database accordingly to the message event: SYNC, ADD, + UPDATE or DELETE. + +.. c:function:: struct ls_element *ls_msg2ted(struct ls_ted *ted, struct ls_message *msg, bool delete) +.. c:function:: struct ls_element *ls_stream2ted(struct ls_ted *ted, struct ls_message *msg, bool delete) + + Convert Link State Message or Stream Buffer in a Link State element (Vertex, + Edge or Subnet) and update the Link State Database accordingly to the + message event: SYNC, ADD, UPDATE or DELETE. The function return the generic + structure ls_element that point to the Vertex, Edge or Subnet which has been + added, updated or synchronous in the database. Note that the delete boolean + parameter governs the action for the DELETE action: true, Link State Element + is removed from the database and NULL is return. If set to false, database + is not updated and the function sets the Link State Element status to + Delete and return the element for futur deletion by the calling function. + .. c:function:: int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, struct zapi_opaque_reg_info *dst) + Send all the content of the Link State Data Base to the given destination. + Link State content is sent is this order: Vertices, Edges then Subnet. + This function must be used when a daemon request a Link State Data Base + Synchronization. diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index a35e60619c..b827afd6cc 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -1,5 +1,7 @@ .. _logging: +.. highlight:: c + Logging ======= @@ -52,46 +54,50 @@ are available: if (ret != buf) XFREE(MTYPE_FOO, ret); -Extensions -^^^^^^^^^^ - -``printfrr()`` format strings can be extended with suffixes after `%p` or -`%d`. The following extended format specifiers are available: - -+-----------+--------------------------+----------------------------------------------+ -| Specifier | Argument | Output | -+===========+==========================+==============================================+ -| ``%Lu`` | ``uint64_t`` | ``12345`` | -+-----------+--------------------------+----------------------------------------------+ -| ``%Ld`` | ``int64_t`` | ``-12345`` | -+-----------+--------------------------+----------------------------------------------+ -| ``%pI4`` | ``struct in_addr *`` | ``1.2.3.4`` | -| | | | -| | ``in_addr_t *`` | | -+-----------+--------------------------+----------------------------------------------+ -| ``%pI6`` | ``struct in6_addr *`` | ``fe80::1234`` | -+-----------+--------------------------+----------------------------------------------+ -| ``%pIA`` | ``struct ipaddr *`` | ``1.2.3.4`` | -| | | | -| | | ``fe80::1234`` | -+-----------+--------------------------+----------------------------------------------+ -| ``%pFX`` | ``struct prefix *`` | ``fe80::1234/64`` | -+-----------+--------------------------+----------------------------------------------+ -| ``%pSG4`` | ``struct prefix_sg *`` | ``(*,1.2.3.4)`` | -+-----------+--------------------------+----------------------------------------------+ -| ``%pRN`` | ``struct route_node *`` | ``192.168.1.0/24`` (dst-only node) | -| | | | -| | | ``2001:db8::/32 from fe80::/64`` (SADR node) | -+-----------+--------------------------+----------------------------------------------+ -| ``%pNHv`` | ``struct nexthop *`` | ``1.2.3.4, via eth0`` | -+-----------+--------------------------+----------------------------------------------+ -| ``%pNHs`` | ``struct nexthop *`` | ``1.2.3.4 if 15`` | -+-----------+--------------------------+----------------------------------------------+ -| ``%pFX`` | ``struct bgp_dest *`` | ``fe80::1234/64`` (available in BGP only) | -+-----------+--------------------------+----------------------------------------------+ +.. c:function:: ssize_t bprintfrr(struct fbuf *fb, const char *fmt, ...) +.. c:function:: ssize_t vbprintfrr(struct fbuf *fb, const char *fmt, va_list) + + These are the "lowest level" functions, which the other variants listed + above use to implement their functionality on top. Mainly useful for + implementing printfrr extensions since those get a ``struct fbuf *`` to + write their output to. + +.. c:macro:: FMT_NSTD(expr) + + This macro turns off/on format warnings as needed when non-ISO-C + compatible printfrr extensions are used (e.g. ``%.*p`` or ``%Ld``.):: + + vty_out(vty, "standard compatible %pI4\n", &addr); + FMT_NSTD(vty_out(vty, "non-standard %-47.*pHX\n", (int)len, buf)); + + When the frr-format plugin is in use, this macro is a no-op since the + frr-format plugin supports all printfrr extensions. Since the FRR CI + includes a system with the plugin enabled, this means format errors will + not slip by undetected even with FMT_NSTD. + +.. note:: + ``printfrr()`` does not support the ``%n`` format. + +AS-Safety +^^^^^^^^^ + +``printfrr()`` are AS-Safe under the following conditions: + +* the ``[v]as[n]printfrr`` variants are not AS-Safe (allocating memory) +* floating point specifiers are not AS-Safe (system printf is used for these) +* the positional ``%1$d`` syntax should not be used (8 arguments are supported + while AS-Safe) +* extensions are only AS-Safe if their printer is AS-Safe + +printfrr Extensions +------------------- + +``printfrr()`` format strings can be extended with suffixes after `%p` or `%d`. Printf features like field lengths can be used normally with these extensions, -e.g. ``%-15pI4`` works correctly. +e.g. ``%-15pI4`` works correctly, **except if the extension consumes the +width or precision**. Extensions that do so are listed below as ``%*pXX`` +rather than ``%pXX``. The extension specifier after ``%p`` or ``%d`` is always an uppercase letter; by means of established pattern uppercase letters and numbers form the type @@ -99,10 +105,7 @@ identifier which may be followed by lowercase flags. You can grep the FRR source for ``printfrr_ext_autoreg`` to see all extended printers and what exactly they do. More printers are likely to be added as -needed/useful, so the list above may become outdated. - -``%Ld`` is not an "extension" for printfrr; it's wired directly into the main -printf logic. +needed/useful, so the list here may be outdated. .. note:: @@ -111,16 +114,218 @@ printf logic. **not** available when calling ``snprintf`` directly. You need to call ``snprintfrr`` instead. -AS-Safety -^^^^^^^^^ +Networking data types +^^^^^^^^^^^^^^^^^^^^^ -``printfrr()`` are AS-Safe under the following conditions: +.. role:: frrfmtout(code) -* the ``[v]as[n]printfrr`` variants are not AS-Safe (allocating memory) -* floating point specifiers are not AS-Safe (system printf is used for these) -* the positional ``%1$d`` syntax should not be used (8 arguments are supported - while AS-Safe) -* extensions are only AS-Safe if their printer is AS-Safe +.. frrfmt:: %pI4 (struct in_addr *, in_addr_t *) + + :frrfmtout:`1.2.3.4` + +.. frrfmt:: %pI6 (struct in6_addr *) + + :frrfmtout:`fe80::1234` + +.. frrfmt:: %pEA (struct ethaddr *) + + :frrfmtout:`01:23:45:67:89:ab` + +.. frrfmt:: %pIA (struct ipaddr *) + + :frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234` + +.. frrfmt:: %pFX (struct prefix *) + + :frrfmtout:`1.2.3.0/24` / :frrfmtout:`fe80::1234/64` + + This accepts the following types: + + - :c:struct:`prefix` + - :c:struct:`prefix_ipv4` + - :c:struct:`prefix_ipv6` + - :c:struct:`prefix_eth` + - :c:struct:`prefix_evpn` + - :c:struct:`prefix_fs` + + It does **not** accept the following types: + + - :c:struct:`prefix_ls` + - :c:struct:`prefix_rd` + - :c:struct:`prefix_ptr` + - :c:struct:`prefix_sg` (use :frrfmt:`%pSG4`) + - :c:union:`prefixptr` (dereference to get :c:struct:`prefix`) + - :c:union:`prefixconstptr` (dereference to get :c:struct:`prefix`) + +.. frrfmt:: %pSG4 (struct prefix_sg *) + + :frrfmtout:`(*,1.2.3.4)` + + This is *(S,G)* output for use in pimd. (Note prefix_sg is not a prefix + "subclass" like the other prefix_* structs.) + +.. frrfmt:: %pSU (union sockunion *) + + ``%pSU``: :frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234` + + ``%pSUs``: :frrfmtout:`1.2.3.4` / :frrfmtout:`fe80::1234%89` + (adds IPv6 scope ID as integer) + + ``%pSUp``: :frrfmtout:`1.2.3.4:567` / :frrfmtout:`[fe80::1234]:567` + (adds port) + + ``%pSUps``: :frrfmtout:`1.2.3.4:567` / :frrfmtout:`[fe80::1234%89]:567` + (adds port and scope ID) + +.. frrfmt:: %pRN (struct route_node *, struct bgp_node *, struct agg_node *) + + :frrfmtout:`192.168.1.0/24` (dst-only node) + + :frrfmtout:`2001:db8::/32 from fe80::/64` (SADR node) + +.. frrfmt:: %pNH (struct nexthop *) + + ``%pNHvv``: :frrfmtout:`via 1.2.3.4, eth0` — verbose zebra format + + ``%pNHv``: :frrfmtout:`1.2.3.4, via eth0` — slightly less verbose zebra format + + ``%pNHs``: :frrfmtout:`1.2.3.4 if 15` — same as :c:func:`nexthop2str()` + +.. frrfmt:: %pBD (struct bgp_dest *) + + :frrfmtout:`fe80::1234/64` + + (only available in bgpd.) + +.. frrfmt:: %dPF (int) + + :frrfmtout:`AF_INET` + + Prints an `AF_*` / `PF_*` constant. ``PF`` is used here to avoid confusion + with `AFI` constants, even though the FRR codebase prefers `AF_INET` over + `PF_INET` & co. + +.. frrfmt:: %dSO (int) + + :frrfmtout:`SOCK_STREAM` + +General utility formats +^^^^^^^^^^^^^^^^^^^^^^^ + +.. frrfmt:: %m (no argument) + + :frrfmtout:`Permission denied` + + Prints ``strerror(errno)``. Does **not** consume any input argument, don't + pass ``errno``! + + (This is a GNU extension not specific to FRR. FRR guarantees it is + available on all systems in printfrr, though BSDs support it in printf too.) + +.. frrfmt:: %pSQ (char *) + + ([S]tring [Q]uote.) Like ``%s``, but produce a quoted string. Options: + + ``n`` - treat ``NULL`` as empty string instead. + + ``q`` - include ``""`` quotation marks. Note: ``NULL`` is printed as + ``(null)``, not ``"(null)"`` unless ``n`` is used too. This is + intentional. + + ``s`` - use escaping suitable for RFC5424 syslog. This means ``]`` is + escaped too. + + If a length is specified (``%*pSQ`` or ``%.*pSQ``), null bytes in the input + string do not end the string and are just printed as ``\x00``. + +.. frrfmt:: %pSE (char *) + + ([S]tring [E]scape.) Like ``%s``, but escape special characters. + Options: + + ``n`` - treat ``NULL`` as empty string instead. + + Unlike :frrfmt:`%pSQ`, this escapes many more characters that are fine for + a quoted string but not on their own. + + If a length is specified (``%*pSE`` or ``%.*pSE``), null bytes in the input + string do not end the string and are just printed as ``\x00``. + +.. frrfmt:: %pVA (struct va_format *) + + Recursively invoke printfrr, with arguments passed in through: + + .. c:struct:: va_format + + .. c:member:: const char *fmt + + Format string to use for the recursive printfrr call. + + .. c:member:: va_list *va + + Formatting arguments. Note this is passed as a pointer, not - as in + most other places - a direct struct reference. Internally uses + ``va_copy()`` so repeated calls can be made (e.g. for determining + output length.) + +.. frrfmt:: %pFB (struct fbuf *) + + Insert text from a ``struct fbuf *``, i.e. the output of a call to + :c:func:`bprintfrr()`. + +.. frrfmt:: %*pHX (void *, char *, unsigned char *) + + ``%pHX``: :frrfmtout:`12 34 56 78` + + ``%pHXc``: :frrfmtout:`12:34:56:78` (separate with [c]olon) + + ``%pHXn``: :frrfmtout:`12345678` (separate with [n]othing) + + Insert hexdump. This specifier requires a precision or width to be + specified. A precision (``%.*pHX``) takes precedence, but generates a + compiler warning since precisions are undefined for ``%p`` in ISO C. If + no precision is given, the width is used instead (and normal handling of + the width is suppressed). + + Note that width and precision are ``int`` arguments, not ``size_t``. Use + like:: + + char *buf; + size_t len; + + snprintfrr(out, sizeof(out), "... %*pHX ...", (int)len, buf); + + /* with padding to width - would generate a warning due to %.*p */ + FMT_NSTD(snprintfrr(out, sizeof(out), "... %-47.*pHX ...", (int)len, buf)); + +.. frrfmt:: %*pHS (void *, char *, unsigned char *) + + ``%pHS``: :frrfmtout:`hex.dump` + + This is a complementary format for :frrfmt:`%*pHX` to print the text + representation for a hexdump. Non-printable characters are replaced with + a dot. + +Integer formats +^^^^^^^^^^^^^^^ + +.. note:: + + These formats currently only exist for advanced type checking with the + ``frr-format`` GCC plugin. They should not be used directly since they will + cause compiler warnings when used without the plugin. Use with + :c:macro:`FMT_NSTD` if necessary. + + It is possible ISO C23 may introduce another format for these, possibly + ``%w64d`` discussed in `JTC 1/SC 22/WG 14/N2680 <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2680.pdf>`_. + +.. frrfmt:: %Lu (uint64_t) + + :frrfmtout:`12345` + +.. frrfmt:: %Ld (int64_t) + + :frrfmtout:`-12345` Log levels ---------- diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 7976a206f7..a86566dbb0 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -60,6 +60,7 @@ following steps will get you there on Ubuntu 20.04. .. code:: shell + apt install libsnmp-dev apt install snmpd snmp apt install snmp-mibs-downloader download-mibs @@ -232,6 +233,85 @@ for ``master`` branch: and create ``frr`` user and ``frrvty`` group as shown above. +Debugging Topotest Failures +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For the below debugging options which launch programs, if the topotest is run +within screen_ or tmux_, ``gdb``, the shell or ``vtysh`` will be launched using +that windowing program, otherwise mininet's ``xterm`` functionality will be used +to launch the given program. + +If you wish to force the use of ``xterm`` rather than ``tmux`` or ``screen``, or +wish to use ``gnome-terminal`` instead of ``xterm``, set the environment +variable ``FRR_TOPO_TERMINAL`` to either ``xterm`` or ``gnome-terminal``. + +.. _screen: https://www.gnu.org/software/screen/ +.. _tmux: https://github.com/tmux/tmux/wiki + +Spawning ``vtysh`` or Shells on Routers +""""""""""""""""""""""""""""""""""""""" + +Topotest can automatically launch a shell or ``vtysh`` for any or all routers in +a test. This is enabled by specifying 1 of 2 CLI arguments ``--shell`` or +``--vtysh``. Both of these options can be set to a single router value, multiple +comma-seperated values, or ``all``. + +When either of these options are specified topotest will pause after each test +to allow for inspection of the router state. + +Here's an example of launching ``vtysh`` on routers ``rt1`` and ``rt2``. + +.. code:: shell + + pytest --vtysh=rt1,rt2 all-protocol-startup + +Spawning Mininet CLI, ``vtysh`` or Shells on Routers on Test Failure +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +Similar to the previous section one can have ``vtysh`` or a shell launched on +routers, but in this case only when a test fails. To launch the given process on +each router after a test failure specify one of ``--shell-on-error`` or +``--vtysh-on-error``. + + +Here's an example of having ``vtysh`` launched on test failure. + +.. code:: shell + + pytest --vtysh-on-error all-protocol-startup + + +Additionally, one can have the mininet CLI invoked on test failures by +specifying the ``--mininet-on-error`` CLI option as shown in the example below. + +.. code:: shell + + pytest --mininet-on-error all-protocol-startup + +Debugging with GDB +"""""""""""""""""" + +Topotest can automatically launch any daemon with ``gdb``, possibly setting +breakpoints for any test run. This is enabled by specifying 1 or 2 CLI arguments +``--gdb-routers`` and ``--gdb-daemons``. Additionally ``--gdb-breakpoints`` can +be used to automatically set breakpoints in the launched ``gdb`` processes. + +Each of these options can be set to a single value, multiple comma-seperated +values, or ``all``. If ``--gdb-routers`` is empty but ``--gdb_daemons`` is set +then the given daemons will be launched in ``gdb`` on all routers in the test. +Likewise if ``--gdb_routers`` is set, but ``--gdb_daemons`` is empty then all +daemons on the given routers will be launched in ``gdb``. + +Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router +``r1`` with a breakpoint set on ``nb_config_diff`` + +.. code:: shell + + pytest --gdb-routers=r1 \ + --gdb-daemons=bgpd,zebra \ + --gdb-breakpoints=nb_config_diff \ + all-protocol-startup + .. _topotests_docker: Running Tests with Docker @@ -400,6 +480,15 @@ Some things to keep in mind: - Avoid including unstable data in your test: don't rely on link-local addresses or ifindex values, for example, because these can change from run to run. +- Using sleep is almost never appropriate to wait for some convergence + event as the sole item done. As an example: if the test resets the peers + in BGP, the test should look for the peers reconverging instead of just + sleeping an arbitrary amount of time and continuing on. It is ok to + use sleep in a tight loop with appropriate show commands to ensure that + the protocol reaches the desired state. This should be bounded by + appropriate timeouts for the protocol in question though. See + verify_bgp_convergence as a good example of this. If you are having + troubles figuring out what to look for, please do not be afraid to ask. Topotest File Hierarchy diff --git a/doc/developer/tracing.rst b/doc/developer/tracing.rst index ae4d621a8e..63b04585f1 100644 --- a/doc/developer/tracing.rst +++ b/doc/developer/tracing.rst @@ -396,7 +396,7 @@ modifying ``frr.service`` like so: --- a/frr.service +++ b/frr.service @@ -7,6 +7,7 @@ Before=network.target - OnFailure=heartbeat-failed@%n.service + OnFailure=heartbeat-failed@%n [Service] +Environment="LD_PRELOAD=liblttng-ust-fork.so" diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index abdbea5a9c..b4ddec10c9 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -500,10 +500,22 @@ made. For example, a change in :file:`bgpd/rfapi` would be formatted as:: The first line should be no longer than 50 characters. Subsequent lines should be wrapped to 72 characters. +The purpose of commit messages is to briefly summarize what the commit is +changing. Therefore, the extended summary portion should be in the form of an +English paragraph. Brief examples of program output are acceptable but if +present should be short (on the order of 10 lines) and clearly demonstrate what +has changed. The goal should be that someone with only passing familiarity with +the code in question can understand what is being changed. + +Commit messages consisting entirely of program output are *unacceptable*. These +do not describe the behavior changed. For example, putting VTYSH output or the +result of test runs as the sole content of commit messages is unacceptable. + You must also sign off on your commit. .. seealso:: :ref:`signing-off` + Source File Header ------------------ diff --git a/doc/user/babeld.rst b/doc/user/babeld.rst index e6d4aa5c97..c8015bb7e5 100644 --- a/doc/user/babeld.rst +++ b/doc/user/babeld.rst @@ -233,3 +233,41 @@ Babel debugging commands .. note:: If you have compiled with the ``NO_DEBUG`` flag, then these commands aren't available. + + +Babel sample configuration file +=============================== + +.. code-block:: frr + + debug babel common + !debug babel kernel + !debug babel filter + !debug babel timeout + !debug babel interface + !debug babel route + !debug babel all + + router babel + ! network wlan0 + ! network eth0 + ! redistribute ipv4 kernel + ! no redistribute ipv6 static + + ! The defaults are fine for a wireless interface + + !interface wlan0 + + ! A few optimisation tweaks are optional but recommended on a wired interface + ! Disable link quality estimation, enable split horizon processing, and + ! increase the hello and update intervals. + + !interface eth0 + ! babel wired + ! babel split-horizon + ! babel hello-interval 12000 + ! babel update-interval 36000 + + ! log file /var/log/quagga/babeld.log + log stdout + diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 5cbd3692dc..519f30d5e6 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -444,7 +444,7 @@ Terminal Mode Commands Shows the current log filters applied to each daemon. -.. clicmd:: show memory +.. clicmd:: show memory [DAEMON] Show information on how much memory is used for which specific things in |PACKAGE_NAME|. Output may vary depending on system capabilities but will @@ -502,7 +502,8 @@ Terminal Mode Commands the column may be missing if system support is not available. When executing this command from ``vtysh``, each of the daemons' memory - usage is printed sequentially. + usage is printed sequentially. You can specify the daemon's name to print + only its memory usage. .. clicmd:: show history @@ -513,32 +514,43 @@ Terminal Mode Commands Send a message to all logging destinations that are enabled for messages of the given severity. -.. clicmd:: find COMMAND... +.. clicmd:: find REGEX... - This command performs a simple substring search across all defined commands - in all modes. As an example, suppose you're in enable mode and can't - remember where the command to turn OSPF segment routing on is: + This command performs a regex search across all defined commands in all + modes. As an example, suppose you're in enable mode and can't remember where + the command to turn OSPF segment routing on is: :: frr# find segment-routing on (ospf) segment-routing on + (isis) segment-routing on + The CLI mode is displayed next to each command. In this example, :clicmd:`segment-routing on` is under the `router ospf` mode. - Similarly, suppose you want a listing of all commands that contain "l2vpn": + Similarly, suppose you want a listing of all commands that contain "l2vpn" + and "neighbor": :: - frr# find l2vpn - (view) show [ip] bgp l2vpn evpn [json] - (view) show [ip] bgp l2vpn evpn all <A.B.C.D|A.B.C.D/M> [json] - (view) show [ip] bgp l2vpn evpn all neighbors A.B.C.D advertised-routes [json] - (view) show [ip] bgp l2vpn evpn all neighbors A.B.C.D routes [json] - (view) show [ip] bgp l2vpn evpn all overlay + frr# find l2vpn.*neighbor + (view) show [ip] bgp l2vpn evpn neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json] + (view) show [ip] bgp l2vpn evpn neighbors <A.B.C.D|X:X::X:X|WORD> routes [json] + (view) show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json] + (view) show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors <A.B.C.D|X:X::X:X|WORD> routes [json] ... + + Note that when entering spaces as part of a regex specification, repeated + spaces will be compressed into a single space for matching purposes. This is + a consequence of spaces being used to delimit CLI tokens. If you need to + match more than one space, use the ``\s`` escape. + + POSIX Extended Regular Expressions are supported. + + .. _common-show-commands: .. clicmd:: show thread cpu [r|w|t|e|x] diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 7d136b183e..6f797f7cc1 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -313,6 +313,11 @@ The following commands are available inside the interface configuration node. a new neighbor is found a BFD peer is created to monitor the link status for fast convergence. +.. clicmd:: ip ospf bfd profile BFDPROF + + Same as command ``ip ospf bfd``, but applies the BFD profile to the sessions + it creates or that already exist. + .. _bfd-ospf6-peer-config: diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 3f8dafa09d..1da9e3ba73 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -394,6 +394,13 @@ Route Selection other measures were taken to avoid these. The exact behaviour will be sensitive to the iBGP and reflection topology. +.. clicmd:: bgp bestpath peer-type multipath-relax + + This command specifies that BGP decision process should consider paths + from all peers for multipath computation. If this option is enabled, + paths learned from any of eBGP, iBGP, or confederation neighbors will + be multipath if they are otherwise considered equal cost. + .. _bgp-distance: Administrative Distance Metrics @@ -1087,6 +1094,9 @@ IPv6 Support be used in a setup with two upstreams where each of the upstreams should only receive either IPv4 or IPv6 annocuments. + Using the ``bgp default ipv6-unicast`` configuration, IPv6 unicast + address family is enabled by default for all new neighbors. + .. _bgp-route-aggregation: @@ -1353,6 +1363,15 @@ Defining Peers ``net.core.optmem_max`` to allow the kernel to allocate the necessary option memory. +.. clicmd:: bgp listen limit <1-65535> + + Define the maximum number of peers accepted for one BGP instance. This + limit is set to 100 by default. Increasing this value will really be + possible if more file descriptors are available in the BGP process. This + value is defined by the underlying system (ulimit value), and can be + overriden by `--limit-fds`. More information is available in chapter + (:ref:`common-invocation-options`). + .. clicmd:: coalesce-time (0-4294967295) The time in milliseconds that BGP will delay before deciding what peers @@ -1417,6 +1436,12 @@ Configuring Peers This command is deprecated and may be removed in a future release. Its use should be avoided. +.. clicmd:: neighbor PEER interface remote-as <internal|external|ASN> + + Configure an unnumbered BGP peer. ``PEER`` should be an interface name. The + session will be established via IPv6 link locals. Use ``internal`` for iBGP + and ``external`` for eBGP sessions, or specify an ASN if you wish. + .. clicmd:: neighbor PEER next-hop-self [all] This command specifies an announced route's nexthop as being equivalent to @@ -1576,6 +1601,12 @@ Configuring Peers on by default or not. This command defaults to on and is not displayed. The `no bgp default ipv4-unicast` form of the command is displayed. +.. clicmd:: bgp default ipv6-unicast + + This command allows the user to specify that v6 peering is turned + on by default or not. This command defaults to off and is not displayed. + The `bgp default ipv6-unicast` form of the command is displayed. + .. clicmd:: bgp default show-hostname This command shows the hostname of the peer in certain BGP commands @@ -1593,6 +1624,16 @@ Configuring Peers peer in question. This number is between 0 and 600 seconds, with the default advertisement interval being 0. +.. clicmd:: neighbor PEER timers (0-65535) (0-65535) + + Set keepalive and hold timers for a neighbor. The first value is keepalive + and the second is hold time. + +.. clicmd:: neighbor PEER connect (1-65535) + + Set connect timer for a neighbor. The connect timer controls how long BGP + waits between connection attempts to a neighbor. + .. clicmd:: neighbor PEER timers delayopen (1-240) This command allows the user enable the @@ -2808,6 +2849,7 @@ When default route is present in R2'2 BGP table, 10.139.224.0/20 and 192.0.2.1/3 i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 0.0.0.0/0 10.10.10.1 0 0 1 i @@ -2837,6 +2879,7 @@ When default route is present in R2'2 BGP table, 10.139.224.0/20 and 192.0.2.1/3 i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 0.0.0.0/0 0.0.0.0 0 1 i @@ -2855,6 +2898,7 @@ When default route is not present in R2'2 BGP table, 10.139.224.0/20 and 192.0.2 i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 10.139.224.0/20 10.10.10.1 0 0 1 ? @@ -2884,6 +2928,7 @@ When default route is not present in R2'2 BGP table, 10.139.224.0/20 and 192.0.2 i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete + RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 10.139.224.0/20 0.0.0.0 0 1 ? @@ -2907,6 +2952,12 @@ Debugging Display Listen sockets and the vrf that created them. Useful for debugging of when listen is not working and this is considered a developer debug statement. +.. clicmd:: debug bgp bfd + + Enable or disable debugging for BFD events. This will show BFD integration + library messages and BGP BFD integration messages that are mostly state + transitions and validation problems. + .. clicmd:: debug bgp neighbor-events Enable or disable debugging for neighbor events. This provides general @@ -3068,11 +3119,11 @@ daemon project, while :clicmd:`show bgp` command is the new format. The choice has been done to keep old format with IPv4 routing table, while new format displays IPv6 routing table. -.. clicmd:: show ip bgp [all] [wide|json] +.. clicmd:: show ip bgp [all] [wide|json [detail]] .. clicmd:: show ip bgp A.B.C.D [json] -.. clicmd:: show bgp [all] [wide|json] +.. clicmd:: show bgp [all] [wide|json [detail]] .. clicmd:: show bgp X:X::X:X [json] @@ -3101,6 +3152,9 @@ displays IPv6 routing table. If ``json`` option is specified, output is displayed in JSON format. + If ``detail`` option is specified after ``json``, more verbose JSON output + will be displayed. + Some other commands provide additional options for filtering the output. .. clicmd:: show [ip] bgp regexp LINE @@ -3138,17 +3192,15 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. .. clicmd:: show bgp [afi] [safi] [all] [wide|json] -.. clicmd:: show bgp <ipv4|ipv6> <unicast|multicast|vpn|labeled-unicast> +.. clicmd:: show bgp [<ipv4|ipv6> <unicast|multicast|vpn|labeled-unicast|flowspec> | l2vpn evpn] These commands display BGP routes for the specific routing table indicated by the selected afi and the selected safi. If no afi and no safi value is given, the command falls back to the default IPv6 routing table. - For EVPN prefixes, you can display the full BGP table for this AFI/SAFI - using the standard `show bgp [afi] [safi]` syntax. .. clicmd:: show bgp l2vpn evpn route [type <macip|2|multicast|3|es|4|prefix|5>] - Additionally, you can also filter this output by route type. + EVPN prefixes can also be filtered by EVPN route type. .. clicmd:: show bgp [afi] [safi] [all] summary [json] @@ -3165,11 +3217,21 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. Show a bgp peer summary for peers that are succesfully exchanging routes for the specified address family, and subsequent address-family. -.. clicmd:: show bgp [afi] [safi] neighbor [PEER] +.. clicmd:: show bgp [afi] [safi] [neighbor [PEER] [routes|advertised-routes|received-routes] [json] This command shows information on a specific BGP peer of the relevant afi and safi selected. + The ``routes`` keyword displays only routes in this address-family's BGP + table that were received by this peer and accepted by inbound policy. + + The ``advertised-routes`` keyword displays only the routes in this + address-family's BGP table that were permitted by outbound policy and + advertised to to this peer. + + The ``received-routes`` keyword displays all routes belonging to this + address-family (prior to inbound policy) that were received by this peer. + .. clicmd:: show bgp [afi] [safi] [all] dampening dampened-paths [wide|json] Display paths suppressed due to dampening of the selected afi and safi @@ -3179,6 +3241,26 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. Display flap statistics of routes of the selected afi and safi selected. +.. clicmd:: show bgp [afi] [safi] [all] version (1-4294967295) [wide|json] + + Display prefixes with matching version numbers. The version number and + above having prefixes will be listed here. + + It helps to identify which prefixes were installed at some point. + + Here is an example of how to check what prefixes were installed starting + with an arbitrary version:: + + .. code-block:: frr + + ~# vtysh -c 'show bgp ipv4 unicast json' | jq '.tableVersion' + 9 + ~# vtysh -c 'show ip bgp version 9 json' | jq -r '.routes | keys[]' + 192.168.3.0/24 + ~# vtysh -c 'show ip bgp version 8 json' | jq -r '.routes | keys[]' + 192.168.2.0/24 + 192.168.3.0/24 + .. clicmd:: show bgp [afi] [safi] statistics Display statistics of routes of the selected afi and safi. @@ -3331,6 +3413,25 @@ Displaying Routes by AS Path Print a summary of neighbor connections for the specified AFI/SAFI combination. +Displaying Routes by Route Distinguisher +---------------------------------------- + +.. clicmd:: show bgp [<ipv4|ipv6> vpn | l2vpn evpn [route]] rd <all|RD> + + For L3VPN and EVPN address-families, routes can be displayed on a per-RD + (Route Distinguisher) basis or for all RD's. + +.. clicmd:: show bgp l2vpn evpn rd <all|RD> [overlay | tags] + + Use the ``overlay`` or ``tags`` keywords to display the overlay/tag + information about the EVPN prefixes in the selected Route Distinguisher. + +.. clicmd:: show bgp l2vpn evpn route rd <all|RD> mac <MAC> [ip <MAC>] [json] + + For EVPN Type 2 (macip) routes, a MAC address (and optionally an IP address) + can be supplied to the command to only display matching prefixes in the + specified RD. + Displaying Update Group Information ----------------------------------- diff --git a/doc/user/eigrpd.rst b/doc/user/eigrpd.rst index 88d289d27e..573e2ca2e4 100644 --- a/doc/user/eigrpd.rst +++ b/doc/user/eigrpd.rst @@ -183,3 +183,18 @@ Debug for EIGRP protocol. ``show debugging eigrp`` will show all information currently set for eigrpd debug. + +Sample configuration +==================== + +.. code-block:: frr + + hostname eigrpd + password zebra + enable password please-set-at-here + ! + router eigrp 4453 + network 192.168.1.0/24 + ! + log stdout + diff --git a/doc/user/fabricd.rst b/doc/user/fabricd.rst index 611bc1caaa..48d264f30e 100644 --- a/doc/user/fabricd.rst +++ b/doc/user/fabricd.rst @@ -60,7 +60,6 @@ in the configuration: .. clicmd:: set-overload-bit - Set overload bit to avoid any transit traffic. .. clicmd:: purge-originator @@ -256,9 +255,8 @@ Debugging OpenFabric Print which OpenFabric debug levels are active. - -OpenFabric configuration example -================================ +Sample configuration +==================== A simple example: @@ -281,3 +279,26 @@ A simple example: ! router openfabric 1 net 49.0000.0000.0001.00 + + +Alternative example: + +.. code-block:: frr + + hostname fabricd + + router openfabric DEAD + net 47.0023.0000.0003.0300.0100.0102.0304.0506.00 + lsp-lifetime 65535 + + hostname isisd-router + domain-password foobar + + interface eth0 + ip router openfabric DEAD + openfabric hello-interval 5 + openfabric lsp-interval 1000 + + ! -- optional + openfabric retransmit-interval 10 + openfabric retransmit-throttle-interval diff --git a/doc/user/ldpd.rst b/doc/user/ldpd.rst index 3e662b14d8..a6b3d9d97f 100644 --- a/doc/user/ldpd.rst +++ b/doc/user/ldpd.rst @@ -242,8 +242,9 @@ LDP debugging commands - ``messages`` - ``zebra`` -LDP Example Configuration -========================= + +Sample configuration +==================== Below configuration gives a typical MPLS configuration of a device located in a MPLS backbone. LDP is enabled on two interfaces and will attempt to peer with @@ -306,3 +307,45 @@ that traffic to that destination will be applied. O>* 10.200.0.0/24 [110/210] via 10.115.0.1, eth2, label 17, 00:00:15 north-vm# + +Additional example demonstrating use of some miscellaneous config options: + +.. code-block:: frr + + interface eth0 + ! + interface eth1 + ! + interface lo + ! + mpls ldp + dual-stack cisco-interop + neighbor 10.0.1.5 password opensourcerouting + neighbor 172.16.0.1 password opensourcerouting + ! + address-family ipv4 + discovery transport-address 10.0.1.1 + label local advertise explicit-null + ! + interface eth0 + ! + interface eth1 + ! + ! + address-family ipv6 + discovery transport-address 2001:db8::1 + ! + interface eth1 + ! + ! + ! + l2vpn ENG type vpls + bridge br0 + member interface eth2 + ! + member pseudowire mpw0 + neighbor lsr-id 1.1.1.1 + pw-id 100 + ! + ! + diff --git a/doc/user/nhrpd.rst b/doc/user/nhrpd.rst index b02e761acc..cbbc2dc10a 100644 --- a/doc/user/nhrpd.rst +++ b/doc/user/nhrpd.rst @@ -180,6 +180,37 @@ https://git-old.alpinelinux.org/user/tteras/strongswan/ Actively maintained patches are also available at: https://gitlab.alpinelinux.org/alpine/aports/-/tree/master/main/strongswan +.. _multicast-functionality: + +Multicast Functionality +======================= + +nhrpd can be configured to forward multicast packets, allowing routing +protocols that use multicast (such as OSPF) to be supported in the DMVPN +network. + +This support requires an iptables NFLOG rule to allow nhrpd to intercept +multicast packets. A second iptables rule is also usually used to drop the +original multicast packet. + + .. code-block:: shell + + iptables -A OUTPUT -d 224.0.0.0/24 -o gre1 -j NFLOG --nflog-group 2 + iptables -A OUTPUT -d 224.0.0.0/24 -o gre1 -j DROP + +.. index:: nhrp multicast-nflog-group (1-65535) +.. clicmd:: nhrp multicast-nflog-group (1-65535) + + Sets the nflog group that nhrpd will listen on for multicast packets. This + value must match the nflog-group value set in the iptables rule. + +.. index:: ip nhrp map multicast A.B.C.D|X:X::X:X A.B.C.D|dynamic +.. clicmd:: ip nhrp map multicast A.B.C.D|X:X::X:X A.B.C.D|dynamic + + Sends multicast packets to the specified NBMA address. If dynamic is + specified then destination NBMA address (or addresses) are learnt + dynamically. + .. _nhrp-events: NHRP Events diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 00571487d7..43c0f62ea3 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -139,6 +139,12 @@ Redistribute routes to OSPF6 Redistribute routes from other protocols into OSPFv3. +.. index:: default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map WORD}] +.. clicmd:: default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map WORD}] + + The command injects default route in the connected areas. The always + argument injects the default route regardless of it being present in the + router. Metric values and route-map can also be specified optionally. .. _showing-ospf6-information: @@ -229,7 +235,6 @@ Showing OSPF6 information Interface name can also be given. JSON output can be obtained by appending 'json' to the end of command. -.. index:: show ipv6 ospf6 spf tree [json] .. clicmd:: show ipv6 ospf6 spf tree [json] This commands shows the spf tree from the recent spf calculation with the @@ -237,9 +242,10 @@ Showing OSPF6 information tree in JSON format. Each area that the router belongs to has it's own JSON object, with each router having "cost", "isLeafNode" and "children" as arguments. - -OSPF6 Configuration Examples -============================ + + +Sample configuration +==================== Example of ospf6d configured on one interface and area: @@ -253,3 +259,53 @@ Example of ospf6d configured on one interface and area: area 0.0.0.0 range 2001:770:105:2::/64 interface eth0 area 0.0.0.0 ! + + +Larger example with policy and various options set: + + +.. code-block:: frr + + debug ospf6 neighbor state + ! + interface fxp0 + ipv6 ospf6 cost 1 + ipv6 ospf6 hello-interval 10 + ipv6 ospf6 dead-interval 40 + ipv6 ospf6 retransmit-interval 5 + ipv6 ospf6 priority 0 + ipv6 ospf6 transmit-delay 1 + ipv6 ospf6 instance-id 0 + ! + interface lo0 + ipv6 ospf6 cost 1 + ipv6 ospf6 hello-interval 10 + ipv6 ospf6 dead-interval 40 + ipv6 ospf6 retransmit-interval 5 + ipv6 ospf6 priority 1 + ipv6 ospf6 transmit-delay 1 + ipv6 ospf6 instance-id 0 + ! + router ospf6 + router-id 255.1.1.1 + redistribute static route-map static-ospf6 + interface fxp0 area 0.0.0.0 + ! + access-list access4 permit 127.0.0.1/32 + ! + ipv6 access-list access6 permit 3ffe:501::/32 + ipv6 access-list access6 permit 2001:200::/48 + ipv6 access-list access6 permit ::1/128 + ! + ipv6 prefix-list test-prefix seq 1000 deny any + ! + route-map static-ospf6 permit 10 + match ipv6 address prefix-list test-prefix + set metric-type type-2 + set metric 2000 + ! + line vty + access-class access4 + ipv6 access-class access6 + exec-timeout 0 0 + ! diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 8689bc4ccb..7d1e91dc41 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -280,7 +280,7 @@ To start OSPF process you have to specify the OSPF router. This command enables or disables sending ARP requests to update neighbor table entries. It speeds up convergence for /32 networks on a P2P - connection. + connection. This feature is enabled by default. @@ -299,6 +299,16 @@ To start OSPF process you have to specify the OSPF router. command can be used when the neighbor state get stuck at some state and this can be used to recover it from that state. +.. index:: maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM) +.. clicmd:: maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM) + +.. index:: maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM) +.. clicmd:: no maximum-paths + + CLI to control maximum number of equal cost paths to reach a specific + destination.(ECMP) + Reset CLI, resets the maximum supported multi path to the default value. + .. _ospf-area: Areas @@ -390,6 +400,27 @@ Areas Prevents an *ospfd* ABR from injecting inter-area summaries into the specified stub area. +.. clicmd:: area A.B.C.D nssa + +.. clicmd:: area (0-4294967295) nssa + + Configure the area to be a NSSA (Not-So-Stubby Area). This is an area that + allows OSPF to import external routes into a stub area via a new LSA type + (type 7). An NSSA autonomous system boundary router (ASBR) will generate this + type of LSA. The area border router (ABR) translates the LSA type 7 into LSA + type 5, which is propagated into the OSPF domain. NSSA areas are defined in + RFC 3101. + +.. clicmd:: area A.B.C.D nssa suppress-fa + +.. clicmd:: area (0-4294967295) nssa suppress-fa + + Configure the router to set the forwarding address to 0.0.0.0 in all LSA type 5 + translated from LSA type 7. The router needs to be elected the translator of the + area for this command to take effect. This feature causes routers that are + configured not to advertise forwarding addresses into the backbone to direct + forwarded traffic to the NSSA ABR translator. + .. clicmd:: area A.B.C.D default-cost (0-16777215) @@ -556,7 +587,7 @@ Interfaces :clicmd:`ip ospf dead-interval minimal hello-multiplier (2-20)` is also specified for the interface. -.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point) +.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn]) When configuring a point-to-point network on an interface and the interface has a /32 address associated with then OSPF will treat the interface @@ -564,6 +595,9 @@ Interfaces net.ipv4.conf.<interface name>.rp_filter value to 0. In order for the ospf multicast packets to be delivered by the kernel. + When used in a DMVPN network at a spoke, this OSPF will be configured in + point-to-point, but the HUB will be a point-to-multipoint. To make this + topology work, specify the optional 'dmvpn' parameter at the spoke. Set explicitly network type for specified interface. @@ -733,23 +767,25 @@ Showing Information Json o/p of this command covers base route information i.e all LSAs except opaque lsa info. -.. clicmd:: show ip ospf database [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database [json] + +.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) [json] -.. clicmd:: show ip ospf database (asbr-summary|external|network|router|summary) [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) LINK-STATE-ID [json] -.. clicmd:: show ip ospf database (asbr-summary|external|network|router|summary) LINK-STATE-ID [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) LINK-STATE-ID adv-router ADV-ROUTER [json] -.. clicmd:: show ip ospf database (asbr-summary|external|network|router|summary) LINK-STATE-ID adv-router ADV-ROUTER [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) adv-router ADV-ROUTER [json] -.. clicmd:: show ip ospf database (asbr-summary|external|network|router|summary) adv-router ADV-ROUTER [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) LINK-STATE-ID self-originate [json] -.. clicmd:: show ip ospf database (asbr-summary|external|network|router|summary) LINK-STATE-ID self-originate [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) self-originate [json] -.. clicmd:: show ip ospf database (asbr-summary|external|network|router|summary) self-originate [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database max-age [json] -.. clicmd:: show ip ospf database max-age [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database self-originate [json] -.. clicmd:: show ip ospf database self-originate [json] + Show the OSPF database summary. .. clicmd:: show ip ospf route [json] @@ -780,17 +816,17 @@ Opaque LSA extensions that are used with MPLS-TE; it does not support a complete RSVP-TE solution. -.. clicmd:: show ip ospf database (opaque-link|opaque-area|opaque-external) +.. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external) -.. clicmd:: show ip ospf database (opaque-link|opaque-area|opaque-external) LINK-STATE-ID +.. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external) LINK-STATE-ID -.. clicmd:: show ip ospf database (opaque-link|opaque-area|opaque-external) LINK-STATE-ID adv-router ADV-ROUTER +.. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external) LINK-STATE-ID adv-router ADV-ROUTER -.. clicmd:: show ip ospf database (opaque-link|opaque-area|opaque-external) adv-router ADV-ROUTER +.. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external) adv-router ADV-ROUTER -.. clicmd:: show ip ospf database (opaque-link|opaque-area|opaque-external) LINK-STATE-ID self-originate +.. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external) LINK-STATE-ID self-originate -.. clicmd:: show ip ospf database (opaque-link|opaque-area|opaque-external) self-originate +.. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external) self-originate Show Opaque LSA from the database. @@ -823,6 +859,13 @@ Traffic Engineering flood in AREA <area-id> with Opaque Type-10, respectively in AS with Opaque Type-11. In all case, Opaque-LSA TLV=6. +.. index:: mpls-te export +.. clicmd:: no mpls-te export + + Export Traffic Engineering Data Base to other daemons through the ZAPI + Opaque Link State messages. + +.. index:: show ip ospf mpls-te interface .. clicmd:: show ip ospf mpls-te interface .. clicmd:: show ip ospf mpls-te interface INTERFACE @@ -833,6 +876,20 @@ Traffic Engineering Show Traffic Engineering router parameters. +.. index:: show ip ospf mpls-te database [verbose|json] +.. clicmd:: show ip ospf mpls-te database [verbose|json] + +.. index:: show ip ospf mpls-te database vertex [self-originate|adv-router ADV-ROUTER] [verbose|json] +.. clicmd:: show ip ospf mpls-te database vertex [self-originate|adv-router ADV-ROUTER] [verbose|json] + +.. index:: show ip ospf mpls-te database edge [A.B.C.D] [verbose|json] +.. clicmd:: show ip ospf mpls-te database edge [A.B.C.D] [verbose|json] + +.. index:: show ip ospf mpls-te database subnet [A.B.C.D/M] [verbose|json] +.. clicmd:: show ip ospf mpls-te database subnet [A.B.C.D/M] [verbose|json] + + Show Traffic Engineering Database + .. _router-information: Router Information @@ -966,6 +1023,12 @@ TI-LFA requires a proper Segment Routing configuration. Debugging OSPF ============== +.. clicmd:: debug ospf bfd + + Enable or disable debugging for BFD events. This will show BFD integration + library messages and OSPF BFD integration messages that are mostly state + transitions and validation problems. + .. clicmd:: debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail] @@ -1029,8 +1092,9 @@ Debugging OSPF Debug commnd to enable/disable external route summarisation specific debugs. -OSPF Configuration Examples -=========================== + +Sample Configuration +==================== A simple example, with MD5 authentication enabled: diff --git a/doc/user/overview.rst b/doc/user/overview.rst index b4f56260c9..efe64b72f0 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -58,6 +58,33 @@ routes to Internet exchanges running full Internet tables. FRR runs on all modern \*NIX operating systems, including Linux and the BSDs. Feature support varies by platform; see the :ref:`feature-matrix`. +System Requirements +------------------- + +System resources needed by FRR are highly dependent on workload. Routing +software performance is particularly susceptible to external factors such as: + +* Kernel networking stack +* Physical NIC +* Peer behavior +* Routing information scale + +Because of these factors - especially the last one - it's difficult to lay out +resource requirements. + +To put this in perspective, FRR can be run on very low resource systems such as +SBCs, provided it is not stressed too much. If you want to set up 4 Raspberry +Pis to play with BGP or OSPF, it should work fine. If you ask a FRR to process +a complete internet routing table on a Raspberry Pi, you will be disappointed. +However, given enough resources, FRR ought to be capable of acting as a core IX +router. Such a use case requires at least 4gb of memory and a recent quad-core +server processor at a minimum. + +If you are new to networking, an important thing to remember is that FRR is +control plane software. It does not itself forward packets - it exchanges +information with peers about how to forward packets. Forwarding plane +performance largely depends on choice of NIC / ASIC. + System Architecture ------------------- diff --git a/doc/user/pathd.rst b/doc/user/pathd.rst index fe50a5e7eb..5fc3837839 100644 --- a/doc/user/pathd.rst +++ b/doc/user/pathd.rst @@ -395,3 +395,52 @@ learned through BGP using route-maps: ! In this case, the SR Policy with color `1` and endpoint `1.1.1.1` is selected. + + +Sample configuration +==================== + +.. code-block:: frr + + ! Default pathd configuration sample + ! + password frr + log stdout + + segment-routing + traffic-eng + segment-list test1 + index 10 mpls label 123 + index 20 mpls label 456 + ! + segment-list test2 + index 10 mpls label 321 + index 20 mpls label 654 + ! + policy color 1 endpoint 1.1.1.1 + name one + binding-sid 100 + candidate-path preference 100 name test1 explicit segment-list test1 + candidate-path preference 200 name test2 explicit segment-list test2 + ! + policy color 2 endpoint 2.2.2.2 + name two + binding-sid 101 + candidate-path preference 100 name def explicit segment-list test2 + candidate-path preference 200 name dyn dynamic + bandwidth 12345 + metric bound abc 16 required + metric te 10 + ! + ! + pcep + pcc-peer PCE1 + address ip 127.0.0.1 + sr-draft07 + ! + pcc + peer PCE1 + ! + ! + ! + diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 14a05a69d7..77134a7704 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -290,3 +290,22 @@ kernel that points to a table to use for forwarding once the rule matches. The creation of a nexthop or nexthop-group is translated to a default route in a table with the nexthops specified as the nexthops for the default route. + +Sample configuration +==================== + +.. code-block:: frr + + nexthop-group TEST + nexthop 4.5.6.7 + nexthop 5.6.7.8 + ! + pbr-map BLUE seq 100 + match dst-ip 9.9.9.0/24 + match src-ip 10.10.10.0/24 + set nexthop-group TEST + ! + int swp1 + pbr-policy BLUE + + diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 86716b49a4..6b7cae2c5d 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -656,3 +656,34 @@ setup. This is existing PIM configuration: - Enable pim on the underlay L3 interface via the "ip pim" command. - Configure RPs for the BUM multicast group range. - Ensure the PIM is enabled on the lo of the VTEPs and the RP. + + +Sample configuration +==================== + +.. code-block:: frr + + debug igmp + debug pim + debug pim zebra + + ! You may want to enable ssmpingd for troubleshooting + ! See http://www.venaas.no/multicast/ssmping/ + ! + ip ssmpingd 1.1.1.1 + ip ssmpingd 2.2.2.2 + + ! HINTS: + ! - Enable "ip pim ssm" on the interface directly attached to the + ! multicast source host (if this is the first-hop router) + ! - Enable "ip pim ssm" on pim-routers-facing interfaces + ! - Enable "ip igmp" on IGMPv3-hosts-facing interfaces + ! - In order to inject IGMPv3 local membership information in the + ! PIM protocol state, enable both "ip pim ssm" and "ip igmp" on + ! the same interface; otherwise PIM won't advertise + ! IGMPv3-learned membership to other PIM routers + + interface eth0 + ip pim ssm + ip igmp + diff --git a/doc/user/ripd.rst b/doc/user/ripd.rst index cba93e0d84..83c8c93b1c 100644 --- a/doc/user/ripd.rst +++ b/doc/user/ripd.rst @@ -539,3 +539,21 @@ Debug for RIP protocol. Shows all information currently set for ripd debug. + +Sample configuration +==================== + +.. code-block:: frr + + + debug rip events + debug rip packet + + router rip + network 11.0.0.0/8 + network eth0 + route 10.0.0.0/8 + distribute-list private-only in eth0 + + access-list private-only permit 10.0.0.0/8 + access-list private-only deny any diff --git a/doc/user/ripngd.rst b/doc/user/ripngd.rst index 0387e36305..b273eb3bfa 100644 --- a/doc/user/ripngd.rst +++ b/doc/user/ripngd.rst @@ -71,3 +71,19 @@ ripngd Filtering Commands distribute-list local-only out sit1 + +Sample configuration +==================== + +.. code-block:: frr + + debug ripng events + debug ripng packet + + router ripng + network sit1 + route 3ffe:506::0/32 + distribute-list local-only out sit1 + + ipv6 access-list local-only permit 3ffe:506::0/32 + ipv6 access-list local-only deny any diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 7f357b0925..3cb83cc652 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -333,10 +333,10 @@ Route Map Exit Action Command Route Map Optimization Command ============================== -.. clicmd:: route-map optimization +.. clicmd:: route-map ROUTE-MAP-NAME optimization - Enable route-map processing optimization. The optimization is - enabled by default. + Enable route-map processing optimization for `route-map-name`. + The optimization is enabled by default. Instead of sequentially passing through all the route-map indexes until a match is found, the search for the best-match index will be based on a look-up in a prefix-tree. A per-route-map prefix-tree diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index 01705f607c..d496d437d3 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -206,6 +206,14 @@ Displaying RPKI Display all configured cache servers, whether active or not. +.. clicmd:: show bgp [afi] [safi] <A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> rpki <valid|invalid|notfound> + + Display for the specified prefix or address the bgp paths that match the given rpki state. + +.. clicmd:: show bgp [afi] [safi] rpki <valid|invalid|notfound> + + Display all prefixes that match the given rpki state. + RPKI Configuration Example -------------------------- diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst index 9e83e44222..bef2748afa 100644 --- a/doc/user/sharp.rst +++ b/doc/user/sharp.rst @@ -131,3 +131,15 @@ keyword. At present, no sharp commands will be preserved in the config. Send an ARP/NDP request to trigger the addition of a neighbor in the ARP table. + +.. clicmd:: sharp import-te + + Import Traffic Engineering Database produce by OSPF or IS-IS. + +.. clicmd:: show sharp ted [verbose|json] + +.. clicmd:: show sharp ted [<vertex [A.B.C.D]|edge [A.B.C.D]|subnet [A.B.C.D/M]>] [verbose|json] + + Show imported Traffic Engineering Data Base + + diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index e5cd1de201..205b25e53e 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -1020,3 +1020,68 @@ For protocols requiring an IPv6 router-id, the following commands are available: .. clicmd:: show ipv6 router-id [vrf NAME] Display the user configured IPv6 router-id. + + +Debugging +========= + +.. clicmd:: debug zebra mpls [detailed] + + MPLS-related events and information. + +.. clicmd:: debug zebra events + + Zebra events + +.. clicmd:: debug zebra nht [detailed] + + Nexthop-tracking / reachability information + +.. clicmd:: debug zebra vxlan + + VxLAN (EVPN) events + +.. clicmd:: debug zebra pseudowires + + Pseudowire events. + +.. clicmd:: debug zebra packet [<recv|send>] [detail] + + ZAPI message and packet details + +.. clicmd:: debug zebra kernel + + Kernel / OS events. + +.. clicmd:: debug zebra kernel msgdump [<recv|send>] + + Raw OS (netlink) message details. + +.. clicmd:: debug zebra rib [detailed] + + RIB events. + +.. clicmd:: debug zebra fpm + + FPM (forwarding-plane manager) events. + +.. clicmd:: debug zebra dplane [detailed] + + Dataplane / FIB events. + +.. clicmd:: debug zebra pbr + + PBR (policy-based routing) events. + +.. clicmd:: debug zebra mlag + + MLAG events. + +.. clicmd:: debug zebra evpn mh <es|mac|neigh|nh> + + EVPN multi-hop events. + +.. clicmd:: debug zebra nexthop [detail] + + Nexthop and nexthop-group events. + diff --git a/eigrpd/eigrp_filter.c b/eigrpd/eigrp_filter.c index c77a6fc1b1..8f80b78d20 100644 --- a/eigrpd/eigrp_filter.c +++ b/eigrpd/eigrp_filter.c @@ -57,7 +57,6 @@ #include "eigrpd/eigrp_const.h" #include "eigrpd/eigrp_filter.h" #include "eigrpd/eigrp_packet.h" -#include "eigrpd/eigrp_memory.h" /* * Distribute-list update functions. diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c index bb7a930e6d..02e943043f 100644 --- a/eigrpd/eigrp_interface.c +++ b/eigrpd/eigrp_interface.c @@ -52,12 +52,14 @@ #include "eigrpd/eigrp_vty.h" #include "eigrpd/eigrp_network.h" #include "eigrpd/eigrp_topology.h" -#include "eigrpd/eigrp_memory.h" #include "eigrpd/eigrp_fsm.h" #include "eigrpd/eigrp_dump.h" #include "eigrpd/eigrp_types.h" #include "eigrpd/eigrp_metric.h" +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_IF, "EIGRP interface"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_IF_INFO, "EIGRP Interface Information"); + struct eigrp_interface *eigrp_if_new(struct eigrp *eigrp, struct interface *ifp, struct prefix *p) { diff --git a/eigrpd/eigrp_memory.c b/eigrpd/eigrp_memory.c deleted file mode 100644 index f2cb9a5c98..0000000000 --- a/eigrpd/eigrp_memory.c +++ /dev/null @@ -1,42 +0,0 @@ -/* eigrpd memory type definitions - * - * Copyright (C) 2017 Donald Sharp - * - * This file is part of FRR - * - * FRR is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * FRR is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "eigrp_memory.h" - -DEFINE_MGROUP(EIGRPD, "eigrpd"); -DEFINE_MTYPE(EIGRPD, EIGRP_TOP, "EIGRP structure"); -DEFINE_MTYPE(EIGRPD, EIGRP_IF, "EIGRP interface"); -DEFINE_MTYPE(EIGRPD, EIGRP_NEIGHBOR, "EIGRP neighbor"); -DEFINE_MTYPE(EIGRPD, EIGRP_IF_PARAMS, "EIGRP Interface Parameters"); -DEFINE_MTYPE(EIGRPD, EIGRP_IF_INFO, "EIGRP Interface Information"); -DEFINE_MTYPE(EIGRPD, EIGRP_FIFO, "EIGRP FIFO"); -DEFINE_MTYPE(EIGRPD, EIGRP_PACKET, "EIGRP Packet"); -DEFINE_MTYPE(EIGRPD, EIGRP_IPV4_INT_TLV, "EIGRP IPv4 TLV"); -DEFINE_MTYPE(EIGRPD, EIGRP_SEQ_TLV, "EIGRP SEQ TLV"); -DEFINE_MTYPE(EIGRPD, EIGRP_AUTH_TLV, "EIGRP AUTH TLV"); -DEFINE_MTYPE(EIGRPD, EIGRP_AUTH_SHA256_TLV, "EIGRP SHA TLV"); -DEFINE_MTYPE(EIGRPD, EIGRP_PREFIX_DESCRIPTOR, "EIGRP Prefix"); -DEFINE_MTYPE(EIGRPD, EIGRP_ROUTE_DESCRIPTOR, "EIGRP Nexthop Entry"); -DEFINE_MTYPE(EIGRPD, EIGRP_FSM_MSG, "EIGRP FSM Message"); diff --git a/eigrpd/eigrp_neighbor.c b/eigrpd/eigrp_neighbor.c index 1da2f7a108..f2d5217eb0 100644 --- a/eigrpd/eigrp_neighbor.c +++ b/eigrpd/eigrp_neighbor.c @@ -52,9 +52,10 @@ #include "eigrpd/eigrp_vty.h" #include "eigrpd/eigrp_network.h" #include "eigrpd/eigrp_topology.h" -#include "eigrpd/eigrp_memory.h" #include "eigrpd/eigrp_errors.h" +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_NEIGHBOR, "EIGRP neighbor"); + struct eigrp_neighbor *eigrp_nbr_new(struct eigrp_interface *ei) { struct eigrp_neighbor *nbr; diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c index 7eee254627..0b37733990 100644 --- a/eigrpd/eigrp_packet.c +++ b/eigrpd/eigrp_packet.c @@ -56,9 +56,15 @@ #include "eigrpd/eigrp_network.h" #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" -#include "eigrpd/eigrp_memory.h" #include "eigrpd/eigrp_errors.h" +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_FIFO, "EIGRP FIFO"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_PACKET, "EIGRP Packet"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_IPV4_INT_TLV, "EIGRP IPv4 TLV"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_SEQ_TLV, "EIGRP SEQ TLV"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_AUTH_TLV, "EIGRP AUTH TLV"); +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_AUTH_SHA256_TLV, "EIGRP SHA TLV"); + /* Packet Type String. */ const struct message eigrp_packet_type_str[] = { {EIGRP_OPC_UPDATE, "Update"}, diff --git a/eigrpd/eigrp_query.c b/eigrpd/eigrp_query.c index 0ab7b59dbb..c8769fb11f 100644 --- a/eigrpd/eigrp_query.c +++ b/eigrpd/eigrp_query.c @@ -52,7 +52,6 @@ #include "eigrpd/eigrp_macros.h" #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" -#include "eigrpd/eigrp_memory.h" uint32_t eigrp_query_send_all(struct eigrp *eigrp) { diff --git a/eigrpd/eigrp_reply.c b/eigrpd/eigrp_reply.c index d16482173c..015daa768f 100644 --- a/eigrpd/eigrp_reply.c +++ b/eigrpd/eigrp_reply.c @@ -58,7 +58,6 @@ #include "eigrpd/eigrp_macros.h" #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" -#include "eigrpd/eigrp_memory.h" #include "eigrpd/eigrp_errors.h" void eigrp_send_reply(struct eigrp_neighbor *nbr, diff --git a/eigrpd/eigrp_siaquery.c b/eigrpd/eigrp_siaquery.c index 027700fe11..9c2a8c9d84 100644 --- a/eigrpd/eigrp_siaquery.c +++ b/eigrpd/eigrp_siaquery.c @@ -52,7 +52,6 @@ #include "eigrpd/eigrp_macros.h" #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" -#include "eigrpd/eigrp_memory.h" /*EIGRP SIA-QUERY read function*/ void eigrp_siaquery_receive(struct eigrp *eigrp, struct ip *iph, diff --git a/eigrpd/eigrp_siareply.c b/eigrpd/eigrp_siareply.c index 590b224d68..2d298c20bf 100644 --- a/eigrpd/eigrp_siareply.c +++ b/eigrpd/eigrp_siareply.c @@ -51,7 +51,6 @@ #include "eigrpd/eigrp_macros.h" #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" -#include "eigrpd/eigrp_memory.h" /*EIGRP SIA-REPLY read function*/ void eigrp_siareply_receive(struct eigrp *eigrp, struct ip *iph, diff --git a/eigrpd/eigrp_snmp.c b/eigrpd/eigrp_snmp.c index 3b232be386..9ada292feb 100644 --- a/eigrpd/eigrp_snmp.c +++ b/eigrpd/eigrp_snmp.c @@ -588,8 +588,7 @@ static struct eigrp_neighbor *eigrpNbrLookup(struct variable *v, oid *name, if (nbr) { *length = v->namelen + IN_ADDR_SIZE + 1; - oid_copy_addr(name + v->namelen, nbr_addr, - IN_ADDR_SIZE); + oid_copy_in_addr(name + v->namelen, nbr_addr); name[v->namelen + IN_ADDR_SIZE] = *ifindex; return nbr; } diff --git a/eigrpd/eigrp_topology.c b/eigrpd/eigrp_topology.c index 6da7756f84..846e211622 100644 --- a/eigrpd/eigrp_topology.c +++ b/eigrpd/eigrp_topology.c @@ -51,9 +51,11 @@ #include "eigrpd/eigrp_dump.h" #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" -#include "eigrpd/eigrp_memory.h" #include "eigrpd/eigrp_metric.h" +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_ROUTE_DESCRIPTOR, "EIGRP Nexthop Entry"); +DEFINE_MTYPE(EIGRPD, EIGRP_PREFIX_DESCRIPTOR, "EIGRP Prefix"); + static int eigrp_route_descriptor_cmp(struct eigrp_route_descriptor *rd1, struct eigrp_route_descriptor *rd2); diff --git a/eigrpd/eigrp_topology.h b/eigrpd/eigrp_topology.h index 26fa1a11b0..d7f79057ab 100644 --- a/eigrpd/eigrp_topology.h +++ b/eigrpd/eigrp_topology.h @@ -32,6 +32,10 @@ #ifndef _ZEBRA_EIGRP_TOPOLOGY_H #define _ZEBRA_EIGRP_TOPOLOGY_H +#include "memory.h" + +DECLARE_MTYPE(EIGRP_PREFIX_DESCRIPTOR); + /* EIGRP Topology table related functions. */ extern struct route_table *eigrp_topology_new(void); extern void eigrp_topology_init(struct route_table *table); diff --git a/eigrpd/eigrp_update.c b/eigrpd/eigrp_update.c index 91f3b3218b..0dc509706c 100644 --- a/eigrpd/eigrp_update.c +++ b/eigrpd/eigrp_update.c @@ -62,7 +62,6 @@ #include "eigrpd/eigrp_topology.h" #include "eigrpd/eigrp_fsm.h" #include "eigrpd/eigrp_network.h" -#include "eigrpd/eigrp_memory.h" #include "eigrpd/eigrp_metric.h" bool eigrp_update_prefix_apply(struct eigrp *eigrp, struct eigrp_interface *ei, diff --git a/eigrpd/eigrpd.c b/eigrpd/eigrpd.c index 8c16cf3d58..1030154907 100644 --- a/eigrpd/eigrpd.c +++ b/eigrpd/eigrpd.c @@ -55,9 +55,12 @@ #include "eigrpd/eigrp_packet.h" #include "eigrpd/eigrp_network.h" #include "eigrpd/eigrp_topology.h" -#include "eigrpd/eigrp_memory.h" #include "eigrpd/eigrp_filter.h" +DEFINE_MGROUP(EIGRPD, "eigrpd"); + +DEFINE_MTYPE_STATIC(EIGRPD, EIGRP_TOP, "EIGRP structure"); + DEFINE_QOBJ_TYPE(eigrp); static struct eigrp_master eigrp_master; diff --git a/eigrpd/eigrpd.conf.sample b/eigrpd/eigrpd.conf.sample deleted file mode 100644 index 662e667d3f..0000000000 --- a/eigrpd/eigrpd.conf.sample +++ /dev/null @@ -1,13 +0,0 @@ -! -*- eigrpd -*- -! -! EIGRPDd sample configuration file -! -! -hostname eigrpd -password zebra -!enable password please-set-at-here -! -!router eigrp 4453 -! network 192.168.1.0/24 -! -log stdout diff --git a/eigrpd/eigrpd.h b/eigrpd/eigrpd.h index 01173768ba..949fe682c7 100644 --- a/eigrpd/eigrpd.h +++ b/eigrpd/eigrpd.h @@ -32,6 +32,9 @@ #include "filter.h" #include "log.h" +#include "memory.h" + +DECLARE_MGROUP(EIGRPD); /* Set EIGRP version is "classic" - wide metrics comes next */ #define EIGRP_MAJOR_VERSION 1 diff --git a/eigrpd/subdir.am b/eigrpd/subdir.am index 13c9f7f8ae..82873a4960 100644 --- a/eigrpd/subdir.am +++ b/eigrpd/subdir.am @@ -5,7 +5,6 @@ if EIGRPD noinst_LIBRARIES += eigrpd/libeigrp.a sbin_PROGRAMS += eigrpd/eigrpd -dist_examples_DATA += eigrpd/eigrpd.conf.sample vtysh_scan += \ eigrpd/eigrp_cli.c \ eigrpd/eigrp_dump.c \ @@ -24,7 +23,6 @@ eigrpd_libeigrp_a_SOURCES = \ eigrpd/eigrp_fsm.c \ eigrpd/eigrp_hello.c \ eigrpd/eigrp_interface.c \ - eigrpd/eigrp_memory.c \ eigrpd/eigrp_metric.c \ eigrpd/eigrp_neighbor.c \ eigrpd/eigrp_network.c \ @@ -63,7 +61,6 @@ noinst_HEADERS += \ eigrpd/eigrp_fsm.h \ eigrpd/eigrp_interface.h \ eigrpd/eigrp_macros.h \ - eigrpd/eigrp_memory.h \ eigrpd/eigrp_metric.h \ eigrpd/eigrp_neighbor.h \ eigrpd/eigrp_network.h \ diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 426b96e90c..20651706d3 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -22,7 +22,6 @@ #include <zebra.h> #include "isisd/fabricd.h" #include "isisd/isisd.h" -#include "isisd/isis_memory.h" #include "isisd/isis_circuit.h" #include "isisd/isis_misc.h" #include "isisd/isis_adjacency.h" diff --git a/isisd/fabricd.conf.sample b/isisd/fabricd.conf.sample deleted file mode 100644 index be9e33ba62..0000000000 --- a/isisd/fabricd.conf.sample +++ /dev/null @@ -1,27 +0,0 @@ -! -*- openfabric -*- -! -! fabricd sample configuration file -! -hostname fabricd -password foo -enable password foo -log stdout -!log file /tmp/fabricd.log -! -! -router openfabric DEAD - net 47.0023.0000.0003.0300.0100.0102.0304.0506.00 -! lsp-lifetime 65535 - -! hostname isisd-router -! domain-password foobar - -interface eth0 - ip router openfabric DEAD -! openfabric hello-interval 5 -! openfabric lsp-interval 1000 - -! -- optional -! openfabric retransmit-interval 10 -! openfabric retransmit-throttle-interval -! diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index a5482635eb..c1f5e49eca 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -49,6 +49,9 @@ #include "isisd/fabricd.h" #include "isisd/isis_nb.h" +DEFINE_MTYPE_STATIC(ISISD, ISIS_ADJACENCY, "ISIS adjacency"); +DEFINE_MTYPE(ISISD, ISIS_ADJACENCY_INFO, "ISIS adjacency info"); + static struct isis_adjacency *adj_alloc(struct isis_circuit *circuit, const uint8_t *id) { diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 4daf7b5db2..754345c008 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -27,6 +27,8 @@ #include "isisd/isis_tlvs.h" +DECLARE_MTYPE(ISIS_ADJACENCY_INFO); + enum isis_adj_usage { ISIS_ADJ_NONE, ISIS_ADJ_LEVEL1, diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 6fc76fce43..a637429e84 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -61,6 +61,8 @@ #include "isisd/isis_nb.h" #include "isisd/isis_ldp_sync.h" +DEFINE_MTYPE_STATIC(ISISD, ISIS_CIRCUIT, "ISIS circuit"); + DEFINE_QOBJ_TYPE(isis_circuit); DEFINE_HOOK(isis_if_new_hook, (struct interface *ifp), (ifp)); diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index 039483b9b2..b108210686 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -392,7 +392,7 @@ void cli_show_ip_isis_ipv4(struct vty *vty, struct lyd_node *dnode, if (!yang_dnode_get_bool(dnode, NULL)) vty_out(vty, " no"); - vty_out(vty, " ip router isis %s ", + vty_out(vty, " ip router isis %s", yang_dnode_get_string(dnode, "../area-tag")); if (!strmatch(vrf, VRF_DEFAULT_NAME)) vty_out(vty, " vrf %s", vrf); diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index d2c5d93e25..decd3e8922 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -40,6 +40,8 @@ #include "isisd/isis_misc.h" #include "isisd/isis_constants.h" +DEFINE_MTYPE_STATIC(ISISD, ISIS_DYNHN, "ISIS dyn hostname"); + extern struct host host; struct list *dyn_cache = NULL; diff --git a/isisd/isis_lfa.c b/isisd/isis_lfa.c index 3ebac8aaa9..085177b943 100644 --- a/isisd/isis_lfa.c +++ b/isisd/isis_lfa.c @@ -46,6 +46,7 @@ DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_NODE, "ISIS SPF Node"); DEFINE_MTYPE_STATIC(ISISD, ISIS_LFA_TIEBREAKER, "ISIS LFA Tiebreaker"); DEFINE_MTYPE_STATIC(ISISD, ISIS_LFA_EXCL_IFACE, "ISIS LFA Excluded Interface"); DEFINE_MTYPE_STATIC(ISISD, ISIS_RLFA, "ISIS Remote LFA"); +DEFINE_MTYPE(ISISD, ISIS_NEXTHOP_LABELS, "ISIS nexthop MPLS labels"); static inline int isis_spf_node_compare(const struct isis_spf_node *a, const struct isis_spf_node *b) diff --git a/isisd/isis_lfa.h b/isisd/isis_lfa.h index d75632e2cd..9db03a3a19 100644 --- a/isisd/isis_lfa.h +++ b/isisd/isis_lfa.h @@ -22,6 +22,9 @@ #include "lib/typesafe.h" #include "lib/zclient.h" +#include "lib/memory.h" + +DECLARE_MTYPE(ISIS_NEXTHOP_LABELS); PREDECL_RBTREE_UNIQ(lfa_tiebreaker_tree); PREDECL_RBTREE_UNIQ(rlfa_tree); diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index faf16aa174..056e29e8de 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -60,6 +60,8 @@ #include "isisd/isis_tx_queue.h" #include "isisd/isis_nb.h" +DEFINE_MTYPE_STATIC(ISISD, ISIS_LSP, "ISIS LSP"); + static int lsp_refresh(struct thread *thread); static int lsp_l1_refresh_pseudo(struct thread *thread); static int lsp_l2_refresh_pseudo(struct thread *thread); diff --git a/isisd/isis_memory.c b/isisd/isis_memory.c deleted file mode 100644 index 34f6b6089a..0000000000 --- a/isisd/isis_memory.c +++ /dev/null @@ -1,49 +0,0 @@ -/* isisd memory type definitions - * - * Copyright (C) 2015 David Lamparter - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "isis_memory.h" - -DEFINE_MGROUP(ISISD, "isisd"); -DEFINE_MTYPE(ISISD, ISIS, "ISIS"); -DEFINE_MTYPE(ISISD, ISIS_TMP, "ISIS TMP"); -DEFINE_MTYPE(ISISD, ISIS_CIRCUIT, "ISIS circuit"); -DEFINE_MTYPE(ISISD, ISIS_LSP, "ISIS LSP"); -DEFINE_MTYPE(ISISD, ISIS_ADJACENCY, "ISIS adjacency"); -DEFINE_MTYPE(ISISD, ISIS_ADJACENCY_INFO, "ISIS adjacency info"); -DEFINE_MTYPE(ISISD, ISIS_AREA, "ISIS area"); -DEFINE_MTYPE(ISISD, ISIS_AREA_ADDR, "ISIS area address"); -DEFINE_MTYPE(ISISD, ISIS_DYNHN, "ISIS dyn hostname"); -DEFINE_MTYPE(ISISD, ISIS_SPFTREE, "ISIS SPFtree"); -DEFINE_MTYPE(ISISD, ISIS_VERTEX, "ISIS vertex"); -DEFINE_MTYPE(ISISD, ISIS_ROUTE_INFO, "ISIS route info"); -DEFINE_MTYPE(ISISD, ISIS_NEXTHOP, "ISIS nexthop"); -DEFINE_MTYPE(ISISD, ISIS_NEXTHOP_LABELS, "ISIS nexthop MPLS labels"); -DEFINE_MTYPE(ISISD, ISIS_DICT, "ISIS dictionary"); -DEFINE_MTYPE(ISISD, ISIS_DICT_NODE, "ISIS dictionary node"); -DEFINE_MTYPE(ISISD, ISIS_EXT_ROUTE, "ISIS redistributed route"); -DEFINE_MTYPE(ISISD, ISIS_EXT_INFO, "ISIS redistributed route info"); -DEFINE_MTYPE(ISISD, ISIS_MPLS_TE, "ISIS MPLS_TE parameters"); -DEFINE_MTYPE(ISISD, ISIS_ACL_NAME, "ISIS access-list name"); -DEFINE_MTYPE(ISISD, ISIS_PLIST_NAME, "ISIS prefix-list name"); diff --git a/isisd/isis_memory.h b/isisd/isis_memory.h deleted file mode 100644 index 5fd881e42d..0000000000 --- a/isisd/isis_memory.h +++ /dev/null @@ -1,50 +0,0 @@ -/* isisd memory type declarations - * - * Copyright (C) 2015 David Lamparter - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _QUAGGA_ISIS_MEMORY_H -#define _QUAGGA_ISIS_MEMORY_H - -#include "memory.h" - -DECLARE_MGROUP(ISISD); -DECLARE_MTYPE(ISIS); -DECLARE_MTYPE(ISIS_TMP); -DECLARE_MTYPE(ISIS_CIRCUIT); -DECLARE_MTYPE(ISIS_LSP); -DECLARE_MTYPE(ISIS_ADJACENCY); -DECLARE_MTYPE(ISIS_ADJACENCY_INFO); -DECLARE_MTYPE(ISIS_AREA); -DECLARE_MTYPE(ISIS_AREA_ADDR); -DECLARE_MTYPE(ISIS_DYNHN); -DECLARE_MTYPE(ISIS_SPFTREE); -DECLARE_MTYPE(ISIS_VERTEX); -DECLARE_MTYPE(ISIS_ROUTE_INFO); -DECLARE_MTYPE(ISIS_NEXTHOP); -DECLARE_MTYPE(ISIS_NEXTHOP_LABELS); -DECLARE_MTYPE(ISIS_DICT); -DECLARE_MTYPE(ISIS_DICT_NODE); -DECLARE_MTYPE(ISIS_EXT_ROUTE); -DECLARE_MTYPE(ISIS_EXT_INFO); -DECLARE_MTYPE(ISIS_MPLS_TE); -DECLARE_MTYPE(ISIS_ACL_NAME); -DECLARE_MTYPE(ISIS_PLIST_NAME); - -#endif /* _QUAGGA_ISIS_MEMORY_H */ diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c index a6e236c796..c024549fcc 100644 --- a/isisd/isis_mt.c +++ b/isisd/isis_mt.c @@ -21,7 +21,6 @@ */ #include <zebra.h> #include "isisd/isisd.h" -#include "isisd/isis_memory.h" #include "isisd/isis_circuit.h" #include "isisd/isis_adjacency.h" #include "isisd/isis_misc.h" diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index f0663c691c..c8ad816b58 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -48,12 +48,14 @@ #include "isisd/isis_spf.h" #include "isisd/isis_spf_private.h" #include "isisd/isis_te.h" -#include "isisd/isis_memory.h" #include "isisd/isis_mt.h" #include "isisd/isis_redist.h" #include "isisd/isis_ldp_sync.h" #include "isisd/isis_dr.h" +DEFINE_MTYPE_STATIC(ISISD, ISIS_MPLS_TE, "ISIS MPLS_TE parameters"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_PLIST_NAME, "ISIS prefix-list name"); + extern struct zclient *zclient; /* @@ -131,7 +133,7 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) switch (args->event) { case NB_EV_VALIDATE: - area = nb_running_get_entry(args->dnode, NULL, true); + area = nb_running_get_entry(args->dnode, NULL, false); if (area == NULL) return NB_ERR_VALIDATION; addr.addr_len = dotformat2buff(buff, net_title); diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index 856c47b9b7..c33d56e625 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -24,7 +24,6 @@ #include "if.h" #include "linklist.h" #include "memory.h" -#include "isis_memory.h" #include "prefix.h" #include "routemap.h" #include "stream.h" @@ -42,6 +41,10 @@ #include "isisd/isis_route.h" #include "isisd/isis_zebra.h" +DEFINE_MTYPE_STATIC(ISISD, ISIS_EXT_ROUTE, "ISIS redistributed route"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_EXT_INFO, "ISIS redistributed route info"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_RMAP_NAME, "ISIS redistribute route-map name"); + static int redist_protocol(int family) { if (family == AF_INET) @@ -327,13 +330,13 @@ static void isis_redist_routemap_set(struct isis_redist *redist, const char *routemap) { if (redist->map_name) { - XFREE(MTYPE_ISIS, redist->map_name); + XFREE(MTYPE_ISIS_RMAP_NAME, redist->map_name); route_map_counter_decrement(redist->map); redist->map = NULL; } if (routemap && strlen(routemap)) { - redist->map_name = XSTRDUP(MTYPE_ISIS, routemap); + redist->map_name = XSTRDUP(MTYPE_ISIS_RMAP_NAME, routemap); redist->map = route_map_lookup_by_name(routemap); route_map_counter_increment(redist->map); } @@ -507,7 +510,7 @@ void isis_redist_area_finish(struct isis_area *area) redist = &area->redist_settings[protocol][type] [level]; redist->redist = 0; - XFREE(MTYPE_ISIS, redist->map_name); + XFREE(MTYPE_ISIS_RMAP_NAME, redist->map_name); } route_table_finish(area->ext_reach[protocol][level]); } diff --git a/isisd/isis_route.c b/isisd/isis_route.c index ed5fadde10..eb534a543a 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -50,6 +50,9 @@ #include "isis_route.h" #include "isis_zebra.h" +DEFINE_MTYPE_STATIC(ISISD, ISIS_NEXTHOP, "ISIS nexthop"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_ROUTE_INFO, "ISIS route info"); + DEFINE_HOOK(isis_route_update_hook, (struct isis_area * area, struct prefix *prefix, struct isis_route_info *route_info), diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c index 522026dde4..fe6a2f4052 100644 --- a/isisd/isis_snmp.c +++ b/isisd/isis_snmp.c @@ -1037,6 +1037,8 @@ static int isis_snmp_circuit_level_lookup_next( break; } + assert(oid_idx != NULL); + /* We have to check level specified by index */ if (oid_idx[1] < IS_LEVEL_1) { level = IS_LEVEL_1; diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 7bcc6fea90..3e8ec8817e 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -60,8 +60,10 @@ #include "fabricd.h" #include "isis_spf_private.h" -DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info"); -DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_ADJ, "ISIS SPF Adjacency"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_SPFTREE, "ISIS SPFtree"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_ADJ, "ISIS SPF Adjacency"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_VERTEX, "ISIS vertex"); DEFINE_MTYPE_STATIC(ISISD, ISIS_VERTEX_ADJ, "ISIS SPF Vertex Adjacency"); static void spf_adj_list_parse_lsp(struct isis_spftree *spftree, @@ -248,6 +250,20 @@ static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree, return vertex; } +void isis_vertex_del(struct isis_vertex *vertex) +{ + list_delete(&vertex->Adj_N); + list_delete(&vertex->parents); + if (vertex->firsthops) { + hash_clean(vertex->firsthops, NULL); + hash_free(vertex->firsthops); + vertex->firsthops = NULL; + } + + memset(vertex, 0, sizeof(struct isis_vertex)); + XFREE(MTYPE_ISIS_VERTEX, vertex); +} + struct isis_vertex_adj * isis_vertex_adj_add(struct isis_spftree *spftree, struct isis_vertex *vertex, struct list *vadj_list, struct isis_spf_adj *sadj, diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 79dfa3e164..07d4ff5a0e 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -179,20 +179,7 @@ static void isis_vertex_queue_init(struct isis_vertex_queue *queue, isis_vertex_queue_hash_cmp, name); } -__attribute__((__unused__)) -static void isis_vertex_del(struct isis_vertex *vertex) -{ - list_delete(&vertex->Adj_N); - list_delete(&vertex->parents); - if (vertex->firsthops) { - hash_clean(vertex->firsthops, NULL); - hash_free(vertex->firsthops); - vertex->firsthops = NULL; - } - - memset(vertex, 0, sizeof(struct isis_vertex)); - XFREE(MTYPE_ISIS_VERTEX, vertex); -} +void isis_vertex_del(struct isis_vertex *vertex); bool isis_vertex_adj_exists(const struct isis_spftree *spftree, const struct isis_vertex *vertex, diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 6af9290467..2bac8e7fd5 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -33,7 +33,6 @@ #include "network.h" #include "isisd/isisd.h" -#include "isisd/isis_memory.h" #include "isisd/isis_tlvs.h" #include "isisd/isis_common.h" #include "isisd/isis_mt.h" @@ -52,7 +51,8 @@ DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists"); typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type, uint8_t tlv_len, struct stream *s, struct sbuf *log, void *dest, int indent); -typedef int (*pack_item_func)(struct isis_item *item, struct stream *s); +typedef int (*pack_item_func)(struct isis_item *item, struct stream *s, + size_t *min_length); typedef void (*free_item_func)(struct isis_item *i); typedef int (*unpack_item_func)(uint16_t mtid, uint8_t len, struct stream *s, struct sbuf *log, void *dest, int indent); @@ -368,12 +368,14 @@ static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts) } static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, - struct stream *s) + struct stream *s, size_t *min_len) { uint8_t size; - if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) + if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) { + *min_len = ISIS_SUBTLV_MAX_SIZE; return 1; + } if (IS_SUBTLV(exts, EXT_ADM_GRP)) { stream_putc(s, ISIS_SUBTLV_ADMIN_GRP); @@ -829,14 +831,17 @@ static void free_item_prefix_sid(struct isis_item *i) XFREE(MTYPE_ISIS_SUBTLV, i); } -static int pack_item_prefix_sid(struct isis_item *i, struct stream *s) +static int pack_item_prefix_sid(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; uint8_t size = (sid->flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6; - if (STREAM_WRITEABLE(s) < size) + if (STREAM_WRITEABLE(s) < size) { + *min_len = size; return 1; + } stream_putc(s, sid->flags); stream_putc(s, sid->algorithm); @@ -1121,12 +1126,15 @@ static void free_item_area_address(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_area_address(struct isis_item *i, struct stream *s) +static int pack_item_area_address(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_area_address *addr = (struct isis_area_address *)i; - if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len) + if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len) { + *min_len = (unsigned)1 + addr->len; return 1; + } stream_putc(s, addr->len); stream_put(s, addr->addr, addr->len); return 0; @@ -1200,12 +1208,15 @@ static void free_item_oldstyle_reach(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s) +static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i; - if (STREAM_WRITEABLE(s) < 11) + if (STREAM_WRITEABLE(s) < 11) { + *min_len = 11; return 1; + } stream_putc(s, r->metric); stream_putc(s, 0x80); /* delay metric - unsupported */ @@ -1269,12 +1280,15 @@ static void free_item_lan_neighbor(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s) +static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i; - if (STREAM_WRITEABLE(s) < 6) + if (STREAM_WRITEABLE(s) < 6) { + *min_len = 6; return 1; + } stream_put(s, n->mac, 6); @@ -1334,12 +1348,15 @@ static void free_item_lsp_entry(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_lsp_entry(struct isis_item *i, struct stream *s) +static int pack_item_lsp_entry(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_lsp_entry *e = (struct isis_lsp_entry *)i; - if (STREAM_WRITEABLE(s) < 16) + if (STREAM_WRITEABLE(s) < 16) { + *min_len = 16; return 1; + } stream_putw(s, e->rem_lifetime); stream_put(s, e->id, 8); @@ -1414,14 +1431,17 @@ static void free_item_extended_reach(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, item); } -static int pack_item_extended_reach(struct isis_item *i, struct stream *s) +static int pack_item_extended_reach(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_extended_reach *r = (struct isis_extended_reach *)i; size_t len; size_t len_pos; - if (STREAM_WRITEABLE(s) < 11 + ISIS_SUBTLV_MAX_SIZE) + if (STREAM_WRITEABLE(s) < 11 + ISIS_SUBTLV_MAX_SIZE) { + *min_len = 11 + ISIS_SUBTLV_MAX_SIZE; return 1; + } stream_put(s, r->id, sizeof(r->id)); stream_put3(s, r->metric); @@ -1429,7 +1449,7 @@ static int pack_item_extended_reach(struct isis_item *i, struct stream *s) /* Real length will be adjust after adding subTLVs */ stream_putc(s, 11); if (r->subtlvs) - pack_item_ext_subtlvs(r->subtlvs, s); + pack_item_ext_subtlvs(r->subtlvs, s, min_len); /* Adjust length */ len = stream_get_endp(s) - len_pos - 1; stream_putc_at(s, len_pos, len); @@ -1522,12 +1542,15 @@ static void free_item_oldstyle_ip_reach(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s) +static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i; - if (STREAM_WRITEABLE(s) < 12) + if (STREAM_WRITEABLE(s) < 12) { + *min_len = 12; return 1; + } stream_putc(s, r->metric); stream_putc(s, 0x80); /* delay metric - unsupported */ @@ -1677,12 +1700,15 @@ static void free_item_ipv4_address(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_ipv4_address(struct isis_item *i, struct stream *s) +static int pack_item_ipv4_address(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_ipv4_address *a = (struct isis_ipv4_address *)i; - if (STREAM_WRITEABLE(s) < 4) + if (STREAM_WRITEABLE(s) < 4) { + *min_len = 4; return 1; + } stream_put(s, &a->addr, 4); @@ -1738,12 +1764,15 @@ static void free_item_ipv6_address(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_ipv6_address(struct isis_item *i, struct stream *s) +static int pack_item_ipv6_address(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_ipv6_address *a = (struct isis_ipv6_address *)i; - if (STREAM_WRITEABLE(s) < 16) + if (STREAM_WRITEABLE(s) < 16) { + *min_len = 16; return 1; + } stream_put(s, &a->addr, 16); @@ -1802,12 +1831,15 @@ static void free_item_mt_router_info(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_mt_router_info(struct isis_item *i, struct stream *s) +static int pack_item_mt_router_info(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_mt_router_info *info = (struct isis_mt_router_info *)i; - if (STREAM_WRITEABLE(s) < 2) + if (STREAM_WRITEABLE(s) < 2) { + *min_len = 2; return 1; + } uint16_t entry = info->mtid; @@ -1962,13 +1994,16 @@ static void free_item_extended_ip_reach(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, item); } -static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s) +static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i; uint8_t control; - if (STREAM_WRITEABLE(s) < 5) + if (STREAM_WRITEABLE(s) < 5) { + *min_len = 5; return 1; + } stream_putl(s, r->metric); control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0; @@ -1977,8 +2012,10 @@ static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s) stream_putc(s, control); - if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) + if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) { + *min_len = 5 + (unsigned)PSIZE(r->prefix.prefixlen); return 1; + } stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen)); if (r->subtlvs) @@ -2444,13 +2481,16 @@ static void free_item_ipv6_reach(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, item); } -static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s) +static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i; uint8_t control; - if (STREAM_WRITEABLE(s) < 6) + if (STREAM_WRITEABLE(s) < 6 + (unsigned)PSIZE(r->prefix.prefixlen)) { + *min_len = 6 + (unsigned)PSIZE(r->prefix.prefixlen); return 1; + } stream_putl(s, r->metric); control = r->down ? ISIS_IPV6_REACH_DOWN : 0; @@ -2460,8 +2500,6 @@ static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s) stream_putc(s, control); stream_putc(s, r->prefix.prefixlen); - if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) - return 1; stream_put(s, &r->prefix.prefix.s6_addr, PSIZE(r->prefix.prefixlen)); if (r->subtlvs) @@ -2909,23 +2947,30 @@ static void free_item_auth(struct isis_item *i) XFREE(MTYPE_ISIS_TLV, i); } -static int pack_item_auth(struct isis_item *i, struct stream *s) +static int pack_item_auth(struct isis_item *i, struct stream *s, + size_t *min_len) { struct isis_auth *auth = (struct isis_auth *)i; - if (STREAM_WRITEABLE(s) < 1) + if (STREAM_WRITEABLE(s) < 1) { + *min_len = 1; return 1; + } stream_putc(s, auth->type); switch (auth->type) { case ISIS_PASSWD_TYPE_CLEARTXT: - if (STREAM_WRITEABLE(s) < auth->length) + if (STREAM_WRITEABLE(s) < auth->length) { + *min_len = 1 + auth->length; return 1; + } stream_put(s, auth->passwd, auth->length); break; case ISIS_PASSWD_TYPE_HMAC_MD5: - if (STREAM_WRITEABLE(s) < 16) + if (STREAM_WRITEABLE(s) < 16) { + *min_len = 1 + 16; return 1; + } auth->offset = stream_get_endp(s); stream_put(s, NULL, 16); break; @@ -3160,14 +3205,14 @@ static void free_items(enum isis_tlv_context context, enum isis_tlv_type type, } static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type, - struct isis_item *i, struct stream *s, + struct isis_item *i, struct stream *s, size_t *min_len, struct isis_tlvs **fragment_tlvs, const struct pack_order_entry *pe, uint16_t mtid) { const struct tlv_ops *ops = tlv_table[context][type]; if (ops && ops->pack_item) { - return ops->pack_item(i, s); + return ops->pack_item(i, s, min_len); } assert(!"Unknown item tlv type!"); @@ -3201,6 +3246,7 @@ static int pack_items_(uint16_t mtid, enum isis_tlv_context context, size_t len_pos, last_len, len; struct isis_item *item = NULL; int rv; + size_t min_len = 0; if (!items->head) return 0; @@ -3228,7 +3274,8 @@ top: last_len = len = 0; for (item = item ? item : items->head; item; item = item->next) { - rv = pack_item(context, type, item, s, fragment_tlvs, pe, mtid); + rv = pack_item(context, type, item, s, &min_len, fragment_tlvs, + pe, mtid); if (rv) goto too_long; @@ -3272,6 +3319,8 @@ too_long: if (!fragment_tlvs) return 1; stream_reset(s); + if (STREAM_WRITEABLE(s) < min_len) + return 1; *fragment_tlvs = new_fragment(new_fragment_arg); goto top; } diff --git a/isisd/isis_tx_queue.c b/isisd/isis_tx_queue.c index 7db76db0df..c7266152b7 100644 --- a/isisd/isis_tx_queue.c +++ b/isisd/isis_tx_queue.c @@ -25,7 +25,6 @@ #include "jhash.h" #include "isisd/isisd.h" -#include "isisd/isis_memory.h" #include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" #include "isisd/isis_lsp.h" diff --git a/isisd/isisd.c b/isisd/isisd.c index 039a8db27e..714961c177 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -81,6 +81,14 @@ unsigned long debug_sr; unsigned long debug_ldp_sync; unsigned long debug_lfa; +DEFINE_MGROUP(ISISD, "isisd"); + +DEFINE_MTYPE_STATIC(ISISD, ISIS, "ISIS process"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_NAME, "ISIS process name"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_AREA, "ISIS area"); +DEFINE_MTYPE(ISISD, ISIS_AREA_ADDR, "ISIS area address"); +DEFINE_MTYPE(ISISD, ISIS_ACL_NAME, "ISIS access-list name"); + DEFINE_QOBJ_TYPE(isis_area); /* ISIS process wide configuration. */ @@ -198,10 +206,10 @@ struct isis *isis_new(const char *vrf_name) if (vrf) { isis->vrf_id = vrf->vrf_id; isis_vrf_link(isis, vrf); - isis->name = XSTRDUP(MTYPE_ISIS, vrf->name); + isis->name = XSTRDUP(MTYPE_ISIS_NAME, vrf->name); } else { isis->vrf_id = VRF_UNKNOWN; - isis->name = XSTRDUP(MTYPE_ISIS, vrf_name); + isis->name = XSTRDUP(MTYPE_ISIS_NAME, vrf_name); } if (IS_DEBUG_EVENTS) @@ -565,7 +573,7 @@ static int isis_vrf_enable(struct vrf *vrf) isis = isis_lookup_by_vrfname(vrf->name); if (isis) { if (isis->name && strmatch(vrf->name, VRF_DEFAULT_NAME)) { - XFREE(MTYPE_ISIS, isis->name); + XFREE(MTYPE_ISIS_NAME, isis->name); isis->name = NULL; } old_vrf_id = isis->vrf_id; @@ -631,7 +639,7 @@ void isis_finish(struct isis *isis) vrf = vrf_lookup_by_name(isis->name); if (vrf) isis_vrf_unlink(isis, vrf); - XFREE(MTYPE_ISIS, isis->name); + XFREE(MTYPE_ISIS_NAME, isis->name); } else { vrf = vrf_lookup_by_id(VRF_DEFAULT); if (vrf) diff --git a/isisd/isisd.conf.sample b/isisd/isisd.conf.sample deleted file mode 100644 index 47b15953fd..0000000000 --- a/isisd/isisd.conf.sample +++ /dev/null @@ -1,39 +0,0 @@ -! -*- isis -*- -! -! ISISd sample configuration file -! -hostname isisd -password foo -enable password foo -log stdout -!log file /tmp/isisd.log -! -! -router isis DEAD - net 47.0023.0000.0003.0300.0100.0102.0304.0506.00 -! is-type level-1 - -! -- set the lifetime either for level-1, level-2 or both -! lsp-lifetime level-1 65535 -! lsp-lifetime level-2 65535 -! lsp-lifetime 65535 - -! hostname isisd-router -! area-password foobar -! domain-password foobar - -interface eth0 - ip router isis DEAD -! isis hello-interval 5 -! isis lsp-interval 1000 - -! -- optional -! isis circuit-type level-1 -! isis password lallaa level-1 -! isis metric 1 level-1 -! isis csnp-interval 5 level-1 -! isis retransmit-interval 10 -! isis retransmit-throttle-interval -! isis hello-multiplier 2 level-1 -! isis priority 64 -! diff --git a/isisd/isisd.h b/isisd/isisd.h index ea716f998e..a2e821ad2f 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -24,6 +24,7 @@ #define ISISD_H #include "vty.h" +#include "memory.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" @@ -34,10 +35,11 @@ #include "isis_flags.h" #include "isis_lsp.h" #include "isis_lfa.h" -#include "isis_memory.h" #include "qobj.h" #include "ldp_sync.h" +DECLARE_MGROUP(ISISD); + #ifdef FABRICD static const bool fabricd = true; #define PROTO_TYPE ZEBRA_ROUTE_OPENFABRIC @@ -240,6 +242,9 @@ struct isis_area { }; DECLARE_QOBJ_TYPE(isis_area); +DECLARE_MTYPE(ISIS_ACL_NAME); /* isis_area->spf_prefix_prioritites */ +DECLARE_MTYPE(ISIS_AREA_ADDR); /* isis_area->area_addrs */ + DECLARE_HOOK(isis_area_overload_bit_update, (struct isis_area * area), (area)); void isis_terminate(void); diff --git a/isisd/subdir.am b/isisd/subdir.am index 61dc39bdae..11bae41657 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -5,7 +5,6 @@ if ISISD noinst_LIBRARIES += isisd/libisis.a sbin_PROGRAMS += isisd/isisd -dist_examples_DATA += isisd/isisd.conf.sample vtysh_scan += \ isisd/isis_cli.c \ isisd/isis_ldp_sync.c \ @@ -26,7 +25,6 @@ endif if FABRICD noinst_LIBRARIES += isisd/libfabric.a sbin_PROGRAMS += isisd/fabricd -dist_examples_DATA += isisd/fabricd.conf.sample if !ISISD vtysh_scan += \ isisd/isis_cli.c \ @@ -57,7 +55,6 @@ noinst_HEADERS += \ isisd/isis_ldp_sync.h \ isisd/isis_lfa.h \ isisd/isis_lsp.h \ - isisd/isis_memory.h \ isisd/isis_misc.h \ isisd/isis_mt.h \ isisd/isis_nb.h \ @@ -92,7 +89,6 @@ LIBISIS_SOURCES = \ isisd/isis_ldp_sync.c \ isisd/isis_lfa.c \ isisd/isis_lsp.c \ - isisd/isis_memory.c \ isisd/isis_misc.c \ isisd/isis_mt.c \ isisd/isis_pdu.c \ diff --git a/ldpd/ldp_snmp.c b/ldpd/ldp_snmp.c index 3f59d18aa8..9fb4e46515 100644 --- a/ldpd/ldp_snmp.c +++ b/ldpd/ldp_snmp.c @@ -301,8 +301,7 @@ static uint8_t *ldpEntityTable(struct variable *v, oid name[], size_t *length, /* Append index */ *length = LDP_ENTITY_TOTAL_LEN; - oid_copy_addr(name + v->namelen, &entityLdpId, - IN_ADDR_SIZE); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX; @@ -402,8 +401,7 @@ static uint8_t *ldpEntityStatsTable(struct variable *v, oid name[], /* Append index */ *length = LDP_ENTITY_TOTAL_LEN; - oid_copy_addr(name + v->namelen, &entityLdpId, - IN_ADDR_SIZE); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX; @@ -640,13 +638,11 @@ static uint8_t *ldpHelloAdjacencyTable(struct variable *v, oid name[], size_t *l struct in_addr peerLdpId = ctl_adj->id; - oid_copy_addr(name + v->namelen, &entityLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX; - oid_copy_addr(name + v->namelen + 7, &peerLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen + 7, &peerLdpId); name[v->namelen + 11] = 0; name[v->namelen + 12] = 0; name[v->namelen + 13] = adjacencyIndex; @@ -804,14 +800,12 @@ static uint8_t *ldpPeerTable(struct variable *v, oid name[], size_t *length, memcpy(name, v->name, v->namelen * sizeof(oid)); /* Append index */ - oid_copy_addr(name + v->namelen, &entityLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = entityIndex; - oid_copy_addr(name + v->namelen + 7, &peerLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen + 7, &peerLdpId); name[v->namelen + 11] = 0; name[v->namelen + 12] = 0; @@ -875,14 +869,12 @@ static uint8_t *ldpSessionTable(struct variable *v, oid name[], size_t *length, memcpy(name, v->name, v->namelen * sizeof(oid)); /* Append index */ - oid_copy_addr(name + v->namelen, &entityLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = entityIndex; - oid_copy_addr(name + v->namelen + 7, &peerLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen + 7, &peerLdpId); name[v->namelen + 11] = 0; name[v->namelen + 12] = 0; @@ -961,13 +953,11 @@ static uint8_t *ldpSessionStatsTable(struct variable *v, oid name[], memcpy(name, v->name, v->namelen * sizeof(oid)); /* Append index */ - oid_copy_addr(name + v->namelen, &entityLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, &entityLdpId); name[v->namelen + 4] = 0; name[v->namelen + 5] = 0; name[v->namelen + 6] = entityIndex; - oid_copy_addr(name + v->namelen + 7, &peerLdpId, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen + 7, &peerLdpId); name[v->namelen + 11] = 0; name[v->namelen + 12] = 0; @@ -1147,15 +1137,15 @@ ldpTrapSession(struct nbr * nbr, unsigned int sptrap) entityIndex = LDP_DEFAULT_ENTITY_INDEX; peerLdpId = ctl_nbr->id; - oid_copy_addr(index, &entityLdpId, sizeof(struct in_addr)); - index[4] = 0; - index[5] = 0; - index[6] = entityIndex; - oid_copy_addr(&index[7], &peerLdpId, sizeof(struct in_addr)); - index[11] = 0; - index[12] = 0; + oid_copy_in_addr(index, &entityLdpId); + index[4] = 0; + index[5] = 0; + index[6] = entityIndex; + oid_copy_in_addr(&index[7], &peerLdpId); + index[11] = 0; + index[12] = 0; - index[LDP_PEER_ENTRY_MAX_IDX_LEN] = 0; + index[LDP_PEER_ENTRY_MAX_IDX_LEN] = 0; smux_trap(ldpe_variables, array_size(ldpe_variables), ldp_trap_oid, array_size(ldp_trap_oid), ldp_oid, diff --git a/ldpd/ldpd.conf.sample b/ldpd/ldpd.conf.sample deleted file mode 100644 index 49da35c284..0000000000 --- a/ldpd/ldpd.conf.sample +++ /dev/null @@ -1,46 +0,0 @@ -! -*- ldp -*- -! -! LDPd sample configuration file -! -hostname ldpd -password zebra -log stdout -! -interface eth0 -! -interface eth1 -! -interface lo -! -mpls ldp - dual-stack cisco-interop - neighbor 10.0.1.5 password opensourcerouting - neighbor 172.16.0.1 password opensourcerouting - ! - address-family ipv4 - discovery transport-address 10.0.1.1 - label local advertise explicit-null - ! - interface eth0 - ! - interface eth1 - ! - ! - address-family ipv6 - discovery transport-address 2001:db8::1 - ! - interface eth1 - ! - ! -! -l2vpn ENG type vpls - bridge br0 - member interface eth2 - ! - member pseudowire mpw0 - neighbor lsr-id 1.1.1.1 - pw-id 100 - ! -! -line vty -! diff --git a/ldpd/subdir.am b/ldpd/subdir.am index aa9b751bcc..5fc3847c6d 100644 --- a/ldpd/subdir.am +++ b/ldpd/subdir.am @@ -5,7 +5,6 @@ if LDPD noinst_LIBRARIES += ldpd/libldp.a sbin_PROGRAMS += ldpd/ldpd -dist_examples_DATA += ldpd/ldpd.conf.sample vtysh_scan += ldpd/ldp_vty_cmds.c vtysh_daemons += ldpd man8 += $(MANBUILD)/frr-ldpd.8 @@ -27,6 +27,7 @@ #include "prefix.h" #include "thread.h" #include "stream.h" +#include "vrf.h" #include "zclient.h" #include "table.h" #include "vty.h" @@ -568,3 +569,643 @@ int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args) return 0; } + +/** + * BFD protocol integration configuration. + */ + +/** Events definitions. */ +enum bfd_session_event { + /** Remove the BFD session configuration. */ + BSE_UNINSTALL, + /** Install the BFD session configuration. */ + BSE_INSTALL, +}; + +/** + * Data structure to do the necessary tricks to hide the BFD protocol + * integration internals. + */ +struct bfd_session_params { + /** Contains the session parameters and more. */ + struct bfd_session_arg args; + /** Contains the session state. */ + struct bfd_session_status bss; + /** Protocol implementation status update callback. */ + bsp_status_update updatecb; + /** Protocol implementation custom data pointer. */ + void *arg; + + /** + * Next event. + * + * This variable controls what action to execute when the command batch + * finishes. Normally we'd use `thread_add_event` value, however since + * that function is going to be called multiple times and the value + * might be different we'll use this variable to keep track of it. + */ + enum bfd_session_event lastev; + /** + * BFD session configuration event. + * + * Multiple actions might be asked during a command batch (either via + * configuration load or northbound batch), so we'll use this to + * install/uninstall the BFD session parameters only once. + */ + struct thread *installev; + + /** BFD session installation state. */ + bool installed; + /** BFD session enabled. */ + bool enabled; + + /** Global BFD paramaters list. */ + TAILQ_ENTRY(bfd_session_params) entry; +}; + +struct bfd_sessions_global { + /** + * Global BFD session parameters list for (re)installation and update + * without code duplication among daemons. + */ + TAILQ_HEAD(bsplist, bfd_session_params) bsplist; + + /** Pointer to FRR's event manager. */ + struct thread_master *tm; + /** Pointer to zebra client data structure. */ + struct zclient *zc; + + /** Debugging state. */ + bool debugging; + /** Is shutting down? */ + bool shutting_down; +}; + +/** Global configuration variable. */ +static struct bfd_sessions_global bsglobal; + +/** Global empty address for IPv4/IPv6. */ +static const struct in6_addr i6a_zero; + +struct bfd_session_params *bfd_sess_new(bsp_status_update updatecb, void *arg) +{ + struct bfd_session_params *bsp; + + bsp = XCALLOC(MTYPE_BFD_INFO, sizeof(*bsp)); + + /* Save application data. */ + bsp->updatecb = updatecb; + bsp->arg = arg; + + /* Set defaults. */ + bsp->args.detection_multiplier = BFD_DEF_DETECT_MULT; + bsp->args.ttl = BFD_SINGLE_HOP_TTL; + bsp->args.min_rx = BFD_DEF_MIN_RX; + bsp->args.min_tx = BFD_DEF_MIN_TX; + bsp->args.vrf_id = VRF_DEFAULT; + + /* Register in global list. */ + TAILQ_INSERT_TAIL(&bsglobal.bsplist, bsp, entry); + + return bsp; +} + +static bool _bfd_sess_valid(const struct bfd_session_params *bsp) +{ + /* Peer/local address not configured. */ + if (bsp->args.family == 0) + return false; + + /* Address configured but invalid. */ + if (bsp->args.family != AF_INET && bsp->args.family != AF_INET6) { + if (bsglobal.debugging) + zlog_debug("%s: invalid session family: %d", __func__, + bsp->args.family); + return false; + } + + /* Invalid address. */ + if (memcmp(&bsp->args.dst, &i6a_zero, sizeof(i6a_zero)) == 0) { + if (bsglobal.debugging) { + if (bsp->args.family == AF_INET) + zlog_debug("%s: invalid address: %pI4", + __func__, + (struct in_addr *)&bsp->args.dst); + else + zlog_debug("%s: invalid address: %pI6", + __func__, &bsp->args.dst); + } + return false; + } + + /* Multi hop requires local address. */ + if (bsp->args.mhop + && memcmp(&i6a_zero, &bsp->args.src, sizeof(i6a_zero)) == 0) { + if (bsglobal.debugging) + zlog_debug( + "%s: multi hop but no local address provided", + __func__); + return false; + } + + /* Check VRF ID. */ + if (bsp->args.vrf_id == VRF_UNKNOWN) { + if (bsglobal.debugging) + zlog_debug("%s: asked for unknown VRF", __func__); + return false; + } + + return true; +} + +static int _bfd_sess_send(struct thread *t) +{ + struct bfd_session_params *bsp = THREAD_ARG(t); + int rv; + + /* Validate configuration before trying to send bogus data. */ + if (!_bfd_sess_valid(bsp)) + return 0; + + if (bsp->lastev == BSE_INSTALL) { + bsp->args.command = bsp->installed ? ZEBRA_BFD_DEST_UPDATE + : ZEBRA_BFD_DEST_REGISTER; + } else + bsp->args.command = ZEBRA_BFD_DEST_DEREGISTER; + + /* If not installed and asked for uninstall, do nothing. */ + if (!bsp->installed && bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER) + return 0; + + rv = zclient_bfd_command(bsglobal.zc, &bsp->args); + /* Command was sent successfully. */ + if (rv == 0) { + /* Update installation status. */ + if (bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER) + bsp->installed = false; + else if (bsp->args.command == ZEBRA_BFD_DEST_REGISTER) + bsp->installed = true; + } + + return 0; +} + +static void _bfd_sess_remove(struct bfd_session_params *bsp) +{ + /* Not installed, nothing to do. */ + if (!bsp->installed) + return; + + /* Cancel any pending installation request. */ + THREAD_OFF(bsp->installev); + + /* Send request to remove any session. */ + bsp->lastev = BSE_UNINSTALL; + thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0); +} + +void bfd_sess_free(struct bfd_session_params **bsp) +{ + if (*bsp == NULL) + return; + + /* Remove any installed session. */ + _bfd_sess_remove(*bsp); + + /* Remove from global list. */ + TAILQ_REMOVE(&bsglobal.bsplist, (*bsp), entry); + + /* Free the memory and point to NULL. */ + XFREE(MTYPE_BFD_INFO, (*bsp)); +} + +void bfd_sess_enable(struct bfd_session_params *bsp, bool enable) +{ + /* Remove the session when disabling. */ + if (!enable) + _bfd_sess_remove(bsp); + + bsp->enabled = enable; +} + +void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp, + struct in_addr *src, struct in_addr *dst) +{ + /* If already installed, remove the old setting. */ + _bfd_sess_remove(bsp); + + bsp->args.family = AF_INET; + + /* Clean memory, set zero value and avoid static analyser warnings. */ + memset(&bsp->args.src, 0, sizeof(bsp->args.src)); + memset(&bsp->args.dst, 0, sizeof(bsp->args.dst)); + + /* Copy the equivalent of IPv4 to arguments structure. */ + if (src) + memcpy(&bsp->args.src, src, sizeof(struct in_addr)); + + assert(dst); + memcpy(&bsp->args.dst, dst, sizeof(struct in_addr)); +} + +void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp, + struct in6_addr *src, struct in6_addr *dst) +{ + /* If already installed, remove the old setting. */ + _bfd_sess_remove(bsp); + + bsp->args.family = AF_INET6; + + /* Clean memory, set zero value and avoid static analyser warnings. */ + memset(&bsp->args.src, 0, sizeof(bsp->args.src)); + + if (src) + bsp->args.src = *src; + + assert(dst); + bsp->args.dst = *dst; +} + +void bfd_sess_set_interface(struct bfd_session_params *bsp, const char *ifname) +{ + /* If already installed, remove the old setting. */ + _bfd_sess_remove(bsp); + + if (ifname == NULL) { + bsp->args.ifname[0] = 0; + bsp->args.ifnamelen = 0; + return; + } + + if (strlcpy(bsp->args.ifname, ifname, sizeof(bsp->args.ifname)) + > sizeof(bsp->args.ifname)) + zlog_warn("%s: interface name truncated: %s", __func__, ifname); + + bsp->args.ifnamelen = strlen(bsp->args.ifname); +} + +void bfd_sess_set_profile(struct bfd_session_params *bsp, const char *profile) +{ + if (profile == NULL) { + bsp->args.profile[0] = 0; + bsp->args.profilelen = 0; + return; + } + + if (strlcpy(bsp->args.profile, profile, sizeof(bsp->args.profile)) + > sizeof(bsp->args.profile)) + zlog_warn("%s: profile name truncated: %s", __func__, profile); + + bsp->args.profilelen = strlen(bsp->args.profile); +} + +void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id) +{ + /* If already installed, remove the old setting. */ + _bfd_sess_remove(bsp); + + bsp->args.vrf_id = vrf_id; +} + +void bfd_sess_set_mininum_ttl(struct bfd_session_params *bsp, uint8_t min_ttl) +{ + assert(min_ttl != 0); + + /* If already installed, remove the old setting. */ + _bfd_sess_remove(bsp); + + /* Invert TTL value: protocol expects number of hops. */ + min_ttl = (BFD_SINGLE_HOP_TTL + 1) - min_ttl; + bsp->args.ttl = min_ttl; + bsp->args.mhop = (min_ttl > 1); +} + +void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t min_ttl) +{ + /* If already installed, remove the old setting. */ + _bfd_sess_remove(bsp); + + bsp->args.ttl = min_ttl; + bsp->args.mhop = (min_ttl > 1); +} + + +void bfd_sess_set_cbit(struct bfd_session_params *bsp, bool enable) +{ + bsp->args.cbit = enable; +} + +void bfd_sess_set_timers(struct bfd_session_params *bsp, + uint8_t detection_multiplier, uint32_t min_rx, + uint32_t min_tx) +{ + bsp->args.detection_multiplier = detection_multiplier; + bsp->args.min_rx = min_rx; + bsp->args.min_tx = min_tx; +} + +void bfd_sess_install(struct bfd_session_params *bsp) +{ + /* Don't attempt to install/update when disabled. */ + if (!bsp->enabled) + return; + + bsp->lastev = BSE_INSTALL; + thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev); +} + +void bfd_sess_uninstall(struct bfd_session_params *bsp) +{ + bsp->lastev = BSE_UNINSTALL; + thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev); +} + +enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp) +{ + return bsp->bss.state; +} + +uint8_t bfd_sess_minimum_ttl(const struct bfd_session_params *bsp) +{ + return ((BFD_SINGLE_HOP_TTL + 1) - bsp->args.ttl); +} + +uint8_t bfd_sess_hop_count(const struct bfd_session_params *bsp) +{ + return bsp->args.ttl; +} + +const char *bfd_sess_profile(const struct bfd_session_params *bsp) +{ + return bsp->args.profilelen ? bsp->args.profile : NULL; +} + +void bfd_sess_addresses(const struct bfd_session_params *bsp, int *family, + struct in6_addr *src, struct in6_addr *dst) +{ + *family = bsp->args.family; + if (src) + *src = bsp->args.src; + if (dst) + *dst = bsp->args.dst; +} + +const char *bfd_sess_interface(const struct bfd_session_params *bsp) +{ + if (bsp->args.ifnamelen) + return bsp->args.ifname; + + return NULL; +} + +const char *bfd_sess_vrf(const struct bfd_session_params *bsp) +{ + return vrf_id_to_name(bsp->args.vrf_id); +} + +vrf_id_t bfd_sess_vrf_id(const struct bfd_session_params *bsp) +{ + return bsp->args.vrf_id; +} + +bool bfd_sess_cbit(const struct bfd_session_params *bsp) +{ + return bsp->args.cbit; +} + +void bfd_sess_timers(const struct bfd_session_params *bsp, + uint8_t *detection_multiplier, uint32_t *min_rx, + uint32_t *min_tx) +{ + *detection_multiplier = bsp->args.detection_multiplier; + *min_rx = bsp->args.min_rx; + *min_tx = bsp->args.min_tx; +} + +void bfd_sess_show(struct vty *vty, struct json_object *json, + struct bfd_session_params *bsp) +{ + json_object *json_bfd = NULL; + char time_buf[64]; + + if (!bsp) + return; + + /* Show type. */ + if (json) { + json_bfd = json_object_new_object(); + if (bsp->args.mhop) + json_object_string_add(json_bfd, "type", "multi hop"); + else + json_object_string_add(json_bfd, "type", "single hop"); + } else + vty_out(vty, " BFD: Type: %s\n", + bsp->args.mhop ? "multi hop" : "single hop"); + + /* Show configuration. */ + if (json) { + json_object_int_add(json_bfd, "detectMultiplier", + bsp->args.detection_multiplier); + json_object_int_add(json_bfd, "rxMinInterval", + bsp->args.min_rx); + json_object_int_add(json_bfd, "txMinInterval", + bsp->args.min_tx); + } else { + vty_out(vty, + " Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n", + bsp->args.detection_multiplier, bsp->args.min_rx, + bsp->args.min_tx); + } + + bfd_last_update(bsp->bss.last_event, time_buf, sizeof(time_buf)); + if (json) { + json_object_string_add(json_bfd, "status", + bfd_get_status_str(bsp->bss.state)); + json_object_string_add(json_bfd, "lastUpdate", time_buf); + } else + vty_out(vty, " Status: %s, Last update: %s\n", + bfd_get_status_str(bsp->bss.state), time_buf); + + if (json) + json_object_object_add(json, "peerBfdInfo", json_bfd); + else + vty_out(vty, "\n"); +} + +/* + * Zebra communication related. + */ + +/** + * Callback for reinstallation of all registered BFD sessions. + * + * Use this as `zclient` `bfd_dest_replay` callback. + */ +static int zclient_bfd_session_reply(ZAPI_CALLBACK_ARGS) +{ + struct bfd_session_params *bsp; + + /* Do nothing when shutting down. */ + if (bsglobal.shutting_down) + return 0; + + if (bsglobal.debugging) + zlog_debug("%s: sending all sessions registered", __func__); + + /* Send the client registration */ + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); + + /* Replay all activated peers. */ + TAILQ_FOREACH (bsp, &bsglobal.bsplist, entry) { + /* Skip disabled sessions. */ + if (!bsp->enabled) + continue; + + /* We are reconnecting, so we must send installation. */ + bsp->installed = false; + + /* Cancel any pending installation request. */ + THREAD_OFF(bsp->installev); + + /* Ask for installation. */ + bsp->lastev = BSE_INSTALL; + thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0); + } + + return 0; +} + +static int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS) +{ + struct bfd_session_params *bsp; + size_t sessions_updated = 0; + struct interface *ifp; + int remote_cbit = false; + int state = BFD_STATUS_UNKNOWN; + time_t now; + size_t addrlen; + struct prefix dp; + struct prefix sp; + char ifstr[128], cbitstr[32]; + + /* Do nothing when shutting down. */ + if (bsglobal.shutting_down) + return 0; + + ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit, + vrf_id); + + if (bsglobal.debugging) { + ifstr[0] = 0; + if (ifp) + snprintf(ifstr, sizeof(ifstr), " (interface %s)", + ifp->name); + + snprintf(cbitstr, sizeof(cbitstr), " (CPI bit %s)", + remote_cbit ? "yes" : "no"); + + zlog_debug("%s: %pFX -> %pFX%s VRF %s(%u)%s: %s", __func__, &sp, + &dp, ifstr, vrf_id_to_name(vrf_id), vrf_id, cbitstr, + bfd_get_status_str(state)); + } + + switch (dp.family) { + case AF_INET: + addrlen = sizeof(struct in_addr); + break; + case AF_INET6: + addrlen = sizeof(struct in6_addr); + break; + + default: + /* Unexpected value. */ + assert(0); + break; + } + + /* Cache current time to avoid multiple monotime clock calls. */ + now = monotime(NULL); + + /* Notify all matching sessions about update. */ + TAILQ_FOREACH (bsp, &bsglobal.bsplist, entry) { + /* Skip disabled or not installed entries. */ + if (!bsp->enabled || !bsp->installed) + continue; + /* Skip different VRFs. */ + if (bsp->args.vrf_id != vrf_id) + continue; + /* Skip different families. */ + if (bsp->args.family != dp.family) + continue; + /* Skip different interface. */ + if (bsp->args.ifnamelen && ifp + && strcmp(bsp->args.ifname, ifp->name) != 0) + continue; + /* Skip non matching destination addresses. */ + if (memcmp(&bsp->args.dst, &dp.u, addrlen) != 0) + continue; + /* + * Source comparison test: + * We will only compare source if BFD daemon provided the + * source address and the protocol set a source address in + * the configuration otherwise we'll just skip it. + */ + if (sp.family && memcmp(&bsp->args.src, &i6a_zero, addrlen) != 0 + && memcmp(&sp.u, &i6a_zero, addrlen) != 0 + && memcmp(&bsp->args.src, &sp.u, addrlen) != 0) + continue; + /* No session state change. */ + if ((int)bsp->bss.state == state) + continue; + + bsp->bss.last_event = now; + bsp->bss.previous_state = bsp->bss.state; + bsp->bss.state = state; + bsp->bss.remote_cbit = remote_cbit; + bsp->updatecb(bsp, &bsp->bss, bsp->arg); + sessions_updated++; + } + + if (bsglobal.debugging) + zlog_debug("%s: sessions updated: %zu", __func__, + sessions_updated); + + return 0; +} + +void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm) +{ + /* Initialize data structure. */ + TAILQ_INIT(&bsglobal.bsplist); + + /* Copy pointers. */ + bsglobal.zc = zc; + bsglobal.tm = tm; + + /* Install our callbacks. */ + zc->interface_bfd_dest_update = zclient_bfd_session_update; + zc->bfd_dest_replay = zclient_bfd_session_reply; + + /* Send the client registration */ + bfd_client_sendmsg(zc, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); +} + +void bfd_protocol_integration_set_debug(bool enable) +{ + bsglobal.debugging = enable; +} + +void bfd_protocol_integration_set_shutdown(bool enable) +{ + bsglobal.shutting_down = enable; +} + +bool bfd_protocol_integration_debug(void) +{ + return bsglobal.debugging; +} + +bool bfd_protocol_integration_shutting_down(void) +{ + return bsglobal.shutting_down; +} @@ -128,6 +128,305 @@ extern void bfd_gbl_exit(void); * BFD new API. */ +/* Forward declaration of argument struct. */ +struct bfd_session_params; + +/** Session state definitions. */ +enum bfd_session_state { + /** Session state is unknown or not initialized. */ + BSS_UNKNOWN = BFD_STATUS_UNKNOWN, + /** Local or remote peer administratively shutdown the session. */ + BSS_ADMIN_DOWN = BFD_STATUS_ADMIN_DOWN, + /** Session is down. */ + BSS_DOWN = BFD_STATUS_DOWN, + /** Session is up and working correctly. */ + BSS_UP = BFD_STATUS_UP, +}; + +/** BFD session status information */ +struct bfd_session_status { + /** Current session state. */ + enum bfd_session_state state; + /** Previous session state. */ + enum bfd_session_state previous_state; + /** Remote Control Plane Independent bit state. */ + bool remote_cbit; + /** Last event occurrence. */ + time_t last_event; +}; + +/** + * Session status update callback. + * + * \param bsp BFD session parameters. + * \param bss BFD session status. + * \param arg application independent data. + */ +typedef void (*bsp_status_update)(struct bfd_session_params *bsp, + const struct bfd_session_status *bss, + void *arg); + +/** + * Allocates and initializes the session parameters. + * + * \param updatedb status update notification callback. + * \param args application independent data. + * + * \returns pointer to configuration storage. + */ +struct bfd_session_params *bfd_sess_new(bsp_status_update updatecb, void *args); + +/** + * Uninstall session if installed and free resources allocated by the + * parameters. Already sets pointer to `NULL` to avoid dangling references. + * + * \param bsp session parameters. + */ +void bfd_sess_free(struct bfd_session_params **bsp); + +/** + * Enable/disable session installation. + * + * \param bsp session parameters. + * \param enable knob variable. + */ +void bfd_sess_enable(struct bfd_session_params *bsp, bool enable); + +/** + * Set the local and peer address of the BFD session. + * + * \param bsp BFD session parameters. + * \param src local address (optional, can be `NULL`). + * \param dst remote address (mandatory). + */ +void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp, + struct in_addr *src, struct in_addr *dst); + +/** + * Set the local and peer address of the BFD session. + * + * \param bsp BFD session parameters. + * \param src local address (optional, can be `NULL`). + * \param dst remote address (mandatory). + */ +void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp, + struct in6_addr *src, struct in6_addr *dst); + +/** + * Configure the BFD session interface. + * + * \param bsp BFD session parameters. + * \param ifname interface name (or `NULL` to remove it). + */ +void bfd_sess_set_interface(struct bfd_session_params *bsp, const char *ifname); + +/** + * Configure the BFD session profile name. + * + * \param bsp BFD session parameters. + * \param profile profile name (or `NULL` to remove it). + */ +void bfd_sess_set_profile(struct bfd_session_params *bsp, const char *profile); + +/** + * Configure the BFD session VRF. + * + * \param bsp BFD session parameters. + * \param vrf_id the VRF identification number. + */ +void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id); + +/** + * Configure the BFD session single/multi hop setting. + * + * \param bsp BFD session parameters. + * \param min_ttl minimum TTL value expected (255 for single hop, 254 for + * multi hop with single hop, 253 for multi hop with two hops + * and so on). See `BFD_SINGLE_HOP_TTL` and + * `BFD_MULTI_HOP_MIN_TTL` for defaults. + * + * To simplify things if your protocol only knows the amount of hops it is + * better to use `bfd_sess_set_hops` instead. + */ +void bfd_sess_set_mininum_ttl(struct bfd_session_params *bsp, uint8_t min_ttl); + +/** To use single hop the minimum TTL must be set to this. */ +#define BFD_SINGLE_HOP_TTL 255 +/** To use multi hop the minimum TTL must be set to this or less. */ +#define BFD_MULTI_HOP_MIN_TTL 254 + +/** + * This function is the inverted version of `bfd_sess_set_minimum_ttl`. + * Instead of receiving the minimum expected TTL, it receives the amount of + * hops the protocol will jump. + * + * \param bsp BFD session parameters. + * \param min_ttl minimum amount of hops expected (1 for single hop, 2 or + * more for multi hop). + */ +void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t min_ttl); + +/** + * Configure the BFD session to set the Control Plane Independent bit. + * + * \param bsp BFD session parameters. + * \param enable BFD Control Plane Independent state. + */ +void bfd_sess_set_cbit(struct bfd_session_params *bsp, bool enable); + +/** + * DEPRECATED: please avoid using timers directly and use profiles instead. + * + * Configures the BFD session timers to use. This is specially useful with + * `ptm-bfd` which does not support timers. + * + * \param bsp BFD session parameters. + * \param detection_multiplier the detection multiplier value. + * \param min_rx minimum required receive period. + * \param min_tx minimum required transmission period. + */ +void bfd_sess_set_timers(struct bfd_session_params *bsp, + uint8_t detection_multiplier, uint32_t min_rx, + uint32_t min_tx); + +/** + * Installs or updates the BFD session based on the saved session arguments. + * + * \param bsp session parameters. + */ +void bfd_sess_install(struct bfd_session_params *bsp); + +/** + * Uninstall the BFD session based on the saved session arguments. + * + * \param bsp session parameters. + */ +void bfd_sess_uninstall(struct bfd_session_params *bsp); + +/** + * Get BFD session current status. + * + * \param bsp session parameters. + * + * \returns BFD session status data structure. + */ +enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp); + +/** + * Get BFD session minimum TTL configured value. + * + * \param bsp session parameters. + * + * \returns configured minimum TTL. + */ +uint8_t bfd_sess_minimum_ttl(const struct bfd_session_params *bsp); + +/** + * Inverted version of `bfd_sess_minimum_ttl`. Gets the amount of hops in the + * way to the peer. + * + * \param bsp session parameters. + * + * \returns configured amount of hops. + */ +uint8_t bfd_sess_hop_count(const struct bfd_session_params *bsp); + +/** + * Get BFD session profile configured value. + * + * \param bsp session parameters. + * + * \returns configured profile name (or `NULL` if empty). + */ +const char *bfd_sess_profile(const struct bfd_session_params *bsp); + +/** + * Get BFD session addresses. + * + * \param bsp session parameters. + * \param family the address family being used (AF_INET or AF_INET6). + * \param src source address (optional, may be `NULL`). + * \param dst peer address (optional, may be `NULL`). + */ +void bfd_sess_addresses(const struct bfd_session_params *bsp, int *family, + struct in6_addr *src, struct in6_addr *dst); +/** + * Get BFD session interface name. + * + * \param bsp session parameters. + * + * \returns `NULL` if not set otherwise the interface name. + */ +const char *bfd_sess_interface(const struct bfd_session_params *bsp); + +/** + * Get BFD session VRF name. + * + * \param bsp session parameters. + * + * \returns the VRF name. + */ +const char *bfd_sess_vrf(const struct bfd_session_params *bsp); + +/** + * Get BFD session VRF ID. + * + * \param bsp session parameters. + * + * \returns the VRF name. + */ +vrf_id_t bfd_sess_vrf_id(const struct bfd_session_params *bsp); + +/** + * Get BFD session control plane independent bit configuration state. + * + * \param bsp session parameters. + * + * \returns `true` if enabled otherwise `false`. + */ +bool bfd_sess_cbit(const struct bfd_session_params *bsp); + +/** + * DEPRECATED: please avoid using timers directly and use profiles instead. + * + * Gets the configured timers. + * + * \param bsp BFD session parameters. + * \param detection_multiplier the detection multiplier value. + * \param min_rx minimum required receive period. + * \param min_tx minimum required transmission period. + */ +void bfd_sess_timers(const struct bfd_session_params *bsp, + uint8_t *detection_multiplier, uint32_t *min_rx, + uint32_t *min_tx); + +/** + * Show BFD session configuration and status. If `json` is provided (e.g. not + * `NULL`) then information will be inserted in object, otherwise printed to + * `vty`. + * + * \param vty Pointer to `vty` for outputting text. + * \param json (optional) JSON object pointer. + * \param bsp session parameters. + */ +void bfd_sess_show(struct vty *vty, struct json_object *json, + struct bfd_session_params *bsp); + +/** + * Initializes the BFD integration library. This function executes the + * following actions: + * + * - Copy the `struct thread_master` pointer to use as "thread" to execute + * the BFD session parameters installation. + * - Copy the `struct zclient` pointer to install its callbacks. + * - Initializes internal data structures. + * + * \param tm normally the daemon main thread event manager. + * \param zc the zebra client of the daemon. + */ +void bfd_protocol_integration_init(struct zclient *zc, + struct thread_master *tm); + /** * BFD session registration arguments. */ @@ -205,6 +504,34 @@ struct bfd_session_arg { */ extern int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *arg); +/** + * Enables or disables BFD protocol integration API debugging. + * + * \param enable new API debug state. + */ +extern void bfd_protocol_integration_set_debug(bool enable); + +/** + * Sets shutdown mode so no more events are processed. + * + * This is useful to avoid the event storm that happens caused by network, + * interfaces or VRFs removal. It should also avoid some crashes due hanging + * pointers left overs by protocol. + * + * \param enable new API shutdown state. + */ +extern void bfd_protocol_integration_set_shutdown(bool enable); + +/** + * Get API debugging state. + */ +extern bool bfd_protocol_integration_debug(void); + +/** + * Get API shutdown state. + */ +extern bool bfd_protocol_integration_shutting_down(void); + #ifdef __cplusplus } #endif diff --git a/lib/command.c b/lib/command.c index 770e2fc5ac..d2798b5002 100644 --- a/lib/command.c +++ b/lib/command.c @@ -2226,18 +2226,19 @@ DEFUN (no_banner_motd, DEFUN(find, find_cmd, - "find REGEX", + "find REGEX...", "Find CLI command matching a regular expression\n" "Search pattern (POSIX regex)\n") { - char *pattern = argv[1]->arg; const struct cmd_node *node; const struct cmd_element *cli; vector clis; regex_t exp = {}; + char *pattern = argv_concat(argv, argc, 1); int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED); + XFREE(MTYPE_TMP, pattern); if (cr != 0) { switch (cr) { diff --git a/lib/elf_py.c b/lib/elf_py.c index d26e443b82..b47aa3d795 100644 --- a/lib/elf_py.c +++ b/lib/elf_py.c @@ -1032,7 +1032,7 @@ static char *elfdata_strptr(Elf_Data *data, size_t offset) static void elffile_add_dynreloc(struct elffile *w, Elf_Data *reldata, size_t entries, Elf_Data *symdata, - Elf_Data *strdata) + Elf_Data *strdata, Elf_Type typ) { size_t i; @@ -1041,12 +1041,59 @@ static void elffile_add_dynreloc(struct elffile *w, Elf_Data *reldata, size_t symidx; GElf_Rela *rela; GElf_Sym *sym; + GElf_Addr rel_offs = 0; relw = (struct elfreloc *)typeobj_elfreloc.tp_alloc( &typeobj_elfreloc, 0); relw->ef = w; - rela = relw->rela = gelf_getrela(reldata, i, &relw->_rela); + if (typ == ELF_T_REL) { + GElf_Rel _rel, *rel; + GElf_Addr offs; + + rel = gelf_getrel(reldata, i, &_rel); + relw->rela = &relw->_rela; + relw->rela->r_offset = rel->r_offset; + relw->rela->r_info = rel->r_info; + relw->rela->r_addend = 0; + relw->relative = true; + + /* REL uses the pointer contents itself instead of the + * RELA addend field :( ... theoretically this could + * be some weird platform specific encoding, but since + * we only care about data relocations it should + * always be a pointer... + */ + if (elffile_virt2file(w, rel->r_offset, &offs)) { + Elf_Data *ptr, *conv; + GElf_Addr tmp; + Elf_Data mem = { + .d_buf = (void *)&tmp, + .d_type = ELF_T_ADDR, + .d_version = EV_CURRENT, + .d_size = sizeof(tmp), + .d_off = 0, + .d_align = 0, + }; + + ptr = elf_getdata_rawchunk(w->elf, offs, + w->elfclass / 8, + ELF_T_ADDR); + + conv = gelf_xlatetom(w->elf, &mem, ptr, + w->mmap[EI_DATA]); + if (conv) { + memcpy(&rel_offs, conv->d_buf, + conv->d_size); + + relw->relative = false; + relw->rela->r_addend = rel_offs; + } + } + } else + relw->rela = gelf_getrela(reldata, i, &relw->_rela); + + rela = relw->rela; symidx = relw->symidx = GELF_R_SYM(rela->r_info); sym = relw->sym = gelf_getsym(symdata, symidx, &relw->_sym); if (sym) { @@ -1062,9 +1109,16 @@ static void elffile_add_dynreloc(struct elffile *w, Elf_Data *reldata, relw->st_value = 0; } - debugf("dynreloc @ %016llx sym %5llu %016llx %s\n", - (long long)rela->r_offset, (unsigned long long)symidx, - (long long)rela->r_addend, relw->symname); + if (typ == ELF_T_RELA) + debugf("dynrela @ %016llx sym %5llu %016llx %s\n", + (long long)rela->r_offset, + (unsigned long long)symidx, + (long long)rela->r_addend, relw->symname); + else + debugf("dynrel @ %016llx sym %5llu (%016llx) %s\n", + (long long)rela->r_offset, + (unsigned long long)symidx, + (unsigned long long)rel_offs, relw->symname); elfrelocs_add(&w->dynrelocs, relw); } @@ -1166,8 +1220,10 @@ static PyObject *elffile_load(PyTypeObject *type, PyObject *args, Elf_Data *dyndata = elf_getdata_rawchunk(w->elf, phdr->p_offset, phdr->p_filesz, ELF_T_DYN); - GElf_Addr dynrela = 0, symtab = 0, strtab = 0; - size_t dynrelasz = 0, dynrelaent = 0, strsz = 0; + GElf_Addr dynrela = 0, dynrel = 0, symtab = 0, strtab = 0; + size_t dynrelasz = 0, dynrelaent = 0; + size_t dynrelsz = 0, dynrelent = 0; + size_t strsz = 0; GElf_Dyn _dyn, *dyn; for (size_t j = 0;; j++) { @@ -1198,16 +1254,20 @@ static PyObject *elffile_load(PyTypeObject *type, PyObject *args, dynrelaent = dyn->d_un.d_val; break; + case DT_REL: + dynrel = dyn->d_un.d_ptr; + break; case DT_RELSZ: - if (dyn->d_un.d_val) - fprintf(stderr, - "WARNING: ignoring non-empty DT_REL!\n"); + dynrelsz = dyn->d_un.d_val; + break; + case DT_RELENT: + dynrelent = dyn->d_un.d_val; break; } } GElf_Addr offset; - Elf_Data *symdata = NULL, *strdata = NULL, *reladata = NULL; + Elf_Data *symdata = NULL, *strdata = NULL; if (elffile_virt2file(w, symtab, &offset)) symdata = elf_getdata_rawchunk(w->elf, offset, @@ -1217,19 +1277,37 @@ static PyObject *elffile_load(PyTypeObject *type, PyObject *args, strdata = elf_getdata_rawchunk(w->elf, offset, strsz, ELF_T_BYTE); - if (!dynrela || !dynrelasz || !dynrelaent) - continue; + size_t c; - if (!elffile_virt2file(w, dynrela, &offset)) - continue; + if (dynrela && dynrelasz && dynrelaent + && elffile_virt2file(w, dynrela, &offset)) { + Elf_Data *reladata = NULL; - debugf("dynrela @%llx/%llx+%llx\n", (long long)dynrela, - (long long)offset, (long long)dynrelasz); + debugf("dynrela @%llx/%llx+%llx\n", (long long)dynrela, + (long long)offset, (long long)dynrelasz); - reladata = elf_getdata_rawchunk(w->elf, offset, dynrelasz, - ELF_T_RELA); - elffile_add_dynreloc(w, reladata, dynrelasz / dynrelaent, - symdata, strdata); + reladata = elf_getdata_rawchunk(w->elf, offset, + dynrelasz, ELF_T_RELA); + + c = dynrelasz / dynrelaent; + elffile_add_dynreloc(w, reladata, c, symdata, strdata, + ELF_T_RELA); + } + + if (dynrel && dynrelsz && dynrelent + && elffile_virt2file(w, dynrel, &offset)) { + Elf_Data *reldata = NULL; + + debugf("dynrel @%llx/%llx+%llx\n", (long long)dynrel, + (long long)offset, (long long)dynrelsz); + + reldata = elf_getdata_rawchunk(w->elf, offset, dynrelsz, + ELF_T_REL); + + c = dynrelsz / dynrelent; + elffile_add_dynreloc(w, reldata, c, symdata, strdata, + ELF_T_REL); + } } #endif diff --git a/lib/filter.c b/lib/filter.c index 83423ba321..ab62e95fb6 100644 --- a/lib/filter.c +++ b/lib/filter.c @@ -469,59 +469,6 @@ void access_list_filter_add(struct access_list *access, host A single host address */ -struct filter *filter_lookup_cisco(struct access_list *access, - struct filter *mnew) -{ - struct filter *mfilter; - struct filter_cisco *filter; - struct filter_cisco *new; - - new = &mnew->u.cfilter; - - for (mfilter = access->head; mfilter; mfilter = mfilter->next) { - filter = &mfilter->u.cfilter; - - if (filter->extended) { - if (mfilter->type == mnew->type - && filter->addr.s_addr == new->addr.s_addr - && filter->addr_mask.s_addr == new->addr_mask.s_addr - && filter->mask.s_addr == new->mask.s_addr - && filter->mask_mask.s_addr - == new->mask_mask.s_addr) - return mfilter; - } else { - if (mfilter->type == mnew->type - && filter->addr.s_addr == new->addr.s_addr - && filter->addr_mask.s_addr - == new->addr_mask.s_addr) - return mfilter; - } - } - - return NULL; -} - -struct filter *filter_lookup_zebra(struct access_list *access, - struct filter *mnew) -{ - struct filter *mfilter; - struct filter_zebra *filter; - struct filter_zebra *new; - - new = &mnew->u.zfilter; - - for (mfilter = access->head; mfilter; mfilter = mfilter->next) { - filter = &mfilter->u.zfilter; - - if (filter->exact == new->exact - && mfilter->type == mnew->type) { - if (prefix_same(&filter->prefix, &new->prefix)) - return mfilter; - } - } - return NULL; -} - static void config_write_access_zebra(struct vty *, struct filter *); static void config_write_access_cisco(struct vty *, struct filter *); diff --git a/lib/filter.h b/lib/filter.h index 091a5197f6..ade68a4567 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -151,10 +151,6 @@ void access_list_filter_add(struct access_list *access, void access_list_filter_delete(struct access_list *access, struct filter *filter); int64_t filter_new_seq_get(struct access_list *access); -struct filter *filter_lookup_cisco(struct access_list *access, - struct filter *mnew); -struct filter *filter_lookup_zebra(struct access_list *access, - struct filter *mnew); extern const struct frr_yang_module_info frr_filter_info; @@ -182,6 +178,9 @@ struct acl_dup_args { /** Access list name. */ const char *ada_name; + /** Entry action. */ + const char *ada_action; + #define ADA_MAX_VALUES 4 /** Entry XPath for value. */ const char *ada_xpath[ADA_MAX_VALUES]; @@ -191,6 +190,9 @@ struct acl_dup_args { /** Duplicated entry found in list? */ bool ada_found; + /** Sequence number of the found entry */ + int64_t ada_seq; + /** (Optional) Already existing `dnode`. */ const struct lyd_node *ada_entry_dnode; }; @@ -209,6 +211,9 @@ struct plist_dup_args { /** Access list name. */ const char *pda_name; + /** Entry action. */ + const char *pda_action; + #define PDA_MAX_VALUES 4 /** Entry XPath for value. */ const char *pda_xpath[PDA_MAX_VALUES]; @@ -218,6 +223,9 @@ struct plist_dup_args { /** Duplicated entry found in list? */ bool pda_found; + /** Sequence number of the found entry */ + int64_t pda_seq; + /** (Optional) Already existing `dnode`. */ const struct lyd_node *pda_entry_dnode; }; @@ -234,10 +242,12 @@ bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda); struct lyd_node; struct vty; +extern int access_list_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); extern void access_list_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults); extern void access_list_remark_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +extern int prefix_list_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2); extern void prefix_list_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults); extern void prefix_list_remark_show(struct vty *vty, struct lyd_node *dnode, diff --git a/lib/filter_cli.c b/lib/filter_cli.c index 5d66a9fc73..e147ed5639 100644 --- a/lib/filter_cli.c +++ b/lib/filter_cli.c @@ -52,69 +52,6 @@ #define PREFIX_LIST_NAME_STR "Prefix list entry name\n" /* - * Helper function to locate filter data structures for Cisco-style ACLs. - */ -static int64_t acl_cisco_get_seq(struct access_list *acl, const char *action, - const char *src, const char *src_mask, - const char *dst, const char *dst_mask) -{ - struct filter_cisco *fc; - struct filter f, *fn; - - memset(&f, 0, sizeof(f)); - f.cisco = 1; - if (strcmp(action, "permit") == 0) - f.type = FILTER_PERMIT; - else - f.type = FILTER_DENY; - - fc = &f.u.cfilter; - inet_pton(AF_INET, src, &fc->addr); - inet_pton(AF_INET, src_mask, &fc->addr_mask); - fc->addr.s_addr &= ~fc->addr_mask.s_addr; - if (dst != NULL) { - fc->extended = 1; - inet_pton(AF_INET, dst, &fc->mask); - inet_pton(AF_INET, dst_mask, &fc->mask_mask); - fc->mask.s_addr &= ~fc->mask_mask.s_addr; - } - - fn = filter_lookup_cisco(acl, &f); - if (fn == NULL) - return -1; - - return fn->seq; -} - -/* - * Helper function to locate filter data structures for zebra-style ACLs. - */ -static int64_t acl_zebra_get_seq(struct access_list *acl, const char *action, - const struct prefix *p, bool exact) -{ - struct filter_zebra *fz; - struct filter f, *fn; - - memset(&f, 0, sizeof(f)); - memset(&fz, 0, sizeof(fz)); - if (strcmp(action, "permit") == 0) - f.type = FILTER_PERMIT; - else - f.type = FILTER_DENY; - - fz = &f.u.zfilter; - if (p->family) - prefix_copy(&fz->prefix, p); - fz->exact = exact; - - fn = filter_lookup_zebra(acl, &f); - if (fn == NULL) - return -1; - - return fn->seq; -} - -/* * Helper function to generate a sequence number for legacy commands. */ static int acl_get_seq_cb(const struct lyd_node *dnode, void *arg) @@ -146,6 +83,53 @@ static long acl_get_seq(struct vty *vty, const char *xpath) return seq + 5; } +static int acl_remove_if_empty(struct vty *vty, const char *iptype, + const char *name) +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='%s'][name='%s']/remark", + iptype, name); + /* List is not empty if there is a remark, check that: */ + if (yang_dnode_exists(vty->candidate_config->dnode, xpath)) + return CMD_SUCCESS; + + /* Check if we have any entries: */ + snprintf(xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='%s'][name='%s']", iptype, + name); + /* + * NOTE: if the list is empty it will return the first sequence + * number: 5. + */ + if (acl_get_seq(vty, xpath) != 5) + return CMD_SUCCESS; + + /* Nobody is using this list, lets remove it. */ + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static int acl_remove(struct vty *vty, const char *iptype, const char *name, + int64_t sseq) +{ + char xpath[XPATH_MAXLEN]; + int rv; + + snprintfrr( + xpath, sizeof(xpath), + "/frr-filter:lib/access-list[type='%s'][name='%s']/entry[sequence='%" PRId64 "']", + iptype, name, sseq); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return acl_remove_if_empty(vty, iptype, name); + + return rv; +} + /* * Cisco (legacy) access lists. */ @@ -173,6 +157,7 @@ DEFPY_YANG( if (seq_str == NULL) { ada.ada_type = "ipv4"; ada.ada_name = name; + ada.ada_action = action; if (host_str && mask_str == NULL) { ada.ada_xpath[0] = "./host"; ada.ada_value[0] = host_str; @@ -183,7 +168,7 @@ DEFPY_YANG( ada.ada_value[1] = mask_str; } else { ada.ada_xpath[0] = "./source-any"; - ada.ada_value[0] = "true"; + ada.ada_value[0] = ""; } /* Duplicated entry without sequence, just quit. */ @@ -237,44 +222,36 @@ DEFPY_YANG( "Address to match\n" "Wildcard bits\n") { - struct access_list *acl; - struct lyd_node *dnode; int64_t sseq; - char xpath[XPATH_MAXLEN]; - char xpath_entry[XPATH_MAXLEN + 32]; + struct acl_dup_args ada = {}; /* If the user provided sequence number, then just go for it. */ - if (seq_str != NULL) { - snprintf( - xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", - name, seq_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); - } + if (seq_str != NULL) + return acl_remove(vty, "ipv4", name, seq); /* Otherwise, to keep compatibility, we need to figure it out. */ - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); - - /* Access-list must exist before entries. */ - if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING_CONFIG_FAILED; + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + if (host_str && mask_str == NULL) { + ada.ada_xpath[0] = "./host"; + ada.ada_value[0] = host_str; + } else if (host_str && mask_str) { + ada.ada_xpath[0] = "./network/address"; + ada.ada_value[0] = host_str; + ada.ada_xpath[1] = "./network/mask"; + ada.ada_value[1] = mask_str; + } else { + ada.ada_xpath[0] = "./source-any"; + ada.ada_value[0] = ""; + } - /* Use access-list data structure to fetch sequence. */ - dnode = yang_dnode_get(running_config->dnode, xpath); - acl = nb_running_get_entry(dnode, NULL, true); - sseq = acl_cisco_get_seq(acl, action, host_str, - mask_str ? mask_str : CISCO_HOST_WILDCARD_MASK, - NULL, NULL); - if (sseq == -1) + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + sseq = ada.ada_seq; + else return CMD_WARNING_CONFIG_FAILED; - snprintfrr(xpath_entry, sizeof(xpath_entry), - "%s/entry[sequence='%" PRId64 "']", xpath, sseq); - nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); + return acl_remove(vty, "ipv4", name, sseq); } DEFPY_YANG( @@ -309,6 +286,7 @@ DEFPY_YANG( if (seq_str == NULL) { ada.ada_type = "ipv4"; ada.ada_name = name; + ada.ada_action = action; if (src_str && src_mask_str == NULL) { ada.ada_xpath[idx] = "./host"; ada.ada_value[idx] = src_str; @@ -322,7 +300,7 @@ DEFPY_YANG( idx++; } else { ada.ada_xpath[idx] = "./source-any"; - ada.ada_value[idx] = "true"; + ada.ada_value[idx] = ""; idx++; } @@ -339,7 +317,7 @@ DEFPY_YANG( idx++; } else { ada.ada_xpath[idx] = "./destination-any"; - ada.ada_value[idx] = "true"; + ada.ada_value[idx] = ""; idx++; } @@ -414,68 +392,58 @@ DEFPY_YANG( "Destination address to match\n" "Any destination host\n") { - struct access_list *acl; - struct lyd_node *dnode; + int idx = 0; int64_t sseq; - char xpath[XPATH_MAXLEN]; - char xpath_entry[XPATH_MAXLEN + 32]; + struct acl_dup_args ada = {}; /* If the user provided sequence number, then just go for it. */ - if (seq_str != NULL) { - snprintfrr( - xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", - name, seq_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); - } + if (seq_str != NULL) + return acl_remove(vty, "ipv4", name, seq); /* Otherwise, to keep compatibility, we need to figure it out. */ - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); - - /* Access-list must exist before entries. */ - if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING_CONFIG_FAILED; + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + if (src_str && src_mask_str == NULL) { + ada.ada_xpath[idx] = "./host"; + ada.ada_value[idx] = src_str; + idx++; + } else if (src_str && src_mask_str) { + ada.ada_xpath[idx] = "./network/address"; + ada.ada_value[idx] = src_str; + idx++; + ada.ada_xpath[idx] = "./network/mask"; + ada.ada_value[idx] = src_mask_str; + idx++; + } else { + ada.ada_xpath[idx] = "./source-any"; + ada.ada_value[idx] = ""; + idx++; + } - /* Use access-list data structure to fetch sequence. */ - dnode = yang_dnode_get(running_config->dnode, xpath); - acl = nb_running_get_entry(dnode, NULL, true); - if (src_str != NULL) { - if (dst_str != NULL) - sseq = acl_cisco_get_seq( - acl, action, src_str, - src_mask_str ? src_mask_str - : CISCO_HOST_WILDCARD_MASK, - dst_str, - dst_mask_str ? dst_mask_str - : CISCO_HOST_WILDCARD_MASK); - else - sseq = acl_cisco_get_seq( - acl, action, src_str, - src_mask_str ? src_mask_str - : CISCO_HOST_WILDCARD_MASK, - "0.0.0.0", CISCO_ANY_WILDCARD_MASK); + if (dst_str && dst_mask_str == NULL) { + ada.ada_xpath[idx] = "./destination-host"; + ada.ada_value[idx] = dst_str; + idx++; + } else if (dst_str && dst_mask_str) { + ada.ada_xpath[idx] = "./destination-network/address"; + ada.ada_value[idx] = dst_str; + idx++; + ada.ada_xpath[idx] = "./destination-network/mask"; + ada.ada_value[idx] = dst_mask_str; + idx++; } else { - if (dst_str != NULL) - sseq = acl_cisco_get_seq( - acl, action, "0.0.0.0", CISCO_ANY_WILDCARD_MASK, - dst_str, - dst_mask_str ? dst_mask_str - : CISCO_HOST_WILDCARD_MASK); - else - sseq = acl_cisco_get_seq( - acl, action, "0.0.0.0", CISCO_ANY_WILDCARD_MASK, - "0.0.0.0", CISCO_ANY_WILDCARD_MASK); + ada.ada_xpath[idx] = "./destination-any"; + ada.ada_value[idx] = ""; + idx++; } - if (sseq == -1) - return CMD_WARNING_CONFIG_FAILED; - snprintfrr(xpath_entry, sizeof(xpath_entry), - "%s/entry[sequence='%" PRId64 "']", xpath, sseq); - nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + sseq = ada.ada_seq; + else + return CMD_WARNING_CONFIG_FAILED; - return nb_cli_apply_changes(vty, NULL); + return acl_remove(vty, "ipv4", name, sseq); } /* @@ -504,6 +472,7 @@ DEFPY_YANG( if (seq_str == NULL) { ada.ada_type = "ipv4"; ada.ada_name = name; + ada.ada_action = action; if (prefix_str) { ada.ada_xpath[0] = "./ipv4-prefix"; @@ -514,7 +483,7 @@ DEFPY_YANG( } } else { ada.ada_xpath[0] = "./any"; - ada.ada_value[0] = "true"; + ada.ada_value[0] = ""; } /* Duplicated entry without sequence, just quit. */ @@ -565,49 +534,36 @@ DEFPY_YANG( "Exact match of the prefixes\n" "Match any IPv4\n") { - struct access_list *acl; - struct lyd_node *dnode; int64_t sseq; - struct prefix pany; - char xpath[XPATH_MAXLEN]; - char xpath_entry[XPATH_MAXLEN + 32]; + struct acl_dup_args ada = {}; /* If the user provided sequence number, then just go for it. */ - if (seq_str != NULL) { - snprintf( - xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", - name, seq_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); - } + if (seq_str != NULL) + return acl_remove(vty, "ipv4", name, seq); /* Otherwise, to keep compatibility, we need to figure it out. */ - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); - - /* Access-list must exist before entries. */ - if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING_CONFIG_FAILED; + ada.ada_type = "ipv4"; + ada.ada_name = name; + ada.ada_action = action; + + if (prefix_str) { + ada.ada_xpath[0] = "./ipv4-prefix"; + ada.ada_value[0] = prefix_str; + if (exact) { + ada.ada_xpath[1] = "./ipv4-exact-match"; + ada.ada_value[1] = "true"; + } + } else { + ada.ada_xpath[0] = "./any"; + ada.ada_value[0] = ""; + } - /* Use access-list data structure to fetch sequence. */ - dnode = yang_dnode_get(running_config->dnode, xpath); - acl = nb_running_get_entry(dnode, NULL, true); - if (prefix_str == NULL) { - memset(&pany, 0, sizeof(pany)); - pany.family = AF_INET; - sseq = acl_zebra_get_seq(acl, action, &pany, exact); - } else - sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, - exact); - if (sseq == -1) + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + sseq = ada.ada_seq; + else return CMD_WARNING_CONFIG_FAILED; - snprintfrr(xpath_entry, sizeof(xpath_entry), - "%s/entry[sequence='%" PRId64 "']", xpath, sseq); - nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); + return acl_remove(vty, "ipv4", name, sseq); } DEFPY_YANG( @@ -659,13 +615,18 @@ DEFPY_YANG( ACCESS_LIST_REMARK_STR) { char xpath[XPATH_MAXLEN]; + int rv; snprintf(xpath, sizeof(xpath), "/frr-filter:lib/access-list[type='ipv4'][name='%s']/remark", name); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return acl_remove_if_empty(vty, "ipv4", name); + + return rv; } ALIAS( @@ -701,6 +662,7 @@ DEFPY_YANG( if (seq_str == NULL) { ada.ada_type = "ipv6"; ada.ada_name = name; + ada.ada_action = action; if (prefix_str) { ada.ada_xpath[0] = "./ipv6-prefix"; @@ -711,7 +673,7 @@ DEFPY_YANG( } } else { ada.ada_xpath[0] = "./any"; - ada.ada_value[0] = "true"; + ada.ada_value[0] = ""; } /* Duplicated entry without sequence, just quit. */ @@ -763,49 +725,36 @@ DEFPY_YANG( "Exact match of the prefixes\n" "Match any IPv6\n") { - struct access_list *acl; - struct lyd_node *dnode; int64_t sseq; - struct prefix pany; - char xpath[XPATH_MAXLEN]; - char xpath_entry[XPATH_MAXLEN + 32]; + struct acl_dup_args ada = {}; /* If the user provided sequence number, then just go for it. */ - if (seq_str != NULL) { - snprintf( - xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv6'][name='%s']/entry[sequence='%s']", - name, seq_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); - } + if (seq_str != NULL) + return acl_remove(vty, "ipv6", name, seq); /* Otherwise, to keep compatibility, we need to figure it out. */ - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv6'][name='%s']", name); - - /* Access-list must exist before entries. */ - if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING_CONFIG_FAILED; + ada.ada_type = "ipv6"; + ada.ada_name = name; + ada.ada_action = action; + + if (prefix_str) { + ada.ada_xpath[0] = "./ipv6-prefix"; + ada.ada_value[0] = prefix_str; + if (exact) { + ada.ada_xpath[1] = "./ipv6-exact-match"; + ada.ada_value[1] = "true"; + } + } else { + ada.ada_xpath[0] = "./any"; + ada.ada_value[0] = ""; + } - /* Use access-list data structure to fetch sequence. */ - dnode = yang_dnode_get(running_config->dnode, xpath); - acl = nb_running_get_entry(dnode, NULL, true); - if (prefix == NULL) { - memset(&pany, 0, sizeof(pany)); - pany.family = AF_INET6; - sseq = acl_zebra_get_seq(acl, action, &pany, exact); - } else - sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, - exact); - if (sseq == -1) + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + sseq = ada.ada_seq; + else return CMD_WARNING_CONFIG_FAILED; - snprintfrr(xpath_entry, sizeof(xpath_entry), - "%s/entry[sequence='%" PRId64 "']", xpath, sseq); - nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); + return acl_remove(vty, "ipv6", name, sseq); } DEFPY_YANG( @@ -860,13 +809,18 @@ DEFPY_YANG( ACCESS_LIST_REMARK_STR) { char xpath[XPATH_MAXLEN]; + int rv; snprintf(xpath, sizeof(xpath), "/frr-filter:lib/access-list[type='ipv6'][name='%s']/remark", name); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return acl_remove_if_empty(vty, "ipv6", name); + + return rv; } ALIAS( @@ -902,13 +856,14 @@ DEFPY_YANG( if (seq_str == NULL) { ada.ada_type = "mac"; ada.ada_name = name; + ada.ada_action = action; if (mac_str) { ada.ada_xpath[0] = "./mac"; ada.ada_value[0] = mac_str; } else { ada.ada_xpath[0] = "./any"; - ada.ada_value[0] = "true"; + ada.ada_value[0] = ""; } /* Duplicated entry without sequence, just quit. */ @@ -946,7 +901,7 @@ DEFPY_YANG( DEFPY_YANG( no_mac_access_list, no_mac_access_list_cmd, - "no mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$prefix|any>", + "no mac access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <X:X:X:X:X:X$mac|any>", NO_STR MAC_STR ACCESS_LIST_STR @@ -956,49 +911,32 @@ DEFPY_YANG( "MAC address\n" "Match any MAC address\n") { - struct access_list *acl; - struct lyd_node *dnode; int64_t sseq; - struct prefix pany; - char xpath[XPATH_MAXLEN]; - char xpath_entry[XPATH_MAXLEN + 32]; + struct acl_dup_args ada = {}; /* If the user provided sequence number, then just go for it. */ - if (seq_str != NULL) { - snprintf( - xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='mac'][name='%s']/entry[sequence='%s']", - name, seq_str); - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); - } + if (seq_str != NULL) + return acl_remove(vty, "mac", name, seq); /* Otherwise, to keep compatibility, we need to figure it out. */ - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='mac'][name='%s']", name); + ada.ada_type = "mac"; + ada.ada_name = name; + ada.ada_action = action; - /* Access-list must exist before entries. */ - if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING_CONFIG_FAILED; + if (mac_str) { + ada.ada_xpath[0] = "./mac"; + ada.ada_value[0] = mac_str; + } else { + ada.ada_xpath[0] = "./any"; + ada.ada_value[0] = ""; + } - /* Use access-list data structure to fetch sequence. */ - dnode = yang_dnode_get(running_config->dnode, xpath); - acl = nb_running_get_entry(dnode, NULL, true); - if (prefix == NULL) { - memset(&pany, 0, sizeof(pany)); - pany.family = AF_ETHERNET; - sseq = acl_zebra_get_seq(acl, action, &pany, false); - } else - sseq = acl_zebra_get_seq(acl, action, (struct prefix *)prefix, - false); - if (sseq == -1) + if (acl_is_dup(vty->candidate_config->dnode, &ada)) + sseq = ada.ada_seq; + else return CMD_WARNING_CONFIG_FAILED; - snprintfrr(xpath_entry, sizeof(xpath_entry), - "%s/entry[sequence='%" PRId64 "']", xpath, sseq); - nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); + return acl_remove(vty, "mac", name, sseq); } DEFPY_YANG( @@ -1053,13 +991,18 @@ DEFPY_YANG( ACCESS_LIST_REMARK_STR) { char xpath[XPATH_MAXLEN]; + int rv; snprintf(xpath, sizeof(xpath), "/frr-filter:lib/access-list[type='mac'][name='%s']/remark", name); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return acl_remove_if_empty(vty, "mac", name); + + return rv; } ALIAS( @@ -1072,6 +1015,14 @@ ALIAS( ACCESS_LIST_REMARK_STR ACCESS_LIST_REMARK_LINE_STR) +int access_list_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + uint32_t seq1 = yang_dnode_get_uint32(dnode1, "./sequence"); + uint32_t seq2 = yang_dnode_get_uint32(dnode2, "./sequence"); + + return seq1 - seq2; +} + void access_list_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { @@ -1244,13 +1195,13 @@ static int plist_remove_if_empty(struct vty *vty, const char *iptype, } static int plist_remove(struct vty *vty, const char *iptype, const char *name, - const char *seq, const char *action, struct prefix *p, - long ge, long le) + const char *seq, const char *action, + const char *prefix_str, const char *ge_str, + const char *le_str) { - struct prefix_list_entry *pentry; - enum prefix_list_type plt; - struct prefix_list *pl; - struct lyd_node *dnode; + int64_t sseq; + int arg_idx = 0; + struct plist_dup_args pda = {}; char xpath[XPATH_MAXLEN]; char xpath_entry[XPATH_MAXLEN + 32]; int rv; @@ -1271,29 +1222,57 @@ static int plist_remove(struct vty *vty, const char *iptype, const char *name, } /* Otherwise, to keep compatibility, we need to figure it out. */ - snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/prefix-list[type='%s'][name='%s']", iptype, - name); - - /* Access-list must exist before entries. */ - if (yang_dnode_exists(running_config->dnode, xpath) == false) - return CMD_WARNING_CONFIG_FAILED; + pda.pda_type = iptype; + pda.pda_name = name; + pda.pda_action = action; + if (prefix_str) { + if (strmatch(iptype, "ipv4")) { + pda.pda_xpath[arg_idx] = "./ipv4-prefix"; + pda.pda_value[arg_idx] = prefix_str; + arg_idx++; + if (ge_str) { + pda.pda_xpath[arg_idx] = + "./ipv4-prefix-length-greater-or-equal"; + pda.pda_value[arg_idx] = ge_str; + arg_idx++; + } + if (le_str) { + pda.pda_xpath[arg_idx] = + "./ipv4-prefix-length-lesser-or-equal"; + pda.pda_value[arg_idx] = le_str; + arg_idx++; + } + } else { + pda.pda_xpath[arg_idx] = "./ipv6-prefix"; + pda.pda_value[arg_idx] = prefix_str; + arg_idx++; + if (ge_str) { + pda.pda_xpath[arg_idx] = + "./ipv6-prefix-length-greater-or-equal"; + pda.pda_value[arg_idx] = ge_str; + arg_idx++; + } + if (le_str) { + pda.pda_xpath[arg_idx] = + "./ipv6-prefix-length-lesser-or-equal"; + pda.pda_value[arg_idx] = le_str; + arg_idx++; + } + } + } else { + pda.pda_xpath[0] = "./any"; + pda.pda_value[0] = ""; + } - /* Use access-list data structure to fetch sequence. */ - assert(action != NULL); - if (strcmp(action, "permit") == 0) - plt = PREFIX_PERMIT; + if (plist_is_dup(vty->candidate_config->dnode, &pda)) + sseq = pda.pda_seq; else - plt = PREFIX_DENY; - - dnode = yang_dnode_get(running_config->dnode, xpath); - pl = nb_running_get_entry(dnode, NULL, true); - pentry = prefix_list_entry_lookup(pl, p, plt, -1, le, ge); - if (pentry == NULL) return CMD_WARNING_CONFIG_FAILED; - snprintfrr(xpath_entry, sizeof(xpath_entry), - "%s/entry[sequence='%" PRId64 "']", xpath, pentry->seq); + snprintfrr( + xpath_entry, sizeof(xpath_entry), + "/frr-filter:lib/prefix-list[type='%s'][name='%s']/entry[sequence='%" PRId64 "']", + iptype, name, sseq); nb_cli_enqueue_change(vty, xpath_entry, NB_OP_DESTROY, NULL); rv = nb_cli_apply_changes(vty, NULL); @@ -1331,6 +1310,7 @@ DEFPY_YANG( if (seq_str == NULL) { pda.pda_type = "ipv4"; pda.pda_name = name; + pda.pda_action = action; if (prefix_str) { pda.pda_xpath[arg_idx] = "./ipv4-prefix"; pda.pda_value[arg_idx] = prefix_str; @@ -1411,8 +1391,8 @@ DEFPY_YANG( "Maximum prefix length to be matched\n" "Maximum prefix length\n") { - return plist_remove(vty, "ipv4", name, seq_str, action, - (struct prefix *)prefix, ge, le); + return plist_remove(vty, "ipv4", name, seq_str, action, prefix_str, + ge_str, le_str); } DEFPY_YANG( @@ -1424,7 +1404,7 @@ DEFPY_YANG( PREFIX_LIST_NAME_STR ACCESS_LIST_SEQ_STR) { - return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, 0, 0); + return plist_remove(vty, "ipv4", name, seq_str, NULL, NULL, NULL, NULL); } DEFPY_YANG( @@ -1479,13 +1459,18 @@ DEFPY_YANG( ACCESS_LIST_REMARK_STR) { char xpath[XPATH_MAXLEN]; + int rv; snprintf(xpath, sizeof(xpath), "/frr-filter:lib/prefix-list[type='ipv4'][name='%s']/remark", name); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return plist_remove_if_empty(vty, "ipv4", name); + + return rv; } ALIAS( @@ -1526,6 +1511,7 @@ DEFPY_YANG( if (seq_str == NULL) { pda.pda_type = "ipv6"; pda.pda_name = name; + pda.pda_action = action; if (prefix_str) { pda.pda_xpath[arg_idx] = "./ipv6-prefix"; pda.pda_value[arg_idx] = prefix_str; @@ -1606,8 +1592,8 @@ DEFPY_YANG( "Minimum prefix length to be matched\n" "Minimum prefix length\n") { - return plist_remove(vty, "ipv6", name, seq_str, action, - (struct prefix *)prefix, ge, le); + return plist_remove(vty, "ipv6", name, seq_str, action, prefix_str, + ge_str, le_str); } DEFPY_YANG( @@ -1619,7 +1605,7 @@ DEFPY_YANG( PREFIX_LIST_NAME_STR ACCESS_LIST_SEQ_STR) { - return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, 0, 0); + return plist_remove(vty, "ipv6", name, seq_str, NULL, NULL, NULL, NULL); } DEFPY_YANG( @@ -1674,13 +1660,18 @@ DEFPY_YANG( ACCESS_LIST_REMARK_STR) { char xpath[XPATH_MAXLEN]; + int rv; snprintf(xpath, sizeof(xpath), "/frr-filter:lib/prefix-list[type='ipv6'][name='%s']/remark", name); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - return nb_cli_apply_changes(vty, NULL); + rv = nb_cli_apply_changes(vty, NULL); + if (rv == CMD_SUCCESS) + return plist_remove_if_empty(vty, "ipv6", name); + + return rv; } ALIAS( @@ -1693,6 +1684,14 @@ ALIAS( ACCESS_LIST_REMARK_STR ACCESS_LIST_REMARK_LINE_STR) +int prefix_list_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + uint32_t seq1 = yang_dnode_get_uint32(dnode1, "./sequence"); + uint32_t seq2 = yang_dnode_get_uint32(dnode2, "./sequence"); + + return seq1 - seq2; +} + void prefix_list_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { diff --git a/lib/filter_nb.c b/lib/filter_nb.c index c83738e729..08c29789b9 100644 --- a/lib/filter_nb.c +++ b/lib/filter_nb.c @@ -152,6 +152,27 @@ prefix_list_nb_validate_v6_af_type(const struct lyd_node *plist_dnode, return NB_OK; } +static int lib_prefix_list_entry_prefix_length_greater_or_equal_modify( + struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->ge = yang_dnode_get_uint8(args->dnode, NULL); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + static int lib_prefix_list_entry_prefix_length_lesser_or_equal_modify( struct nb_cb_modify_args *args) { @@ -238,6 +259,9 @@ static int _acl_is_dup(const struct lyd_node *dnode, void *arg) && ada->ada_entry_dnode == dnode) return YANG_ITER_CONTINUE; + if (strcmp(yang_dnode_get_string(dnode, "action"), ada->ada_action)) + return YANG_ITER_CONTINUE; + /* Check if all values match. */ for (idx = 0; idx < ADA_MAX_VALUES; idx++) { /* No more values. */ @@ -255,6 +279,7 @@ static int _acl_is_dup(const struct lyd_node *dnode, void *arg) } ada->ada_found = true; + ada->ada_seq = yang_dnode_get_uint32(dnode, "sequence"); return YANG_ITER_STOP; } @@ -292,6 +317,7 @@ static bool acl_cisco_is_dup(const struct lyd_node *dnode) /* Initialize. */ ada.ada_type = "ipv4"; ada.ada_name = yang_dnode_get_string(entry_dnode, "../name"); + ada.ada_action = yang_dnode_get_string(entry_dnode, "action"); ada.ada_entry_dnode = entry_dnode; /* Load all values/XPaths. */ @@ -341,6 +367,7 @@ static bool acl_zebra_is_dup(const struct lyd_node *dnode, break; } ada.ada_name = yang_dnode_get_string(entry_dnode, "../name"); + ada.ada_action = yang_dnode_get_string(entry_dnode, "action"); ada.ada_entry_dnode = entry_dnode; /* Load all values/XPaths. */ @@ -370,6 +397,9 @@ static int _plist_is_dup(const struct lyd_node *dnode, void *arg) && pda->pda_entry_dnode == dnode) return YANG_ITER_CONTINUE; + if (strcmp(yang_dnode_get_string(dnode, "action"), pda->pda_action)) + return YANG_ITER_CONTINUE; + /* Check if all values match. */ for (idx = 0; idx < PDA_MAX_VALUES; idx++) { /* No more values. */ @@ -387,6 +417,7 @@ static int _plist_is_dup(const struct lyd_node *dnode, void *arg) } pda->pda_found = true; + pda->pda_seq = yang_dnode_get_uint32(dnode, "sequence"); return YANG_ITER_STOP; } @@ -403,6 +434,46 @@ bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda) return pda->pda_found; } +static bool plist_is_dup_nb(const struct lyd_node *dnode) +{ + const struct lyd_node *entry_dnode = + yang_dnode_get_parent(dnode, "entry"); + struct plist_dup_args pda = {}; + int idx = 0, arg_idx = 0; + static const char *entries[] = { + "./ipv4-prefix", + "./ipv4-prefix-length-greater-or-equal", + "./ipv4-prefix-length-lesser-or-equal", + "./ipv6-prefix", + "./ipv6-prefix-length-greater-or-equal", + "./ipv6-prefix-length-lesser-or-equal", + "./any", + NULL + }; + + /* Initialize. */ + pda.pda_type = yang_dnode_get_string(entry_dnode, "../type"); + pda.pda_name = yang_dnode_get_string(entry_dnode, "../name"); + pda.pda_action = yang_dnode_get_string(entry_dnode, "action"); + pda.pda_entry_dnode = entry_dnode; + + /* Load all values/XPaths. */ + while (entries[idx] != NULL) { + if (!yang_dnode_exists(entry_dnode, entries[idx])) { + idx++; + continue; + } + + pda.pda_xpath[arg_idx] = entries[idx]; + pda.pda_value[arg_idx] = + yang_dnode_get_string(entry_dnode, entries[idx]); + arg_idx++; + idx++; + } + + return plist_is_dup(entry_dnode, &pda); +} + /* * XPath: /frr-filter:lib/access-list */ @@ -1265,6 +1336,13 @@ lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return prefix_list_nb_validate_v4_af_type( plist_dnode, args->errmsg, args->errmsg_len); } @@ -1293,6 +1371,13 @@ lib_prefix_list_entry_ipv6_prefix_modify(struct nb_cb_modify_args *args) const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return prefix_list_nb_validate_v6_af_type( plist_dnode, args->errmsg, args->errmsg_len); } @@ -1316,26 +1401,27 @@ lib_prefix_list_entry_ipv6_prefix_destroy(struct nb_cb_destroy_args *args) static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify( struct nb_cb_modify_args *args) { - struct prefix_list_entry *ple; - - if (args->event == NB_EV_VALIDATE && - prefix_list_length_validate(args) != NB_OK) + if (args->event == NB_EV_VALIDATE + && prefix_list_length_validate(args) != NB_OK) return NB_ERR_VALIDATION; - if (args->event != NB_EV_APPLY) - return NB_OK; - - ple = nb_running_get_entry(args->dnode, NULL, true); - - /* Start prefix entry update procedure. */ - prefix_list_entry_update_start(ple); + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); - ple->ge = yang_dnode_get_uint8(args->dnode, NULL); + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); + return prefix_list_nb_validate_v4_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } - return NB_OK; + return lib_prefix_list_entry_prefix_length_greater_or_equal_modify( + args); } static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy( @@ -1367,11 +1453,19 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return prefix_list_nb_validate_v4_af_type( plist_dnode, args->errmsg, args->errmsg_len); } - return lib_prefix_list_entry_prefix_length_lesser_or_equal_modify(args); + return lib_prefix_list_entry_prefix_length_lesser_or_equal_modify( + args); } static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy( @@ -1395,8 +1489,6 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy( static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_modify( struct nb_cb_modify_args *args) { - struct prefix_list_entry *ple; - if (args->event == NB_EV_VALIDATE && prefix_list_length_validate(args) != NB_OK) return NB_ERR_VALIDATION; @@ -1405,24 +1497,19 @@ static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_modify( const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return prefix_list_nb_validate_v6_af_type( plist_dnode, args->errmsg, args->errmsg_len); } - if (args->event != NB_EV_APPLY) - return NB_OK; - - ple = nb_running_get_entry(args->dnode, NULL, true); - - /* Start prefix entry update procedure. */ - prefix_list_entry_update_start(ple); - - ple->ge = yang_dnode_get_uint8(args->dnode, NULL); - - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); - - return NB_OK; + return lib_prefix_list_entry_prefix_length_greater_or_equal_modify( + args); } static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_destroy( @@ -1454,28 +1541,30 @@ static int lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_modify( const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return prefix_list_nb_validate_v6_af_type( plist_dnode, args->errmsg, args->errmsg_len); } - return lib_prefix_list_entry_prefix_length_lesser_or_equal_modify(args); + return lib_prefix_list_entry_prefix_length_lesser_or_equal_modify( + args); } static int lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_destroy( struct nb_cb_destroy_args *args) { - int af_type; - if (args->event == NB_EV_VALIDATE) { const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); - af_type = yang_dnode_get_enum(plist_dnode, "./type"); - if (af_type != YPLT_IPV6) { - snprintf(args->errmsg, args->errmsg_len, - "prefix-list type %u is mismatched.", af_type); - return NB_ERR_VALIDATION; - } - return NB_OK; + + return prefix_list_nb_validate_v6_af_type( + plist_dnode, args->errmsg, args->errmsg_len); } return lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy( @@ -1490,6 +1579,17 @@ static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args) struct prefix_list_entry *ple; int type; + if (args->event == NB_EV_VALIDATE) { + if (plist_is_dup_nb(args->dnode)) { + snprintf(args->errmsg, args->errmsg_len, + "duplicated prefix list value: %s", + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + return NB_OK; + } + if (args->event != NB_EV_APPLY) return NB_OK; @@ -1567,6 +1667,7 @@ const struct frr_yang_module_info frr_filter_info = { .cbs = { .create = lib_access_list_entry_create, .destroy = lib_access_list_entry_destroy, + .cli_cmp = access_list_cmp, .cli_show = access_list_show, } }, @@ -1690,6 +1791,7 @@ const struct frr_yang_module_info frr_filter_info = { .cbs = { .create = lib_prefix_list_entry_create, .destroy = lib_prefix_list_entry_destroy, + .cli_cmp = prefix_list_cmp, .cli_show = prefix_list_show, } }, @@ -1056,15 +1056,30 @@ struct connected *connected_get_linklocal(struct interface *ifp) void if_terminate(struct vrf *vrf) { struct interface *ifp; + bool delete; + + /* + * If the default VRF is being terminated or has + * already been terminated it means that + * the program is shutting down and we need to + * delete all the interfaces. Otherwise, we only + * need to move VRF's interfaces to the default VRF. + */ + delete = vrf_is_backend_netns() || vrf->vrf_id == VRF_DEFAULT + || !vrf_lookup_by_id(VRF_DEFAULT); while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) { ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name); - if (ifp->node) { - ifp->node->info = NULL; - route_unlock_node(ifp->node); + if (delete) { + if (ifp->node) { + ifp->node->info = NULL; + route_unlock_node(ifp->node); + } + if_delete(&ifp); + } else { + if_update_to_new_vrf(ifp, VRF_DEFAULT); } - if_delete(&ifp); } } diff --git a/lib/link_state.c b/lib/link_state.c index 7f0d2a1245..8606f8eb09 100644 --- a/lib/link_state.c +++ b/lib/link_state.c @@ -33,6 +33,9 @@ #include "vty.h" #include "zclient.h" #include "stream.h" +#include "sbuf.h" +#include "printfrr.h" +#include <lib/json.h> #include "link_state.h" /* Link State Memory allocation */ @@ -46,7 +49,7 @@ struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid, { struct ls_node *new; - if (adv.origin == NONE) + if (adv.origin == UNKNOWN) return NULL; new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node)); @@ -70,6 +73,9 @@ struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid, void ls_node_del(struct ls_node *node) { + if (!node) + return; + XFREE(MTYPE_LS_DB, node); node = NULL; } @@ -111,7 +117,7 @@ struct ls_attributes *ls_attributes_new(struct ls_node_id adv, { struct ls_attributes *new; - if (adv.origin == NONE) + if (adv.origin == UNKNOWN) return NULL; new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes)); @@ -139,7 +145,7 @@ struct ls_attributes *ls_attributes_new(struct ls_node_id adv, return new; } -void ls_attributes_del(struct ls_attributes *attr) +void ls_attributes_srlg_del(struct ls_attributes *attr) { if (!attr) return; @@ -147,6 +153,18 @@ void ls_attributes_del(struct ls_attributes *attr) if (attr->srlgs) XFREE(MTYPE_LS_DB, attr->srlgs); + attr->srlgs = NULL; + attr->srlg_len = 0; + UNSET_FLAG(attr->flags, LS_ATTR_SRLG); +} + +void ls_attributes_del(struct ls_attributes *attr) +{ + if (!attr) + return; + + ls_attributes_srlg_del(attr); + XFREE(MTYPE_LS_DB, attr); attr = NULL; } @@ -179,75 +197,155 @@ int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2) } /** - * Link State Vertices management functions + * Link State prefix management functions */ -struct ls_vertex *ls_vertex_new(struct ls_node *node) +struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p) { - struct ls_vertex *new; + struct ls_prefix *new; - if (node == NULL) + if (adv.origin == UNKNOWN) return NULL; - new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_vertex)); - new->node = node; - new->incoming_edges = list_new(); - new->outgoing_edges = list_new(); - new->prefixes = list_new(); + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes)); + new->adv = adv; + new->pref = p; return new; } -void ls_vertex_del(struct ls_vertex *vertex) +void ls_prefix_del(struct ls_prefix *pref) { - if (vertex == NULL) + if (!pref) return; - list_delete_all_node(vertex->incoming_edges); - list_delete_all_node(vertex->outgoing_edges); - list_delete_all_node(vertex->prefixes); - XFREE(MTYPE_LS_DB, vertex); - vertex = NULL; + XFREE(MTYPE_LS_DB, pref); + pref = NULL; } +int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2) +{ + if ((p1 && !p2) || (!p1 && p2)) + return 0; + + if (p1 == p2) + return 1; + + if (p1->flags != p2->flags) + return 0; + + if (p1->adv.origin != p2->adv.origin) + return 0; + + if (!memcmp(&p1->adv.id, &p2->adv.id, sizeof(struct ls_node_id))) + return 0; + + /* Do we need to test individually each field, instead performing a + * global memcmp? There is a risk that an old value that is bit masked + * i.e. corresponding flag = 0, will result into a false negative + */ + if (!memcmp(p1, p2, sizeof(struct ls_prefix))) + return 0; + else + return 1; +} + +/** + * Link State Vertices management functions + */ struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node) { struct ls_vertex *new; + uint64_t key = 0; if ((ted == NULL) || (node == NULL)) return NULL; - new = ls_vertex_new(node); - if (!new) - return NULL; - /* set Key as the IPv4/Ipv6 Router ID or ISO System ID */ switch (node->adv.origin) { case OSPFv2: case STATIC: case DIRECT: - memcpy(&new->key, &node->adv.id.ip.addr, IPV4_MAX_BYTELEN); + key = ((uint64_t)ntohl(node->adv.id.ip.addr.s_addr)) + & 0xffffffff; break; case ISIS_L1: case ISIS_L2: - memcpy(&new->key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN); + memcpy(&key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN); break; default: - new->key = 0; + key = 0; break; } - /* Remove Vertex if key is not set */ - if (new->key == 0) { - ls_vertex_del(new); + /* Check that key is valid */ + if (key == 0) return NULL; - } - /* Add Vertex to TED */ + /* Create Vertex and add it to the TED */ + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_vertex)); + if (!new) + return NULL; + + new->key = key; + new->node = node; + new->status = NEW; + new->type = VERTEX; + new->incoming_edges = list_new(); + new->incoming_edges->cmp = (int (*)(void *, void *))edge_cmp; + new->outgoing_edges = list_new(); + new->outgoing_edges->cmp = (int (*)(void *, void *))edge_cmp; + new->prefixes = list_new(); + new->prefixes->cmp = (int (*)(void *, void *))subnet_cmp; vertices_add(&ted->vertices, new); return new; } +void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex) +{ + struct listnode *node, *nnode; + struct ls_edge *edge; + struct ls_subnet *subnet; + + if (!ted || !vertex) + return; + + /* Remove outgoing Edges and list */ + for (ALL_LIST_ELEMENTS(vertex->outgoing_edges, node, nnode, edge)) + ls_edge_del_all(ted, edge); + list_delete(&vertex->outgoing_edges); + + /* Disconnect incoming Edges and remove list */ + for (ALL_LIST_ELEMENTS(vertex->incoming_edges, node, nnode, edge)) { + ls_disconnect(vertex, edge, false); + if (edge->source == NULL) + ls_edge_del_all(ted, edge); + } + list_delete(&vertex->incoming_edges); + + /* Remove subnet and list */ + for (ALL_LIST_ELEMENTS(vertex->prefixes, node, nnode, subnet)) + ls_subnet_del_all(ted, subnet); + list_delete(&vertex->prefixes); + + /* Then remove Vertex from Link State Data Base and free memory */ + vertices_del(&ted->vertices, vertex); + XFREE(MTYPE_LS_DB, vertex); + vertex = NULL; +} + +void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex) +{ + if (!ted || !vertex) + return; + + /* First remove associated Link State Node */ + ls_node_del(vertex->node); + + /* Then, Vertex itself */ + ls_vertex_del(ted, vertex); +} + struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node) { struct ls_vertex *old; @@ -261,49 +359,46 @@ struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node) ls_node_del(old->node); old->node = node; } + old->status = UPDATE; return old; } return ls_vertex_add(ted, node); } -void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex) -{ - vertices_del(&ted->vertices, vertex); - ls_vertex_del(vertex); -} - struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key) { - struct ls_vertex node = {}; + struct ls_vertex vertex = {}; if (key == 0) return NULL; - node.key = key; - return vertices_find(&ted->vertices, &node); + vertex.key = key; + return vertices_find(&ted->vertices, &vertex); } struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted, struct ls_node_id nid) { - struct ls_vertex node = {}; + struct ls_vertex vertex = {}; + vertex.key = 0; switch (nid.origin) { case OSPFv2: case STATIC: case DIRECT: - memcpy(&node.key, &nid.id.ip.addr, IPV4_MAX_BYTELEN); + vertex.key = + ((uint64_t)ntohl(nid.id.ip.addr.s_addr)) & 0xffffffff; break; case ISIS_L1: case ISIS_L2: - memcpy(&node.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN); + memcpy(&vertex.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN); break; default: return NULL; } - return vertices_find(&ted->vertices, &node); + return vertices_find(&ted->vertices, &vertex); } int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2) @@ -323,6 +418,49 @@ int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2) return ls_node_same(v1->node, v2->node); } +void ls_vertex_clean(struct ls_ted *ted, struct ls_vertex *vertex, + struct zclient *zclient) +{ + struct listnode *node, *nnode; + struct ls_edge *edge; + struct ls_subnet *subnet; + struct ls_message msg; + + /* Remove Orphan Edge ... */ + for (ALL_LIST_ELEMENTS(vertex->outgoing_edges, node, nnode, edge)) { + if (edge->status == ORPHAN) { + if (zclient) { + edge->status = DELETE; + ls_edge2msg(&msg, edge); + ls_send_msg(zclient, &msg, NULL); + } + ls_edge_del_all(ted, edge); + } + } + for (ALL_LIST_ELEMENTS(vertex->incoming_edges, node, nnode, edge)) { + if (edge->status == ORPHAN) { + if (zclient) { + edge->status = DELETE; + ls_edge2msg(&msg, edge); + ls_send_msg(zclient, &msg, NULL); + } + ls_edge_del_all(ted, edge); + } + } + + /* ... and Subnet from the Vertex */ + for (ALL_LIST_ELEMENTS(vertex->prefixes, node, nnode, subnet)) { + if (subnet->status == ORPHAN) { + if (zclient) { + subnet->status = DELETE; + ls_subnet2msg(&msg, subnet); + ls_send_msg(zclient, &msg, NULL); + } + ls_subnet_del_all(ted, subnet); + } + } +} + /** * Link State Edges management functions */ @@ -354,18 +492,18 @@ static void ls_edge_connect_to(struct ls_ted *ted, struct ls_edge *edge) vertex = ls_vertex_add(ted, node); } /* and attach the edge as source to the vertex */ - listnode_add(vertex->outgoing_edges, edge); + listnode_add_sort_nodup(vertex->outgoing_edges, edge); edge->source = vertex; /* Then search if there is a reverse Edge */ dst = ls_find_edge_by_destination(ted, edge->attributes); /* attach the destination edge to the vertex */ if (dst) { - listnode_add(vertex->incoming_edges, dst); + listnode_add_sort_nodup(vertex->incoming_edges, dst); dst->destination = vertex; /* and destination vertex to this edge */ vertex = dst->source; - listnode_add(vertex->incoming_edges, edge); + listnode_add_sort_nodup(vertex->incoming_edges, edge); edge->destination = vertex; } } @@ -374,37 +512,43 @@ struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes) { struct ls_edge *new; + uint64_t key = 0; if (attributes == NULL) return NULL; - new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge)); - new->attributes = attributes; /* Key is the IPv4 local address */ if (!IPV4_NET0(attributes->standard.local.s_addr)) - new->key = ((uint64_t)attributes->standard.local.s_addr) - & 0xffffffff; + key = ((uint64_t)ntohl(attributes->standard.local.s_addr)) + & 0xffffffff; /* or the IPv6 local address if IPv4 is not defined */ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6)) - new->key = (uint64_t)(attributes->standard.local6.s6_addr32[0] - & 0xffffffff) - | ((uint64_t)attributes->standard.local6.s6_addr32[1] - << 32); + key = (uint64_t)(attributes->standard.local6.s6_addr32[0] + & 0xffffffff) + | ((uint64_t)attributes->standard.local6.s6_addr32[1] + << 32); /* of local identifier if no IP addresses are defined */ else if (attributes->standard.local_id != 0) - new->key = (uint64_t)( + key = (uint64_t)( (attributes->standard.local_id & 0xffffffff) | ((uint64_t)attributes->standard.remote_id << 32)); - /* Remove Edge if key is not known */ - if (new->key == 0) { - XFREE(MTYPE_LS_DB, new); + /* Check that key is valid */ + if (key == 0) + return NULL; + + /* Create Edge and add it to the TED */ + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge)); + if (!new) return NULL; - } + new->attributes = attributes; + new->key = key; + new->status = NEW; + new->type = EDGE; edges_add(&ted->edges, new); - /* Finally, connect edge to vertices */ + /* Finally, connect Edge to Vertices */ ls_edge_connect_to(ted, new); return new; @@ -429,9 +573,10 @@ struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted, if (attributes == NULL) return NULL; + edge.key = 0; /* Key is the IPv4 local address */ if (!IPV4_NET0(attributes->standard.local.s_addr)) - edge.key = ((uint64_t)attributes->standard.local.s_addr) + edge.key = ((uint64_t)ntohl(attributes->standard.local.s_addr)) & 0xffffffff; /* or the IPv6 local address if IPv4 is not defined */ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6)) @@ -459,18 +604,19 @@ struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted, if (attributes == NULL) return NULL; - /* Key is the IPv4 local address */ + edge.key = 0; + /* Key is the IPv4 remote address */ if (!IPV4_NET0(attributes->standard.remote.s_addr)) - edge.key = ((uint64_t)attributes->standard.remote.s_addr) + edge.key = ((uint64_t)ntohl(attributes->standard.remote.s_addr)) & 0xffffffff; - /* or the IPv6 local address if IPv4 is not defined */ + /* or the IPv6 remote address if IPv4 is not defined */ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.remote6)) edge.key = (uint64_t)(attributes->standard.remote6.s6_addr32[0] & 0xffffffff) | ((uint64_t)attributes->standard.remote6.s6_addr32[1] << 32); - /* of local identifier if no IP addresses are defined */ + /* of remote identifier if no IP addresses are defined */ else if (attributes->standard.remote_id != 0) edge.key = (uint64_t)( (attributes->standard.remote_id & 0xffffffff) @@ -498,6 +644,7 @@ struct ls_edge *ls_edge_update(struct ls_ted *ted, ls_attributes_del(old->attributes); old->attributes = attributes; } + old->status = UPDATE; return old; } @@ -505,15 +652,46 @@ struct ls_edge *ls_edge_update(struct ls_ted *ted, return ls_edge_add(ted, attributes); } +int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2) +{ + if ((e1 && !e2) || (!e1 && e2)) + return 0; + + if (!e1 && !e2) + return 1; + + if (e1->key != e2->key) + return 0; + + if (e1->attributes == e2->attributes) + return 1; + + return ls_attributes_same(e1->attributes, e2->attributes); +} + void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge) { - /* Fist disconnect Edge */ + if (!ted || !edge) + return; + + /* Fist disconnect Edge from Vertices */ ls_disconnect_edge(edge); /* Then remove it from the Data Base */ edges_del(&ted->edges, edge); XFREE(MTYPE_LS_DB, edge); } +void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge) +{ + if (!ted || !edge) + return; + + /* Remove associated Link State Attributes */ + ls_attributes_del(edge->attributes); + /* Then Edge itself */ + ls_edge_del(ted, edge); +} + /** * Link State Subnet Management functions. */ @@ -531,6 +709,8 @@ struct ls_subnet *ls_subnet_add(struct ls_ted *ted, new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_subnet)); new->ls_pref = ls_pref; new->key = ls_pref->pref; + new->status = NEW; + new->type = SUBNET; /* Find Vertex */ vertex = ls_find_vertex_by_id(ted, ls_pref->adv); @@ -541,19 +721,73 @@ struct ls_subnet *ls_subnet_add(struct ls_ted *ted, } /* And attach the subnet to the corresponding Vertex */ new->vertex = vertex; - listnode_add(vertex->prefixes, new); + listnode_add_sort_nodup(vertex->prefixes, new); subnets_add(&ted->subnets, new); return new; } +struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref) +{ + struct ls_subnet *old; + + if (pref == NULL) + return NULL; + + old = ls_find_subnet(ted, pref->pref); + if (old) { + if (!ls_prefix_same(old->ls_pref, pref)) { + ls_prefix_del(old->ls_pref); + old->ls_pref = pref; + } + old->status = UPDATE; + return old; + } + + return ls_subnet_add(ted, pref); +} + +int ls_subnet_same(struct ls_subnet *s1, struct ls_subnet *s2) +{ + if ((s1 && !s2) || (!s1 && s2)) + return 0; + + if (!s1 && !s2) + return 1; + + if (!prefix_same(&s1->key, &s2->key)) + return 0; + + if (s1->ls_pref == s2->ls_pref) + return 1; + + return ls_prefix_same(s1->ls_pref, s2->ls_pref); +} + void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet) { + if (!ted || !subnet) + return; + + /* First, disconnect Subnet from associated Vertex */ + listnode_delete(subnet->vertex->prefixes, subnet); + /* Then delete Subnet */ subnets_del(&ted->subnets, subnet); XFREE(MTYPE_LS_DB, subnet); } +void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet) +{ + if (!ted || !subnet) + return; + + /* First, remove associated Link State Subnet */ + ls_prefix_del(subnet->ls_pref); + /* Then, delete Subnet itself */ + ls_subnet_del(ted, subnet); +} + struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix) { struct ls_subnet subnet = {}; @@ -592,6 +826,11 @@ void ls_ted_del(struct ls_ted *ted) if (ted == NULL) return; + /* Check that TED is empty */ + if (vertices_count(&ted->vertices) || edges_count(&ted->edges) + || subnets_count(&ted->subnets)) + return; + /* Release RB Tree */ vertices_fini(&ted->vertices); edges_fini(&ted->edges); @@ -601,16 +840,63 @@ void ls_ted_del(struct ls_ted *ted) ted = NULL; } +void ls_ted_del_all(struct ls_ted *ted) +{ + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + + if (ted == NULL) + return; + + /* First remove Vertices, Edges and Subnets and associated Link State */ + frr_each (vertices, &ted->vertices, vertex) + ls_vertex_del_all(ted, vertex); + frr_each (edges, &ted->edges, edge) + ls_edge_del_all(ted, edge); + frr_each (subnets, &ted->subnets, subnet) + ls_subnet_del_all(ted, subnet); + + /* then remove TED itself */ + ls_ted_del(ted); +} + +void ls_ted_clean(struct ls_ted *ted) +{ + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + + if (ted == NULL) + return; + + /* First, start with Vertices */ + frr_each (vertices, &ted->vertices, vertex) + if (vertex->status == ORPHAN) + ls_vertex_del_all(ted, vertex); + + /* Then Edges */ + frr_each (edges, &ted->edges, edge) + if (edge->status == ORPHAN) + ls_edge_del_all(ted, edge); + + /* and Subnets */ + frr_each (subnets, &ted->subnets, subnet) + if (subnet->status == ORPHAN) + ls_subnet_del_all(ted, subnet); + +} + void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source) { if (vertex == NULL || edge == NULL) return; if (source) { - listnode_add(vertex->outgoing_edges, edge); + listnode_add_sort_nodup(vertex->outgoing_edges, edge); edge->source = vertex; } else { - listnode_add(vertex->incoming_edges, edge); + listnode_add_sort_nodup(vertex->incoming_edges, edge); edge->destination = vertex; } } @@ -640,11 +926,10 @@ void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, edge->destination = dst; if (src != NULL) - listnode_add(src->outgoing_edges, edge); + listnode_add_sort_nodup(src->outgoing_edges, edge); if (dst != NULL) - listnode_add(dst->incoming_edges, edge); - + listnode_add_sort_nodup(dst->incoming_edges, edge); } void ls_disconnect_edge(struct ls_edge *edge) @@ -654,12 +939,68 @@ void ls_disconnect_edge(struct ls_edge *edge) ls_disconnect(edge->source, edge, true); ls_disconnect(edge->destination, edge, false); + + /* Mark this Edge as ORPHAN for future cleanup */ + edge->status = ORPHAN; } /** * Link State Message management functions */ +int ls_register(struct zclient *zclient, bool server) +{ + int rc; + + if (server) + rc = zclient_register_opaque(zclient, LINK_STATE_SYNC); + else + rc = zclient_register_opaque(zclient, LINK_STATE_UPDATE); + + return rc; +} + +int ls_unregister(struct zclient *zclient, bool server) +{ + int rc; + + if (server) + rc = zclient_unregister_opaque(zclient, LINK_STATE_SYNC); + else + rc = zclient_unregister_opaque(zclient, LINK_STATE_UPDATE); + + return rc; +} + +int ls_request_sync(struct zclient *zclient) +{ + struct stream *s; + uint16_t flags = 0; + + /* Check buffer size */ + if (STREAM_SIZE(zclient->obuf) + < (ZEBRA_HEADER_SIZE + 3 * sizeof(uint32_t))) + return -1; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); + + /* Set type and flags */ + stream_putl(s, LINK_STATE_SYNC); + stream_putw(s, flags); + /* Send destination client info */ + stream_putc(s, zclient->redist_default); + stream_putw(s, zclient->instance); + stream_putl(s, zclient->session_id); + + /* Put length into the header at the start of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + static struct ls_node *ls_parse_node(struct stream *s) { struct ls_node *node; @@ -723,7 +1064,7 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s) STREAM_GET(attr->name, s, len); } if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC)) - STREAM_GETL(s, attr->standard.metric); + STREAM_GETL(s, attr->metric); if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) STREAM_GETL(s, attr->standard.te_metric); if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) @@ -804,7 +1145,7 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s) stream_failure: zlog_err("LS(%s): Could not parse Link State Attributes. Abort!", __func__); - /* Clean memeory allocation */ + /* Clean memory allocation */ if (attr->srlgs != NULL) XFREE(MTYPE_LS_DB, attr->srlgs); XFREE(MTYPE_LS_DB, attr); @@ -947,7 +1288,7 @@ static int ls_format_attributes(struct stream *s, struct ls_attributes *attr) stream_putc(s, '\0'); } if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC)) - stream_putl(s, attr->standard.metric); + stream_putl(s, attr->metric); if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) stream_putl(s, attr->standard.te_metric); if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) @@ -1084,6 +1425,10 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg, struct stream *s; uint16_t flags = 0; + /* Check if we have a valid message */ + if (msg->event == LS_MSG_EVENT_UNDEF) + return -1; + /* Check buffer size */ if (STREAM_SIZE(zclient->obuf) < (ZEBRA_HEADER_SIZE + sizeof(uint32_t) + sizeof(msg))) @@ -1094,7 +1439,7 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg, zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); - /* Send sub-type, flags and destination for unicast message */ + /* Set sub-type, flags and destination for unicast message */ stream_putl(s, LINK_STATE_UPDATE); if (dst != NULL) { SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST); @@ -1103,8 +1448,9 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg, stream_putc(s, dst->proto); stream_putw(s, dst->instance); stream_putl(s, dst->session_id); - } else + } else { stream_putw(s, flags); + } /* Format Link State message */ if (ls_format_msg(s, msg) < 0) { @@ -1128,8 +1474,25 @@ struct ls_message *ls_vertex2msg(struct ls_message *msg, memset(msg, 0, sizeof(*msg)); msg->type = LS_MSG_TYPE_NODE; + switch (vertex->status) { + case NEW: + msg->event = LS_MSG_EVENT_ADD; + break; + case UPDATE: + msg->event = LS_MSG_EVENT_UPDATE; + break; + case DELETE: + msg->event = LS_MSG_EVENT_DELETE; + break; + case SYNC: + msg->event = LS_MSG_EVENT_SYNC; + break; + default: + msg->event = LS_MSG_EVENT_UNDEF; + break; + } msg->data.node = vertex->node; - msg->remote_id.origin = NONE; + msg->remote_id.origin = UNKNOWN; return msg; } @@ -1143,11 +1506,28 @@ struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge) memset(msg, 0, sizeof(*msg)); msg->type = LS_MSG_TYPE_ATTRIBUTES; + switch (edge->status) { + case NEW: + msg->event = LS_MSG_EVENT_ADD; + break; + case UPDATE: + msg->event = LS_MSG_EVENT_UPDATE; + break; + case DELETE: + msg->event = LS_MSG_EVENT_DELETE; + break; + case SYNC: + msg->event = LS_MSG_EVENT_SYNC; + break; + default: + msg->event = LS_MSG_EVENT_UNDEF; + break; + } msg->data.attr = edge->attributes; if (edge->destination != NULL) msg->remote_id = edge->destination->node->adv; else - msg->remote_id.origin = NONE; + msg->remote_id.origin = UNKNOWN; return msg; } @@ -1162,34 +1542,189 @@ struct ls_message *ls_subnet2msg(struct ls_message *msg, memset(msg, 0, sizeof(*msg)); msg->type = LS_MSG_TYPE_PREFIX; + switch (subnet->status) { + case NEW: + msg->event = LS_MSG_EVENT_ADD; + break; + case UPDATE: + msg->event = LS_MSG_EVENT_UPDATE; + break; + case DELETE: + msg->event = LS_MSG_EVENT_DELETE; + break; + case SYNC: + msg->event = LS_MSG_EVENT_SYNC; + break; + default: + msg->event = LS_MSG_EVENT_UNDEF; + break; + } msg->data.prefix = subnet->ls_pref; - msg->remote_id.origin = NONE; + msg->remote_id.origin = UNKNOWN; return msg; } -void ls_delete_msg(struct ls_message *msg) +struct ls_vertex *ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg, + bool delete) { - if (msg == NULL) - return; + struct ls_node *node = (struct ls_node *)msg->data.node; + struct ls_vertex *vertex = NULL; + + switch (msg->event) { + case LS_MSG_EVENT_SYNC: + vertex = ls_vertex_add(ted, node); + if (vertex) + vertex->status = SYNC; + break; + case LS_MSG_EVENT_ADD: + vertex = ls_vertex_add(ted, node); + if (vertex) + vertex->status = NEW; + break; + case LS_MSG_EVENT_UPDATE: + vertex = ls_vertex_update(ted, node); + if (vertex) + vertex->status = UPDATE; + break; + case LS_MSG_EVENT_DELETE: + vertex = ls_find_vertex_by_id(ted, node->adv); + if (vertex) { + if (delete) + ls_vertex_del_all(ted, vertex); + else + vertex->status = DELETE; + } + break; + default: + vertex = NULL; + break; + } + + return vertex; +} + +struct ls_edge *ls_msg2edge(struct ls_ted *ted, struct ls_message *msg, + bool delete) +{ + struct ls_attributes *attr = (struct ls_attributes *)msg->data.attr; + struct ls_edge *edge = NULL; + + switch (msg->event) { + case LS_MSG_EVENT_SYNC: + edge = ls_edge_add(ted, attr); + if (edge) + edge->status = SYNC; + break; + case LS_MSG_EVENT_ADD: + edge = ls_edge_add(ted, attr); + if (edge) + edge->status = NEW; + break; + case LS_MSG_EVENT_UPDATE: + edge = ls_edge_update(ted, attr); + if (edge) + edge->status = UPDATE; + break; + case LS_MSG_EVENT_DELETE: + edge = ls_find_edge_by_source(ted, attr); + if (edge) { + if (delete) + ls_edge_del_all(ted, edge); + else + edge->status = DELETE; + } + break; + default: + edge = NULL; + break; + } + + return edge; +} + +struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg, + bool delete) +{ + struct ls_prefix *pref = (struct ls_prefix *)msg->data.prefix; + struct ls_subnet *subnet = NULL; + + switch (msg->event) { + case LS_MSG_EVENT_SYNC: + subnet = ls_subnet_add(ted, pref); + if (subnet) + subnet->status = SYNC; + break; + case LS_MSG_EVENT_ADD: + subnet = ls_subnet_add(ted, pref); + if (subnet) + subnet->status = NEW; + break; + case LS_MSG_EVENT_UPDATE: + subnet = ls_subnet_update(ted, pref); + if (subnet) + subnet->status = UPDATE; + break; + case LS_MSG_EVENT_DELETE: + subnet = ls_find_subnet(ted, pref->pref); + if (subnet) { + if (delete) + ls_subnet_del_all(ted, subnet); + else + subnet->status = DELETE; + } + break; + default: + subnet = NULL; + break; + } + + return subnet; +} + +struct ls_element *ls_msg2ted(struct ls_ted *ted, struct ls_message *msg, + bool delete) +{ + struct ls_element *lse = NULL; switch (msg->type) { case LS_MSG_TYPE_NODE: - if (msg->data.node) - XFREE(MTYPE_LS_DB, msg->data.node); + lse = (struct ls_element *)ls_msg2vertex(ted, msg, delete); break; case LS_MSG_TYPE_ATTRIBUTES: - if (msg->data.attr) - XFREE(MTYPE_LS_DB, msg->data.attr); + lse = (struct ls_element *)ls_msg2edge(ted, msg, delete); break; case LS_MSG_TYPE_PREFIX: - if (msg->data.prefix) - XFREE(MTYPE_LS_DB, msg->data.prefix); + lse = (struct ls_element *)ls_msg2subnet(ted, msg, delete); break; default: + lse = NULL; break; } + return lse; +} + +struct ls_element *ls_stream2ted(struct ls_ted *ted, struct stream *s, + bool delete) +{ + struct ls_message *msg; + struct ls_element *lse = NULL; + + msg = ls_parse_msg(s); + if (msg) { + lse = ls_msg2ted(ted, msg, delete); + ls_delete_msg(msg); + } + + return lse; +} + +void ls_delete_msg(struct ls_message *msg) +{ + if (msg == NULL) + return; + XFREE(MTYPE_LS_DB, msg); } @@ -1201,9 +1736,6 @@ int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, struct ls_subnet *subnet; struct ls_message msg; - /* Prepare message */ - msg.event = LS_MSG_EVENT_SYNC; - /* Loop TED, start sending Node, then Attributes and finally Prefix */ frr_each(vertices, &ted->vertices, vertex) { ls_vertex2msg(&msg, vertex); @@ -1220,33 +1752,696 @@ int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, return 0; } +/** + * Link State Show functions + */ +static const char *const origin2txt[] = { + "Unknown", + "ISIS_L1", + "ISIS_L2", + "OSPFv2", + "Direct", + "Static" +}; + +static const char *const type2txt[] = { + "Unknown", + "Standard", + "ABR", + "ASBR", + "Remote ASBR", + "Pseudo" +}; + +static const char *const status2txt[] = { + "Unknown", + "New", + "Update", + "Delete", + "Sync", + "Orphan" +}; + +static const char *ls_node_id_to_text(struct ls_node_id lnid, char *str, + size_t size) +{ + if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2) { + uint8_t *id; + + id = lnid.id.iso.sys_id; + snprintfrr(str, size, "%02x%02x.%02x%02x.%02x%02x", id[0], + id[1], id[2], id[3], id[4], id[5]); + } else + snprintfrr(str, size, "%pI4", &lnid.id.ip.addr); + + return str; +} + +static void ls_show_vertex_vty(struct ls_vertex *vertex, struct vty *vty, + bool verbose) +{ + struct listnode *node; + struct ls_node *lsn; + struct ls_edge *edge; + struct ls_subnet *subnet; + struct sbuf sbuf; + uint32_t upper; + + /* Sanity Check */ + if (!vertex) + return; + + lsn = vertex->node; + + sbuf_init(&sbuf, NULL, 0); + + sbuf_push(&sbuf, 2, "Vertex (%" PRIu64 "): %s", vertex->key, lsn->name); + sbuf_push(&sbuf, 0, "\tRouter Id: %pI4", &lsn->router_id); + sbuf_push(&sbuf, 0, "\tOrigin: %s", origin2txt[lsn->adv.origin]); + sbuf_push(&sbuf, 0, "\tStatus: %s\n", status2txt[vertex->status]); + if (!verbose) { + sbuf_push( + &sbuf, 0, + "\t%d Outgoing Edges, %d Incoming Edges, %d Subnets\n", + listcount(vertex->outgoing_edges), + listcount(vertex->incoming_edges), + listcount(vertex->prefixes)); + goto end; + } + + if (CHECK_FLAG(lsn->flags, LS_NODE_TYPE)) + sbuf_push(&sbuf, 4, "Type: %s\n", type2txt[lsn->type]); + if (CHECK_FLAG(lsn->flags, LS_NODE_AS_NUMBER)) + sbuf_push(&sbuf, 4, "AS number: %u\n", lsn->as_number); + if (CHECK_FLAG(lsn->flags, LS_NODE_SR)) { + sbuf_push(&sbuf, 4, "Segment Routing Capabilities:\n"); + upper = lsn->srgb.lower_bound + lsn->srgb.range_size - 1; + sbuf_push(&sbuf, 8, "SRGB: [%d/%d]", lsn->srgb.lower_bound, + upper); + if (CHECK_FLAG(lsn->flags, LS_NODE_SRLB)) { + upper = lsn->srlb.lower_bound + lsn->srlb.range_size + - 1; + sbuf_push(&sbuf, 0, "\tSRLB: [%d/%d]", + lsn->srlb.lower_bound, upper); + } + sbuf_push(&sbuf, 0, "\tAlgo: "); + for (int i = 0; i < 2; i++) { + if (lsn->algo[i] == 255) + continue; + + sbuf_push(&sbuf, 0, + lsn->algo[i] == 0 ? "SPF " : "S-SPF "); + } + if (CHECK_FLAG(lsn->flags, LS_NODE_MSD)) + sbuf_push(&sbuf, 0, "\tMSD: %d", lsn->msd); + sbuf_push(&sbuf, 0, "\n"); + } + + sbuf_push(&sbuf, 4, "Outgoing Edges: %d\n", + listcount(vertex->outgoing_edges)); + for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) { + if (edge->destination) { + lsn = edge->destination->node; + sbuf_push(&sbuf, 6, "To:\t%s(%pI4)", lsn->name, + &lsn->router_id); + } else { + sbuf_push(&sbuf, 6, "To:\t- (0.0.0.0)"); + } + sbuf_push(&sbuf, 0, "\tLocal: %pI4\tRemote: %pI4\n", + &edge->attributes->standard.local, + &edge->attributes->standard.remote); + } + + sbuf_push(&sbuf, 4, "Incoming Edges: %d\n", + listcount(vertex->incoming_edges)); + for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, node, edge)) { + if (edge->source) { + lsn = edge->source->node; + sbuf_push(&sbuf, 6, "From:\t%s(%pI4)", lsn->name, + &lsn->router_id); + } else { + sbuf_push(&sbuf, 6, "From:\t- (0.0.0.0)"); + } + sbuf_push(&sbuf, 0, "\tRemote: %pI4\tLocal: %pI4\n", + &edge->attributes->standard.local, + &edge->attributes->standard.remote); + } + + sbuf_push(&sbuf, 4, "Subnets: %d\n", listcount(vertex->prefixes)); + for (ALL_LIST_ELEMENTS_RO(vertex->prefixes, node, subnet)) + sbuf_push(&sbuf, 6, "Prefix:\t%pFX\n", &subnet->key); + +end: + vty_out(vty, "%s\n", sbuf_buf(&sbuf)); + sbuf_free(&sbuf); +} + +static void ls_show_vertex_json(struct ls_vertex *vertex, + struct json_object *json) +{ + struct ls_node *lsn; + json_object *jsr, *jalgo, *jobj; + char buf[INET6_BUFSIZ]; + + /* Sanity Check */ + if (!vertex) + return; + + lsn = vertex->node; + + json_object_int_add(json, "vertex-id", vertex->key); + json_object_string_add(json, "status", status2txt[vertex->status]); + json_object_string_add(json, "origin", origin2txt[lsn->adv.origin]); + if (CHECK_FLAG(lsn->flags, LS_NODE_NAME)) + json_object_string_add(json, "name", lsn->name); + if (CHECK_FLAG(lsn->flags, LS_NODE_ROUTER_ID)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI4", &lsn->router_id); + json_object_string_add(json, "router-id", buf); + } + if (CHECK_FLAG(lsn->flags, LS_NODE_ROUTER_ID6)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI6", &lsn->router6_id); + json_object_string_add(json, "router-id-v6", buf); + } + if (CHECK_FLAG(lsn->flags, LS_NODE_TYPE)) + json_object_string_add(json, "vertex-type", + type2txt[lsn->type]); + if (CHECK_FLAG(lsn->flags, LS_NODE_AS_NUMBER)) + json_object_int_add(json, "asn", lsn->as_number); + if (CHECK_FLAG(lsn->flags, LS_NODE_SR)) { + jsr = json_object_new_object(); + json_object_object_add(json, "segment-routing", jsr); + json_object_int_add(jsr, "srgb-size", lsn->srgb.range_size); + json_object_int_add(jsr, "srgb-lower", lsn->srgb.lower_bound); + jalgo = json_object_new_array(); + json_object_object_add(jsr, "algorithms", jalgo); + for (int i = 0; i < 2; i++) { + if (lsn->algo[i] == 255) + continue; + jobj = json_object_new_object(); + + snprintfrr(buf, 2, "%u", i); + json_object_string_add( + jobj, buf, lsn->algo[i] == 0 ? "SPF" : "S-SPF"); + json_object_array_add(jalgo, jobj); + } + if (CHECK_FLAG(lsn->flags, LS_NODE_SRLB)) { + json_object_int_add(jsr, "srlb-size", + lsn->srlb.range_size); + json_object_int_add(jsr, "srlb-lower", + lsn->srlb.lower_bound); + } + if (CHECK_FLAG(lsn->flags, LS_NODE_MSD)) + json_object_int_add(jsr, "msd", lsn->msd); + } +} + +void ls_show_vertex(struct ls_vertex *vertex, struct vty *vty, + struct json_object *json, bool verbose) +{ + if (json) + ls_show_vertex_json(vertex, json); + else if (vty) + ls_show_vertex_vty(vertex, vty, verbose); +} + +void ls_show_vertices(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose) +{ + struct ls_vertex *vertex; + json_object *jnodes, *jnode; + + if (json) { + jnodes = json_object_new_array(); + json_object_object_add(json, "vertices", jnodes); + frr_each (vertices, &ted->vertices, vertex) { + jnode = json_object_new_object(); + ls_show_vertex(vertex, NULL, jnode, verbose); + json_object_array_add(jnodes, jnode); + } + } else if (vty) { + frr_each (vertices, &ted->vertices, vertex) + ls_show_vertex(vertex, vty, NULL, verbose); + } +} + +static void ls_show_edge_vty(struct ls_edge *edge, struct vty *vty, + bool verbose) +{ + struct ls_attributes *attr; + struct sbuf sbuf; + char buf[INET6_BUFSIZ]; + + attr = edge->attributes; + sbuf_init(&sbuf, NULL, 0); + + sbuf_push(&sbuf, 2, "Edge (%" PRIu64 "): ", edge->key); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) + sbuf_push(&sbuf, 0, "%pI4", &attr->standard.local); + else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) + sbuf_push(&sbuf, 0, "%pI6", &attr->standard.local6); + else + sbuf_push(&sbuf, 0, "%u/%u", attr->standard.local_id, + attr->standard.remote_id); + ls_node_id_to_text(attr->adv, buf, INET6_BUFSIZ); + sbuf_push(&sbuf, 0, "\tAdv. Vertex: %s", buf); + sbuf_push(&sbuf, 0, "\tMetric: %u", attr->metric); + sbuf_push(&sbuf, 0, "\tStatus: %s\n", status2txt[edge->status]); + + if (!verbose) + goto end; + + sbuf_push(&sbuf, 4, "Origin: %s\n", origin2txt[attr->adv.origin]); + if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) + sbuf_push(&sbuf, 4, "Name: %s\n", attr->name); + if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) + sbuf_push(&sbuf, 4, "TE Metric: %u\n", + attr->standard.te_metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) + sbuf_push(&sbuf, 4, "Admin Group: 0x%x\n", + attr->standard.admin_group); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) + sbuf_push(&sbuf, 4, "Local IPv4 address: %pI4\n", + &attr->standard.local); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) + sbuf_push(&sbuf, 4, "Remote IPv4 address: %pI4\n", + &attr->standard.remote); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) + sbuf_push(&sbuf, 4, "Local IPv6 address: %pI6\n", + &attr->standard.local6); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) + sbuf_push(&sbuf, 4, "Remote IPv6 address: %pI6\n", + &attr->standard.remote6); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) + sbuf_push(&sbuf, 4, "Local Identifier: %u\n", + attr->standard.local_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) + sbuf_push(&sbuf, 4, "Remote Identifier: %u\n", + attr->standard.remote_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW)) + sbuf_push(&sbuf, 4, "Maximum Bandwidth: %g (Bytes/s)\n", + attr->standard.max_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW)) + sbuf_push(&sbuf, 4, + "Maximum Reservable Bandwidth: %g (Bytes/s)\n", + attr->standard.max_rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW)) { + sbuf_push(&sbuf, 4, "Unreserved Bandwidth per Class Type\n"); + for (int i = 0; i < MAX_CLASS_TYPE; i += 2) + sbuf_push(&sbuf, 8, + "[%d]: %g (Bytes/sec)\t[%d]: %g (Bytes/s)\n", + i, attr->standard.unrsv_bw[i], i + 1, + attr->standard.unrsv_bw[i + 1]); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS)) + sbuf_push(&sbuf, 4, "Remote AS: %u\n", + attr->standard.remote_as); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR)) + sbuf_push(&sbuf, 4, "Remote ASBR IPv4 address: %pI4\n", + &attr->standard.remote_addr); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6)) + sbuf_push(&sbuf, 4, "Remote ASBR IPv6 address: %pI6\n", + &attr->standard.remote_addr6); + if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY)) + sbuf_push(&sbuf, 4, "Average Link Delay: %d (micro-sec)\n", + attr->extended.delay); + if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) + sbuf_push(&sbuf, 4, "Min/Max Link Delay: %d/%d (micro-sec)\n", + attr->extended.min_delay, attr->extended.max_delay); + if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER)) + sbuf_push(&sbuf, 4, "Delay Variation: %d (micro-sec)\n", + attr->extended.jitter); + if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS)) + sbuf_push(&sbuf, 4, "Link Loss: %g (%%)\n", + (float)(attr->extended.pkt_loss * LOSS_PRECISION)); + if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW)) + sbuf_push(&sbuf, 4, "Available Bandwidth: %g (Bytes/s)\n", + attr->extended.ava_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW)) + sbuf_push(&sbuf, 4, "Residual Bandwidth: %g (Bytes/s)\n", + attr->extended.rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW)) + sbuf_push(&sbuf, 4, "Utilized Bandwidth: %g (Bytes/s)\n", + attr->extended.used_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) { + sbuf_push(&sbuf, 4, "Adjacency-SID: %u", attr->adj_sid[0].sid); + sbuf_push(&sbuf, 0, "\tFlags: 0x%x\tWeight: 0x%x\n", + attr->adj_sid[0].flags, attr->adj_sid[0].weight); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) { + sbuf_push(&sbuf, 4, "Bck. Adjacency-SID: %u", + attr->adj_sid[1].sid); + sbuf_push(&sbuf, 0, "\tFlags: 0x%x\tWeight: 0x%x\n", + attr->adj_sid[1].flags, attr->adj_sid[1].weight); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { + sbuf_push(&sbuf, 4, "SRLGs: %d", attr->srlg_len); + for (int i = 1; i < attr->srlg_len; i++) { + if (i % 8) + sbuf_push(&sbuf, 8, "\n%u", attr->srlgs[i]); + else + sbuf_push(&sbuf, 8, ", %u", attr->srlgs[i]); + } + sbuf_push(&sbuf, 0, "\n"); + } + +end: + vty_out(vty, "%s\n", sbuf_buf(&sbuf)); + sbuf_free(&sbuf); +} + +static void ls_show_edge_json(struct ls_edge *edge, struct json_object *json) +{ + struct ls_attributes *attr; + struct json_object *jte, *jbw, *jobj, *jsr = NULL, *jsrlg; + char buf[INET6_BUFSIZ]; + + attr = edge->attributes; + + json_object_int_add(json, "edge-id", edge->key); + json_object_string_add(json, "status", status2txt[edge->status]); + json_object_string_add(json, "origin", origin2txt[attr->adv.origin]); + ls_node_id_to_text(attr->adv, buf, INET6_BUFSIZ); + json_object_string_add(json, "advertised-router", buf); + if (edge->source) + json_object_int_add(json, "local-vertex-id", edge->source->key); + if (edge->destination) + json_object_int_add(json, "remote-vertex-id", + edge->destination->key); + json_object_int_add(json, "metric", attr->metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) + json_object_string_add(json, "name", attr->name); + jte = json_object_new_object(); + json_object_object_add(json, "edge-attributes", jte); + if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) + json_object_int_add(jte, "te-metric", attr->standard.te_metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) + json_object_int_add(jte, "admin-group", + attr->standard.admin_group); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI4", &attr->standard.local); + json_object_string_add(jte, "local-address", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI4", &attr->standard.remote); + json_object_string_add(jte, "remote-address", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI6", &attr->standard.local6); + json_object_string_add(jte, "local-address-v6", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI6", &attr->standard.remote6); + json_object_string_add(jte, "remote-address-v6", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) + json_object_int_add(jte, "local-identifier", + attr->standard.local_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) + json_object_int_add(jte, "remote-identifier", + attr->standard.remote_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW)) + json_object_double_add(jte, "max-link-bandwidth", + attr->standard.max_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW)) + json_object_double_add(jte, "max-resv-link-bandwidth", + attr->standard.max_rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW)) { + jbw = json_object_new_array(); + json_object_object_add(jte, "unreserved-bandwidth", jbw); + for (int i = 0; i < MAX_CLASS_TYPE; i++) { + jobj = json_object_new_object(); + snprintfrr(buf, 13, "class-type-%u", i); + json_object_double_add(jobj, buf, + attr->standard.unrsv_bw[i]); + json_object_array_add(jbw, jobj); + } + } + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS)) + json_object_int_add(jte, "remote-asn", + attr->standard.remote_as); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI4", + &attr->standard.remote_addr); + json_object_string_add(jte, "remote-as-address", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI6", + &attr->standard.remote_addr6); + json_object_string_add(jte, "remote-as-address-v6", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY)) + json_object_int_add(jte, "delay", attr->extended.delay); + if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) { + json_object_int_add(jte, "min-delay", attr->extended.min_delay); + json_object_int_add(jte, "max-delay", attr->extended.max_delay); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER)) + json_object_int_add(jte, "jitter", attr->extended.jitter); + if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS)) + json_object_double_add( + jte, "loss", attr->extended.pkt_loss * LOSS_PRECISION); + if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW)) + json_object_double_add(jte, "available-bandwidth", + attr->extended.ava_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW)) + json_object_double_add(jte, "residual-bandwidth", + attr->extended.rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW)) + json_object_double_add(jte, "utilized-bandwidth", + attr->extended.used_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { + jsrlg = json_object_new_array(); + json_object_object_add(jte, "srlgs", jsrlg); + for (int i = 1; i < attr->srlg_len; i++) { + jobj = json_object_new_object(); + json_object_int_add(jobj, "srlg", attr->srlgs[i]); + json_object_array_add(jsrlg, jobj); + } + } + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) { + jsr = json_object_new_array(); + json_object_object_add(json, "segment-routing", jsr); + jobj = json_object_new_object(); + json_object_int_add(jobj, "adj-sid", attr->adj_sid[0].sid); + snprintfrr(buf, 6, "0x%x", attr->adj_sid[0].flags); + json_object_string_add(jobj, "flags", buf); + json_object_int_add(jobj, "weight", attr->adj_sid[0].weight); + json_object_array_add(jsr, jobj); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) { + if (!jsr) { + jsr = json_object_new_array(); + json_object_object_add(json, "segment-routing", jsr); + } + jobj = json_object_new_object(); + json_object_int_add(jobj, "adj-sid", attr->adj_sid[1].sid); + snprintfrr(buf, 6, "0x%x", attr->adj_sid[1].flags); + json_object_string_add(jobj, "flags", buf); + json_object_int_add(jobj, "weight", attr->adj_sid[1].weight); + json_object_array_add(jsr, jobj); + } +} + +void ls_show_edge(struct ls_edge *edge, struct vty *vty, + struct json_object *json, bool verbose) +{ + /* Sanity Check */ + if (!edge) + return; + + if (json) + ls_show_edge_json(edge, json); + else if (vty) + ls_show_edge_vty(edge, vty, verbose); +} + +void ls_show_edges(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose) +{ + struct ls_edge *edge; + json_object *jedges, *jedge; + + if (json) { + jedges = json_object_new_array(); + json_object_object_add(json, "edges", jedges); + frr_each (edges, &ted->edges, edge) { + jedge = json_object_new_object(); + ls_show_edge(edge, NULL, jedge, verbose); + json_object_array_add(jedges, jedge); + } + } else if (vty) { + frr_each (edges, &ted->edges, edge) + ls_show_edge(edge, vty, NULL, verbose); + } +} + +static void ls_show_subnet_vty(struct ls_subnet *subnet, struct vty *vty, + bool verbose) +{ + struct ls_prefix *pref; + struct sbuf sbuf; + char buf[INET6_BUFSIZ]; + + pref = subnet->ls_pref; + sbuf_init(&sbuf, NULL, 0); + + sbuf_push(&sbuf, 2, "Subnet: %pFX", &subnet->key); + ls_node_id_to_text(pref->adv, buf, INET6_BUFSIZ); + sbuf_push(&sbuf, 0, "\tAdv. Vertex: %s", buf); + sbuf_push(&sbuf, 0, "\tMetric: %d", pref->metric); + sbuf_push(&sbuf, 0, "\tStatus: %s\n", status2txt[subnet->status]); + + if (!verbose) + goto end; + + sbuf_push(&sbuf, 4, "Origin: %s\n", origin2txt[pref->adv.origin]); + if (CHECK_FLAG(pref->flags, LS_PREF_IGP_FLAG)) + sbuf_push(&sbuf, 4, "Flags: %d\n", pref->igp_flag); + + if (CHECK_FLAG(pref->flags, LS_PREF_ROUTE_TAG)) + sbuf_push(&sbuf, 4, "Tag: %d\n", pref->route_tag); + + if (CHECK_FLAG(pref->flags, LS_PREF_EXTENDED_TAG)) + sbuf_push(&sbuf, 4, "Extended Tag: %" PRIu64 "\n", + pref->extended_tag); + + if (CHECK_FLAG(pref->flags, LS_PREF_SR)) + sbuf_push(&sbuf, 4, "SID: %d\tAlgorithm: %d\tFlags: 0x%x\n", + pref->sr.sid, pref->sr.algo, pref->sr.sid_flag); + +end: + vty_out(vty, "%s\n", sbuf_buf(&sbuf)); + sbuf_free(&sbuf); +} + +static void ls_show_subnet_json(struct ls_subnet *subnet, + struct json_object *json) +{ + struct ls_prefix *pref; + json_object *jsr; + char buf[INET6_BUFSIZ]; + + pref = subnet->ls_pref; + + snprintfrr(buf, INET6_BUFSIZ, "%pFX", &subnet->key); + json_object_string_add(json, "subnet-id", buf); + json_object_string_add(json, "status", status2txt[subnet->status]); + json_object_string_add(json, "origin", origin2txt[pref->adv.origin]); + ls_node_id_to_text(pref->adv, buf, INET6_BUFSIZ); + json_object_string_add(json, "advertised-router", buf); + if (subnet->vertex) + json_object_int_add(json, "vertex-id", subnet->vertex->key); + json_object_int_add(json, "metric", pref->metric); + if (CHECK_FLAG(pref->flags, LS_PREF_IGP_FLAG)) { + snprintfrr(buf, INET6_BUFSIZ, "0x%x", pref->igp_flag); + json_object_string_add(json, "flags", buf); + } + if (CHECK_FLAG(pref->flags, LS_PREF_ROUTE_TAG)) + json_object_int_add(json, "tag", pref->route_tag); + if (CHECK_FLAG(pref->flags, LS_PREF_EXTENDED_TAG)) + json_object_int_add(json, "extended-tag", pref->extended_tag); + if (CHECK_FLAG(pref->flags, LS_PREF_SR)) { + jsr = json_object_new_object(); + json_object_object_add(json, "segment-routing", jsr); + json_object_int_add(jsr, "pref-sid", pref->sr.sid); + json_object_int_add(jsr, "algo", pref->sr.algo); + snprintfrr(buf, INET6_BUFSIZ, "0x%x", pref->sr.sid_flag); + json_object_string_add(jsr, "flags", buf); + } +} + +void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty, + struct json_object *json, bool verbose) +{ + /* Sanity Check */ + if (!subnet) + return; + + if (json) + ls_show_subnet_json(subnet, json); + else if (vty) + ls_show_subnet_vty(subnet, vty, verbose); +} + +void ls_show_subnets(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose) +{ + struct ls_subnet *subnet; + json_object *jsubs, *jsub; + + if (json) { + jsubs = json_object_new_array(); + json_object_object_add(json, "subnets", jsubs); + frr_each (subnets, &ted->subnets, subnet) { + jsub = json_object_new_object(); + ls_show_subnet(subnet, NULL, jsub, verbose); + json_object_array_add(jsubs, jsub); + } + } else if (vty) { + frr_each (subnets, &ted->subnets, subnet) + ls_show_subnet(subnet, vty, NULL, verbose); + } +} + +void ls_show_ted(struct ls_ted *ted, struct vty *vty, struct json_object *json, + bool verbose) +{ + json_object *jted; + + if (json) { + jted = json_object_new_object(); + json_object_object_add(json, "ted", jted); + json_object_string_add(jted, "name", ted->name); + json_object_int_add(jted, "key", ted->key); + json_object_int_add(jted, "verticesCount", + vertices_count(&ted->vertices)); + json_object_int_add(jted, "edgesCount", + edges_count(&ted->edges)); + json_object_int_add(jted, "subnetsCount", + subnets_count(&ted->subnets)); + ls_show_vertices(ted, NULL, jted, verbose); + ls_show_edges(ted, NULL, jted, verbose); + ls_show_subnets(ted, NULL, jted, verbose); + return; + } + + if (vty) { + vty_out(vty, + "\n\tTraffic Engineering Database: %s (key: %d)\n\n", + ted->name, ted->key); + ls_show_vertices(ted, vty, NULL, verbose); + ls_show_edges(ted, vty, NULL, verbose); + ls_show_subnets(ted, vty, NULL, verbose); + vty_out(vty, + "\n\tTotal: %zu Vertices, %zu Edges, %zu Subnets\n\n", + vertices_count(&ted->vertices), + edges_count(&ted->edges), subnets_count(&ted->subnets)); + } +} + void ls_dump_ted(struct ls_ted *ted) { struct ls_vertex *vertex; struct ls_edge *edge; struct ls_subnet *subnet; - struct ls_message msg; + const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; zlog_debug("(%s) Ted init", __func__); - /* Prepare message */ - msg.event = LS_MSG_EVENT_SYNC; /* Loop TED, start printing Node, then Attributes and finally Prefix */ - frr_each(vertices, &ted->vertices, vertex) { - ls_vertex2msg(&msg, vertex); + frr_each (vertices, &ted->vertices, vertex) { zlog_debug(" Ted node (%s %pI4 %s)", vertex->node->name[0] ? vertex->node->name : "no name node", &vertex->node->router_id, - vertex->node->adv.origin == DIRECT ? "DIRECT" - : "NO DIRECT"); + origin2txt[vertex->node->adv.origin]); struct listnode *lst_node; struct ls_edge *vertex_edge; for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node, vertex_edge)) { zlog_debug( - " inc edge key:%"PRIu64"n attr key:%pI4 loc:(%pI4) rmt:(%pI4)", + " inc edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)", vertex_edge->key, &vertex_edge->attributes->adv.id.ip.addr, &vertex_edge->attributes->standard.local, @@ -1255,29 +2450,25 @@ void ls_dump_ted(struct ls_ted *ted) for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node, vertex_edge)) { zlog_debug( - " out edge key:%"PRIu64" attr key:%pI4 loc:(%pI4) rmt:(%pI4)", + " out edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)", vertex_edge->key, &vertex_edge->attributes->adv.id.ip.addr, &vertex_edge->attributes->standard.local, &vertex_edge->attributes->standard.remote); } } - frr_each(edges, &ted->edges, edge) { - ls_edge2msg(&msg, edge); - zlog_debug(" Ted edge key:%"PRIu64" src:%s dst:%s", - edge->key, - edge->source ? edge->source->node->name - : "no_source", - edge->destination ? edge->destination->node->name - : "no_dest"); + frr_each (edges, &ted->edges, edge) { + zlog_debug(" Ted edge key:%" PRIu64 "src:%pI4 dst:%pI4", edge->key, + edge->source ? &edge->source->node->router_id + : &inaddr_any, + edge->destination + ? &edge->destination->node->router_id + : &inaddr_any); } - frr_each(subnets, &ted->subnets, subnet) { - ls_subnet2msg(&msg, subnet); - zlog_debug( - " Ted subnet key:%pFX vertex:%pI4 pfx:%pFX", - &subnet->key, - &subnet->vertex->node->adv.id.ip.addr, - &subnet->ls_pref->pref); + frr_each (subnets, &ted->subnets, subnet) { + zlog_debug(" Ted subnet key:%pFX vertex:%pI4", + &subnet->ls_pref->pref, + &subnet->vertex->node->adv.id.ip.addr); } zlog_debug("(%s) Ted end", __func__); } diff --git a/lib/link_state.h b/lib/link_state.h index f9eb59b76a..de116df89e 100644 --- a/lib/link_state.h +++ b/lib/link_state.h @@ -53,20 +53,26 @@ extern "C" { * id for OSPF and the ISO System id plus the IS-IS level for IS-IS. */ +/* external reference */ +struct zapi_opaque_reg_info; +struct zclient; + /* Link State Common definitions */ #define MAX_NAME_LENGTH 256 #define ISO_SYS_ID_LEN 6 /* Type of Node */ enum ls_node_type { + NONE = 0, /* Unknown */ STANDARD, /* a P or PE node */ ABR, /* an Array Border Node */ ASBR, /* an Autonomous System Border Node */ - PSEUDO, /* a Pseudo Node */ + RMT_ASBR, /* Remote ASBR */ + PSEUDO /* a Pseudo Node */ }; /* Origin of the Link State information */ -enum ls_origin {NONE = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC}; +enum ls_origin { UNKNOWN = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC }; /** * Link State Node Identifier as: @@ -108,19 +114,17 @@ struct ls_node { struct in_addr router_id; /* IPv4 Router ID */ struct in6_addr router6_id; /* IPv6 Router ID */ uint8_t node_flag; /* IS-IS or OSPF Node flag */ - enum node_type type; /* Type of Node */ + enum ls_node_type type; /* Type of Node */ uint32_t as_number; /* Local or neighbor AS number */ - struct { /* Segment Routing Global Block */ + struct ls_srgb { /* Segment Routing Global Block */ uint32_t lower_bound; /* MPLS label lower bound */ uint32_t range_size; /* MPLS label range size */ uint8_t flag; /* IS-IS SRGB flags */ } srgb; -#define LS_NODE_SRGB_SIZE 9 - struct { /* Segment Routing Local Block */ + struct ls_srlb { /* Segment Routing Local Block */ uint32_t lower_bound; /* MPLS label lower bound */ uint32_t range_size; /* MPLS label range size */ } srlb; -#define LS_NODE_SRLB_SIZE 8 uint8_t algo[2]; /* Segment Routing Algorithms */ uint8_t msd; /* Maximum Stack Depth */ }; @@ -150,17 +154,17 @@ struct ls_node { #define LS_ATTR_AVA_BW 0x00100000 #define LS_ATTR_RSV_BW 0x00200000 #define LS_ATTR_USE_BW 0x00400000 -#define LS_ATTR_ADJ_SID 0x00800000 -#define LS_ATTR_BCK_ADJ_SID 0x01000000 -#define LS_ATTR_SRLG 0x02000000 +#define LS_ATTR_ADJ_SID 0x01000000 +#define LS_ATTR_BCK_ADJ_SID 0x02000000 +#define LS_ATTR_SRLG 0x10000000 /* Link State Attributes */ struct ls_attributes { uint32_t flags; /* Flag for parameters validity */ struct ls_node_id adv; /* Adv. Router of this Link State */ char name[MAX_NAME_LENGTH]; /* Name of the Edge. Could be null */ - struct { /* Standard TE metrics */ - uint32_t metric; /* IGP standard metric */ + uint32_t metric; /* IGP standard metric */ + struct ls_standard { /* Standard TE metrics */ uint32_t te_metric; /* Traffic Engineering metric */ uint32_t admin_group; /* Administrative Group */ struct in_addr local; /* Local IPv4 address */ @@ -176,8 +180,7 @@ struct ls_attributes { struct in_addr remote_addr; /* Remote IPv4 address */ struct in6_addr remote_addr6; /* Remote IPv6 address */ } standard; -#define LS_ATTR_STANDARD_SIZE 124 - struct { /* Extended TE Metrics */ + struct ls_extended { /* Extended TE Metrics */ uint32_t delay; /* Unidirectional average delay */ uint32_t min_delay; /* Unidirectional minimum delay */ uint32_t max_delay; /* Unidirectional maximum delay */ @@ -187,8 +190,7 @@ struct ls_attributes { float rsv_bw; /* Reserved Bandwidth */ float used_bw; /* Utilized Bandwidth */ } extended; -#define LS_ATTR_EXTENDED_SIZE 32 - struct { /* (LAN)-Adjacency SID for OSPF */ + struct ls_adjacency { /* (LAN)-Adjacency SID for OSPF */ uint32_t sid; /* SID as MPLS label or index */ uint8_t flags; /* Flags */ uint8_t weight; /* Administrative weight */ @@ -197,7 +199,6 @@ struct ls_attributes { uint8_t sysid[ISO_SYS_ID_LEN]; /* or Sys-ID for ISIS */ } neighbor; } adj_sid[2]; /* Primary & Backup (LAN)-Adj. SID */ -#define LS_ATTR_ADJ_SID_SIZE 120 uint32_t *srlgs; /* List of Shared Risk Link Group */ uint8_t srlg_len; /* number of SRLG in the list */ }; @@ -219,7 +220,7 @@ struct ls_prefix { uint32_t route_tag; /* IGP Route Tag */ uint64_t extended_tag; /* IGP Extended Route Tag */ uint32_t metric; /* Route metric for this prefix */ - struct { + struct ls_sid { uint32_t sid; /* Segment Routing ID */ uint8_t sid_flag; /* Segment Routing Flags */ uint8_t algo; /* Algorithm for Segment Routing */ @@ -273,9 +274,16 @@ extern struct ls_attributes *ls_attributes_new(struct ls_node_id adv, uint32_t local_id); /** + * Remove SRLGs from Link State Attributes if defined. + * + * @param attr Pointer to a valid Link State Attribute structure + */ +extern void ls_attributes_srlg_del(struct ls_attributes *attr); + +/** * Remove Link State Attributes. Data structure is freed. * - * @param attr Pointer to a valid Link State Attribute structure + * @param attr Pointer to a valid Link State Attribute structure */ extern void ls_attributes_del(struct ls_attributes *attr); @@ -292,6 +300,34 @@ extern int ls_attributes_same(struct ls_attributes *a1, struct ls_attributes *a2); /** + * Create a new Link State Prefix. Structure is dynamically allocated. + * + * @param adv Mandatory Link State Node ID i.e. advertise router ID + * @param p Mandatory Prefix + * + * @return New Link State Prefix + */ +extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p); + +/** + * Remove Link State Prefix. Data Structure is freed. + * + * @param pref Pointer to a valid Link State Attribute Prefix. + */ +extern void ls_prefix_del(struct ls_prefix *pref); + +/** + * Check if two Link State Prefix are equal. Note that this routine has the + * same return value sense as '==' (which is different from a comparison). + * + * @param p1 First Link State Prefix to be compare + * @param p2 Second Link State Prefix to be compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2); + +/** * In addition a Graph model is defined as an overlay on top of link state * database in order to ease Path Computation algorithm implementation. * Denoted G(V, E), a graph is composed by a list of Vertices (V) which @@ -323,9 +359,14 @@ extern int ls_attributes_same(struct ls_attributes *a1, * */ +enum ls_status { UNSET = 0, NEW, UPDATE, DELETE, SYNC, ORPHAN }; +enum ls_type { GENERIC = 0, VERTEX, EDGE, SUBNET }; + /* Link State Vertex structure */ PREDECL_RBTREE_UNIQ(vertices); struct ls_vertex { + enum ls_type type; /* Link State Type */ + enum ls_status status; /* Status of the Vertex in the TED */ struct vertices_item entry; /* Entry in RB Tree */ uint64_t key; /* Unique Key identifier */ struct ls_node *node; /* Link State Node */ @@ -337,6 +378,8 @@ struct ls_vertex { /* Link State Edge structure */ PREDECL_RBTREE_UNIQ(edges); struct ls_edge { + enum ls_type type; /* Link State Type */ + enum ls_status status; /* Status of the Edge in the TED */ struct edges_item entry; /* Entry in RB tree */ uint64_t key; /* Unique Key identifier */ struct ls_attributes *attributes; /* Link State attributes */ @@ -347,10 +390,12 @@ struct ls_edge { /* Link State Subnet structure */ PREDECL_RBTREE_UNIQ(subnets); struct ls_subnet { + enum ls_type type; /* Link State Type */ + enum ls_status status; /* Status of the Subnet in the TED */ struct subnets_item entry; /* Entry in RB tree */ struct prefix key; /* Unique Key identifier */ - struct ls_vertex *vertex; /* Back pointer to the Vertex owner */ struct ls_prefix *ls_pref; /* Link State Prefix */ + struct ls_vertex *vertex; /* Back pointer to the Vertex owner */ }; /* Declaration of Vertices, Edges and Prefixes RB Trees */ @@ -386,37 +431,45 @@ struct ls_ted { struct subnets_head subnets; /* List of Subnets */ }; +/* Generic Link State Element */ +struct ls_element { + enum ls_type type; /* Link State Element Type */ + enum ls_status status; /* Link State Status in the TED */ + void *data; /* Link State payload */ +}; + /** - * Create a new Link State Vertex structure and initialize is with the Link - * State Node parameter. + * Add new vertex to the Link State DB. Vertex is created from the Link State + * Node. Vertex data structure is dynamically allocated. * + * @param ted Traffic Engineering Database structure * @param node Link State Node * - * @return New Vertex + * @return New Vertex or NULL in case of error */ -extern struct ls_vertex *ls_vertex_new(struct ls_node *node); +extern struct ls_vertex *ls_vertex_add(struct ls_ted *ted, + struct ls_node *node); /** * Delete Link State Vertex. This function clean internal Vertex lists (incoming - * and outgoing Link State Edge and Link State Subnet). Note that referenced - * objects of the different lists (Edges & SubNet) are not removed as they could - * be connected to other Vertices. + * and outgoing Link State Edge and Link State Subnet). Vertex Data structure + * is freed but not the Link State Node. Link State DB is not modified if Vertex + * is NULL or not found in the Data Base. Note that referenced to Link State + * Edges & SubNets are not removed as they could be connected to other Vertices. * + * @param ted Traffic Engineering Database structure * @param vertex Link State Vertex to be removed */ -extern void ls_vertex_del(struct ls_vertex *vertex); +extern void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex); /** - * Add new vertex to the Link State DB. Vertex is created from the Link State - * Node. Vertex data structure is dynamically allocated. + * Delete Link State Vertex as ls_vertex_del() but also removed associated + * Link State Node. * - * @param ted Traffic Engineering Database structure - * @param node Link State Node - * - * @return New Vertex or NULL in case of error + * @param ted Traffic Engineering Database structure + * @param vertex Link State Vertex to be removed */ -extern struct ls_vertex *ls_vertex_add(struct ls_ted *ted, - struct ls_node *node); +extern void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex); /** * Update Vertex with the Link State Node. A new vertex is created if no one @@ -431,15 +484,15 @@ extern struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node); /** - * Remove Vertex from the Link State DB. Vertex Data structure is freed but - * not the Link State Node. Link State DB is not modified if Vertex is NULL or - * not found in the Data Base. + * Clean Vertex structure by removing all Edges and Subnets marked as ORPHAN + * from this vertex. Link State Update message is sent if zclient is not NULL. * * @param ted Link State Data Base - * @param vertex Vertex to be removed + * @param vertex Link State Vertex to be cleaned + * @param zclient Reference to Zebra Client */ -extern void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex); - +extern void ls_vertex_clean(struct ls_ted *ted, struct ls_vertex *vertex, + struct zclient *zclient); /** * Find Vertex in the Link State DB by its unique key. * @@ -498,6 +551,17 @@ extern struct ls_edge *ls_edge_update(struct ls_ted *ted, struct ls_attributes *attributes); /** + * Check if two Edges are equal. Note that this routine has the same return + * value sense as '==' (which is different from a comparison). + * + * @param e1 First edge to compare + * @param e2 Second edge to compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2); + +/** * Remove Edge from the Link State DB. Edge data structure is freed but not the * Link State Attributes data structure. Link State DB is not modified if Edge * is NULL or not found in the Data Base. @@ -508,6 +572,15 @@ extern struct ls_edge *ls_edge_update(struct ls_ted *ted, extern void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge); /** + * Remove Edge and associated Link State Attributes from the Link State DB. + * Link State DB is not modified if Edge is NULL or not found. + * + * @param ted Link State Data Base + * @param edge Edge to be removed + */ +extern void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge); + +/** * Find Edge in the Link State Data Base by Edge key. * * @param ted Link State Data Base @@ -520,8 +593,7 @@ extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, /** * Find Edge in the Link State Data Base by the source (local IPv4 or IPv6 - * address or local ID) informations of the Link - * State Attributes + * address or local ID) informations of the Link State Attributes * * @param ted Link State Data Base * @param attributes Link State Attributes @@ -557,6 +629,29 @@ extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted, struct ls_prefix *pref); /** + * Update the Link State Prefix information of an existing Subnet. If there is + * no corresponding Subnet in the Link State Data Base, a new Subnet is created. + * + * @param ted Link State Data Base + * @param pref Link State Prefix + * + * @return Updated Link State Subnet, or NULL in case of error + */ +extern struct ls_subnet *ls_subnet_update(struct ls_ted *ted, + struct ls_prefix *pref); + +/** + * Check if two Subnets are equal. Note that this routine has the same return + * value sense as '==' (which is different from a comparison). + * + * @param s1 First subnet to compare + * @param s2 Second subnet to compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_subnet_same(struct ls_subnet *s1, struct ls_subnet *s2); + +/** * Remove Subnet from the Link State DB. Subnet data structure is freed but * not the Link State prefix data structure. Link State DB is not modified * if Subnet is NULL or not found in the Data Base. @@ -567,6 +662,15 @@ extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted, extern void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet); /** + * Remove Subnet and the associated Link State Prefix from the Link State DB. + * Link State DB is not modified if Subnet is NULL or not found. + * + * @param ted Link State Data Base + * @param subnet Subnet to be removed + */ +extern void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet); + +/** * Find Subnet in the Link State Data Base by prefix. * * @param ted Link State Data Base @@ -582,7 +686,7 @@ extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted, * * @param key Unique key of the data base. Must be different from 0 * @param name Name of the data base (may be NULL) - * @param asn AS Number for this data base. Must be different from 0 + * @param asn AS Number for this data base. 0 if unknown * * @return New Link State Database or NULL in case of error */ @@ -590,13 +694,29 @@ extern struct ls_ted *ls_ted_new(const uint32_t key, const char *name, uint32_t asn); /** - * Delete existing Link State Data Base. + * Delete existing Link State Data Base. Vertices, Edges, and Subnets are not + * removed. * * @param ted Link State Data Base */ extern void ls_ted_del(struct ls_ted *ted); /** + * Delete all Link State Vertices, Edges and SubNets and the Link State DB. + * + * @param ted Link State Data Base + */ +extern void ls_ted_del_all(struct ls_ted *ted); + +/** + * Clean Link State Data Base by removing all Vertices, Edges and SubNets marked + * as ORPHAN. + * + * @param ted Link State Data Base + */ +extern void ls_ted_clean(struct ls_ted *ted); + +/** * Connect Source and Destination Vertices by given Edge. Only non NULL source * and destination vertices are connected. * @@ -657,6 +777,7 @@ extern void ls_disconnect_edge(struct ls_edge *edge); */ /* ZAPI Opaque Link State Message Event */ +#define LS_MSG_EVENT_UNDEF 0 #define LS_MSG_EVENT_SYNC 1 #define LS_MSG_EVENT_ADD 2 #define LS_MSG_EVENT_UPDATE 3 @@ -680,6 +801,35 @@ struct ls_message { }; /** + * Register Link State daemon as a server or client for Zebra OPAQUE API. + * + * @param zclient Zebra client structure + * @param server Register daemon as a server (true) or as a client (false) + * + * @return 0 if success, -1 otherwise + */ +extern int ls_register(struct zclient *zclient, bool server); + +/** + * Unregister Link State daemon as a server or client for Zebra OPAQUE API. + * + * @param zclient Zebra client structure + * @param server Unregister daemon as a server (true) or as a client (false) + * + * @return 0 if success, -1 otherwise + */ +extern int ls_unregister(struct zclient *zclient, bool server); + +/** + * Send Link State SYNC message to request the complete Link State Database. + * + * @param zclient Zebra client + * + * @return 0 if success, -1 otherwise + */ +extern int ls_request_sync(struct zclient *zclient); + +/** * Parse Link State Message from stream. Used this function once receiving a * new ZAPI Opaque message of type Link State. * @@ -690,7 +840,7 @@ struct ls_message { extern struct ls_message *ls_parse_msg(struct stream *s); /** - * Delete existing message, freeing all substructure. + * Delete existing message. Data structure is freed. * * @param msg Link state message to be deleted */ @@ -751,6 +901,81 @@ extern struct ls_message *ls_subnet2msg(struct ls_message *msg, struct ls_subnet *subnet); /** + * Convert Link State Message into Vertex and update TED accordingly to + * the message event: SYNC, ADD, UPDATE or DELETE. + * + * @param ted Link State Database + * @param msg Link State Message + * @param delete True to delete the Link State Vertex from the Database, + * False otherwise. If true, return value is NULL in case + * of deletion. + * + * @return Vertex if success, NULL otherwise or if Vertex is removed + */ +extern struct ls_vertex *ls_msg2vertex(struct ls_ted *ted, + struct ls_message *msg, bool delete); + +/** + * Convert Link State Message into Edge and update TED accordingly to + * the message event: SYNC, ADD, UPDATE or DELETE. + * + * @param ted Link State Database + * @param msg Link State Message + * @param delete True to delete the Link State Edge from the Database, + * False otherwise. If true, return value is NULL in case + * of deletion. + * + * @return Edge if success, NULL otherwise or if Edge is removed + */ +extern struct ls_edge *ls_msg2edge(struct ls_ted *ted, struct ls_message *msg, + bool delete); + +/** + * Convert Link State Message into Subnet and update TED accordingly to + * the message event: SYNC, ADD, UPDATE or DELETE. + * + * @param ted Link State Database + * @param msg Link State Message + * @param delete True to delete the Link State Subnet from the Database, + * False otherwise. If true, return value is NULL in case + * of deletion. + * + * @return Subnet if success, NULL otherwise or if Subnet is removed + */ +extern struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, + struct ls_message *msg, bool delete); + +/** + * Convert Link State Message into Link State element (Vertex, Edge or Subnet) + * and update TED accordingly to the message event: SYNC, ADD, UPDATE or DELETE. + * + * @param ted Link State Database + * @param msg Link State Message + * @param delete True to delete the Link State Element from the Database, + * False otherwise. If true, return value is NULL in case + * of deletion. + * + * @return Element if success, NULL otherwise or if Element is removed + */ +extern struct ls_element *ls_msg2ted(struct ls_ted *ted, struct ls_message *msg, + bool delete); + +/** + * Convert stream buffer into Link State element (Vertex, Edge or Subnet) and + * update TED accordingly to the message event: SYNC, ADD, UPDATE or DELETE. + * + * @param ted Link State Database + * @param s Stream buffer + * @param delete True to delete the Link State Element from the Database, + * False otherwise. If true, return value is NULL in case + * of deletion. + * + * @return Element if success, NULL otherwise or if Element is removed + */ +extern struct ls_element *ls_stream2ted(struct ls_ted *ted, struct stream *s, + bool delete); + +/** * Send all the content of the Link State Data Base to the given destination. * Link State content is sent is this order: Vertices, Edges, Subnet. * This function must be used when a daemon request a Link State Data Base @@ -765,6 +990,92 @@ extern struct ls_message *ls_subnet2msg(struct ls_message *msg, extern int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, struct zapi_opaque_reg_info *dst); +struct json_object; +struct vty; +/** + * Show Link State Vertex information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param vertex Link State Vertex to show. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_vertex(struct ls_vertex *vertex, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show all Link State Vertices information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param ted Link State Data Base. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_vertices(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show Link State Edge information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param edge Link State Edge to show. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_edge(struct ls_edge *edge, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show all Link State Edges information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param ted Link State Data Base. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_edges(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show Link State Subnets information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param subnet Link State Subnet to show. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show all Link State Subnet information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param ted Link State Data Base. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_subnets(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show Link State Data Base information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param ted Link State Data Base to show. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_ted(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose); + /** * Dump all Link State Data Base elements for debugging purposes * @@ -462,7 +462,17 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_NHG_DEL), DESC_ENTRY(ZEBRA_NHG_NOTIFY_OWNER), DESC_ENTRY(ZEBRA_ROUTE_NOTIFY_REQUEST), - DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY)}; + DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY), + DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_ADD), + DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_DEL), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_ADDED), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_REMOVED), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_GET), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_REGISTER), + DESC_ENTRY(ZEBRA_NHRP_NEIGH_UNREGISTER), + DESC_ENTRY(ZEBRA_NEIGH_IP_ADD), + DESC_ENTRY(ZEBRA_NEIGH_IP_DEL), + DESC_ENTRY(ZEBRA_CONFIGURE_ARP)}; #undef DESC_ENTRY static const struct zebra_desc_table unknown = {0, "unknown", '?'}; diff --git a/lib/log_vty.c b/lib/log_vty.c index 7dadca8059..c26621ae99 100644 --- a/lib/log_vty.c +++ b/lib/log_vty.c @@ -532,6 +532,28 @@ DEFUN (no_config_log_timestamp_precision, return CMD_SUCCESS; } +DEFPY (config_log_ec, + config_log_ec_cmd, + "[no] log error-category", + NO_STR + "Logging control\n" + "Prefix log message text with [EC 9999] code\n") +{ + zlog_set_prefix_ec(!no); + return CMD_SUCCESS; +} + +DEFPY (config_log_xid, + config_log_xid_cmd, + "[no] log unique-id", + NO_STR + "Logging control\n" + "Prefix log message text with [XXXXX-XXXXX] identifier\n") +{ + zlog_set_prefix_xid(!no); + return CMD_SUCCESS; +} + DEFPY (config_log_filterfile, config_log_filterfile_cmd, "log filtered-file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg]", @@ -699,6 +721,11 @@ void log_config_write(struct vty *vty) if (zt_file.ts_subsec > 0) vty_out(vty, "log timestamp precision %d\n", zt_file.ts_subsec); + + if (!zlog_get_prefix_ec()) + vty_out(vty, "no log error-category\n"); + if (!zlog_get_prefix_xid()) + vty_out(vty, "no log unique-id\n"); } static int log_vty_init(const char *progname, const char *protoname, @@ -707,6 +734,9 @@ static int log_vty_init(const char *progname, const char *protoname, zlog_progname = progname; zlog_protoname = protoname; + zlog_set_prefix_ec(true); + zlog_set_prefix_xid(true); + zlog_filterfile_init(&zt_filterfile); zlog_file_set_fd(&zt_stdout, STDOUT_FILENO); @@ -737,6 +767,8 @@ void log_cmd_init(void) install_element(CONFIG_NODE, &no_config_log_record_priority_cmd); install_element(CONFIG_NODE, &config_log_timestamp_precision_cmd); install_element(CONFIG_NODE, &no_config_log_timestamp_precision_cmd); + install_element(CONFIG_NODE, &config_log_ec_cmd); + install_element(CONFIG_NODE, &config_log_xid_cmd); install_element(VIEW_NODE, &show_log_filter_cmd); install_element(CONFIG_NODE, &log_filter_cmd); diff --git a/lib/nexthop.c b/lib/nexthop.c index 17ef95c687..8439398149 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -730,80 +730,99 @@ int nexthop_str2backups(const char *str, int *num_backups, * nexthop2str() */ printfrr_ext_autoreg_p("NH", printfrr_nh) -static ssize_t printfrr_nh(char *buf, size_t bsz, const char *fmt, - int prec, const void *ptr) +static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) { const struct nexthop *nexthop = ptr; - struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; bool do_ifi = false; - const char *s, *v_is = "", *v_via = "", *v_viaif = "via "; - ssize_t ret = 3; + const char *v_is = "", *v_via = "", *v_viaif = "via "; + ssize_t ret = 0; - /* NULL-check */ - if (nexthop == NULL) { - if (fmt[2] == 'v' && fmt[3] == 'v') - ret++; - - strlcpy(buf, "NULL", bsz); - - return ret; - } - - switch (fmt[2]) { + switch (*ea->fmt) { case 'v': - if (fmt[3] == 'v') { + ea->fmt++; + if (*ea->fmt == 'v') { v_is = "is "; v_via = "via "; v_viaif = ""; - ret++; + ea->fmt++; } + if (!nexthop) + return bputs(buf, "(null)"); + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: - bprintfrr(&fb, "%s%pI4", v_via, &nexthop->gate.ipv4); + ret += bprintfrr(buf, "%s%pI4", v_via, + &nexthop->gate.ipv4); do_ifi = true; break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: - bprintfrr(&fb, "%s%pI6", v_via, &nexthop->gate.ipv6); + ret += bprintfrr(buf, "%s%pI6", v_via, + &nexthop->gate.ipv6); do_ifi = true; break; case NEXTHOP_TYPE_IFINDEX: - bprintfrr(&fb, "%sdirectly connected, %s", v_is, - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); + ret += bprintfrr(buf, "%sdirectly connected, %s", v_is, + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); break; case NEXTHOP_TYPE_BLACKHOLE: + ret += bputs(buf, "unreachable"); + switch (nexthop->bh_type) { case BLACKHOLE_REJECT: - s = " (ICMP unreachable)"; + ret += bputs(buf, " (ICMP unreachable)"); break; case BLACKHOLE_ADMINPROHIB: - s = " (ICMP admin-prohibited)"; + ret += bputs(buf, " (ICMP admin-prohibited)"); break; case BLACKHOLE_NULL: - s = " (blackhole)"; + ret += bputs(buf, " (blackhole)"); break; default: - s = ""; break; } - bprintfrr(&fb, "unreachable%s", s); break; default: break; } if (do_ifi && nexthop->ifindex) - bprintfrr(&fb, ", %s%s", v_viaif, ifindex2ifname( - nexthop->ifindex, - nexthop->vrf_id)); + ret += bprintfrr(buf, ", %s%s", v_viaif, + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); - *fb.pos = '\0'; return ret; case 's': - nexthop2str(nexthop, buf, bsz); - return 3; + ea->fmt++; + + if (!nexthop) + return bputs(buf, "(null)"); + + switch (nexthop->type) { + case NEXTHOP_TYPE_IFINDEX: + ret += bprintfrr(buf, "if %u", nexthop->ifindex); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + ret += bprintfrr(buf, "%pI4 if %u", &nexthop->gate.ipv4, + nexthop->ifindex); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + ret += bprintfrr(buf, "%pI6 if %u", &nexthop->gate.ipv6, + nexthop->ifindex); + break; + case NEXTHOP_TYPE_BLACKHOLE: + ret += bputs(buf, "blackhole"); + break; + default: + ret += bputs(buf, "unknown"); + break; + } + return ret; } - return 0; + return -1; } diff --git a/lib/northbound.c b/lib/northbound.c index 27ba632c9d..34ad5dbfa9 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -1260,27 +1260,36 @@ static int nb_callback_configuration(struct nb_context *context, } if (ret != NB_OK) { - int priority; - enum lib_log_refs ref; - yang_dnode_get_path(dnode, xpath, sizeof(xpath)); switch (event) { case NB_EV_VALIDATE: - priority = LOG_WARNING; - ref = EC_LIB_NB_CB_CONFIG_VALIDATE; + flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, + "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s", + nb_err_name(ret), nb_event_name(event), + nb_operation_name(operation), xpath, + errmsg[0] ? " message: " : "", errmsg); break; case NB_EV_PREPARE: - priority = LOG_WARNING; - ref = EC_LIB_NB_CB_CONFIG_PREPARE; + flog_warn(EC_LIB_NB_CB_CONFIG_PREPARE, + "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s", + nb_err_name(ret), nb_event_name(event), + nb_operation_name(operation), xpath, + errmsg[0] ? " message: " : "", errmsg); break; case NB_EV_ABORT: - priority = LOG_WARNING; - ref = EC_LIB_NB_CB_CONFIG_ABORT; + flog_warn(EC_LIB_NB_CB_CONFIG_ABORT, + "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s", + nb_err_name(ret), nb_event_name(event), + nb_operation_name(operation), xpath, + errmsg[0] ? " message: " : "", errmsg); break; case NB_EV_APPLY: - priority = LOG_ERR; - ref = EC_LIB_NB_CB_CONFIG_APPLY; + flog_err(EC_LIB_NB_CB_CONFIG_APPLY, + "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s", + nb_err_name(ret), nb_event_name(event), + nb_operation_name(operation), xpath, + errmsg[0] ? " message: " : "", errmsg); break; default: flog_err(EC_LIB_DEVELOPMENT, @@ -1288,15 +1297,6 @@ static int nb_callback_configuration(struct nb_context *context, event, xpath); exit(1); } - - flog(priority, ref, - "error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]", - nb_err_name(ret), nb_event_name(event), - nb_operation_name(operation), xpath); - if (strlen(errmsg) > 0) - flog(priority, ref, - "error processing configuration change: %s", - errmsg); } return ret; diff --git a/lib/northbound.h b/lib/northbound.h index 21aad64a09..417ecc81ea 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -474,6 +474,23 @@ struct nb_callbacks { int (*rpc)(struct nb_cb_rpc_args *args); /* + * Optional callback to compare the data nodes when printing + * the CLI commands associated with them. + * + * dnode1 + * The first data node to compare. + * + * dnode2 + * The second data node to compare. + * + * Returns: + * <0 when the CLI command for the dnode1 should be printed first + * >0 when the CLI command for the dnode2 should be printed first + * 0 when there is no difference + */ + int (*cli_cmp)(struct lyd_node *dnode1, struct lyd_node *dnode2); + + /* * Optional callback to show the CLI command associated to the given * YANG data node. * diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index a2c8bc8633..f88c2161da 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -529,25 +529,6 @@ static int nb_cli_candidate_load_transaction(struct vty *vty, return CMD_SUCCESS; } -/* - * ly_iter_next_is_up: detects when iterating up on the yang model. - * - * This function detects whether next node in the iteration is upwards, - * then return the node otherwise return NULL. - */ -static struct lyd_node *ly_iter_next_up(const struct lyd_node *elem) -{ - /* Are we going downwards? Is this still not a leaf? */ - if (!(elem->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA))) - return NULL; - - /* Are there still leaves in this branch? */ - if (elem->next != NULL) - return NULL; - - return elem->parent; -} - /* Prepare the configuration for display. */ void nb_cli_show_config_prepare(struct nb_config *config, bool with_defaults) { @@ -569,53 +550,80 @@ void nb_cli_show_config_prepare(struct nb_config *config, bool with_defaults) ly_native_ctx); } -void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root, - bool with_defaults) +static void show_dnode_children_cmds(struct vty *vty, struct lyd_node *root, + bool with_defaults) { - struct lyd_node *next, *child, *parent; - - LY_TREE_DFS_BEGIN (root, next, child) { - struct nb_node *nb_node; + struct nb_node *nb_node, *sort_node = NULL; + struct listnode *listnode; + struct lyd_node *child; + struct list *sort_list; + void *data; + LY_TREE_FOR (root->child, child) { nb_node = child->schema->priv; - if (!nb_node || !nb_node->cbs.cli_show) - goto next; - /* Skip default values. */ - if (!with_defaults && yang_dnode_is_default_recursive(child)) - goto next; - - (*nb_node->cbs.cli_show)(vty, child, with_defaults); - next: /* - * When transiting upwards in the yang model we should - * give the previous container/list node a chance to - * print its close vty output (e.g. "!" or "end-family" - * etc...). + * We finished processing current list, + * it's time to print the config. */ - parent = ly_iter_next_up(child); - if (parent != NULL) { - nb_node = parent->schema->priv; - if (nb_node && nb_node->cbs.cli_show_end) - (*nb_node->cbs.cli_show_end)(vty, parent); + if (sort_node && nb_node != sort_node) { + for (ALL_LIST_ELEMENTS_RO(sort_list, listnode, data)) + nb_cli_show_dnode_cmds(vty, data, + with_defaults); + + list_delete(&sort_list); + sort_node = NULL; } /* - * There is a possible path in this macro that ends up - * dereferencing child->parent->parent. We just null checked - * child->parent by checking (ly_iter_next_up(child) != NULL) - * above. - * - * I am not sure whether it is possible for the other - * conditions within this macro guarding the problem - * dereference to be satisfied when child->parent == NULL. + * If the config needs to be sorted, + * then add the dnode to the sorting + * list for later processing. */ -#ifndef __clang_analyzer__ - LY_TREE_DFS_END(root, next, child); -#endif + if (nb_node && nb_node->cbs.cli_cmp) { + if (!sort_node) { + sort_node = nb_node; + sort_list = list_new(); + sort_list->cmp = (int (*)(void *, void *)) + nb_node->cbs.cli_cmp; + } + + listnode_add_sort(sort_list, child); + continue; + } + + nb_cli_show_dnode_cmds(vty, child, with_defaults); + } + + if (sort_node) { + for (ALL_LIST_ELEMENTS_RO(sort_list, listnode, data)) + nb_cli_show_dnode_cmds(vty, data, with_defaults); + + list_delete(&sort_list); + sort_node = NULL; } } +void nb_cli_show_dnode_cmds(struct vty *vty, struct lyd_node *root, + bool with_defaults) +{ + struct nb_node *nb_node; + + if (!with_defaults && yang_dnode_is_default_recursive(root)) + return; + + nb_node = root->schema->priv; + + if (nb_node && nb_node->cbs.cli_show) + (*nb_node->cbs.cli_show)(vty, root, with_defaults); + + if (!(root->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA))) + show_dnode_children_cmds(vty, root, with_defaults); + + if (nb_node && nb_node->cbs.cli_show_end) + (*nb_node->cbs.cli_show_end)(vty, root); +} + static void nb_cli_show_config_cmds(struct vty *vty, struct nb_config *config, bool with_defaults) { diff --git a/lib/prefix.c b/lib/prefix.c index afc4d3d5c2..7dbb5f07f0 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1198,15 +1198,6 @@ int netmask_str2prefix_str(const char *net_str, const char *mask_str, return 1; } -/* Utility function for making IPv6 address string. */ -const char *inet6_ntoa(struct in6_addr addr) -{ - static char buf[INET6_ADDRSTRLEN]; - - inet_ntop(AF_INET6, &addr, buf, INET6_ADDRSTRLEN); - return buf; -} - /* converts to internal representation of mac address * returns 1 on success, 0 otherwise * format accepted: AA:BB:CC:DD:EE:FF @@ -1361,92 +1352,92 @@ char *evpn_es_df_alg2str(uint8_t df_alg, char *buf, int buf_len) } printfrr_ext_autoreg_p("EA", printfrr_ea) -static ssize_t printfrr_ea(char *buf, size_t bsz, const char *fmt, - int prec, const void *ptr) +static ssize_t printfrr_ea(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) { const struct ethaddr *mac = ptr; + char cbuf[ETHER_ADDR_STRLEN]; - if (mac) - prefix_mac2str(mac, buf, bsz); - else - strlcpy(buf, "NULL", bsz); + if (!mac) + return bputs(buf, "(null)"); - return 2; + /* need real length even if buffer is too short */ + prefix_mac2str(mac, cbuf, sizeof(cbuf)); + return bputs(buf, cbuf); } printfrr_ext_autoreg_p("IA", printfrr_ia) -static ssize_t printfrr_ia(char *buf, size_t bsz, const char *fmt, - int prec, const void *ptr) +static ssize_t printfrr_ia(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) { const struct ipaddr *ipa = ptr; + char cbuf[INET6_ADDRSTRLEN]; - if (ipa) - ipaddr2str(ipa, buf, bsz); - else - strlcpy(buf, "NULL", bsz); + if (!ipa) + return bputs(buf, "(null)"); - return 2; + ipaddr2str(ipa, cbuf, sizeof(cbuf)); + return bputs(buf, cbuf); } printfrr_ext_autoreg_p("I4", printfrr_i4) -static ssize_t printfrr_i4(char *buf, size_t bsz, const char *fmt, - int prec, const void *ptr) +static ssize_t printfrr_i4(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) { - if (ptr) - inet_ntop(AF_INET, ptr, buf, bsz); - else - strlcpy(buf, "NULL", bsz); + char cbuf[INET_ADDRSTRLEN]; - return 2; + if (!ptr) + return bputs(buf, "(null)"); + + inet_ntop(AF_INET, ptr, cbuf, sizeof(cbuf)); + return bputs(buf, cbuf); } printfrr_ext_autoreg_p("I6", printfrr_i6) -static ssize_t printfrr_i6(char *buf, size_t bsz, const char *fmt, - int prec, const void *ptr) +static ssize_t printfrr_i6(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) { - if (ptr) - inet_ntop(AF_INET6, ptr, buf, bsz); - else - strlcpy(buf, "NULL", bsz); + char cbuf[INET6_ADDRSTRLEN]; - return 2; + if (!ptr) + return bputs(buf, "(null)"); + + inet_ntop(AF_INET6, ptr, cbuf, sizeof(cbuf)); + return bputs(buf, cbuf); } printfrr_ext_autoreg_p("FX", printfrr_pfx) -static ssize_t printfrr_pfx(char *buf, size_t bsz, const char *fmt, - int prec, const void *ptr) +static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) { - if (ptr) - prefix2str(ptr, buf, bsz); - else - strlcpy(buf, "NULL", bsz); + char cbuf[PREFIX_STRLEN]; + + if (!ptr) + return bputs(buf, "(null)"); - return 2; + prefix2str(ptr, cbuf, sizeof(cbuf)); + return bputs(buf, cbuf); } printfrr_ext_autoreg_p("SG4", printfrr_psg) -static ssize_t printfrr_psg(char *buf, size_t bsz, const char *fmt, - int prec, const void *ptr) +static ssize_t printfrr_psg(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) { const struct prefix_sg *sg = ptr; - struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; + ssize_t ret = 0; - if (sg) { - if (sg->src.s_addr == INADDR_ANY) - bprintfrr(&fb, "(*,"); - else - bprintfrr(&fb, "(%pI4,", &sg->src); - - if (sg->grp.s_addr == INADDR_ANY) - bprintfrr(&fb, "*)"); - else - bprintfrr(&fb, "%pI4)", &sg->grp); + if (!sg) + return bputs(buf, "(null)"); - fb.pos[0] = '\0'; + if (sg->src.s_addr == INADDR_ANY) + ret += bputs(buf, "(*,"); + else + ret += bprintfrr(buf, "(%pI4,", &sg->src); - } else { - strlcpy(buf, "NULL", bsz); - } + if (sg->grp.s_addr == INADDR_ANY) + ret += bputs(buf, "*)"); + else + ret += bprintfrr(buf, "%pI4)", &sg->grp); - return 3; + return ret; } diff --git a/lib/prefix.h b/lib/prefix.h index b2f3b0592f..d7ee1b8e4c 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -504,8 +504,6 @@ extern void apply_mask_ipv6(struct prefix_ipv6 *); extern int ip6_masklen(struct in6_addr); extern void masklen2ip6(const int, struct in6_addr *); -extern const char *inet6_ntoa(struct in6_addr); - extern int is_zero_mac(const struct ethaddr *mac); extern bool is_mcast_mac(const struct ethaddr *mac); extern bool is_bcast_mac(const struct ethaddr *mac); diff --git a/lib/printf/glue.c b/lib/printf/glue.c index 29ca26ad5d..1147901236 100644 --- a/lib/printf/glue.c +++ b/lib/printf/glue.c @@ -210,15 +210,16 @@ void printfrr_ext_reg(const struct printfrr_ext *ext) exts[i] = ext; } -ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec, +ssize_t printfrr_extp(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { + const char *fmt = ea->fmt; const struct printfrr_ext *ext; size_t i; for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) - return 0; + return -1; if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) continue; ext = exts[i]; @@ -226,20 +227,22 @@ ssize_t printfrr_extp(char *buf, size_t sz, const char *fmt, int prec, continue; if (strncmp(ext->match, fmt, strlen(ext->match))) continue; - return ext->print_ptr(buf, sz, fmt, prec, ptr); + ea->fmt += strlen(ext->match); + return ext->print_ptr(buf, ea, ptr); } - return 0; + return -1; } -ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec, +ssize_t printfrr_exti(struct fbuf *buf, struct printfrr_eargs *ea, uintmax_t num) { + const char *fmt = ea->fmt; const struct printfrr_ext *ext; size_t i; for (i = ext_offsets[fmt[0] - 'A']; i < MAXEXT; i++) { if (!entries[i].fmt[0] || entries[i].fmt[0] > fmt[0]) - return 0; + return -1; if (entries[i].fmt[1] && entries[i].fmt[1] != fmt[1]) continue; ext = exts[i]; @@ -247,7 +250,48 @@ ssize_t printfrr_exti(char *buf, size_t sz, const char *fmt, int prec, continue; if (strncmp(ext->match, fmt, strlen(ext->match))) continue; - return ext->print_int(buf, sz, fmt, prec, num); + ea->fmt += strlen(ext->match); + return ext->print_int(buf, ea, num); } - return 0; + return -1; +} + +printfrr_ext_autoreg_p("FB", printfrr_fb) +static ssize_t printfrr_fb(struct fbuf *out, struct printfrr_eargs *ea, + const void *ptr) +{ + const struct fbuf *in = ptr; + ptrdiff_t copy_len; + + if (!in) + return bputs(out, "NULL"); + + if (out) { + copy_len = MIN(in->pos - in->buf, + out->buf + out->len - out->pos); + if (copy_len > 0) { + memcpy(out->pos, in->buf, copy_len); + out->pos += copy_len; + } + } + + return in->pos - in->buf; +} + +printfrr_ext_autoreg_p("VA", printfrr_va) +static ssize_t printfrr_va(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + const struct va_format *vaf = ptr; + va_list ap; + + if (!vaf || !vaf->fmt || !vaf->va) + return bputs(buf, "NULL"); + + /* make sure we don't alter the data passed in - especially since + * bprintfrr (and thus this) might be called on the same format twice, + * when allocating a larger buffer in asnprintfrr() + */ + va_copy(ap, *vaf->va); + return vbprintfrr(buf, vaf->fmt, ap); } diff --git a/lib/printf/printflocal.h b/lib/printf/printflocal.h index 335e09872e..bac80e801c 100644 --- a/lib/printf/printflocal.h +++ b/lib/printf/printflocal.h @@ -100,6 +100,8 @@ int _frr_find_arguments(const char *, va_list, union arg **) DSO_LOCAL; int _frr_find_warguments(const wchar_t *, va_list, union arg **) DSO_LOCAL; #endif -/* returns number of bytes consumed for extended specifier */ -ssize_t printfrr_extp(char *, size_t, const char *, int, const void *) DSO_LOCAL; -ssize_t printfrr_exti(char *, size_t, const char *, int, uintmax_t) DSO_LOCAL; +/* returns number of bytes needed for full output, or -1 */ +ssize_t printfrr_extp(struct fbuf *, struct printfrr_eargs *ea, const void *) + DSO_LOCAL; +ssize_t printfrr_exti(struct fbuf *, struct printfrr_eargs *ea, uintmax_t) + DSO_LOCAL; diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c index a0634cde4b..49fa2b718f 100644 --- a/lib/printf/vfprintf.c +++ b/lib/printf/vfprintf.c @@ -138,7 +138,7 @@ __wcsconv(wchar_t *wcsarg, int prec) * write a uintmax_t in octal (plus one byte). */ #if UINTMAX_MAX <= UINT64_MAX -#define BUF 64 +#define BUF 80 #else #error "BUF must be large enough to format a uintmax_t" #endif @@ -147,7 +147,7 @@ __wcsconv(wchar_t *wcsarg, int prec) * Non-MT-safe version */ ssize_t -vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap) +vbprintfrr(struct fbuf *cb_in, const char *fmt0, va_list ap) { const char *fmt; /* format string */ int ch; /* character from fmt */ @@ -177,6 +177,9 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap) int nextarg; /* 1-based argument index */ va_list orgap; /* original argument pointer */ char *convbuf; /* wide to multibyte conversion result */ + char *extstart = NULL; /* where printfrr_ext* started printing */ + struct fbuf cb_copy, *cb; + struct fmt_outpos *opos; static const char xdigs_lower[16] = "0123456789abcdef"; static const char xdigs_upper[16] = "0123456789ABCDEF"; @@ -268,6 +271,16 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap) argtable = NULL; nextarg = 1; va_copy(orgap, ap); + + if (cb_in) { + /* prevent printfrr exts from polluting cb->outpos */ + cb_copy = *cb_in; + cb_copy.outpos = NULL; + cb_copy.outpos_n = cb_copy.outpos_i = 0; + cb = &cb_copy; + } else + cb = NULL; + io_init(&io, cb); ret = 0; @@ -292,11 +305,16 @@ vbprintfrr(struct fbuf *cb, const char *fmt0, va_list ap) flags = 0; dprec = 0; - width = 0; + width = -1; prec = -1; sign = '\0'; ox[1] = '\0'; + if (cb_in && cb_in->outpos_i < cb_in->outpos_n) + opos = &cb_in->outpos[cb_in->outpos_i]; + else + opos = NULL; + rflag: ch = *fmt++; reswitch: switch (ch) { case ' ': @@ -438,15 +456,24 @@ reswitch: switch (ch) { ulval = SARG(); if (printfrr_ext_char(fmt[0])) { - n2 = printfrr_exti(buf, sizeof(buf), fmt, prec, + struct printfrr_eargs ea = { + .fmt = fmt, + .precision = prec, + .width = width, + .alt_repr = !!(flags & ALT), + .leftadj = !!(flags & LADJUST), + }; + + if (cb) + extstart = cb->pos; + + size = printfrr_exti(cb, &ea, (flags & INTMAX_SIZE) ? ujval : (uintmax_t)ulval); - if (n2 > 0) { - fmt += n2; - cp = buf; - size = strlen(cp); - sign = '\0'; - break; + if (size >= 0) { + fmt = ea.fmt; + width = ea.width; + goto ext_printed; } } if (flags & INTMAX_SIZE) { @@ -503,35 +530,6 @@ reswitch: switch (ch) { size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); sign = '\0'; break; -#ifdef DANGEROUS_PERCENT_N - /* FRR does not use %n in printf formats. This is just left - * here in case someone tries to use %n and starts debugging - * why the f* it doesn't work - */ - case 'n': - /* - * Assignment-like behavior is specified if the - * value overflows or is otherwise unrepresentable. - * C99 says to use `signed char' for %hhn conversions. - */ - if (flags & LLONGINT) - *GETARG(long long *) = ret; - else if (flags & SIZET) - *GETARG(ssize_t *) = (ssize_t)ret; - else if (flags & PTRDIFFT) - *GETARG(ptrdiff_t *) = ret; - else if (flags & INTMAXT) - *GETARG(intmax_t *) = ret; - else if (flags & LONGINT) - *GETARG(long *) = ret; - else if (flags & SHORTINT) - *GETARG(short *) = ret; - else if (flags & CHARINT) - *GETARG(signed char *) = ret; - else - *GETARG(int *) = ret; - continue; /* no output */ -#endif case 'O': flags |= LONGINT; /*FALLTHROUGH*/ @@ -551,14 +549,24 @@ reswitch: switch (ch) { * -- ANSI X3J11 */ ptrval = GETARG(void *); - if (printfrr_ext_char(fmt[0]) && - (n2 = printfrr_extp(buf, sizeof(buf), - fmt, prec, ptrval)) > 0) { - fmt += n2; - cp = buf; - size = strlen(cp); - sign = '\0'; - break; + if (printfrr_ext_char(fmt[0])) { + struct printfrr_eargs ea = { + .fmt = fmt, + .precision = prec, + .width = width, + .alt_repr = !!(flags & ALT), + .leftadj = !!(flags & LADJUST), + }; + + if (cb) + extstart = cb->pos; + + size = printfrr_extp(cb, &ea, ptrval); + if (size >= 0) { + fmt = ea.fmt; + width = ea.width; + goto ext_printed; + } } ujval = (uintmax_t)(uintptr_t)ptrval; base = 16; @@ -662,6 +670,7 @@ number: if ((dprec = prec) >= 0) cp = buf; size = 1; sign = '\0'; + opos = NULL; break; } @@ -679,6 +688,9 @@ number: if ((dprec = prec) >= 0) * Compute actual size, so we know how much to pad. * size excludes decimal prec; realsz includes it. */ + if (width < 0) + width = 0; + realsz = dprec > size ? dprec : size; if (sign) realsz++; @@ -686,7 +698,7 @@ number: if ((dprec = prec) >= 0) realsz += 2; prsize = width > realsz ? width : realsz; - if ((unsigned)ret + prsize > INT_MAX) { + if ((unsigned int)ret + prsize > INT_MAX) { ret = EOF; errno = EOVERFLOW; goto error; @@ -696,6 +708,9 @@ number: if ((dprec = prec) >= 0) if ((flags & (LADJUST|ZEROPAD)) == 0) PAD(width - realsz, blanks); + if (opos) + opos->off_start = cb->pos - cb->buf; + /* prefix */ if (sign) PRINT(&sign, 1); @@ -713,6 +728,74 @@ number: if ((dprec = prec) >= 0) /* leading zeroes from decimal precision */ PAD(dprec - size, zeroes); PRINT(cp, size); + + if (opos) { + opos->off_end = cb->pos - cb->buf; + cb_in->outpos_i++; + } + + /* left-adjusting padding (always blank) */ + if (flags & LADJUST) + PAD(width - realsz, blanks); + + /* finally, adjust ret */ + ret += prsize; + + FLUSH(); /* copy out the I/O vectors */ + continue; + +ext_printed: + /* when we arrive here, a printfrr extension has written to cb + * (if non-NULL), but we still need to handle padding. The + * original cb->pos is in extstart; the return value from the + * ext is in size. + * + * Keep analogous to code above please. + */ + + if (width < 0) + width = 0; + + realsz = size; + prsize = width > realsz ? width : realsz; + if ((unsigned int)ret + prsize > INT_MAX) { + ret = EOF; + errno = EOVERFLOW; + goto error; + } + + /* right-adjusting blank padding - need to move the chars + * that the extension has already written. Should be very + * rare. + */ + if (cb && width > size && (flags & (LADJUST|ZEROPAD)) == 0) { + size_t nwritten = cb->pos - extstart; + size_t navail = cb->buf + cb->len - extstart; + size_t npad = width - realsz; + size_t nmove; + + if (navail < npad) + navail = 0; + else + navail -= npad; + nmove = MIN(nwritten, navail); + + memmove(extstart + npad, extstart, nmove); + + cb->pos = extstart; + PAD(npad, blanks); + cb->pos += nmove; + extstart += npad; + } + + io.avail = cb ? cb->len - (cb->pos - cb->buf) : 0; + + if (opos && extstart <= cb->pos) { + opos->off_start = extstart - cb->buf; + opos->off_end = cb->pos - cb->buf; + cb_in->outpos_i++; + } + /* left-adjusting padding (always blank) */ if (flags & LADJUST) PAD(width - realsz, blanks); @@ -730,6 +813,8 @@ error: free(convbuf); if ((argtable != NULL) && (argtable != statargtable)) free (argtable); + if (cb_in) + cb_in->pos = cb->pos; return (ret); /* NOTREACHED */ } diff --git a/lib/printfrr.h b/lib/printfrr.h index a775e1517b..4338ac3a2f 100644 --- a/lib/printfrr.h +++ b/lib/printfrr.h @@ -28,10 +28,17 @@ extern "C" { #endif +struct fmt_outpos { + unsigned int off_start, off_end; +}; + struct fbuf { char *buf; char *pos; size_t len; + + struct fmt_outpos *outpos; + size_t outpos_n, outpos_i; }; #define at(a, b) PRINTFRR(a, b) @@ -105,6 +112,8 @@ char *asnprintfrr(struct memtype *mt, char *out, size_t sz, */ #define printfrr_ext_char(ch) ((ch) >= 'A' && (ch) <= 'Z') +struct printfrr_eargs; + struct printfrr_ext { /* embedded string to minimize cache line pollution */ char match[8]; @@ -112,23 +121,80 @@ struct printfrr_ext { /* both can be given, if not the code continues searching * (you can do %pX and %dX in 2 different entries) * - * return value: number of bytes consumed from the format string, so - * you can consume extra flags (e.g. register for "%pX", consume - * "%pXfoo" or "%pXbar" for flags.) Convention is to make those flags - * lowercase letters or numbers. + * return value: number of bytes that would be printed if the buffer + * was large enough. be careful about not under-reporting this; + * otherwise asnprintf() & co. will get broken. Returning -1 means + * something went wrong & default %p/%d handling should be executed. * - * bsz is a compile-time constant in printf; it's gonna be relatively - * small. This isn't designed to print Shakespeare from a pointer. + * to consume extra input flags after %pXY, increment *fmt. It points + * at the first character after %pXY at entry. Convention is to make + * those flags lowercase letters or numbers. + */ + ssize_t (*print_ptr)(struct fbuf *buf, struct printfrr_eargs *info, + const void *); + ssize_t (*print_int)(struct fbuf *buf, struct printfrr_eargs *info, + uintmax_t); +}; + +/* additional information passed to extended formatters */ + +struct printfrr_eargs { + /* position in the format string. Points to directly after the + * extension specifier. Increment when consuming extra "flag + * characters". + */ + const char *fmt; + + /* %.1234x / %.*x + * not POSIX compatible when used with %p, will cause warnings from + * GCC & clang. Usable with %d. Not used by the printfrr() itself + * for extension specifiers, so essentially available as a "free" + * parameter. -1 if not specified. Value in the format string + * cannot be negative, but negative values can be passed with %.*x + */ + int precision; + + /* %1234x / %*x + * regular width specification. Internally handled by printfrr(), set + * to 0 if consumed by the extension in order to suppress standard + * width/padding behavior. 0 if not specified. * - * prec is the precision specifier (the 999 in "%.999p") -1 means - * none given (value in the format string cannot be negative) + * NB: always positive, even if a negative value is passed in with + * %*x. (The sign is used for the - flag.) */ - ssize_t (*print_ptr)(char *buf, size_t bsz, const char *fmt, int prec, - const void *); - ssize_t (*print_int)(char *buf, size_t bsz, const char *fmt, int prec, - uintmax_t); + int width; + + /* %#x + * "alternate representation" flag, not POSIX compatible when used + * with %p or %d, will cause warnings from GCC & clang. Not used by + * printfrr() itself for extension specifiers. + */ + bool alt_repr; + + /* %-x + * left-pad flag. Internally handled by printfrr() if width is + * nonzero. Only use if the extension sets width to 0. + */ + bool leftadj; }; +/* for any extension that needs a buffer length */ + +static inline ssize_t printfrr_ext_len(struct printfrr_eargs *ea) +{ + ssize_t rv; + + if (ea->precision >= 0) + rv = ea->precision; + else if (ea->width >= 0) { + rv = ea->width; + ea->width = -1; + } else + rv = -1; + + return rv; +} + /* no locking - must be called when single threaded (e.g. at startup.) * this restriction hopefully won't be a huge bother considering normal usage * scenarios... @@ -136,7 +202,7 @@ struct printfrr_ext { void printfrr_ext_reg(const struct printfrr_ext *); #define printfrr_ext_autoreg_p(matchs, print_fn) \ - static ssize_t print_fn(char *, size_t, const char *, int, \ + static ssize_t print_fn(struct fbuf *, struct printfrr_eargs *, \ const void *); \ static const struct printfrr_ext _printext_##print_fn = { \ .match = matchs, \ @@ -149,7 +215,8 @@ void printfrr_ext_reg(const struct printfrr_ext *); /* end */ #define printfrr_ext_autoreg_i(matchs, print_fn) \ - static ssize_t print_fn(char *, size_t, const char *, int, uintmax_t); \ + static ssize_t print_fn(struct fbuf *, struct printfrr_eargs *, \ + uintmax_t); \ static const struct printfrr_ext _printext_##print_fn = { \ .match = matchs, \ .print_int = print_fn, \ @@ -160,6 +227,92 @@ void printfrr_ext_reg(const struct printfrr_ext *); } \ /* end */ +/* fbuf helper functions - note all 3 of these return the length that would + * be written regardless of how much space was available in the buffer, as + * needed for implementing printfrr extensions. (They also accept NULL buf + * for that.) + */ + +static inline ssize_t bputs(struct fbuf *buf, const char *str) +{ + size_t len = strlen(str); + size_t ncopy; + + if (!buf) + return len; + + ncopy = MIN(len, (size_t)(buf->buf + buf->len - buf->pos)); + memcpy(buf->pos, str, ncopy); + buf->pos += ncopy; + + return len; +} + +static inline ssize_t bputch(struct fbuf *buf, char ch) +{ + if (buf && buf->pos < buf->buf + buf->len) + *buf->pos++ = ch; + return 1; +} + +static inline ssize_t bputhex(struct fbuf *buf, uint8_t val) +{ + static const char hexch[] = "0123456789abcdef"; + + if (buf && buf->pos < buf->buf + buf->len) + *buf->pos++ = hexch[(val >> 4) & 0xf]; + if (buf && buf->pos < buf->buf + buf->len) + *buf->pos++ = hexch[val & 0xf]; + return 2; +} + +/* %pVA extension, equivalent to Linux kernel %pV */ + +struct va_format { + const char *fmt; + va_list *va; +}; + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pFB" (struct fbuf *) +#pragma FRR printfrr_ext "%pVA" (struct va_format *) + +#pragma FRR printfrr_ext "%pHX" (signed char *) +#pragma FRR printfrr_ext "%pHX" (unsigned char *) +#pragma FRR printfrr_ext "%pHX" (void *) +#pragma FRR printfrr_ext "%pHS" (signed char *) +#pragma FRR printfrr_ext "%pHS" (unsigned char *) +#pragma FRR printfrr_ext "%pHS" (void *) + +#pragma FRR printfrr_ext "%pSE" (char *) +#pragma FRR printfrr_ext "%pSQ" (char *) +#endif + +/* when using non-ISO-C compatible extension specifiers... */ + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#define FMT_NSTD_BEGIN +#define FMT_NSTD_END +#else /* !_FRR_ATTRIBUTE_PRINTFRR */ +#define FMT_NSTD_BEGIN \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wformat\"") \ + /* end */ +#define FMT_NSTD_END \ + _Pragma("GCC diagnostic pop") \ + /* end */ +#endif + +#define FMT_NSTD(expr) \ + ({ \ + typeof(expr) _v; \ + FMT_NSTD_BEGIN \ + _v = expr; \ + FMT_NSTD_END \ + _v; \ + }) \ + /* end */ + #ifdef __cplusplus } #endif diff --git a/lib/routemap.c b/lib/routemap.c index b836b55aad..b2cb299fd3 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -87,112 +87,126 @@ struct route_map_match_set_hooks rmap_match_set_hook; /* match interface */ void route_map_match_interface_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_interface = func; } /* no match interface */ void route_map_no_match_interface_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_interface = func; } /* match ip address */ void route_map_match_ip_address_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_ip_address = func; } /* no match ip address */ void route_map_no_match_ip_address_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_ip_address = func; } /* match ip address prefix list */ void route_map_match_ip_address_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_ip_address_prefix_list = func; } /* no match ip address prefix list */ void route_map_no_match_ip_address_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_ip_address_prefix_list = func; } /* match ip next hop */ void route_map_match_ip_next_hop_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_ip_next_hop = func; } /* no match ip next hop */ void route_map_no_match_ip_next_hop_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_ip_next_hop = func; } /* match ip next hop prefix list */ void route_map_match_ip_next_hop_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_ip_next_hop_prefix_list = func; } /* no match ip next hop prefix list */ void route_map_no_match_ip_next_hop_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_ip_next_hop_prefix_list = func; } /* match ip next-hop type */ void route_map_match_ip_next_hop_type_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_ip_next_hop_type = func; } /* no match ip next-hop type */ void route_map_no_match_ip_next_hop_type_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_ip_next_hop_type = func; } /* match ipv6 address */ void route_map_match_ipv6_address_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_ipv6_address = func; } /* no match ipv6 address */ void route_map_no_match_ipv6_address_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_ipv6_address = func; } @@ -200,178 +214,183 @@ void route_map_no_match_ipv6_address_hook(int (*func)( /* match ipv6 address prefix list */ void route_map_match_ipv6_address_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_ipv6_address_prefix_list = func; } /* no match ipv6 address prefix list */ void route_map_no_match_ipv6_address_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_ipv6_address_prefix_list = func; } /* match ipv6 next-hop type */ void route_map_match_ipv6_next_hop_type_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_ipv6_next_hop_type = func; } /* no match ipv6 next-hop type */ void route_map_no_match_ipv6_next_hop_type_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_ipv6_next_hop_type = func; } /* match metric */ void route_map_match_metric_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_metric = func; } /* no match metric */ void route_map_no_match_metric_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_metric = func; } /* match tag */ -void route_map_match_tag_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +void route_map_match_tag_hook(int (*func)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type)) + route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.match_tag = func; } /* no match tag */ void route_map_no_match_tag_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)) + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_match_tag = func; } /* set sr-te color */ -void route_map_set_srte_color_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +void route_map_set_srte_color_hook(int (*func)(struct route_map_index *index, const char *command, - const char *arg)) + const char *arg, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.set_srte_color = func; } /* no set sr-te color */ -void route_map_no_set_srte_color_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +void route_map_no_set_srte_color_hook(int (*func)(struct route_map_index *index, const char *command, - const char *arg)) + const char *arg, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_set_srte_color = func; } /* set ip nexthop */ -void route_map_set_ip_nexthop_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +void route_map_set_ip_nexthop_hook(int (*func)(struct route_map_index *index, const char *command, - const char *arg)) + const char *arg, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.set_ip_nexthop = func; } /* no set ip nexthop */ -void route_map_no_set_ip_nexthop_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +void route_map_no_set_ip_nexthop_hook(int (*func)(struct route_map_index *index, const char *command, - const char *arg)) + const char *arg, + char *errmsg, + size_t errmsg_len)) { rmap_match_set_hook.no_set_ip_nexthop = func; } /* set ipv6 nexthop local */ void route_map_set_ipv6_nexthop_local_hook( - int (*func)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg)) + int (*func)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.set_ipv6_nexthop_local = func; } /* no set ipv6 nexthop local */ void route_map_no_set_ipv6_nexthop_local_hook( - int (*func)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg)) + int (*func)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_set_ipv6_nexthop_local = func; } /* set metric */ -void route_map_set_metric_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +void route_map_set_metric_hook(int (*func)(struct route_map_index *index, const char *command, - const char *arg)) + const char *arg, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.set_metric = func; } /* no set metric */ -void route_map_no_set_metric_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +void route_map_no_set_metric_hook(int (*func)(struct route_map_index *index, const char *command, - const char *arg)) + const char *arg, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_set_metric = func; } /* set tag */ -void route_map_set_tag_hook(int (*func)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg)) +void route_map_set_tag_hook(int (*func)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.set_tag = func; } /* no set tag */ -void route_map_no_set_tag_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +void route_map_no_set_tag_hook(int (*func)(struct route_map_index *index, const char *command, - const char *arg)) + const char *arg, + char *errmsg, size_t errmsg_len)) { rmap_match_set_hook.no_set_tag = func; } -int generic_match_add(struct vty *vty, struct route_map_index *index, +int generic_match_add(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type) + route_map_event_t type, + char *errmsg, size_t errmsg_len) { enum rmap_compile_rets ret; ret = route_map_add_match(index, command, arg, type); switch (ret) { case RMAP_RULE_MISSING: - if (vty) - vty_out(vty, "%% [%s] Can't find rule.\n", - frr_protonameinst); - else - zlog_warn("Can't find rule: %s", command); + snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.", + frr_protonameinst); return CMD_WARNING_CONFIG_FAILED; case RMAP_COMPILE_ERROR: - if (vty) - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); - else - zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg); + snprintf(errmsg, errmsg_len, + "%% [%s] Argument form is unsupported or malformed.", + frr_protonameinst); return CMD_WARNING_CONFIG_FAILED; case RMAP_COMPILE_SUCCESS: /* @@ -383,9 +402,10 @@ int generic_match_add(struct vty *vty, struct route_map_index *index, return CMD_SUCCESS; } -int generic_match_delete(struct vty *vty, struct route_map_index *index, +int generic_match_delete(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type) + route_map_event_t type, + char *errmsg, size_t errmsg_len) { enum rmap_compile_rets ret; int retval = CMD_SUCCESS; @@ -409,20 +429,14 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index, ret = route_map_delete_match(index, command, dep_name, type); switch (ret) { case RMAP_RULE_MISSING: - if (vty) - vty_out(vty, "%% [%s] Can't find rule.\n", - frr_protonameinst); - else - zlog_warn("Can't find rule: %s", command); + snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.", + frr_protonameinst); retval = CMD_WARNING_CONFIG_FAILED; break; case RMAP_COMPILE_ERROR: - if (vty) - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); - else - zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg); + snprintf(errmsg, errmsg_len, + "%% [%s] Argument form is unsupported or malformed.", + frr_protonameinst); retval = CMD_WARNING_CONFIG_FAILED; break; case RMAP_COMPILE_SUCCESS: @@ -438,26 +452,22 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index, return retval; } -int generic_set_add(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg) +int generic_set_add(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len) { enum rmap_compile_rets ret; ret = route_map_add_set(index, command, arg); switch (ret) { case RMAP_RULE_MISSING: - if (vty) - vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); - else - zlog_warn("Can't find rule: %s", command); + snprintf(errmsg, errmsg_len, + "%% [%s] Can't find rule.", frr_protonameinst); return CMD_WARNING_CONFIG_FAILED; case RMAP_COMPILE_ERROR: - if (vty) - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); - else - zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg); + snprintf(errmsg, errmsg_len, + "%% [%s] Argument form is unsupported or malformed.", + frr_protonameinst); return CMD_WARNING_CONFIG_FAILED; case RMAP_COMPILE_SUCCESS: break; @@ -466,26 +476,22 @@ int generic_set_add(struct vty *vty, struct route_map_index *index, return CMD_SUCCESS; } -int generic_set_delete(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg) +int generic_set_delete(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len) { enum rmap_compile_rets ret; ret = route_map_delete_set(index, command, arg); switch (ret) { case RMAP_RULE_MISSING: - if (vty) - vty_out(vty, "%% [%s] Can't find rule.\n", frr_protonameinst); - else - zlog_warn("Can't find rule: %s", command); + snprintf(errmsg, errmsg_len, "%% [%s] Can't find rule.", + frr_protonameinst); return CMD_WARNING_CONFIG_FAILED; case RMAP_COMPILE_ERROR: - if (vty) - vty_out(vty, - "%% [%s] Argument form is unsupported or malformed.\n", - frr_protonameinst); - else - zlog_warn("Argument form is unsupported or malformed: %s %s", command, arg); + snprintf(errmsg, errmsg_len, + "%% [%s] Argument form is unsupported or malformed.", + frr_protonameinst); return CMD_WARNING_CONFIG_FAILED; case RMAP_COMPILE_SUCCESS: break; @@ -2627,47 +2633,6 @@ static unsigned int route_map_dep_data_hash_make_key(const void *p) return string_hash_make(dep_data->rname); } -DEFUN (set_srte_color, - set_srte_color_cmd, - "set sr-te color [(1-4294967295)]", - SET_STR - SRTE_STR - SRTE_COLOR_STR - "Color of the SR-TE Policies to match with\n") -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - int idx = 0; - char *arg = argv_find(argv, argc, "(1-4294967295)", &idx) - ? argv[idx]->arg - : NULL; - - if (rmap_match_set_hook.set_srte_color) - return rmap_match_set_hook.set_srte_color(vty, index, - "sr-te color", arg); - return CMD_SUCCESS; -} - -DEFUN (no_set_srte_color, - no_set_srte_color_cmd, - "no set sr-te color [(1-4294967295)]", - NO_STR - SET_STR - SRTE_STR - SRTE_COLOR_STR - "Color of the SR-TE Policies to match with\n") -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - int idx = 0; - char *arg = argv_find(argv, argc, "(1-4294967295)", &idx) - ? argv[idx]->arg - : NULL; - - if (rmap_match_set_hook.no_set_srte_color) - return rmap_match_set_hook.no_set_srte_color( - vty, index, "sr-te color", arg); - return CMD_SUCCESS; -} - static void *route_map_dep_hash_alloc(void *p) { char *dep_name = (char *)p; @@ -2941,29 +2906,6 @@ void route_map_notify_dependencies(const char *affected_name, } /* VTY related functions. */ -DEFUN(no_routemap_optimization, no_routemap_optimization_cmd, - "no route-map optimization", - NO_STR - "route-map\n" - "optimization\n") -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - - index->map->optimization_disabled = true; - return CMD_SUCCESS; -} - -DEFUN(routemap_optimization, routemap_optimization_cmd, - "route-map optimization", - "route-map\n" - "optimization\n") -{ - VTY_DECLVAR_CONTEXT(route_map_index, index); - - index->map->optimization_disabled = false; - return CMD_SUCCESS; -} - static void clear_route_map_helper(struct route_map *map) { struct route_map_index *index; @@ -3276,11 +3218,5 @@ void route_map_init(void) install_element(ENABLE_NODE, &debug_rmap_cmd); install_element(ENABLE_NODE, &no_debug_rmap_cmd); - install_element(RMAP_NODE, &routemap_optimization_cmd); - install_element(RMAP_NODE, &no_routemap_optimization_cmd); - - install_element(RMAP_NODE, &set_srte_color_cmd); - install_element(RMAP_NODE, &no_set_srte_color_cmd); - install_element(ENABLE_NODE, &show_route_map_pfx_tbl_cmd); } diff --git a/lib/routemap.h b/lib/routemap.h index bad3ca6d3d..5b6b64eaeb 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -229,6 +229,144 @@ struct route_map { }; DECLARE_QOBJ_TYPE(route_map); +/* Route-map match conditions */ +#define IS_MATCH_INTERFACE(C) \ + (strmatch(C, "frr-route-map:interface")) +#define IS_MATCH_IPv4_ADDRESS_LIST(C) \ + (strmatch(C, "frr-route-map:ipv4-address-list")) +#define IS_MATCH_IPv6_ADDRESS_LIST(C) \ + (strmatch(C, "frr-route-map:ipv6-address-list")) +#define IS_MATCH_IPv4_NEXTHOP_LIST(C) \ + (strmatch(C, "frr-route-map:ipv4-next-hop-list")) +#define IS_MATCH_IPv4_PREFIX_LIST(C) \ + (strmatch(C, "frr-route-map:ipv4-prefix-list")) +#define IS_MATCH_IPv6_PREFIX_LIST(C) \ + (strmatch(C, "frr-route-map:ipv6-prefix-list")) +#define IS_MATCH_IPv4_NEXTHOP_PREFIX_LIST(C) \ + (strmatch(C, "frr-route-map:ipv4-next-hop-prefix-list")) +#define IS_MATCH_IPv4_NEXTHOP_TYPE(C) \ + (strmatch(C, "frr-route-map:ipv4-next-hop-type")) +#define IS_MATCH_IPv6_NEXTHOP_TYPE(C) \ + (strmatch(C, "frr-route-map:ipv6-next-hop-type")) +#define IS_MATCH_METRIC(C) \ + (strmatch(C, "frr-route-map:match-metric")) +#define IS_MATCH_TAG(C) (strmatch(C, "frr-route-map:match-tag")) +/* Zebra route-map match conditions */ +#define IS_MATCH_IPv4_PREFIX_LEN(C) \ + (strmatch(C, "frr-zebra-route-map:ipv4-prefix-length")) +#define IS_MATCH_IPv6_PREFIX_LEN(C) \ + (strmatch(C, "frr-zebra-route-map:ipv6-prefix-length")) +#define IS_MATCH_IPv4_NH_PREFIX_LEN(C) \ + (strmatch(C, "frr-zebra-route-map:ipv4-next-hop-prefix-length")) +#define IS_MATCH_SRC_PROTO(C) \ + (strmatch(C, "frr-zebra-route-map:source-protocol")) +#define IS_MATCH_SRC_INSTANCE(C) \ + (strmatch(C, "frr-zebra-route-map:source-instance")) +/* BGP route-map match conditions */ +#define IS_MATCH_LOCAL_PREF(C) \ + (strmatch(C, "frr-bgp-route-map:match-local-preference")) +#define IS_MATCH_ORIGIN(C) \ + (strmatch(C, "frr-bgp-route-map:match-origin")) +#define IS_MATCH_RPKI(C) (strmatch(C, "frr-bgp-route-map:rpki")) +#define IS_MATCH_PROBABILITY(C) \ + (strmatch(C, "frr-bgp-route-map:probability")) +#define IS_MATCH_SRC_VRF(C) \ + (strmatch(C, "frr-bgp-route-map:source-vrf")) +#define IS_MATCH_PEER(C) (strmatch(C, "frr-bgp-route-map:peer")) +#define IS_MATCH_AS_LIST(C) \ + (strmatch(C, "frr-bgp-route-map:as-path-list")) +#define IS_MATCH_MAC_LIST(C) \ + (strmatch(C, "frr-bgp-route-map:mac-address-list")) +#define IS_MATCH_EVPN_ROUTE_TYPE(C) \ + (strmatch(C, "frr-bgp-route-map:evpn-route-type")) +#define IS_MATCH_EVPN_DEFAULT_ROUTE(C) \ + (strmatch(C, "frr-bgp-route-map:evpn-default-route")) +#define IS_MATCH_EVPN_VNI(C) \ + (strmatch(C, "frr-bgp-route-map:evpn-vni")) +#define IS_MATCH_EVPN_DEFAULT_ROUTE(C) \ + (strmatch(C, "frr-bgp-route-map:evpn-default-route")) +#define IS_MATCH_EVPN_RD(C) \ + (strmatch(C, "frr-bgp-route-map:evpn-rd")) +#define IS_MATCH_ROUTE_SRC(C) \ + (strmatch(C, "frr-bgp-route-map:ip-route-source")) +#define IS_MATCH_ROUTE_SRC_PL(C) \ + (strmatch(C, "frr-bgp-route-map:ip-route-source-prefix-list")) +#define IS_MATCH_COMMUNITY(C) \ + (strmatch(C, "frr-bgp-route-map:match-community")) +#define IS_MATCH_LCOMMUNITY(C) \ + (strmatch(C, "frr-bgp-route-map:match-large-community")) +#define IS_MATCH_EXTCOMMUNITY(C) \ + (strmatch(C, "frr-bgp-route-map:match-extcommunity")) +#define IS_MATCH_IPV4_NH(C) \ + (strmatch(C, "frr-bgp-route-map:ipv4-nexthop")) +#define IS_MATCH_IPV6_NH(C) \ + (strmatch(C, "frr-bgp-route-map:ipv6-nexthop")) + +/* Route-map set actions */ +#define IS_SET_IPv4_NH(A) \ + (strmatch(A, "frr-route-map:ipv4-next-hop")) +#define IS_SET_IPv6_NH(A) \ + (strmatch(A, "frr-route-map:ipv6-next-hop")) +#define IS_SET_METRIC(A) \ + (strmatch(A, "frr-route-map:set-metric")) +#define IS_SET_TAG(A) (strmatch(A, "frr-route-map:set-tag")) +#define IS_SET_SR_TE_COLOR(A) \ + (strmatch(A, "frr-route-map:set-sr-te-color")) +/* Zebra route-map set actions */ +#define IS_SET_SRC(A) \ + (strmatch(A, "frr-zebra-route-map:src-address")) +/* OSPF route-map set actions */ +#define IS_SET_METRIC_TYPE(A) \ + (strmatch(A, "frr-ospf-route-map:metric-type")) +#define IS_SET_FORWARDING_ADDR(A) \ + (strmatch(A, "frr-ospf6-route-map:forwarding-address")) +/* BGP route-map_set actions */ +#define IS_SET_WEIGHT(A) \ + (strmatch(A, "frr-bgp-route-map:weight")) +#define IS_SET_TABLE(A) (strmatch(A, "frr-bgp-route-map:table")) +#define IS_SET_LOCAL_PREF(A) \ + (strmatch(A, "frr-bgp-route-map:set-local-preference")) +#define IS_SET_LABEL_INDEX(A) \ + (strmatch(A, "frr-bgp-route-map:label-index")) +#define IS_SET_DISTANCE(A) \ + (strmatch(A, "frr-bgp-route-map:distance")) +#define IS_SET_ORIGIN(A) \ + (strmatch(A, "frr-bgp-route-map:set-origin")) +#define IS_SET_ATOMIC_AGGREGATE(A) \ + (strmatch(A, "frr-bgp-route-map:atomic-aggregate")) +#define IS_SET_ORIGINATOR_ID(A) \ + (strmatch(A, "frr-bgp-route-map:originator-id")) +#define IS_SET_COMM_LIST_DEL(A) \ + (strmatch(A, "frr-bgp-route-map:comm-list-delete")) +#define IS_SET_LCOMM_LIST_DEL(A) \ + (strmatch(A, "frr-bgp-route-map:large-comm-list-delete")) +#define IS_SET_LCOMMUNITY(A) \ + (strmatch(A, "frr-bgp-route-map:set-large-community")) +#define IS_SET_COMMUNITY(A) \ + (strmatch(A, "frr-bgp-route-map:set-community")) +#define IS_SET_EXTCOMMUNITY_RT(A) \ + (strmatch(A, "frr-bgp-route-map:set-extcommunity-rt")) +#define IS_SET_EXTCOMMUNITY_SOO(A) \ + (strmatch(A, "frr-bgp-route-map:set-extcommunity-soo")) +#define IS_SET_AGGREGATOR(A) \ + (strmatch(A, "frr-bgp-route-map:aggregator")) +#define IS_SET_AS_PREPEND(A) \ + (strmatch(A, "frr-bgp-route-map:as-path-prepend")) +#define IS_SET_AS_EXCLUDE(A) \ + (strmatch(A, "frr-bgp-route-map:as-path-exclude")) +#define IS_SET_IPV6_NH_GLOBAL(A) \ + (strmatch(A, "frr-bgp-route-map:ipv6-nexthop-global")) +#define IS_SET_IPV6_VPN_NH(A) \ + (strmatch(A, "frr-bgp-route-map:ipv6-vpn-address")) +#define IS_SET_IPV6_PEER_ADDR(A) \ + (strmatch(A, "frr-bgp-route-map:ipv6-peer-address")) +#define IS_SET_IPV6_PREFER_GLOBAL(A) \ + (strmatch(A, "frr-bgp-route-map:ipv6-prefer-global")) +#define IS_SET_IPV4_VPN_NH(A) \ + (strmatch(A, "frr-bgp-route-map:ipv4-vpn-address")) +#define IS_SET_BGP_IPV4_NH(A) \ + (strmatch(A, "frr-bgp-route-map:set-ipv4-nexthop")) + /* Prototypes. */ extern void route_map_init(void); @@ -310,150 +448,186 @@ extern void route_map_notify_pentry_dependencies(const char *affected_name, struct prefix_list_entry *pentry, route_map_event_t event); -extern int generic_match_add(struct vty *vty, struct route_map_index *index, +extern int generic_match_add(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); - -extern int generic_match_delete(struct vty *vty, struct route_map_index *index, + route_map_event_t type, + char *errmsg, size_t errmsg_len); +extern int generic_match_delete(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); -extern int generic_set_add(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); -extern int generic_set_delete(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); + route_map_event_t type, + char *errmsg, size_t errmsg_len); + +extern int generic_set_add(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); +extern int generic_set_delete(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* match interface */ extern void route_map_match_interface_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match interface */ extern void route_map_no_match_interface_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* match ip address */ extern void route_map_match_ip_address_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match ip address */ extern void route_map_no_match_ip_address_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* match ip address prefix list */ extern void route_map_match_ip_address_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match ip address prefix list */ extern void route_map_no_match_ip_address_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* match ip next hop */ extern void route_map_match_ip_next_hop_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match ip next hop */ extern void route_map_no_match_ip_next_hop_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* match ip next hop prefix list */ extern void route_map_match_ip_next_hop_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match ip next hop prefix list */ extern void route_map_no_match_ip_next_hop_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* match ip next hop type */ extern void route_map_match_ip_next_hop_type_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match ip next hop type */ extern void route_map_no_match_ip_next_hop_type_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* match ipv6 address */ extern void route_map_match_ipv6_address_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match ipv6 address */ extern void route_map_no_match_ipv6_address_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* match ipv6 address prefix list */ extern void route_map_match_ipv6_address_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match ipv6 address prefix list */ extern void route_map_no_match_ipv6_address_prefix_list_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* match ipv6 next-hop type */ extern void route_map_match_ipv6_next_hop_type_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match ipv6 next-hop type */ extern void route_map_no_match_ipv6_next_hop_type_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* match metric */ extern void route_map_match_metric_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match metric */ extern void route_map_no_match_metric_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* match tag */ extern void route_map_match_tag_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* no match tag */ extern void route_map_no_match_tag_hook(int (*func)( - struct vty *vty, struct route_map_index *index, const char *command, - const char *arg, route_map_event_t type)); + struct route_map_index *index, const char *command, + const char *arg, route_map_event_t type, + char *errmsg, size_t errmsg_len)); /* set sr-te color */ extern void route_map_set_srte_color_hook( - int (*func)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg)); + int (*func)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len)); /* no set sr-te color */ extern void route_map_no_set_srte_color_hook( - int (*func)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg)); + int (*func)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len)); /* set ip nexthop */ extern void route_map_set_ip_nexthop_hook( - int (*func)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg)); + int (*func)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len)); /* no set ip nexthop */ extern void route_map_no_set_ip_nexthop_hook( - int (*func)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg)); + int (*func)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len)); /* set ipv6 nexthop local */ extern void route_map_set_ipv6_nexthop_local_hook( - int (*func)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg)); + int (*func)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len)); /* no set ipv6 nexthop local */ extern void route_map_no_set_ipv6_nexthop_local_hook( - int (*func)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg)); + int (*func)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len)); /* set metric */ -extern void route_map_set_metric_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +extern void route_map_set_metric_hook(int (*func)(struct route_map_index *index, const char *command, - const char *arg)); + const char *arg, + char *errmsg, + size_t errmsg_len)); /* no set metric */ extern void route_map_no_set_metric_hook( - int (*func)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg)); + int (*func)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len)); /* set tag */ -extern void route_map_set_tag_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +extern void route_map_set_tag_hook(int (*func)(struct route_map_index *index, const char *command, - const char *arg)); + const char *arg, + char *errmsg, + size_t errmsg_len)); /* no set tag */ -extern void route_map_no_set_tag_hook(int (*func)(struct vty *vty, - struct route_map_index *index, +extern void route_map_no_set_tag_hook(int (*func)(struct route_map_index *index, const char *command, - const char *arg)); + const char *arg, + char *errmsg, + size_t errmsg_len)); extern void *route_map_rule_tag_compile(const char *arg); extern void route_map_rule_tag_free(void *rule); @@ -467,181 +641,200 @@ extern void route_map_counter_decrement(struct route_map *map); /* Route map hooks data structure. */ struct route_map_match_set_hooks { /* match interface */ - int (*match_interface)(struct vty *vty, struct route_map_index *index, + int (*match_interface)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* no match interface */ - int (*no_match_interface)(struct vty *vty, - struct route_map_index *index, + int (*no_match_interface)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* match ip address */ - int (*match_ip_address)(struct vty *vty, struct route_map_index *index, + int (*match_ip_address)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* no match ip address */ - int (*no_match_ip_address)(struct vty *vty, - struct route_map_index *index, + int (*no_match_ip_address)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* match ip address prefix list */ - int (*match_ip_address_prefix_list)(struct vty *vty, - struct route_map_index *index, + int (*match_ip_address_prefix_list)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* no match ip address prefix list */ - int (*no_match_ip_address_prefix_list)(struct vty *vty, - struct route_map_index *index, + int (*no_match_ip_address_prefix_list)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* match ip next hop */ - int (*match_ip_next_hop)(struct vty *vty, struct route_map_index *index, + int (*match_ip_next_hop)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* no match ip next hop */ - int (*no_match_ip_next_hop)(struct vty *vty, - struct route_map_index *index, + int (*no_match_ip_next_hop)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* match ip next hop prefix list */ - int (*match_ip_next_hop_prefix_list)(struct vty *vty, - struct route_map_index *index, + int (*match_ip_next_hop_prefix_list)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* no match ip next hop prefix list */ - int (*no_match_ip_next_hop_prefix_list)(struct vty *vty, - struct route_map_index *index, + int (*no_match_ip_next_hop_prefix_list)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, + size_t errmsg_len); /* match ip next-hop type */ - int (*match_ip_next_hop_type)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); + int (*match_ip_next_hop_type)(struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type, + char *errmsg, + size_t errmsg_len); /* no match ip next-hop type */ - int (*no_match_ip_next_hop_type)(struct vty *vty, - struct route_map_index *index, - const char *command, - const char *arg, - route_map_event_t type); + int (*no_match_ip_next_hop_type)(struct route_map_index *index, + const char *command, + const char *arg, + route_map_event_t type, + char *errmsg, + size_t errmsg_len); /* match ipv6 address */ - int (*match_ipv6_address)(struct vty *vty, - struct route_map_index *index, + int (*match_ipv6_address)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* no match ipv6 address */ - int (*no_match_ipv6_address)(struct vty *vty, - struct route_map_index *index, + int (*no_match_ipv6_address)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* match ipv6 address prefix list */ - int (*match_ipv6_address_prefix_list)(struct vty *vty, - struct route_map_index *index, + int (*match_ipv6_address_prefix_list)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* no match ipv6 address prefix list */ - int (*no_match_ipv6_address_prefix_list)(struct vty *vty, - struct route_map_index *index, + int (*no_match_ipv6_address_prefix_list)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, + size_t errmsg_len); /* match ipv6 next-hop type */ - int (*match_ipv6_next_hop_type)(struct vty *vty, - struct route_map_index *index, + int (*match_ipv6_next_hop_type)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* no match ipv6 next-hop type */ - int (*no_match_ipv6_next_hop_type)(struct vty *vty, - struct route_map_index *index, + int (*no_match_ipv6_next_hop_type)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* match metric */ - int (*match_metric)(struct vty *vty, struct route_map_index *index, + int (*match_metric)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* no match metric */ - int (*no_match_metric)(struct vty *vty, struct route_map_index *index, + int (*no_match_metric)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* match tag */ - int (*match_tag)(struct vty *vty, struct route_map_index *index, + int (*match_tag)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* no match tag */ - int (*no_match_tag)(struct vty *vty, struct route_map_index *index, + int (*no_match_tag)(struct route_map_index *index, const char *command, const char *arg, - route_map_event_t type); + route_map_event_t type, + char *errmsg, size_t errmsg_len); /* set sr-te color */ - int (*set_srte_color)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); + int (*set_srte_color)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* no set sr-te color */ - int (*no_set_srte_color)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); + int (*no_set_srte_color)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* set ip nexthop */ - int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); + int (*set_ip_nexthop)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* no set ip nexthop */ - int (*no_set_ip_nexthop)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); + int (*no_set_ip_nexthop)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* set ipv6 nexthop local */ - int (*set_ipv6_nexthop_local)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg); + int (*set_ipv6_nexthop_local)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* no set ipv6 nexthop local */ - int (*no_set_ipv6_nexthop_local)(struct vty *vty, - struct route_map_index *index, - const char *command, const char *arg); + int (*no_set_ipv6_nexthop_local)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* set metric */ - int (*set_metric)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); + int (*set_metric)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* no set metric */ - int (*no_set_metric)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); + int (*no_set_metric)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* set tag */ - int (*set_tag)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); + int (*set_tag)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* no set tag */ - int (*no_set_tag)(struct vty *vty, struct route_map_index *index, - const char *command, const char *arg); + int (*no_set_tag)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); }; extern struct route_map_match_set_hooks rmap_match_set_hook; @@ -666,15 +859,13 @@ extern struct route_map_index *route_map_index_get(struct route_map *map, extern void route_map_index_delete(struct route_map_index *index, int notify); /* routemap_northbound.c */ -typedef int (*routemap_match_hook_fun)(struct vty *vty, - struct route_map_index *rmi, +typedef int (*routemap_match_hook_fun)(struct route_map_index *rmi, const char *command, const char *arg, - route_map_event_t event); - -typedef int (*routemap_set_hook_fun)(struct vty *vty, - struct route_map_index *rmi, - const char *command, const char *arg); - + route_map_event_t event, + char *errmsg, size_t errmsg_len); +typedef int (*routemap_set_hook_fun)(struct route_map_index *rmi, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); struct routemap_hook_context { struct route_map_index *rhc_rmi; const char *rhc_rule; @@ -694,6 +885,8 @@ void routemap_hook_context_free(struct routemap_hook_context *rhc); extern const struct frr_yang_module_info frr_route_map_info; /* routemap_cli.c */ +extern int route_map_instance_cmp(struct lyd_node *dnode1, + struct lyd_node *dnode2); extern void route_map_instance_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults); extern void route_map_instance_show_end(struct vty *vty, @@ -709,6 +902,9 @@ extern void route_map_call_show(struct vty *vty, struct lyd_node *dnode, extern void route_map_description_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +extern void route_map_optimization_disabled_show(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults); extern void route_map_cli_init(void); #ifdef __cplusplus diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 339d025124..e11b9eea74 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -46,9 +46,6 @@ DEFPY_YANG_NOSH( ROUTE_MAP_OP_CMD_STR ROUTE_MAP_SEQUENCE_CMD_STR) { - struct route_map_index *rmi; - struct route_map *rm; - int action_type; char xpath_action[XPATH_MAXLEN + 64]; char xpath_index[XPATH_MAXLEN + 32]; char xpath[XPATH_MAXLEN]; @@ -66,17 +63,9 @@ DEFPY_YANG_NOSH( nb_cli_enqueue_change(vty, xpath_action, NB_OP_MODIFY, action); rv = nb_cli_apply_changes(vty, NULL); - if (rv == CMD_SUCCESS) { + if (rv == CMD_SUCCESS) VTY_PUSH_XPATH(RMAP_NODE, xpath_index); - /* Add support for non-migrated route map users. */ - nb_cli_pending_commit_check(vty); - rm = route_map_get(name); - action_type = (action[0] == 'p') ? RMAP_PERMIT : RMAP_DENY; - rmi = route_map_index_get(rm, action_type, sequence); - VTY_PUSH_CONTEXT(RMAP_NODE, rmi); - } - return rv; } @@ -108,71 +97,29 @@ DEFPY_YANG( snprintf(xpath, sizeof(xpath), "/frr-route-map:lib/route-map[name='%s']/entry[sequence='%lu']", name, sequence); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } +int route_map_instance_cmp(struct lyd_node *dnode1, struct lyd_node *dnode2) +{ + uint16_t seq1 = yang_dnode_get_uint16(dnode1, "./sequence"); + uint16_t seq2 = yang_dnode_get_uint16(dnode2, "./sequence"); + + return seq1 - seq2; +} + void route_map_instance_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - const struct route_map_rule *rmr; - const struct route_map_index *rmi; const char *name = yang_dnode_get_string(dnode, "../name"); const char *action = yang_dnode_get_string(dnode, "./action"); const char *sequence = yang_dnode_get_string(dnode, "./sequence"); vty_out(vty, "route-map %s %s %s\n", name, action, sequence); - rmi = nb_running_get_entry(dnode, NULL, false); - if (rmi == NULL) { - /* - * We can't have outdated rules if route map hasn't - * been created yet. - */ - return; - } - -#define SKIP_RULE(name) if (strcmp((name), rmr->cmd->str) == 0) continue - - /* Print route map `match` for old CLI users. */ - for (rmr = rmi->match_list.head; rmr; rmr = rmr->next) { - /* Skip all matches implemented by northbound. */ - SKIP_RULE("interface"); - SKIP_RULE("ip address"); - SKIP_RULE("ip address prefix-list"); - SKIP_RULE("ip next-hop"); - SKIP_RULE("ip next-hop prefix-list"); - SKIP_RULE("ip next-hop type"); - SKIP_RULE("ipv6 address"); - SKIP_RULE("ipv6 address prefix-list"); - SKIP_RULE("ipv6 next-hop type"); - SKIP_RULE("metric"); - SKIP_RULE("tag"); - /* Zebra specific match conditions. */ - SKIP_RULE("ip address prefix-len"); - SKIP_RULE("ipv6 address prefix-len"); - SKIP_RULE("ip next-hop prefix-len"); - SKIP_RULE("source-protocol"); - SKIP_RULE("source-instance"); - - vty_out(vty, " match %s %s\n", rmr->cmd->str, - rmr->rule_str ? rmr->rule_str : ""); - } - - /* Print route map `set` for old CLI users. */ - for (rmr = rmi->set_list.head; rmr; rmr = rmr->next) { - /* Skip all sets implemented by northbound. */ - SKIP_RULE("metric"); - SKIP_RULE("tag"); - /* Zebra specific set actions. */ - SKIP_RULE("src"); - - vty_out(vty, " set %s %s\n", rmr->cmd->str, - rmr->rule_str ? rmr->rule_str : ""); - } - -#undef SKIP_RULE } void route_map_instance_show_end(struct vty *vty, struct lyd_node *dnode) @@ -187,11 +134,13 @@ DEFPY_YANG( "Match first hop interface of route\n" INTERFACE_STR) { - const char *xpath = "./match-condition[condition='interface']"; + const char *xpath = + "./match-condition[condition='frr-route-map:interface']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/interface", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/interface", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ifname); return nb_cli_apply_changes(vty, NULL); @@ -205,7 +154,8 @@ DEFPY_YANG( "Match first hop interface of route\n" INTERFACE_STR) { - const char *xpath = "./match-condition[condition='interface']"; + const char *xpath = + "./match-condition[condition='frr-route-map:interface']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -222,11 +172,13 @@ DEFPY_YANG( "IP access-list number (expanded range)\n" "IP Access-list name\n") { - const char *xpath = "./match-condition[condition='ipv4-address-list']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv4-address-list']"; char xpath_value[XPATH_MAXLEN + 32]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/list-name", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); return nb_cli_apply_changes(vty, NULL); @@ -243,7 +195,8 @@ DEFPY_YANG( "IP access-list number (expanded range)\n" "IP Access-list name\n") { - const char *xpath = "./match-condition[condition='ipv4-address-list']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv4-address-list']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -260,11 +213,13 @@ DEFPY_YANG( "Match entries of prefix-lists\n" "IP prefix-list name\n") { - const char *xpath = "./match-condition[condition='ipv4-prefix-list']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv4-prefix-list']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/list-name", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); return nb_cli_apply_changes(vty, NULL); @@ -280,7 +235,8 @@ DEFPY_YANG( "Match entries of prefix-lists\n" "IP prefix-list name\n") { - const char *xpath = "./match-condition[condition='ipv4-prefix-list']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv4-prefix-list']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -297,11 +253,13 @@ DEFPY_YANG( "IP access-list number (expanded range)\n" "IP Access-list name\n") { - const char *xpath = "./match-condition[condition='ipv4-next-hop-list']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv4-next-hop-list']"; char xpath_value[XPATH_MAXLEN + 32]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/list-name", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); return nb_cli_apply_changes(vty, NULL); @@ -318,7 +276,8 @@ DEFPY_YANG( "IP access-list number (expanded range)\n" "IP Access-list name\n") { - const char *xpath = "./match-condition[condition='ipv4-next-hop-list']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv4-next-hop-list']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -336,11 +295,12 @@ DEFPY_YANG( "IP prefix-list name\n") { const char *xpath = - "./match-condition[condition='ipv4-next-hop-prefix-list']"; + "./match-condition[condition='frr-route-map:ipv4-next-hop-prefix-list']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/list-name", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); return nb_cli_apply_changes(vty, NULL); @@ -358,7 +318,7 @@ DEFPY_YANG( "IP prefix-list name\n") { const char *xpath = - "./match-condition[condition='ipv4-next-hop-prefix-list']"; + "./match-condition[condition='frr-route-map:ipv4-next-hop-prefix-list']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -374,12 +334,13 @@ DEFPY_YANG( "Match entries by type\n" "Blackhole\n") { - const char *xpath = "./match-condition[condition='ipv4-next-hop-type']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv4-next-hop-type']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-next-hop-type", - xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/ipv4-next-hop-type", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type); return nb_cli_apply_changes(vty, NULL); @@ -393,7 +354,8 @@ DEFPY_YANG( "Match entries by type\n" "Blackhole\n") { - const char *xpath = "./match-condition[condition='ipv4-next-hop-type']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv4-next-hop-type']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -408,11 +370,13 @@ DEFPY_YANG( "Match IPv6 address of route\n" "IPv6 access-list name\n") { - const char *xpath = "./match-condition[condition='ipv6-address-list']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv6-address-list']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/list-name", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); return nb_cli_apply_changes(vty, NULL); @@ -427,7 +391,8 @@ DEFPY_YANG( "Match IPv6 address of route\n" "IPv6 access-list name\n") { - const char *xpath = "./match-condition[condition='ipv6-address-list']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv6-address-list']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -443,11 +408,13 @@ DEFPY_YANG( "Match entries of prefix-lists\n" "IP prefix-list name\n") { - const char *xpath = "./match-condition[condition='ipv6-prefix-list']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv6-prefix-list']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/list-name", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/list-name", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, name); return nb_cli_apply_changes(vty, NULL); @@ -464,7 +431,8 @@ DEFPY_YANG( "Match entries of prefix-lists\n" "IP prefix-list name\n") { - const char *xpath = "./match-condition[condition='ipv6-prefix-list']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv6-prefix-list']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -479,12 +447,13 @@ DEFPY_YANG( "Match entries by type\n" "Blackhole\n") { - const char *xpath = "./match-condition[condition='ipv6-next-hop-type']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv6-next-hop-type']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-next-hop-type", - xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/ipv6-next-hop-type", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, type); return nb_cli_apply_changes(vty, NULL); @@ -498,7 +467,8 @@ DEFPY_YANG( "Match entries by type\n" "Blackhole\n") { - const char *xpath = "./match-condition[condition='ipv6-next-hop-type']"; + const char *xpath = + "./match-condition[condition='frr-route-map:ipv6-next-hop-type']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -512,11 +482,13 @@ DEFPY_YANG( "Match metric of route\n" "Metric value\n") { - const char *xpath = "./match-condition[condition='metric']"; + const char *xpath = + "./match-condition[condition='frr-route-map:match-metric']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/metric", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/metric", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, metric_str); return nb_cli_apply_changes(vty, NULL); @@ -530,7 +502,8 @@ DEFPY_YANG( "Match metric of route\n" "Metric value\n") { - const char *xpath = "./match-condition[condition='metric']"; + const char *xpath = + "./match-condition[condition='frr-route-map:match-metric']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -544,11 +517,13 @@ DEFPY_YANG( "Match tag of route\n" "Tag value\n") { - const char *xpath = "./match-condition[condition='tag']"; + const char *xpath = + "./match-condition[condition='frr-route-map:match-tag']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/tag", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str); return nb_cli_apply_changes(vty, NULL); @@ -562,7 +537,8 @@ DEFPY_YANG( "Match tag of route\n" "Tag value\n") { - const char *xpath = "./match-condition[condition='tag']"; + const char *xpath = + "./match-condition[condition='frr-route-map:match-tag']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -572,78 +548,259 @@ DEFPY_YANG( void route_map_condition_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - int condition = yang_dnode_get_enum(dnode, "./condition"); + const char *condition = yang_dnode_get_string(dnode, "./condition"); + struct lyd_node *ln; + const char *acl; - switch (condition) { - case 0: /* interface */ + if (IS_MATCH_INTERFACE(condition)) { vty_out(vty, " match interface %s\n", - yang_dnode_get_string(dnode, "./interface")); - break; - case 1: /* ipv4-address-list */ - case 3: /* ipv4-next-hop-list */ - switch (condition) { - case 1: - vty_out(vty, " match ip address %s\n", - yang_dnode_get_string(dnode, "./list-name")); - break; - case 3: - vty_out(vty, " match ip next-hop %s\n", - yang_dnode_get_string(dnode, "./list-name")); - break; - } - break; - case 2: /* ipv4-prefix-list */ + yang_dnode_get_string( + dnode, "./rmap-match-condition/interface")); + } else if (IS_MATCH_IPv4_ADDRESS_LIST(condition) + || IS_MATCH_IPv4_NEXTHOP_LIST(condition)) { + acl = NULL; + if ((ln = yang_dnode_get(dnode, + "./rmap-match-condition/list-name")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + + assert(acl); + + if (IS_MATCH_IPv4_ADDRESS_LIST(condition)) + vty_out(vty, " match ip address %s\n", acl); + else + vty_out(vty, " match ip next-hop %s\n", acl); + } else if (IS_MATCH_IPv4_PREFIX_LIST(condition)) { vty_out(vty, " match ip address prefix-list %s\n", - yang_dnode_get_string(dnode, "./list-name")); - break; - case 4: /* ipv4-next-hop-prefix-list */ + yang_dnode_get_string( + dnode, "./rmap-match-condition/list-name")); + } else if (IS_MATCH_IPv4_NEXTHOP_PREFIX_LIST(condition)) { vty_out(vty, " match ip next-hop prefix-list %s\n", - yang_dnode_get_string(dnode, "./list-name")); - break; - case 5: /* ipv4-next-hop-type */ - vty_out(vty, " match ip next-hop type %s\n", - yang_dnode_get_string(dnode, "./ipv4-next-hop-type")); - break; - case 6: /* ipv6-address-list */ + yang_dnode_get_string( + dnode, "./rmap-match-condition/list-name")); + } else if (IS_MATCH_IPv6_ADDRESS_LIST(condition)) { vty_out(vty, " match ipv6 address %s\n", - yang_dnode_get_string(dnode, "./list-name")); - break; - case 7: /* ipv6-prefix-list */ + yang_dnode_get_string( + dnode, "./rmap-match-condition/list-name")); + } else if (IS_MATCH_IPv6_PREFIX_LIST(condition)) { vty_out(vty, " match ipv6 address prefix-list %s\n", - yang_dnode_get_string(dnode, "./list-name")); - break; - case 8: /* ipv6-next-hop-type */ + yang_dnode_get_string( + dnode, "./rmap-match-condition/list-name")); + } else if (IS_MATCH_IPv4_NEXTHOP_TYPE(condition)) { + vty_out(vty, " match ip next-hop type %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/ipv4-next-hop-type")); + } else if (IS_MATCH_IPv6_NEXTHOP_TYPE(condition)) { vty_out(vty, " match ipv6 next-hop type %s\n", - yang_dnode_get_string(dnode, "./ipv6-next-hop-type")); - break; - case 9: /* metric */ + yang_dnode_get_string( + dnode, + "./rmap-match-condition/ipv6-next-hop-type")); + } else if (IS_MATCH_METRIC(condition)) { vty_out(vty, " match metric %s\n", - yang_dnode_get_string(dnode, "./metric")); - break; - case 10: /* tag */ + yang_dnode_get_string(dnode, + "./rmap-match-condition/metric")); + } else if (IS_MATCH_TAG(condition)) { vty_out(vty, " match tag %s\n", - yang_dnode_get_string(dnode, "./tag")); - break; - case 100: /* ipv4-prefix-length */ + yang_dnode_get_string(dnode, + "./rmap-match-condition/tag")); + } else if (IS_MATCH_IPv4_PREFIX_LEN(condition)) { vty_out(vty, " match ip address prefix-len %s\n", - yang_dnode_get_string(dnode,"./frr-zebra:ipv4-prefix-length")); - break; - case 101: /* ipv6-prefix-length */ + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length")); + } else if (IS_MATCH_IPv6_PREFIX_LEN(condition)) { vty_out(vty, " match ipv6 address prefix-len %s\n", - yang_dnode_get_string(dnode, "./frr-zebra:ipv6-prefix-length")); - break; - case 102: /* ipv4-next-hop-prefix-length */ + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-zebra-route-map:ipv6-prefix-length")); + } else if (IS_MATCH_IPv4_NH_PREFIX_LEN(condition)) { vty_out(vty, " match ip next-hop prefix-len %s\n", - yang_dnode_get_string(dnode, "./frr-zebra:ipv4-prefix-length")); - break; - case 103: /* source-protocol */ + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length")); + } else if (IS_MATCH_SRC_PROTO(condition)) { vty_out(vty, " match source-protocol %s\n", - yang_dnode_get_string(dnode, "./frr-zebra:source-protocol")); - break; - case 104: /* source-instance */ + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-zebra-route-map:source-protocol")); + } else if (IS_MATCH_SRC_INSTANCE(condition)) { vty_out(vty, " match source-instance %s\n", - yang_dnode_get_string(dnode, "./frr-zebra:source-instance")); - break; + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-zebra-route-map:source-instance")); + } else if (IS_MATCH_LOCAL_PREF(condition)) { + vty_out(vty, " match local-preference %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:local-preference")); + } else if (IS_MATCH_ORIGIN(condition)) { + vty_out(vty, " match origin %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:origin")); + } else if (IS_MATCH_RPKI(condition)) { + vty_out(vty, " match rpki %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:rpki")); + } else if (IS_MATCH_PROBABILITY(condition)) { + vty_out(vty, " match probability %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:probability")); + } else if (IS_MATCH_SRC_VRF(condition)) { + vty_out(vty, " match source-vrf %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:source-vrf")); + } else if (IS_MATCH_PEER(condition)) { + acl = NULL; + if ((ln = yang_dnode_get( + dnode, + "./rmap-match-condition/frr-bgp-route-map:peer-ipv4-address")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + else if ( + (ln = yang_dnode_get( + dnode, + "./rmap-match-condition/frr-bgp-route-map:peer-ipv6-address")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + else if ( + (ln = yang_dnode_get( + dnode, + "./rmap-match-condition/frr-bgp-route-map:peer-interface")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + else if (yang_dnode_get( + dnode, + "./rmap-match-condition/frr-bgp-route-map:peer-local") + != NULL) + acl = "local"; + + vty_out(vty, " match peer %s\n", acl); + } else if (IS_MATCH_AS_LIST(condition)) { + vty_out(vty, " match as-path %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:list-name")); + } else if (IS_MATCH_EVPN_ROUTE_TYPE(condition)) { + vty_out(vty, " match evpn route-type %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:evpn-route-type")); + } else if (IS_MATCH_EVPN_DEFAULT_ROUTE(condition)) { + vty_out(vty, " match evpn default-route\n"); + } else if (IS_MATCH_EVPN_VNI(condition)) { + vty_out(vty, " match evpn vni %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:evpn-vni")); + } else if (IS_MATCH_EVPN_DEFAULT_ROUTE(condition)) { + vty_out(vty, " match evpn default-route %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:evpn-default-route")); + } else if (IS_MATCH_EVPN_RD(condition)) { + vty_out(vty, " match evpn rd %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:route-distinguisher")); + } else if (IS_MATCH_MAC_LIST(condition)) { + vty_out(vty, " match mac address %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:list-name")); + } else if (IS_MATCH_ROUTE_SRC(condition)) { + acl = NULL; + if ((ln = yang_dnode_get( + dnode, + "./rmap-match-condition/frr-bgp-route-map:list-name")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + + assert(acl); + + vty_out(vty, " match ip route-source %s\n", acl); + } else if (IS_MATCH_ROUTE_SRC_PL(condition)) { + vty_out(vty, " match ip route-source prefix-list %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:list-name")); + } else if (IS_MATCH_ROUTE_SRC(condition)) { + acl = NULL; + if ((ln = yang_dnode_get( + dnode, + "./rmap-match-condition/frr-bgp-route-map:list-name")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + + assert(acl); + + vty_out(vty, " match ip route-source %s\n", acl); + } else if (IS_MATCH_COMMUNITY(condition)) { + acl = NULL; + if ((ln = yang_dnode_get( + dnode, + "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name")) + != NULL) { + acl = yang_dnode_get_string(ln, NULL); + + if (true + == yang_dnode_get_bool( + dnode, + "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match")) + vty_out(vty, + " match community %s exact-match\n", + acl); + else + vty_out(vty, " match community %s\n", acl); + } + + assert(acl); + } else if (IS_MATCH_LCOMMUNITY(condition)) { + acl = NULL; + if ((ln = yang_dnode_get( + dnode, + "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name")) + != NULL) { + acl = yang_dnode_get_string(ln, NULL); + + if (true + == yang_dnode_get_bool( + dnode, + "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match")) + vty_out(vty, + " match large-community %s exact-match\n", + acl); + else + vty_out(vty, " match large-community %s\n", + acl); + } + + assert(acl); + } else if (IS_MATCH_EXTCOMMUNITY(condition)) { + acl = NULL; + if ((ln = yang_dnode_get( + dnode, + "./rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + + assert(acl); + + vty_out(vty, " match extcommunity %s\n", acl); + } else if (IS_MATCH_IPV4_NH(condition)) { + vty_out(vty, " match ip next-hop %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:ipv4-address")); + } else if (IS_MATCH_IPV6_NH(condition)) { + vty_out(vty, " match ipv6 next-hop %s\n", + yang_dnode_get_string( + dnode, + "./rmap-match-condition/frr-bgp-route-map:ipv6-address")); } } @@ -655,11 +812,13 @@ DEFPY_YANG( "Next hop address\n" "IP address of next hop\n") { - const char *xpath = "./set-action[action='ipv4-next-hop']"; + const char *xpath = + "./set-action[action='frr-route-map:ipv4-next-hop']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/ipv4-address", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/ipv4-address", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str); return nb_cli_apply_changes(vty, NULL); @@ -674,7 +833,8 @@ DEFPY_YANG( "Next hop address\n" "IP address of next hop\n") { - const char *xpath = "./set-action[action='ipv4-next-hop']"; + const char *xpath = + "./set-action[action='frr-route-map:ipv4-next-hop']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -690,11 +850,13 @@ DEFPY_YANG( "IPv6 local address\n" "IPv6 address of next hop\n") { - const char *xpath = "./set-action[action='ipv6-next-hop']"; + const char *xpath = + "./set-action[action='frr-route-map:ipv6-next-hop']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/ipv6-address", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/ipv6-address", xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addr_str); return nb_cli_apply_changes(vty, NULL); @@ -710,7 +872,8 @@ DEFPY_YANG( "IPv6 local address\n" "IPv6 address of next hop\n") { - const char *xpath = "./set-action[action='ipv6-next-hop']"; + const char *xpath = + "./set-action[action='frr-route-map:ipv6-next-hop']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -727,33 +890,34 @@ DEFPY_YANG( "Add round trip time\n" "Subtract round trip time\n") { - const char *xpath = "./set-action[action='metric']"; + const char *xpath = "./set-action[action='frr-route-map:set-metric']"; char xpath_value[XPATH_MAXLEN]; char value[64]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); if (rtt) { snprintf(xpath_value, sizeof(xpath_value), - "%s/use-round-trip-time", xpath); + "%s/rmap-set-action/use-round-trip-time", xpath); snprintf(value, sizeof(value), "true"); } else if (artt) { snprintf(xpath_value, sizeof(xpath_value), - "%s/add-round-trip-time", xpath); + "%s/rmap-set-action/add-round-trip-time", xpath); snprintf(value, sizeof(value), "true"); } else if (srtt) { snprintf(xpath_value, sizeof(xpath_value), - "%s/subtract-round-trip-time", xpath); + "%s/rmap-set-action/subtract-round-trip-time", xpath); snprintf(value, sizeof(value), "true"); } else if (metric_str && metric_str[0] == '+') { - snprintf(xpath_value, sizeof(xpath_value), "%s/add-metric", - xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/add-metric", xpath); snprintf(value, sizeof(value), "%s", ++metric_str); } else if (metric_str && metric_str[0] == '-') { - snprintf(xpath_value, sizeof(xpath_value), "%s/subtract-metric", - xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/subtract-metric", xpath); snprintf(value, sizeof(value), "%s", ++metric_str); } else { - snprintf(xpath_value, sizeof(xpath_value), "%s/value", xpath); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/value", xpath); snprintf(value, sizeof(value), "%s", metric_str); } nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, value); @@ -769,7 +933,7 @@ DEFPY_YANG( "Metric value for destination routing protocol\n" "Metric value\n") { - const char *xpath = "./set-action[action='metric']"; + const char *xpath = "./set-action[action='frr-route-map:set-metric']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); @@ -782,11 +946,12 @@ DEFPY_YANG( "Tag value for routing protocol\n" "Tag value\n") { - const char *xpath = "./set-action[action='tag']"; + const char *xpath = "./set-action[action='frr-route-map:set-tag']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), "%s/tag", xpath); + snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-set-action/tag", + xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, tag_str); return nb_cli_apply_changes(vty, NULL); @@ -800,58 +965,289 @@ DEFPY_YANG( "Tag value for routing protocol\n" "Tag value\n") { - const char *xpath = "./set-action[action='tag']"; + const char *xpath = "./set-action[action='frr-route-map:set-tag']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (set_srte_color, + set_srte_color_cmd, + "set sr-te color (1-4294967295)", + SET_STR + SRTE_STR + SRTE_COLOR_STR + "Color of the SR-TE Policies to match with\n") +{ + const char *xpath = + "./set-action[action='frr-route-map:set-sr-te-color']"; + char xpath_value[XPATH_MAXLEN]; + int idx = 0; + + char *arg = argv_find(argv, argc, "(1-4294967295)", &idx) + ? argv[idx]->arg + : NULL; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/policy", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, arg); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_srte_color, + no_set_srte_color_cmd, + "no set sr-te color [(1-4294967295)]", + NO_STR + SET_STR + SRTE_STR + SRTE_COLOR_STR + "Color of the SR-TE Policies to match with\n") +{ + const char *xpath = + "./set-action[action='frr-route-map:set-sr-te-color']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } + void route_map_action_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { - int action = yang_dnode_get_enum(dnode, "./action"); + const char *action = yang_dnode_get_string(dnode, "./action"); + struct lyd_node *ln; + const char *acl; - switch (action) { - case 0: /* ipv4-next-hop */ + if (IS_SET_IPv4_NH(action)) { vty_out(vty, " set ip next-hop %s\n", - yang_dnode_get_string(dnode, "./ipv4-address")); - break; - case 1: /* ipv6-next-hop */ + yang_dnode_get_string( + dnode, "./rmap-set-action/ipv4-address")); + } else if (IS_SET_IPv6_NH(action)) { vty_out(vty, " set ipv6 next-hop local %s\n", - yang_dnode_get_string(dnode, "./ipv6-address")); - break; - case 2: /* metric */ - if (yang_dnode_get(dnode, "./use-round-trip-time")) { + yang_dnode_get_string( + dnode, "./rmap-set-action/ipv6-address")); + } else if (IS_SET_METRIC(action)) { + if (yang_dnode_get(dnode, + "./rmap-set-action/use-round-trip-time")) { vty_out(vty, " set metric rtt\n"); - } else if (yang_dnode_get(dnode, "./add-round-trip-time")) { + } else if (yang_dnode_get( + dnode, + "./rmap-set-action/add-round-trip-time")) { vty_out(vty, " set metric +rtt\n"); - } else if (yang_dnode_get(dnode, "./subtract-round-trip-time")) { + } else if ( + yang_dnode_get( + dnode, + "./rmap-set-action/subtract-round-trip-time")) { vty_out(vty, " set metric -rtt\n"); - } else if (yang_dnode_get(dnode, "./add-metric")) { + } else if (yang_dnode_get(dnode, + "./rmap-set-action/add-metric")) { vty_out(vty, " set metric +%s\n", - yang_dnode_get_string(dnode, "./add-metric")); - } else if (yang_dnode_get(dnode, "./subtract-metric")) { + yang_dnode_get_string( + dnode, "./rmap-set-action/add-metric")); + } else if (yang_dnode_get( + dnode, + "./rmap-set-action/subtract-metric")) { vty_out(vty, " set metric -%s\n", - yang_dnode_get_string(dnode, - "./subtract-metric")); + yang_dnode_get_string( + dnode, + "./rmap-set-action/subtract-metric")); } else { vty_out(vty, " set metric %s\n", - yang_dnode_get_string(dnode, "./value")); + yang_dnode_get_string( + dnode, "./rmap-set-action/value")); } - break; - case 3: /* tag */ + } else if (IS_SET_TAG(action)) { vty_out(vty, " set tag %s\n", - yang_dnode_get_string(dnode, "./tag")); - break; - case 100: /* source */ - if (yang_dnode_exists(dnode, "./frr-zebra:source-v4")) + yang_dnode_get_string(dnode, "./rmap-set-action/tag")); + } else if (IS_SET_SR_TE_COLOR(action)) { + vty_out(vty, " set sr-te color %s\n", + yang_dnode_get_string(dnode, + "./rmap-set-action/policy")); + } else if (IS_SET_SRC(action)) { + if (yang_dnode_exists( + dnode, + "./rmap-set-action/frr-zebra-route-map:ipv4-src-address")) vty_out(vty, " set src %s\n", - yang_dnode_get_string(dnode, "./frr-zebra:source-v4")); + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-zebra-route-map:ipv4-src-address")); else vty_out(vty, " set src %s\n", - yang_dnode_get_string(dnode, "./frr-zebra:source-v6")); - break; + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-zebra-route-map:ipv6-src-address")); + } else if (IS_SET_METRIC_TYPE(action)) { + vty_out(vty, " set metric-type %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-ospf-route-map:metric-type")); + } else if (IS_SET_FORWARDING_ADDR(action)) { + vty_out(vty, " set forwarding-address %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-ospf6-route-map:ipv6-address")); + } else if (IS_SET_WEIGHT(action)) { + vty_out(vty, " set weight %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:weight")); + } else if (IS_SET_TABLE(action)) { + vty_out(vty, " set table %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:table")); + } else if (IS_SET_LOCAL_PREF(action)) { + vty_out(vty, " set local-preference %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:local-pref")); + } else if (IS_SET_LABEL_INDEX(action)) { + vty_out(vty, " set label-index %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:label-index")); + } else if (IS_SET_DISTANCE(action)) { + vty_out(vty, " set distance %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:distance")); + } else if (IS_SET_ORIGIN(action)) { + vty_out(vty, " set origin %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:origin")); + } else if (IS_SET_ATOMIC_AGGREGATE(action)) { + vty_out(vty, " set atomic-aggregate\n"); + } else if (IS_SET_ORIGINATOR_ID(action)) { + vty_out(vty, " set originator-id %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:originator-id")); + } else if (IS_SET_COMM_LIST_DEL(action)) { + acl = NULL; + if ((ln = yang_dnode_get( + dnode, + "./rmap-set-action/frr-bgp-route-map:comm-list-name")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + + assert(acl); + + vty_out(vty, " set comm-list %s delete\n", acl); + } else if (IS_SET_LCOMM_LIST_DEL(action)) { + acl = NULL; + if ((ln = yang_dnode_get( + dnode, + "./rmap-set-action/frr-bgp-route-map:comm-list-name")) + != NULL) + acl = yang_dnode_get_string(ln, NULL); + + assert(acl); + + vty_out(vty, " set large-comm-list %s delete\n", acl); + } else if (IS_SET_LCOMMUNITY(action)) { + if (yang_dnode_exists( + dnode, + "./rmap-set-action/frr-bgp-route-map:large-community-string")) + vty_out(vty, " set large-community %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:large-community-string")); + else { + if (true + == yang_dnode_get_bool( + dnode, + "./rmap-set-action/frr-bgp-route-map:large-community-none")) + vty_out(vty, " set large-community none\n"); + } + } else if (IS_SET_COMMUNITY(action)) { + if (yang_dnode_exists( + dnode, + "./rmap-set-action/frr-bgp-route-map:community-string")) + vty_out(vty, " set community %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:community-string")); + else { + if (true + == yang_dnode_get_bool( + dnode, + "./rmap-set-action/frr-bgp-route-map:community-none")) + vty_out(vty, " set community none\n"); + } + } else if (IS_SET_EXTCOMMUNITY_RT(action)) { + vty_out(vty, " set extcommunity rt %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:extcommunity-rt")); + } else if (IS_SET_EXTCOMMUNITY_SOO(action)) { + vty_out(vty, " set extcommunity soo %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:extcommunity-soo")); + } else if (IS_SET_AGGREGATOR(action)) { + vty_out(vty, " set aggregator as %s %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:aggregator/aggregator-asn"), + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address")); + } else if (IS_SET_AS_EXCLUDE(action)) { + vty_out(vty, " set as-path exclude %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:exclude-as-path")); + } else if (IS_SET_AS_PREPEND(action)) { + if (yang_dnode_exists( + dnode, + "./rmap-set-action/frr-bgp-route-map:prepend-as-path")) + vty_out(vty, " set as-path prepend %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:prepend-as-path")); + else { + vty_out(vty, " set as-path prepend last-as %u\n", + yang_dnode_get_uint8( + dnode, + "./rmap-set-action/frr-bgp-route-map:last-as")); + } + } else if (IS_SET_IPV6_NH_GLOBAL(action)) { + vty_out(vty, " set ipv6 next-hop global %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:ipv6-address")); + } else if (IS_SET_IPV6_VPN_NH(action)) { + vty_out(vty, " set ipv6 vpn next-hop %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:ipv6-address")); + } else if (IS_SET_IPV6_PEER_ADDR(action)) { + if (true + == yang_dnode_get_bool( + dnode, + "./rmap-set-action/frr-bgp-route-map:preference")) + vty_out(vty, " set ipv6 next-hop peer-address\n"); + } else if (IS_SET_IPV6_PREFER_GLOBAL(action)) { + if (true + == yang_dnode_get_bool( + dnode, + "./rmap-set-action/frr-bgp-route-map:preference")) + vty_out(vty, " set ipv6 next-hop prefer-global\n"); + } else if (IS_SET_IPV4_VPN_NH(action)) { + vty_out(vty, " set ipv4 vpn next-hop %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:ipv4-address")); + } else if (IS_SET_BGP_IPV4_NH(action)) { + vty_out(vty, " set ip next-hop %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:ipv4-nexthop")); } } @@ -1000,6 +1396,74 @@ void route_map_description_show(struct vty *vty, struct lyd_node *dnode, vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL)); } +DEFPY_YANG( + route_map_optimization, route_map_optimization_cmd, + "[no] route-map WORD$name optimization", + NO_STR + ROUTE_MAP_CMD_STR + "Configure route-map optimization\n") +{ + char xpath[XPATH_MAXLEN]; + + snprintf(xpath, sizeof(xpath), + "/frr-route-map:lib/route-map[name='%s']", name); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf( + xpath, sizeof(xpath), + "/frr-route-map:lib/route-map[name='%s']/optimization-disabled", + name); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, no ? "true" : "false"); + + return nb_cli_apply_changes(vty, NULL); +} + +void route_map_optimization_disabled_show(struct vty *vty, + struct lyd_node *dnode, + bool show_defaults) +{ + const char *name = yang_dnode_get_string(dnode, "../name"); + const bool disabled = yang_dnode_get_bool(dnode, NULL); + + vty_out(vty, "%sroute-map %s optimization\n", disabled ? "no " : "", + name); +} + +#if CONFDATE > 20220409 +CPP_NOTICE("Time to remove old route-map optimization command") +#endif + +DEFPY_HIDDEN( + routemap_optimization, routemap_optimization_cmd, + "[no] route-map optimization", + NO_STR + "route-map\n" + "optimization\n") +{ + const struct lyd_node *rmi_dnode; + const char *rm_name; + char xpath[XPATH_MAXLEN]; + + vty_out(vty, + "%% This command is deprecated. Please, use `route-map NAME optimization` from the config node.\n"); + + rmi_dnode = + yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); + if (!rmi_dnode) { + vty_out(vty, "%% Failed to get RMI dnode in candidate DB\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + rm_name = yang_dnode_get_string(rmi_dnode, "../name"); + + snprintf( + xpath, sizeof(xpath), + "/frr-route-map:lib/route-map[name='%s']/optimization-disabled", + rm_name); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, no ? "true" : "false"); + return nb_cli_apply_changes(vty, NULL); +} + static int route_map_config_write(struct vty *vty) { struct lyd_node *dnode; @@ -1051,6 +1515,7 @@ void route_map_cli_init(void) install_element(CONFIG_NODE, &route_map_cmd); install_element(CONFIG_NODE, &no_route_map_cmd); install_element(CONFIG_NODE, &no_route_map_all_cmd); + install_element(CONFIG_NODE, &route_map_optimization_cmd); /* Install the on-match stuff */ install_element(RMAP_NODE, &rmap_onmatch_next_cmd); @@ -1114,4 +1579,9 @@ void route_map_cli_init(void) install_element(RMAP_NODE, &set_tag_cmd); install_element(RMAP_NODE, &no_set_tag_cmd); + + install_element(RMAP_NODE, &set_srte_color_cmd); + install_element(RMAP_NODE, &no_set_srte_color_cmd); + + install_element(RMAP_NODE, &routemap_optimization_cmd); } diff --git a/lib/routemap_northbound.c b/lib/routemap_northbound.c index 597a6b1ecf..db06e9caac 100644 --- a/lib/routemap_northbound.c +++ b/lib/routemap_northbound.c @@ -45,8 +45,9 @@ int lib_route_map_entry_match_destroy(struct nb_cb_destroy_args *args) if (rhc->rhc_mhook == NULL) return NB_OK; - rv = rhc->rhc_mhook(NULL, rhc->rhc_rmi, rhc->rhc_rule, NULL, - rhc->rhc_event); + rv = rhc->rhc_mhook(rhc->rhc_rmi, rhc->rhc_rule, NULL, + rhc->rhc_event, + args->errmsg, args->errmsg_len); if (rv != CMD_SUCCESS) return NB_ERR_INCONSISTENCY; @@ -65,7 +66,8 @@ int lib_route_map_entry_set_destroy(struct nb_cb_destroy_args *args) if (rhc->rhc_shook == NULL) return NB_OK; - rv = rhc->rhc_shook(NULL, rhc->rhc_rmi, rhc->rhc_rule, NULL); + rv = rhc->rhc_shook(rhc->rhc_rmi, rhc->rhc_rule, NULL, + args->errmsg, args->errmsg_len); if (rv != CMD_SUCCESS) return NB_ERR_INCONSISTENCY; @@ -139,6 +141,30 @@ static int lib_route_map_destroy(struct nb_cb_destroy_args *args) } /* + * XPath: /frr-route-map:lib/route-map/optimization-disabled + */ +static int +lib_route_map_optimization_disabled_modify(struct nb_cb_modify_args *args) +{ + struct route_map *rm; + bool disabled = yang_dnode_get_bool(args->dnode, NULL); + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + /* NOTHING */ + break; + case NB_EV_APPLY: + rm = nb_running_get_entry(args->dnode, NULL, true); + rm->optimization_disabled = disabled; + break; + } + + return NB_OK; +} + +/* * XPath: /frr-route-map:lib/route-map/entry */ static int lib_route_map_entry_create(struct nb_cb_create_args *args) @@ -498,9 +524,10 @@ static int lib_route_map_entry_match_condition_interface_modify( rhc->rhc_rule = "interface"; rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; - rv = rmap_match_set_hook.match_interface(NULL, rhc->rhc_rmi, + rv = rmap_match_set_hook.match_interface(rhc->rhc_rmi, "interface", ifname, - RMAP_EVENT_MATCH_ADDED); + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); if (rv != CMD_SUCCESS) { rhc->rhc_mhook = NULL; return NB_ERR_INCONSISTENCY; @@ -523,7 +550,7 @@ static int lib_route_map_entry_match_condition_list_name_modify( { struct routemap_hook_context *rhc; const char *acl; - int condition; + const char *condition; int rv; if (args->event != NB_EV_APPLY) @@ -532,19 +559,19 @@ static int lib_route_map_entry_match_condition_list_name_modify( /* Check for hook installation, otherwise we can just stop. */ acl = yang_dnode_get_string(args->dnode, NULL); rhc = nb_running_get_entry(args->dnode, NULL, true); - condition = yang_dnode_get_enum(args->dnode, "../condition"); - switch (condition) { - case 1: /* ipv4-address-list */ + condition = yang_dnode_get_string(args->dnode, "../../condition"); + + if (IS_MATCH_IPv4_ADDRESS_LIST(condition)) { if (rmap_match_set_hook.match_ip_address == NULL) return NB_OK; rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_address; rhc->rhc_rule = "ip address"; rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; rv = rmap_match_set_hook.match_ip_address( - NULL, rhc->rhc_rmi, "ip address", acl, - RMAP_EVENT_FILTER_ADDED); - break; - case 2: /* ipv4-prefix-list */ + rhc->rhc_rmi, "ip address", acl, + RMAP_EVENT_FILTER_ADDED, + args->errmsg, args->errmsg_len); + } else if (IS_MATCH_IPv4_PREFIX_LIST(condition)) { if (rmap_match_set_hook.match_ip_address_prefix_list == NULL) return NB_OK; rhc->rhc_mhook = @@ -552,20 +579,20 @@ static int lib_route_map_entry_match_condition_list_name_modify( rhc->rhc_rule = "ip address prefix-list"; rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; rv = rmap_match_set_hook.match_ip_address_prefix_list( - NULL, rhc->rhc_rmi, "ip address prefix-list", acl, - RMAP_EVENT_PLIST_ADDED); - break; - case 3: /* ipv4-next-hop-list */ + rhc->rhc_rmi, "ip address prefix-list", acl, + RMAP_EVENT_PLIST_ADDED, + args->errmsg, args->errmsg_len); + } else if (IS_MATCH_IPv4_NEXTHOP_LIST(condition)) { if (rmap_match_set_hook.match_ip_next_hop == NULL) return NB_OK; rhc->rhc_mhook = rmap_match_set_hook.no_match_ip_next_hop; rhc->rhc_rule = "ip next-hop"; rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; rv = rmap_match_set_hook.match_ip_next_hop( - NULL, rhc->rhc_rmi, "ip next-hop", acl, - RMAP_EVENT_FILTER_ADDED); - break; - case 4: /* ipv4-next-hop-prefix-list */ + rhc->rhc_rmi, "ip next-hop", acl, + RMAP_EVENT_FILTER_ADDED, + args->errmsg, args->errmsg_len); + } else if (IS_MATCH_IPv4_NEXTHOP_PREFIX_LIST(condition)) { if (rmap_match_set_hook.match_ip_next_hop_prefix_list == NULL) return NB_OK; rhc->rhc_mhook = @@ -573,20 +600,20 @@ static int lib_route_map_entry_match_condition_list_name_modify( rhc->rhc_rule = "ip next-hop prefix-list"; rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; rv = rmap_match_set_hook.match_ip_next_hop_prefix_list( - NULL, rhc->rhc_rmi, "ip next-hop prefix-list", acl, - RMAP_EVENT_PLIST_ADDED); - break; - case 6: /* ipv6-address-list */ + rhc->rhc_rmi, "ip next-hop prefix-list", acl, + RMAP_EVENT_PLIST_ADDED, + args->errmsg, args->errmsg_len); + } else if (IS_MATCH_IPv6_ADDRESS_LIST(condition)) { if (rmap_match_set_hook.match_ipv6_address == NULL) return NB_OK; rhc->rhc_mhook = rmap_match_set_hook.no_match_ipv6_address; rhc->rhc_rule = "ipv6 address"; rhc->rhc_event = RMAP_EVENT_FILTER_DELETED; rv = rmap_match_set_hook.match_ipv6_address( - NULL, rhc->rhc_rmi, "ipv6 address", acl, - RMAP_EVENT_FILTER_ADDED); - break; - case 7: /* ipv6-prefix-list */ + rhc->rhc_rmi, "ipv6 address", acl, + RMAP_EVENT_FILTER_ADDED, + args->errmsg, args->errmsg_len); + } else if (IS_MATCH_IPv6_PREFIX_LIST(condition)) { if (rmap_match_set_hook.match_ipv6_address_prefix_list == NULL) return NB_OK; rhc->rhc_mhook = @@ -594,13 +621,12 @@ static int lib_route_map_entry_match_condition_list_name_modify( rhc->rhc_rule = "ipv6 address prefix-list"; rhc->rhc_event = RMAP_EVENT_PLIST_DELETED; rv = rmap_match_set_hook.match_ipv6_address_prefix_list( - NULL, rhc->rhc_rmi, "ipv6 address prefix-list", acl, - RMAP_EVENT_PLIST_ADDED); - break; - default: + rhc->rhc_rmi, "ipv6 address prefix-list", acl, + RMAP_EVENT_PLIST_ADDED, + args->errmsg, args->errmsg_len); + } else rv = CMD_ERR_NO_MATCH; - break; - } + if (rv != CMD_SUCCESS) { rhc->rhc_mhook = NULL; return NB_ERR_INCONSISTENCY; @@ -642,8 +668,9 @@ static int lib_route_map_entry_match_condition_ipv4_next_hop_type_modify( rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; rv = rmap_match_set_hook.match_ip_next_hop_type( - NULL, rhc->rhc_rmi, "ip next-hop type", type, - RMAP_EVENT_MATCH_ADDED); + rhc->rhc_rmi, "ip next-hop type", type, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); if (rv != CMD_SUCCESS) { rhc->rhc_mhook = NULL; return NB_ERR_INCONSISTENCY; @@ -685,8 +712,9 @@ static int lib_route_map_entry_match_condition_ipv6_next_hop_type_modify( rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; rv = rmap_match_set_hook.match_ipv6_next_hop_type( - NULL, rhc->rhc_rmi, "ipv6 next-hop type", type, - RMAP_EVENT_MATCH_ADDED); + rhc->rhc_rmi, "ipv6 next-hop type", type, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); if (rv != CMD_SUCCESS) { rhc->rhc_mhook = NULL; return NB_ERR_INCONSISTENCY; @@ -727,8 +755,9 @@ static int lib_route_map_entry_match_condition_metric_modify( rhc->rhc_rule = "metric"; rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; - rv = rmap_match_set_hook.match_metric(NULL, rhc->rhc_rmi, "metric", - type, RMAP_EVENT_MATCH_ADDED); + rv = rmap_match_set_hook.match_metric(rhc->rhc_rmi, "metric", + type, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); if (rv != CMD_SUCCESS) { rhc->rhc_mhook = NULL; return NB_ERR_INCONSISTENCY; @@ -769,8 +798,9 @@ lib_route_map_entry_match_condition_tag_modify(struct nb_cb_modify_args *args) rhc->rhc_rule = "tag"; rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; - rv = rmap_match_set_hook.match_tag(NULL, rhc->rhc_rmi, "tag", tag, - RMAP_EVENT_MATCH_ADDED); + rv = rmap_match_set_hook.match_tag(rhc->rhc_rmi, "tag", tag, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); if (rv != CMD_SUCCESS) { rhc->rhc_mhook = NULL; return NB_ERR_INCONSISTENCY; @@ -850,8 +880,9 @@ static int lib_route_map_entry_set_action_ipv4_address_modify( rhc->rhc_shook = rmap_match_set_hook.no_set_ip_nexthop; rhc->rhc_rule = "ip next-hop"; - rv = rmap_match_set_hook.set_ip_nexthop(NULL, rhc->rhc_rmi, - "ip next-hop", address); + rv = rmap_match_set_hook.set_ip_nexthop(rhc->rhc_rmi, "ip next-hop", + address, + args->errmsg, args->errmsg_len); if (rv != CMD_SUCCESS) { rhc->rhc_shook = NULL; return NB_ERR_INCONSISTENCY; @@ -909,7 +940,8 @@ static int lib_route_map_entry_set_action_ipv6_address_modify( rhc->rhc_rule = "ipv6 next-hop local"; rv = rmap_match_set_hook.set_ipv6_nexthop_local( - NULL, rhc->rhc_rmi, "ipv6 next-hop local", address); + rhc->rhc_rmi, "ipv6 next-hop local", address, + args->errmsg, args->errmsg_len); if (rv != CMD_SUCCESS) { rhc->rhc_shook = NULL; return NB_ERR_INCONSISTENCY; @@ -928,7 +960,8 @@ static int lib_route_map_entry_set_action_ipv6_address_destroy( * XPath: /frr-route-map:lib/route-map/entry/set-action/value */ static int set_action_modify(enum nb_event event, const struct lyd_node *dnode, - union nb_resource *resource, const char *value) + union nb_resource *resource, const char *value, + char *errmsg, size_t errmsg_len) { struct routemap_hook_context *rhc; int rv; @@ -952,8 +985,10 @@ static int set_action_modify(enum nb_event event, const struct lyd_node *dnode, rhc->rhc_shook = rmap_match_set_hook.no_set_metric; rhc->rhc_rule = "metric"; - rv = rmap_match_set_hook.set_metric(NULL, rhc->rhc_rmi, "metric", - value); + rv = rmap_match_set_hook.set_metric(rhc->rhc_rmi, "metric", + value, + errmsg, errmsg_len + ); if (rv != CMD_SUCCESS) { rhc->rhc_shook = NULL; return NB_ERR_INCONSISTENCY; @@ -968,7 +1003,7 @@ lib_route_map_entry_set_action_value_modify(struct nb_cb_modify_args *args) const char *metric = yang_dnode_get_string(args->dnode, NULL); return set_action_modify(args->event, args->dnode, args->resource, - metric); + metric, args->errmsg, args->errmsg_len); } static int @@ -995,7 +1030,8 @@ lib_route_map_entry_set_action_add_metric_modify(struct nb_cb_modify_args *args) snprintf(metric_str, sizeof(metric_str), "+%s", yang_dnode_get_string(args->dnode, NULL)); return set_action_modify(args->event, args->dnode, args->resource, - metric_str); + metric_str, + args->errmsg, args->errmsg_len); } static int lib_route_map_entry_set_action_add_metric_destroy( @@ -1022,7 +1058,8 @@ static int lib_route_map_entry_set_action_subtract_metric_modify( snprintf(metric_str, sizeof(metric_str), "-%s", yang_dnode_get_string(args->dnode, NULL)); return set_action_modify(args->event, args->dnode, args->resource, - metric_str); + metric_str, + args->errmsg, args->errmsg_len); } static int lib_route_map_entry_set_action_subtract_metric_destroy( @@ -1038,7 +1075,8 @@ static int lib_route_map_entry_set_action_use_round_trip_time_modify( struct nb_cb_modify_args *args) { return set_action_modify(args->event, args->dnode, args->resource, - "rtt"); + "rtt", + args->errmsg, args->errmsg_len); } static int lib_route_map_entry_set_action_use_round_trip_time_destroy( @@ -1054,7 +1092,8 @@ static int lib_route_map_entry_set_action_add_round_trip_time_modify( struct nb_cb_modify_args *args) { return set_action_modify(args->event, args->dnode, args->resource, - "+rtt"); + "+rtt", + args->errmsg, args->errmsg_len); } static int lib_route_map_entry_set_action_add_round_trip_time_destroy( @@ -1070,7 +1109,7 @@ static int lib_route_map_entry_set_action_subtract_round_trip_time_modify( struct nb_cb_modify_args *args) { return set_action_modify(args->event, args->dnode, args->resource, - "-rtt"); + "-rtt", args->errmsg, args->errmsg_len); } static int lib_route_map_entry_set_action_subtract_round_trip_time_destroy( @@ -1109,7 +1148,8 @@ lib_route_map_entry_set_action_tag_modify(struct nb_cb_modify_args *args) rhc->rhc_shook = rmap_match_set_hook.no_set_tag; rhc->rhc_rule = "tag"; - rv = rmap_match_set_hook.set_tag(NULL, rhc->rhc_rmi, "tag", tag); + rv = rmap_match_set_hook.set_tag(rhc->rhc_rmi, "tag", tag, + args->errmsg, args->errmsg_len); if (rv != CMD_SUCCESS) { rhc->rhc_shook = NULL; return NB_ERR_INCONSISTENCY; @@ -1124,6 +1164,52 @@ lib_route_map_entry_set_action_tag_destroy(struct nb_cb_destroy_args *args) return lib_route_map_entry_set_destroy(args); } +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/policy + */ +static int +lib_route_map_entry_set_action_policy_modify(struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *policy; + int rv; + + /* + * NOTE: validate if 'action' is 'tag', currently it is not + * necessary because this is the only implemented action. Other + * actions might have different validations. + */ + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.set_srte_color == NULL) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + policy = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = rmap_match_set_hook.no_set_tag; + rhc->rhc_rule = "sr-te color"; + + rv = rmap_match_set_hook.set_tag(rhc->rhc_rmi, "sr-te color", policy, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_set_action_policy_destroy(struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} + /* clang-format off */ const struct frr_yang_module_info frr_route_map_info = { .name = "frr-route-map", @@ -1136,10 +1222,18 @@ const struct frr_yang_module_info frr_route_map_info = { } }, { + .xpath = "/frr-route-map:lib/route-map/optimization-disabled", + .cbs = { + .modify = lib_route_map_optimization_disabled_modify, + .cli_show = route_map_optimization_disabled_show, + } + }, + { .xpath = "/frr-route-map:lib/route-map/entry", .cbs = { .create = lib_route_map_entry_create, .destroy = lib_route_map_entry_destroy, + .cli_cmp = route_map_instance_cmp, .cli_show = route_map_instance_show, .cli_show_end = route_map_instance_show_end, } @@ -1189,42 +1283,42 @@ const struct frr_yang_module_info frr_route_map_info = { } }, { - .xpath = "/frr-route-map:lib/route-map/entry/match-condition/interface", + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/interface", .cbs = { .modify = lib_route_map_entry_match_condition_interface_modify, .destroy = lib_route_map_entry_match_condition_interface_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/match-condition/list-name", + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/list-name", .cbs = { .modify = lib_route_map_entry_match_condition_list_name_modify, .destroy = lib_route_map_entry_match_condition_list_name_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv4-next-hop-type", + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/ipv4-next-hop-type", .cbs = { .modify = lib_route_map_entry_match_condition_ipv4_next_hop_type_modify, .destroy = lib_route_map_entry_match_condition_ipv4_next_hop_type_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/match-condition/ipv6-next-hop-type", + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/ipv6-next-hop-type", .cbs = { .modify = lib_route_map_entry_match_condition_ipv6_next_hop_type_modify, .destroy = lib_route_map_entry_match_condition_ipv6_next_hop_type_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/match-condition/metric", + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/metric", .cbs = { .modify = lib_route_map_entry_match_condition_metric_modify, .destroy = lib_route_map_entry_match_condition_metric_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/match-condition/tag", + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/tag", .cbs = { .modify = lib_route_map_entry_match_condition_tag_modify, .destroy = lib_route_map_entry_match_condition_tag_destroy, @@ -1239,69 +1333,77 @@ const struct frr_yang_module_info frr_route_map_info = { } }, { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv4-address", + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/ipv4-address", .cbs = { .modify = lib_route_map_entry_set_action_ipv4_address_modify, .destroy = lib_route_map_entry_set_action_ipv4_address_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/ipv6-address", + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/ipv6-address", .cbs = { .modify = lib_route_map_entry_set_action_ipv6_address_modify, .destroy = lib_route_map_entry_set_action_ipv6_address_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/value", + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/value", .cbs = { .modify = lib_route_map_entry_set_action_value_modify, .destroy = lib_route_map_entry_set_action_value_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-metric", + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/add-metric", .cbs = { .modify = lib_route_map_entry_set_action_add_metric_modify, .destroy = lib_route_map_entry_set_action_add_metric_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-metric", + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/subtract-metric", .cbs = { .modify = lib_route_map_entry_set_action_subtract_metric_modify, .destroy = lib_route_map_entry_set_action_subtract_metric_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/use-round-trip-time", + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/use-round-trip-time", .cbs = { .modify = lib_route_map_entry_set_action_use_round_trip_time_modify, .destroy = lib_route_map_entry_set_action_use_round_trip_time_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/add-round-trip-time", + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/add-round-trip-time", .cbs = { .modify = lib_route_map_entry_set_action_add_round_trip_time_modify, .destroy = lib_route_map_entry_set_action_add_round_trip_time_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/subtract-round-trip-time", + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/subtract-round-trip-time", .cbs = { .modify = lib_route_map_entry_set_action_subtract_round_trip_time_modify, .destroy = lib_route_map_entry_set_action_subtract_round_trip_time_destroy, } }, { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/tag", + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/tag", .cbs = { .modify = lib_route_map_entry_set_action_tag_modify, .destroy = lib_route_map_entry_set_action_tag_destroy, } }, { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/policy", + .cbs = { + .modify = lib_route_map_entry_set_action_policy_modify, + .destroy = lib_route_map_entry_set_action_policy_destroy, + } + }, + + { .xpath = NULL, }, } diff --git a/lib/smux.h b/lib/smux.h index c063833e41..74447341d8 100644 --- a/lib/smux.h +++ b/lib/smux.h @@ -152,7 +152,8 @@ extern void oid2in_addr(oid[], int, struct in_addr *); extern void oid2in6_addr(oid oid[], struct in6_addr *addr); extern void oid2int(oid oid[], int *dest); extern void *oid_copy(void *, const void *, size_t); -extern void oid_copy_addr(oid[], const struct in_addr *, int); +extern void oid_copy_in_addr(oid[], const struct in_addr *); +extern void oid_copy_in6_addr(oid[], const struct in6_addr *); extern void oid_copy_int(oid oid[], int *val); extern void oid2string(oid oid[], int len, char *string); extern void oid_copy_str(oid oid[], const char *string, int len); diff --git a/lib/snmp.c b/lib/snmp.c index 17a4ed4a1d..23d3f38b31 100644 --- a/lib/snmp.c +++ b/lib/snmp.c @@ -88,13 +88,24 @@ void oid2int(oid oid[], int *dest) *dest = ntohl(network_dest); } -void oid_copy_addr(oid oid[], const struct in_addr *addr, int len) +void oid_copy_in_addr(oid oid[], const struct in_addr *addr) { int i; const uint8_t *pnt; + int len = sizeof(struct in_addr); - if (len == 0) - return; + pnt = (uint8_t *)addr; + + for (i = 0; i < len; i++) + oid[i] = *pnt++; +} + + +void oid_copy_in6_addr(oid oid[], const struct in6_addr *addr) +{ + int i; + const uint8_t *pnt; + int len = sizeof(struct in6_addr); pnt = (uint8_t *)addr; diff --git a/lib/sockunion.c b/lib/sockunion.c index d65235b41c..e6340a1743 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -664,54 +664,76 @@ void sockunion_init(union sockunion *su) } printfrr_ext_autoreg_p("SU", printfrr_psu) -static ssize_t printfrr_psu(char *buf, size_t bsz, const char *fmt, - int prec, const void *ptr) +static ssize_t printfrr_psu(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) { const union sockunion *su = ptr; - struct fbuf fb = { .buf = buf, .pos = buf, .len = bsz - 1 }; - bool include_port = false; + bool include_port = false, include_scope = false; bool endflags = false; - ssize_t consumed = 2; - - if (su) { - while (!endflags) { - switch (fmt[consumed++]) { - case 'p': - include_port = true; - break; - default: - consumed--; - endflags = true; - break; - } - }; - - switch (sockunion_family(su)) { - case AF_UNSPEC: - bprintfrr(&fb, "(unspec)"); - break; - case AF_INET: - inet_ntop(AF_INET, &su->sin.sin_addr, buf, bsz); - fb.pos += strlen(fb.buf); - if (include_port) - bprintfrr(&fb, ":%d", su->sin.sin_port); + ssize_t ret = 0; + char cbuf[INET6_ADDRSTRLEN]; + + if (!su) + return bputs(buf, "(null)"); + + while (!endflags) { + switch (*ea->fmt) { + case 'p': + ea->fmt++; + include_port = true; break; - case AF_INET6: - inet_ntop(AF_INET6, &su->sin6.sin6_addr, buf, bsz); - fb.pos += strlen(fb.buf); - if (include_port) - bprintfrr(&fb, ":%d", su->sin6.sin6_port); + case 's': + ea->fmt++; + include_scope = true; break; default: - bprintfrr(&fb, "(af %d)", sockunion_family(su)); + endflags = true; + break; } + } - fb.pos[0] = '\0'; - } else { - strlcpy(buf, "NULL", bsz); + switch (sockunion_family(su)) { + case AF_UNSPEC: + ret += bputs(buf, "(unspec)"); + break; + case AF_INET: + inet_ntop(AF_INET, &su->sin.sin_addr, cbuf, sizeof(cbuf)); + ret += bputs(buf, cbuf); + if (include_port) + ret += bprintfrr(buf, ":%d", ntohs(su->sin.sin_port)); + break; + case AF_INET6: + if (include_port) + ret += bputch(buf, '['); + inet_ntop(AF_INET6, &su->sin6.sin6_addr, cbuf, sizeof(cbuf)); + ret += bputs(buf, cbuf); + if (include_scope && su->sin6.sin6_scope_id) + ret += bprintfrr(buf, "%%%u", + (unsigned int)su->sin6.sin6_scope_id); + if (include_port) + ret += bprintfrr(buf, "]:%d", + ntohs(su->sin6.sin6_port)); + break; + case AF_UNIX: { + int len; +#ifdef __linux__ + if (su->sun.sun_path[0] == '\0' && su->sun.sun_path[1]) { + len = strnlen(su->sun.sun_path + 1, + sizeof(su->sun.sun_path) - 1); + ret += bprintfrr(buf, "@%*pSE", len, + su->sun.sun_path + 1); + break; + } +#endif + len = strnlen(su->sun.sun_path, sizeof(su->sun.sun_path)); + ret += bprintfrr(buf, "%*pSE", len, su->sun.sun_path); + break; + } + default: + ret += bprintfrr(buf, "(af %d)", sockunion_family(su)); } - return consumed; + return ret; } int sockunion_is_null(const union sockunion *su) @@ -730,3 +752,49 @@ int sockunion_is_null(const union sockunion *su) return 0; } } + +printfrr_ext_autoreg_i("PF", printfrr_pf) +static ssize_t printfrr_pf(struct fbuf *buf, struct printfrr_eargs *ea, + uintmax_t val) +{ + switch (val) { + case AF_INET: + return bputs(buf, "AF_INET"); + case AF_INET6: + return bputs(buf, "AF_INET6"); + case AF_UNIX: + return bputs(buf, "AF_UNIX"); +#ifdef AF_PACKET + case AF_PACKET: + return bputs(buf, "AF_PACKET"); +#endif +#ifdef AF_NETLINK + case AF_NETLINK: + return bputs(buf, "AF_NETLINK"); +#endif + } + return bprintfrr(buf, "AF_(%ju)", val); +} + +printfrr_ext_autoreg_i("SO", printfrr_so) +static ssize_t printfrr_so(struct fbuf *buf, struct printfrr_eargs *ea, + uintmax_t val) +{ + switch (val) { + case SOCK_STREAM: + return bputs(buf, "SOCK_STREAM"); + case SOCK_DGRAM: + return bputs(buf, "SOCK_DGRAM"); + case SOCK_SEQPACKET: + return bputs(buf, "SOCK_SEQPACKET"); +#ifdef SOCK_RAW + case SOCK_RAW: + return bputs(buf, "SOCK_RAW"); +#endif +#ifdef SOCK_PACKET + case SOCK_PACKET: + return bputs(buf, "SOCK_PACKET"); +#endif + } + return bprintfrr(buf, "SOCK_(%ju)", val); +} diff --git a/lib/sockunion.h b/lib/sockunion.h index 5e80ba1090..2cc80bb70f 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -24,6 +24,7 @@ #include "privs.h" #include "if.h" +#include <sys/un.h> #ifdef __OpenBSD__ #include <netmpls/mpls.h> #endif @@ -36,6 +37,7 @@ union sockunion { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; + struct sockaddr_un sun; #ifdef __OpenBSD__ struct sockaddr_mpls smpls; struct sockaddr_rtlabel rtlabel; @@ -106,6 +108,16 @@ extern int sockunion_is_null(const union sockunion *su); #ifdef _FRR_ATTRIBUTE_PRINTFRR #pragma FRR printfrr_ext "%pSU" (union sockunion *) +#pragma FRR printfrr_ext "%pSU" (struct sockaddr *) +#pragma FRR printfrr_ext "%pSU" (struct sockaddr_storage *) +#pragma FRR printfrr_ext "%pSU" (struct sockaddr_in *) +#pragma FRR printfrr_ext "%pSU" (struct sockaddr_in6 *) +#pragma FRR printfrr_ext "%pSU" (struct sockaddr_un *) + +/* AF_INET/PF_INET & co., using "PF" to avoid confusion with AFI/SAFI */ +#pragma FRR printfrr_ext "%dPF" (int) +/* SOCK_STREAM & co. */ +#pragma FRR printfrr_ext "%dSO" (int) #endif #ifdef __cplusplus diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index a115507192..d2e0682e95 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -307,20 +307,20 @@ const char *srcdest_rnode2str(const struct route_node *rn, char *str, int size) } printfrr_ext_autoreg_p("RN", printfrr_rn) -static ssize_t printfrr_rn(char *buf, size_t bsz, const char *fmt, - int prec, const void *ptr) +static ssize_t printfrr_rn(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) { const struct route_node *rn = ptr; const struct prefix *dst_p, *src_p; + char cbuf[PREFIX_STRLEN * 2 + 6]; - if (rn) { - srcdest_rnode_prefixes(rn, &dst_p, &src_p); - srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, buf, bsz); - } else { - strlcpy(buf, "NULL", bsz); - } + if (!rn) + return bputs(buf, "(null)"); - return 2; + srcdest_rnode_prefixes(rn, &dst_p, &src_p); + srcdest2str(dst_p, (const struct prefix_ipv6 *)src_p, + cbuf, sizeof(cbuf)); + return bputs(buf, cbuf); } struct route_table *srcdest_srcnode_table(struct route_node *rn) diff --git a/lib/strformat.c b/lib/strformat.c new file mode 100644 index 0000000000..431e573a0c --- /dev/null +++ b/lib/strformat.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2019 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "compiler.h" + +#include <string.h> +#include <ctype.h> + +#include "printfrr.h" + +printfrr_ext_autoreg_p("HX", printfrr_hexdump) +static ssize_t printfrr_hexdump(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + ssize_t ret = 0; + ssize_t input_len = printfrr_ext_len(ea); + char sep = ' '; + const uint8_t *pos, *end; + + if (ea->fmt[0] == 'c') { + ea->fmt++; + sep = ':'; + } else if (ea->fmt[0] == 'n') { + ea->fmt++; + sep = '\0'; + } + + if (input_len < 0) + return 0; + + for (pos = ptr, end = pos + input_len; pos < end; pos++) { + if (sep && pos != ptr) + ret += bputch(buf, sep); + ret += bputhex(buf, *pos); + } + + return ret; +} + +/* string analog for hexdumps / the "this." in ("74 68 69 73 0a |this.|") */ + +printfrr_ext_autoreg_p("HS", printfrr_hexdstr) +static ssize_t printfrr_hexdstr(struct fbuf *buf, struct printfrr_eargs *ea, + const void *ptr) +{ + ssize_t ret = 0; + ssize_t input_len = printfrr_ext_len(ea); + const uint8_t *pos, *end; + + if (input_len < 0) + return 0; + + for (pos = ptr, end = pos + input_len; pos < end; pos++) { + if (*pos >= 0x20 && *pos < 0x7f) + ret += bputch(buf, *pos); + else + ret += bputch(buf, '.'); + } + + return ret; +} + +enum escape_flags { + ESC_N_R_T = (1 << 0), /* use \n \r \t instead of \x0a ...*/ + ESC_SPACE = (1 << 1), /* \ */ + ESC_BACKSLASH = (1 << 2), /* \\ */ + ESC_DBLQUOTE = (1 << 3), /* \" */ + ESC_SGLQUOTE = (1 << 4), /* \' */ + ESC_BACKTICK = (1 << 5), /* \` */ + ESC_DOLLAR = (1 << 6), /* \$ */ + ESC_CLBRACKET = (1 << 7), /* \] for RFC5424 syslog */ + ESC_OTHER = (1 << 8), /* remaining non-alpha */ + + ESC_ALL = ESC_N_R_T | ESC_SPACE | ESC_BACKSLASH | ESC_DBLQUOTE + | ESC_SGLQUOTE | ESC_DOLLAR | ESC_OTHER, + ESC_QUOTSTRING = ESC_N_R_T | ESC_BACKSLASH | ESC_DBLQUOTE, + /* if needed: ESC_SHELL = ... */ +}; + +static ssize_t bquote(struct fbuf *buf, const uint8_t *pos, size_t len, + unsigned int flags) +{ + ssize_t ret = 0; + const uint8_t *end = pos + len; + + for (; pos < end; pos++) { + /* here's to hoping this might be a bit faster... */ + if (__builtin_expect(!!isalnum(*pos), 1)) { + ret += bputch(buf, *pos); + continue; + } + + switch (*pos) { + case '%': + case '+': + case ',': + case '-': + case '.': + case '/': + case ':': + case '@': + case '_': + ret += bputch(buf, *pos); + continue; + + case '\r': + if (!(flags & ESC_N_R_T)) + break; + ret += bputch(buf, '\\'); + ret += bputch(buf, 'r'); + continue; + case '\n': + if (!(flags & ESC_N_R_T)) + break; + ret += bputch(buf, '\\'); + ret += bputch(buf, 'n'); + continue; + case '\t': + if (!(flags & ESC_N_R_T)) + break; + ret += bputch(buf, '\\'); + ret += bputch(buf, 't'); + continue; + + case ' ': + if (flags & ESC_SPACE) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case '\\': + if (flags & ESC_BACKSLASH) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case '"': + if (flags & ESC_DBLQUOTE) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case '\'': + if (flags & ESC_SGLQUOTE) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case '`': + if (flags & ESC_BACKTICK) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case '$': + if (flags & ESC_DOLLAR) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + case ']': + if (flags & ESC_CLBRACKET) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + + /* remaining: !#&'()*;<=>?[^{|}~ */ + + default: + if (*pos >= 0x20 && *pos < 0x7f) { + if (flags & ESC_OTHER) + ret += bputch(buf, '\\'); + ret += bputch(buf, *pos); + continue; + } + } + ret += bputch(buf, '\\'); + ret += bputch(buf, 'x'); + ret += bputhex(buf, *pos); + } + + return ret; +} + +printfrr_ext_autoreg_p("SE", printfrr_escape) +static ssize_t printfrr_escape(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + ssize_t len = printfrr_ext_len(ea); + const uint8_t *ptr = vptr; + bool null_is_empty = false; + + if (ea->fmt[0] == 'n') { + null_is_empty = true; + ea->fmt++; + } + + if (!ptr) { + if (null_is_empty) + return 0; + return bputs(buf, "(null)"); + } + + if (len < 0) + len = strlen((const char *)ptr); + + return bquote(buf, ptr, len, ESC_ALL); +} + +printfrr_ext_autoreg_p("SQ", printfrr_quote) +static ssize_t printfrr_quote(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + ssize_t len = printfrr_ext_len(ea); + const uint8_t *ptr = vptr; + ssize_t ret = 0; + bool null_is_empty = false; + bool do_quotes = false; + unsigned int flags = ESC_QUOTSTRING; + + while (ea->fmt[0]) { + switch (ea->fmt[0]) { + case 'n': + null_is_empty = true; + ea->fmt++; + continue; + case 'q': + do_quotes = true; + ea->fmt++; + continue; + case 's': + flags |= ESC_CLBRACKET; + flags &= ~ESC_N_R_T; + ea->fmt++; + continue; + } + break; + } + + if (!ptr) { + if (null_is_empty) + return bputs(buf, do_quotes ? "\"\"" : ""); + return bputs(buf, "(null)"); + } + + if (len < 0) + len = strlen((const char *)ptr); + + if (do_quotes) + ret += bputch(buf, '"'); + ret += bquote(buf, ptr, len, flags); + if (do_quotes) + ret += bputch(buf, '"'); + return ret; +} diff --git a/lib/subdir.am b/lib/subdir.am index bfd367b134..0853d4bb2b 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -90,6 +90,7 @@ lib_libfrr_la_SOURCES = \ lib/spf_backoff.c \ lib/srcdest_table.c \ lib/stream.c \ + lib/strformat.c \ lib/strlcat.c \ lib/strlcpy.c \ lib/systemd.c \ @@ -1110,7 +1110,7 @@ static int lib_vrf_create(struct nb_cb_create_args *args) vrfp = vrf_get(VRF_UNKNOWN, vrfname); - vrf_set_user_cfged(vrfp); + SET_FLAG(vrfp->status, VRF_CONFIGURED); nb_running_set_entry(args->dnode, vrfp); return NB_OK; @@ -1136,7 +1136,7 @@ static int lib_vrf_destroy(struct nb_cb_destroy_args *args) vrfp = nb_running_unset_entry(args->dnode); /* Clear configured flag and invoke delete. */ - vrf_reset_user_cfged(vrfp); + UNSET_FLAG(vrfp->status, VRF_CONFIGURED); vrf_delete(vrfp); break; } @@ -159,18 +159,6 @@ static inline int vrf_is_user_cfged(struct vrf *vrf) return vrf && CHECK_FLAG(vrf->status, VRF_CONFIGURED); } -/* Mark that VRF has user configuration */ -static inline void vrf_set_user_cfged(struct vrf *vrf) -{ - SET_FLAG(vrf->status, VRF_CONFIGURED); -} - -/* Mark that VRF no longer has any user configuration */ -static inline void vrf_reset_user_cfged(struct vrf *vrf) -{ - UNSET_FLAG(vrf->status, VRF_CONFIGURED); -} - static inline uint32_t vrf_interface_count(struct vrf *vrf) { uint32_t count = 0; @@ -159,6 +159,8 @@ int vty_out(struct vty *vty, const char *format, ...) char buf[1024]; char *p = NULL; char *filtered; + /* format string may contain %m, keep errno intact for printfrr */ + int saved_errno = errno; if (vty->frame_pos) { vty->frame_pos = 0; @@ -166,6 +168,7 @@ int vty_out(struct vty *vty, const char *format, ...) } va_start(args, format); + errno = saved_errno; p = vasnprintfrr(MTYPE_VTY_OUT_BUF, buf, sizeof(buf), format, args); va_end(args); diff --git a/lib/xref.c b/lib/xref.c index 40efe51363..a41f91a228 100644 --- a/lib/xref.c +++ b/lib/xref.c @@ -93,8 +93,6 @@ static void xref_add_one(const struct xref *xref) q = memrchr(filename, '/', p - filename); if (q) filename = q + 1; - else - filename = p + 1; } SHA256_Init(&sha); diff --git a/lib/zclient.c b/lib/zclient.c index c78937c1ec..d613906d82 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -3916,6 +3916,21 @@ static int zclient_read(struct thread *thread) (*zclient->zebra_client_close_notify)(command, zclient, length, vrf_id); break; + case ZEBRA_NHRP_NEIGH_ADDED: + if (zclient->neighbor_added) + (*zclient->neighbor_added)(command, zclient, length, + vrf_id); + break; + case ZEBRA_NHRP_NEIGH_REMOVED: + if (zclient->neighbor_removed) + (*zclient->neighbor_removed)(command, zclient, length, + vrf_id); + break; + case ZEBRA_NHRP_NEIGH_GET: + if (zclient->neighbor_get) + (*zclient->neighbor_get)(command, zclient, length, + vrf_id); + break; default: break; } @@ -4181,3 +4196,59 @@ char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf, size_t len) return buf; } + +static int zclient_neigh_ip_read_entry(struct stream *s, struct ipaddr *add) +{ + uint8_t family; + + STREAM_GETC(s, family); + if (family != AF_INET && family != AF_INET6) + return -1; + + STREAM_GET(&add->ip.addr, s, family2addrsize(family)); + add->ipa_type = family; + return 0; + stream_failure: + return -1; +} + +int zclient_neigh_ip_encode(struct stream *s, + uint16_t cmd, + union sockunion *in, + union sockunion *out, + struct interface *ifp) +{ + int ret = 0; + + zclient_create_header(s, cmd, ifp->vrf_id); + stream_putc(s, sockunion_family(in)); + stream_write(s, sockunion_get_addr(in), sockunion_get_addrlen(in)); + if (out && sockunion_family(out) != AF_UNSPEC) { + stream_putc(s, sockunion_family(out)); + stream_write(s, sockunion_get_addr(out), + sockunion_get_addrlen(out)); + } else + stream_putc(s, AF_UNSPEC); + stream_putl(s, ifp->ifindex); + if (out) + stream_putl(s, ZEBRA_NEIGH_STATE_REACHABLE); + else + stream_putl(s, ZEBRA_NEIGH_STATE_FAILED); + return ret; +} + +int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api) +{ + int ret; + + ret = zclient_neigh_ip_read_entry(s, &api->ip_in); + if (ret < 0) + return -1; + zclient_neigh_ip_read_entry(s, &api->ip_out); + + STREAM_GETL(s, api->index); + STREAM_GETL(s, api->ndm_state); + return 0; + stream_failure: + return -1; +} diff --git a/lib/zclient.h b/lib/zclient.h index 43197534a8..90240e40b2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -23,6 +23,7 @@ /* For struct zapi_route. */ #include "prefix.h" +#include "ipaddr.h" /* For struct interface and struct connected. */ #include "if.h" @@ -213,6 +214,8 @@ typedef enum { ZEBRA_NHG_ADD, ZEBRA_NHG_DEL, ZEBRA_NHG_NOTIFY_OWNER, + ZEBRA_EVPN_REMOTE_NH_ADD, + ZEBRA_EVPN_REMOTE_NH_DEL, ZEBRA_ERROR, ZEBRA_CLIENT_CAPABILITIES, ZEBRA_OPAQUE_MESSAGE, @@ -221,6 +224,14 @@ typedef enum { ZEBRA_NEIGH_DISCOVER, ZEBRA_ROUTE_NOTIFY_REQUEST, ZEBRA_CLIENT_CLOSE_NOTIFY, + ZEBRA_NHRP_NEIGH_ADDED, + ZEBRA_NHRP_NEIGH_REMOVED, + ZEBRA_NHRP_NEIGH_GET, + ZEBRA_NHRP_NEIGH_REGISTER, + ZEBRA_NHRP_NEIGH_UNREGISTER, + ZEBRA_NEIGH_IP_ADD, + ZEBRA_NEIGH_IP_DEL, + ZEBRA_CONFIGURE_ARP, } zebra_message_types_t; enum zebra_error_types { @@ -379,6 +390,9 @@ struct zclient { int (*opaque_unregister_handler)(ZAPI_CALLBACK_ARGS); int (*sr_policy_notify_status)(ZAPI_CALLBACK_ARGS); int (*zebra_client_close_notify)(ZAPI_CALLBACK_ARGS); + void (*neighbor_added)(ZAPI_CALLBACK_ARGS); + void (*neighbor_removed)(ZAPI_CALLBACK_ARGS); + void (*neighbor_get)(ZAPI_CALLBACK_ARGS); }; /* Zebra API message flag. */ @@ -792,6 +806,27 @@ struct zclient_options { extern struct zclient_options zclient_options_default; +/* link layer representation for GRE like interfaces + * ip_in is the underlay IP, ip_out is the tunnel dest + * index stands for the index of the interface + * ndm state stands for the NDM value in netlink + */ +#define ZEBRA_NEIGH_STATE_REACHABLE (0x02) +#define ZEBRA_NEIGH_STATE_FAILED (0x20) +struct zapi_neigh_ip { + int cmd; + struct ipaddr ip_in; + struct ipaddr ip_out; + ifindex_t index; + uint32_t ndm_state; +}; +int zclient_neigh_ip_decode(struct stream *s, struct zapi_neigh_ip *api); +int zclient_neigh_ip_encode(struct stream *s, + uint16_t cmd, + union sockunion *in, + union sockunion *out, + struct interface *ifp); + /* * We reserve the top 4 bits for l2-NHG, everything else * is for zebra/proto l3-NHG. @@ -1132,7 +1167,7 @@ int zapi_opaque_reg_decode(struct stream *msg, */ enum zapi_opaque_registry { /* Request link-state database dump, at restart for example */ - LINK_STATE_REQUEST = 1, + LINK_STATE_SYNC = 1, /* Update containing link-state db info */ LINK_STATE_UPDATE = 2, /* Request LDP-SYNC state from LDP */ diff --git a/lib/zlog.c b/lib/zlog.c index 7304854648..f546709328 100644 --- a/lib/zlog.c +++ b/lib/zlog.c @@ -68,6 +68,8 @@ char zlog_prefix[128]; size_t zlog_prefixsz; int zlog_tmpdirfd = -1; +static atomic_bool zlog_ec = true, zlog_xid = true; + /* these are kept around because logging is initialized (and directories * & files created) before zprivs code switches to the FRR user; therefore * we need to chown() things so we don't get permission errors later when @@ -530,12 +532,54 @@ const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen) { if (!msg->text) { va_list args; + bool do_xid, do_ec; + size_t need = 0, hdrlen; + struct fbuf fb = { + .buf = msg->stackbuf, + .pos = msg->stackbuf, + .len = msg->stackbufsz, + }; + + do_ec = atomic_load_explicit(&zlog_ec, memory_order_relaxed); + do_xid = atomic_load_explicit(&zlog_xid, memory_order_relaxed); + + if (msg->xref && do_xid && msg->xref->xref.xrefdata->uid[0]) { + need += bputch(&fb, '['); + need += bputs(&fb, msg->xref->xref.xrefdata->uid); + need += bputch(&fb, ']'); + } + if (msg->xref && do_ec && msg->xref->ec) + need += bprintfrr(&fb, "[EC %u]", msg->xref->ec); + if (need) + need += bputch(&fb, ' '); + + hdrlen = need; + assert(hdrlen < msg->stackbufsz); va_copy(args, msg->args); - msg->text = vasnprintfrr(MTYPE_LOG_MESSAGE, msg->stackbuf, - msg->stackbufsz, msg->fmt, args); - msg->textlen = strlen(msg->text); + need += vbprintfrr(&fb, msg->fmt, args); va_end(args); + + msg->textlen = need; + need += bputch(&fb, '\0'); + + if (need <= msg->stackbufsz) + msg->text = msg->stackbuf; + else { + msg->text = XMALLOC(MTYPE_LOG_MESSAGE, need); + + memcpy(msg->text, msg->stackbuf, hdrlen); + + fb.buf = msg->text; + fb.len = need; + fb.pos = msg->text + hdrlen; + + va_copy(args, msg->args); + vbprintfrr(&fb, msg->fmt, args); + va_end(args); + + bputch(&fb, '\0'); + } } if (textlen) *textlen = msg->textlen; @@ -619,6 +663,26 @@ size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz, } } +void zlog_set_prefix_ec(bool enable) +{ + atomic_store_explicit(&zlog_ec, enable, memory_order_relaxed); +} + +bool zlog_get_prefix_ec(void) +{ + return atomic_load_explicit(&zlog_ec, memory_order_relaxed); +} + +void zlog_set_prefix_xid(bool enable) +{ + atomic_store_explicit(&zlog_xid, enable, memory_order_relaxed); +} + +bool zlog_get_prefix_xid(void) +{ + return atomic_load_explicit(&zlog_xid, memory_order_relaxed); +} + /* setup functions */ struct zlog_target *zlog_target_clone(struct memtype *mt, diff --git a/lib/zlog.h b/lib/zlog.h index 140392bae6..66d8f1e5d7 100644 --- a/lib/zlog.h +++ b/lib/zlog.h @@ -85,31 +85,6 @@ static inline void zlog_ref(const struct xref_logmsg *xref, va_end(ap); } -#define _zlog_ref(prio, msg, ...) \ - do { \ - static struct xrefdata _xrefdata = { \ - .xref = NULL, \ - .uid = {}, \ - .hashstr = (msg), \ - .hashu32 = {(prio), 0}, \ - }; \ - static const struct xref_logmsg _xref __attribute__( \ - (used)) = { \ - .xref = XREF_INIT(XREFT_LOGMSG, &_xrefdata, __func__), \ - .fmtstring = (msg), \ - .priority = (prio), \ - .args = (#__VA_ARGS__), \ - }; \ - XREF_LINK(_xref.xref); \ - zlog_ref(&_xref, (msg), ##__VA_ARGS__); \ - } while (0) - -#define zlog_err(...) _zlog_ref(LOG_ERR, __VA_ARGS__) -#define zlog_warn(...) _zlog_ref(LOG_WARNING, __VA_ARGS__) -#define zlog_info(...) _zlog_ref(LOG_INFO, __VA_ARGS__) -#define zlog_notice(...) _zlog_ref(LOG_NOTICE, __VA_ARGS__) -#define zlog_debug(...) _zlog_ref(LOG_DEBUG, __VA_ARGS__) - #define _zlog_ecref(ec_, prio, msg, ...) \ do { \ static struct xrefdata _xrefdata = { \ @@ -127,18 +102,22 @@ static inline void zlog_ref(const struct xref_logmsg *xref, .args = (#__VA_ARGS__), \ }; \ XREF_LINK(_xref.xref); \ - zlog_ref(&_xref, "[EC %u] " msg, ec_, ##__VA_ARGS__); \ + zlog_ref(&_xref, (msg), ##__VA_ARGS__); \ } while (0) +#define zlog_err(...) _zlog_ecref(0, LOG_ERR, __VA_ARGS__) +#define zlog_warn(...) _zlog_ecref(0, LOG_WARNING, __VA_ARGS__) +#define zlog_info(...) _zlog_ecref(0, LOG_INFO, __VA_ARGS__) +#define zlog_notice(...) _zlog_ecref(0, LOG_NOTICE, __VA_ARGS__) +#define zlog_debug(...) _zlog_ecref(0, LOG_DEBUG, __VA_ARGS__) + #define flog_err(ferr_id, format, ...) \ _zlog_ecref(ferr_id, LOG_ERR, format, ## __VA_ARGS__) #define flog_warn(ferr_id, format, ...) \ _zlog_ecref(ferr_id, LOG_WARNING, format, ## __VA_ARGS__) #define flog_err_sys(ferr_id, format, ...) \ - flog_err(ferr_id, format, ##__VA_ARGS__) -#define flog(priority, ferr_id, format, ...) \ - zlog(priority, "[EC %u] " format, ferr_id, ##__VA_ARGS__) + _zlog_ecref(ferr_id, LOG_ERR, format, ## __VA_ARGS__) extern void zlog_sigsafe(const char *text, size_t len); @@ -252,6 +231,11 @@ DECLARE_HOOK(zlog_init, (const char *progname, const char *protoname, extern void zlog_fini(void); DECLARE_KOOH(zlog_fini, (), ()); +extern void zlog_set_prefix_ec(bool enable); +extern bool zlog_get_prefix_ec(void); +extern void zlog_set_prefix_xid(bool enable); +extern bool zlog_get_prefix_xid(void); + /* for tools & test programs, i.e. anything not a daemon. * (no cleanup needed at exit) */ diff --git a/nhrpd/linux.c b/nhrpd/linux.c index 59c82b1c55..bcf97f030a 100644 --- a/nhrpd/linux.c +++ b/nhrpd/linux.c @@ -15,6 +15,7 @@ #include <stdio.h> #include <unistd.h> #include <string.h> +#include <errno.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/types.h> @@ -31,6 +32,11 @@ #include "os.h" #include "netlink.h" +#ifndef HAVE_STRLCPY +size_t strlcpy(char *__restrict dest, + const char *__restrict src, size_t destsize); +#endif + static int nhrp_socket_fd = -1; int os_socket(void) @@ -42,7 +48,7 @@ int os_socket(void) } int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, - size_t addrlen) + size_t addrlen, uint16_t protocol) { struct sockaddr_ll lladdr; struct iovec iov = { @@ -61,16 +67,16 @@ int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, memset(&lladdr, 0, sizeof(lladdr)); lladdr.sll_family = AF_PACKET; - lladdr.sll_protocol = htons(ETH_P_NHRP); + lladdr.sll_protocol = htons(protocol); lladdr.sll_ifindex = ifindex; lladdr.sll_halen = addrlen; memcpy(lladdr.sll_addr, addr, addrlen); - status = sendmsg(nhrp_socket_fd, &msg, 0); + status = sendmsg(os_socket(), &msg, 0); if (status < 0) - return -1; + return -errno; - return 0; + return status; } int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, @@ -111,7 +117,7 @@ static int linux_configure_arp(const char *iface, int on) { struct ifreq ifr; - strncpy(ifr.ifr_name, iface, IFNAMSIZ - 1); + strlcpy(ifr.ifr_name, iface, IFNAMSIZ); if (ioctl(nhrp_socket_fd, SIOCGIFFLAGS, &ifr)) return -1; @@ -154,7 +160,6 @@ int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af) break; } ret |= linux_configure_arp(ifname, 1); - ret |= netlink_configure_arp(ifindex, af); return ret; } diff --git a/nhrpd/netlink.h b/nhrpd/netlink.h index 74cb81daaa..5e971cabf1 100644 --- a/nhrpd/netlink.h +++ b/nhrpd/netlink.h @@ -13,6 +13,7 @@ union sockunion; struct interface; extern int netlink_nflog_group; +extern int netlink_mcast_nflog_group; extern int netlink_req_fd; void netlink_init(void); diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c index dc4697cda0..ecea0a9ec5 100644 --- a/nhrpd/netlink_arp.c +++ b/nhrpd/netlink_arp.c @@ -19,6 +19,8 @@ #include <linux/netfilter/nfnetlink_log.h> #include "thread.h" +#include "stream.h" +#include "prefix.h" #include "nhrpd.h" #include "netlink.h" #include "znl.h" @@ -27,129 +29,11 @@ int netlink_req_fd = -1; int netlink_nflog_group; static int netlink_log_fd = -1; static struct thread *netlink_log_thread; -static int netlink_listen_fd = -1; - -typedef void (*netlink_dispatch_f)(struct nlmsghdr *msg, struct zbuf *zb); void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma) { - struct nlmsghdr *n; - struct ndmsg *ndm; - struct zbuf *zb = zbuf_alloc(512); - - n = znl_nlmsg_push(zb, nbma ? RTM_NEWNEIGH : RTM_DELNEIGH, - NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE); - ndm = znl_push(zb, sizeof(*ndm)); - *ndm = (struct ndmsg){ - .ndm_family = sockunion_family(proto), - .ndm_ifindex = ifp->ifindex, - .ndm_type = RTN_UNICAST, - .ndm_state = nbma ? NUD_REACHABLE : NUD_FAILED, - }; - znl_rta_push(zb, NDA_DST, sockunion_get_addr(proto), - family2addrsize(sockunion_family(proto))); - if (nbma) - znl_rta_push(zb, NDA_LLADDR, sockunion_get_addr(nbma), - family2addrsize(sockunion_family(nbma))); - znl_nlmsg_complete(zb, n); - zbuf_send(zb, netlink_req_fd); - zbuf_recv(zb, netlink_req_fd); - zbuf_free(zb); -} - -static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb) -{ - struct ndmsg *ndm; - struct rtattr *rta; - struct nhrp_cache *c; - struct interface *ifp; - struct zbuf payload; - union sockunion addr, lladdr; - size_t len; - int state; - - memset(&lladdr, 0, sizeof(lladdr)); - ndm = znl_pull(zb, sizeof(*ndm)); - if (!ndm) - return; - - sockunion_family(&addr) = AF_UNSPEC; - while ((rta = znl_rta_pull(zb, &payload)) != NULL) { - len = zbuf_used(&payload); - switch (rta->rta_type) { - case NDA_DST: - sockunion_set(&addr, ndm->ndm_family, - zbuf_pulln(&payload, len), len); - break; - case NDA_LLADDR: - sockunion_set(&lladdr, ndm->ndm_family, - zbuf_pulln(&payload, len), len); - break; - } - } - - ifp = if_lookup_by_index(ndm->ndm_ifindex, VRF_DEFAULT); - if (!ifp || sockunion_family(&addr) == AF_UNSPEC) - return; - - c = nhrp_cache_get(ifp, &addr, 0); - if (!c) - return; - - debugf(NHRP_DEBUG_KERNEL, - "Netlink: %s %pSU dev %s lladdr %pSU nud 0x%x cache used %u type %u", - (msg->nlmsg_type == RTM_GETNEIGH) - ? "who-has" - : (msg->nlmsg_type == RTM_NEWNEIGH) ? "new-neigh" - : "del-neigh", - &addr, ifp->name, &lladdr, ndm->ndm_state, c->used, c->cur.type); - - if (msg->nlmsg_type == RTM_GETNEIGH) { - if (c->cur.type >= NHRP_CACHE_CACHED) { - nhrp_cache_set_used(c, 1); - debugf(NHRP_DEBUG_KERNEL, - "Netlink: update binding for %pSU dev %s from c %pSU peer.vc.nbma %pSU to lladdr %pSU", - &addr, ifp->name, &c->cur.remote_nbma_natoa, - &c->cur.peer->vc->remote.nbma, &lladdr); - /* In case of shortcuts, nbma is given by lladdr, not - * vc->remote.nbma. - */ - netlink_update_binding(ifp, &addr, &lladdr); - } - } else { - state = (msg->nlmsg_type == RTM_NEWNEIGH) ? ndm->ndm_state - : NUD_FAILED; - nhrp_cache_set_used(c, state == NUD_REACHABLE); - } -} - -static int netlink_route_recv(struct thread *t) -{ - uint8_t buf[ZNL_BUFFER_SIZE]; - int fd = THREAD_FD(t); - struct zbuf payload, zb; - struct nlmsghdr *n; - - zbuf_init(&zb, buf, sizeof(buf), 0); - while (zbuf_recv(&zb, fd) > 0) { - while ((n = znl_nlmsg_pull(&zb, &payload)) != NULL) { - debugf(NHRP_DEBUG_KERNEL, - "Netlink: Received msg_type %u, msg_flags %u", - n->nlmsg_type, n->nlmsg_flags); - switch (n->nlmsg_type) { - case RTM_GETNEIGH: - case RTM_NEWNEIGH: - case RTM_DELNEIGH: - netlink_neigh_msg(n, &payload); - break; - } - } - } - - thread_add_read(master, netlink_route_recv, 0, fd, NULL); - - return 0; + nhrp_send_zebra_nbr(proto, nbma, ifp); } static void netlink_log_register(int fd, int group) @@ -265,47 +149,64 @@ void netlink_set_nflog_group(int nlgroup) } } -void netlink_init(void) +void nhrp_neighbor_operation(ZAPI_CALLBACK_ARGS) { - netlink_req_fd = znl_open(NETLINK_ROUTE, 0); - if (netlink_req_fd < 0) - return; + union sockunion addr = {}, lladdr = {}; + struct interface *ifp; + int state, ndm_state; + struct nhrp_cache *c; + struct zapi_neigh_ip api = {}; - netlink_listen_fd = znl_open(NETLINK_ROUTE, RTMGRP_NEIGH); - if (netlink_listen_fd < 0) + zclient_neigh_ip_decode(zclient->ibuf, &api); + if (api.ip_in.ipa_type == AF_UNSPEC) return; + sockunion_family(&addr) = api.ip_in.ipa_type; + memcpy((uint8_t *)sockunion_get_addr(&addr), &api.ip_in.ip.addr, + family2addrsize(api.ip_in.ipa_type)); - thread_add_read(master, netlink_route_recv, 0, netlink_listen_fd, NULL); -} + sockunion_family(&lladdr) = api.ip_out.ipa_type; + if (api.ip_out.ipa_type != AF_UNSPEC) + memcpy((uint8_t *)sockunion_get_addr(&lladdr), + &api.ip_out.ip.addr, + family2addrsize(api.ip_out.ipa_type)); -int netlink_configure_arp(unsigned int ifindex, int pf) -{ - struct nlmsghdr *n; - struct ndtmsg *ndtm; - struct rtattr *rta; - struct zbuf *zb = zbuf_alloc(512); - int r; - - n = znl_nlmsg_push(zb, RTM_SETNEIGHTBL, NLM_F_REQUEST | NLM_F_REPLACE); - ndtm = znl_push(zb, sizeof(*ndtm)); - *ndtm = (struct ndtmsg){ - .ndtm_family = pf, - }; + ifp = if_lookup_by_index(api.index, vrf_id); + ndm_state = api.ndm_state; - znl_rta_push(zb, NDTA_NAME, pf == AF_INET ? "arp_cache" : "ndisc_cache", - 10); - - rta = znl_rta_nested_push(zb, NDTA_PARMS); - znl_rta_push_u32(zb, NDTPA_IFINDEX, ifindex); - znl_rta_push_u32(zb, NDTPA_APP_PROBES, 1); - znl_rta_push_u32(zb, NDTPA_MCAST_PROBES, 0); - znl_rta_push_u32(zb, NDTPA_UCAST_PROBES, 0); - znl_rta_nested_complete(zb, rta); - - znl_nlmsg_complete(zb, n); - r = zbuf_send(zb, netlink_req_fd); - zbuf_recv(zb, netlink_req_fd); - zbuf_free(zb); + if (!ifp) + return; + c = nhrp_cache_get(ifp, &addr, 0); + if (!c) + return; + debugf(NHRP_DEBUG_KERNEL, + "Netlink: %s %pSU dev %s lladdr %pSU nud 0x%x cache used %u type %u", + (cmd == ZEBRA_NHRP_NEIGH_GET) + ? "who-has" + : (cmd == ZEBRA_NHRP_NEIGH_ADDED) ? "new-neigh" + : "del-neigh", + &addr, ifp->name, &lladdr, ndm_state, c->used, c->cur.type); + if (cmd == ZEBRA_NHRP_NEIGH_GET) { + if (c->cur.type >= NHRP_CACHE_CACHED) { + nhrp_cache_set_used(c, 1); + debugf(NHRP_DEBUG_KERNEL, + "Netlink: update binding for %pSU dev %s from c %pSU peer.vc.nbma %pSU to lladdr %pSU", + &addr, ifp->name, &c->cur.remote_nbma_natoa, + &c->cur.peer->vc->remote.nbma, &lladdr); + /* In case of shortcuts, nbma is given by lladdr, not + * vc->remote.nbma. + */ + netlink_update_binding(ifp, &addr, &lladdr); + } + } else { + state = (cmd == ZEBRA_NHRP_NEIGH_ADDED) ? ndm_state + : ZEBRA_NEIGH_STATE_FAILED; + nhrp_cache_set_used(c, state == ZEBRA_NEIGH_STATE_REACHABLE); + } +} - return r; +void netlink_init(void) +{ + netlink_req_fd = znl_open(NETLINK_ROUTE, 0); + if (netlink_req_fd < 0) + return; } diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c index 4de8e220ce..bcf0e2168c 100644 --- a/nhrpd/nhrp_cache.c +++ b/nhrpd/nhrp_cache.c @@ -212,7 +212,8 @@ static int nhrp_cache_do_timeout(struct thread *t) c->t_timeout = NULL; if (c->cur.type != NHRP_CACHE_INVALID) - nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL); + nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL, + NULL); return 0; } @@ -301,7 +302,8 @@ static void nhrp_cache_peer_notifier(struct notifier_block *n, case NOTIFY_PEER_DOWN: case NOTIFY_PEER_IFCONFIG_CHANGED: notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN); - nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL); + nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL, + NULL); break; case NOTIFY_PEER_NBMA_CHANGING: if (c->cur.type == NHRP_CACHE_DYNAMIC) @@ -422,7 +424,8 @@ static void nhrp_cache_newpeer_notifier(struct notifier_block *n, int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, - uint32_t mtu, union sockunion *nbma_oa) + uint32_t mtu, union sockunion *nbma_oa, + union sockunion *nbma_claimed) { char buf[2][SU_ADDRSTRLEN]; @@ -464,6 +467,12 @@ int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, memset(&c->cur.remote_nbma_natoa, 0, sizeof(c->cur.remote_nbma_natoa)); + if (nbma_claimed) + c->cur.remote_nbma_claimed = *nbma_claimed; + else + memset(&c->cur.remote_nbma_claimed, 0, + sizeof(c->cur.remote_nbma_claimed)); + nhrp_peer_unref(p); } else { debugf(NHRP_DEBUG_COMMON, @@ -474,9 +483,13 @@ int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, c->new.type = type; c->new.peer = p; c->new.mtu = mtu; + c->new.holding_time = holding_time; if (nbma_oa) c->new.remote_nbma_natoa = *nbma_oa; + if (nbma_claimed) + c->new.remote_nbma_claimed = *nbma_claimed; + if (holding_time > 0) c->new.expires = monotime(NULL) + holding_time; else if (holding_time < 0) diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c index a64708d88e..402ffe9a24 100644 --- a/nhrpd/nhrp_interface.c +++ b/nhrpd/nhrp_interface.c @@ -42,6 +42,7 @@ static int nhrp_if_new_hook(struct interface *ifp) struct nhrp_afi_data *ad = &nifp->afi[afi]; ad->holdtime = NHRPD_DEFAULT_HOLDTIME; list_init(&ad->nhslist_head); + list_init(&ad->mcastlist_head); } return 0; @@ -55,6 +56,7 @@ static int nhrp_if_delete_hook(struct interface *ifp) nhrp_cache_interface_del(ifp); nhrp_nhs_interface_del(ifp); + nhrp_multicast_interface_del(ifp); nhrp_peer_interface_del(ifp); if (nifp->ipsec_profile) @@ -255,7 +257,7 @@ static void nhrp_interface_update_address(struct interface *ifp, afi_t afi, nc = nhrp_cache_get(ifp, &if_ad->addr, 0); if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, -1, - NULL, 0, NULL); + NULL, 0, NULL, NULL); } debugf(NHRP_DEBUG_KERNEL, "%s: IPv%d address changed to %s", ifp->name, @@ -267,7 +269,7 @@ static void nhrp_interface_update_address(struct interface *ifp, afi_t afi, nc = nhrp_cache_get(ifp, &addr, 1); if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, 0, NULL, - 0, NULL); + 0, NULL, NULL); } notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED); @@ -298,6 +300,7 @@ void nhrp_interface_update(struct interface *ifp) if (!if_ad->configured) { os_configure_dmvpn(ifp->ifindex, ifp->name, afi2family(afi)); + nhrp_send_zebra_configure_arp(ifp, afi2family(afi)); if_ad->configured = 1; nhrp_interface_update_address(ifp, afi, 1); } @@ -364,7 +367,7 @@ static void interface_config_update_nhrp_map(struct nhrp_cache_config *cc, if (c && c->map) { nhrp_cache_update_binding( c, c->cur.type, -1, - nhrp_peer_get(ifp, &nbma_addr), 0, NULL); + nhrp_peer_get(ifp, &nbma_addr), 0, NULL, NULL); } return; } @@ -375,11 +378,11 @@ static void interface_config_update_nhrp_map(struct nhrp_cache_config *cc, c->map = 1; if (cc->type == NHRP_CACHE_LOCAL) nhrp_cache_update_binding(c, NHRP_CACHE_LOCAL, 0, NULL, 0, - NULL); + NULL, NULL); else { nhrp_cache_update_binding(c, NHRP_CACHE_STATIC, 0, nhrp_peer_get(ifp, &cc->nbma), 0, - NULL); + NULL, NULL); } } diff --git a/nhrpd/nhrp_multicast.c b/nhrpd/nhrp_multicast.c new file mode 100644 index 0000000000..b78afda2c4 --- /dev/null +++ b/nhrpd/nhrp_multicast.c @@ -0,0 +1,305 @@ +/* NHRP Multicast Support + * Copyright (c) 2020-2021 4RF Limited + * + * This file is free software: you may copy, redistribute and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <fcntl.h> +#include <net/if.h> +#include <net/ethernet.h> +#include <netinet/if_ether.h> +#include <linux/netlink.h> +#include <linux/neighbour.h> +#include <linux/netfilter/nfnetlink_log.h> +#include <linux/if_packet.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include "thread.h" +#include "nhrpd.h" +#include "netlink.h" +#include "znl.h" +#include "os.h" + +DEFINE_MTYPE_STATIC(NHRPD, NHRP_MULTICAST, "NHRP Multicast"); + +int netlink_mcast_nflog_group; +static int netlink_mcast_log_fd = -1; +static struct thread *netlink_mcast_log_thread; + +struct mcast_ctx { + struct interface *ifp; + struct zbuf *pkt; +}; + +static void nhrp_multicast_send(struct nhrp_peer *p, struct zbuf *zb) +{ + size_t addrlen; + int ret; + + addrlen = sockunion_get_addrlen(&p->vc->remote.nbma); + ret = os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex, + sockunion_get_addr(&p->vc->remote.nbma), addrlen, + addrlen == 4 ? ETH_P_IP : ETH_P_IPV6); + + debugf(NHRP_DEBUG_COMMON, + "Multicast Packet: %pSU -> %pSU, ret = %d, size = %zu, addrlen = %zu", + &p->vc->local.nbma, &p->vc->remote.nbma, ret, zbuf_used(zb), + addrlen); +} + +static void nhrp_multicast_forward_nbma(union sockunion *nbma_addr, + struct interface *ifp, struct zbuf *pkt) +{ + struct nhrp_peer *p = nhrp_peer_get(ifp, nbma_addr); + + if (p && p->online) { + /* Send packet */ + nhrp_multicast_send(p, pkt); + } + nhrp_peer_unref(p); +} + +static void nhrp_multicast_forward_cache(struct nhrp_cache *c, void *pctx) +{ + struct mcast_ctx *ctx = (struct mcast_ctx *)pctx; + + if (c->cur.type == NHRP_CACHE_DYNAMIC && c->cur.peer) + nhrp_multicast_forward_nbma(&c->cur.peer->vc->remote.nbma, + ctx->ifp, ctx->pkt); +} + +static void nhrp_multicast_forward(struct nhrp_multicast *mcast, void *pctx) +{ + struct mcast_ctx *ctx = (struct mcast_ctx *)pctx; + struct nhrp_interface *nifp = ctx->ifp->info; + + if (!nifp->enabled) + return; + + /* dynamic */ + if (sockunion_family(&mcast->nbma_addr) == AF_UNSPEC) { + nhrp_cache_foreach(ctx->ifp, nhrp_multicast_forward_cache, + pctx); + return; + } + + /* Fixed IP Address */ + nhrp_multicast_forward_nbma(&mcast->nbma_addr, ctx->ifp, ctx->pkt); +} + +static void netlink_mcast_log_handler(struct nlmsghdr *msg, struct zbuf *zb) +{ + struct nfgenmsg *nf; + struct rtattr *rta; + struct zbuf rtapl; + uint32_t *out_ndx = NULL; + afi_t afi; + struct mcast_ctx ctx; + + nf = znl_pull(zb, sizeof(*nf)); + if (!nf) + return; + + ctx.pkt = NULL; + while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) { + switch (rta->rta_type) { + case NFULA_IFINDEX_OUTDEV: + out_ndx = znl_pull(&rtapl, sizeof(*out_ndx)); + break; + case NFULA_PAYLOAD: + ctx.pkt = &rtapl; + break; + /* NFULA_HWHDR exists and is supposed to contain source + * hardware address. However, for ip_gre it seems to be + * the nexthop destination address if the packet matches + * route. + */ + } + } + + if (!out_ndx || !ctx.pkt) + return; + + ctx.ifp = if_lookup_by_index(htonl(*out_ndx), VRF_DEFAULT); + if (!ctx.ifp) + return; + + debugf(NHRP_DEBUG_COMMON, + "Intercepted multicast packet leaving %s len %zu", + ctx.ifp->name, zbuf_used(ctx.pkt)); + + for (afi = 0; afi < AFI_MAX; afi++) { + nhrp_multicast_foreach(ctx.ifp, afi, nhrp_multicast_forward, + (void *)&ctx); + } +} + +static int netlink_mcast_log_recv(struct thread *t) +{ + uint8_t buf[65535]; /* Max OSPF Packet size */ + int fd = THREAD_FD(t); + struct zbuf payload, zb; + struct nlmsghdr *n; + + netlink_mcast_log_thread = NULL; + + zbuf_init(&zb, buf, sizeof(buf), 0); + while (zbuf_recv(&zb, fd) > 0) { + while ((n = znl_nlmsg_pull(&zb, &payload)) != NULL) { + debugf(NHRP_DEBUG_COMMON, + "Netlink-mcast-log: Received msg_type %u, msg_flags %u", + n->nlmsg_type, n->nlmsg_flags); + switch (n->nlmsg_type) { + case (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_PACKET: + netlink_mcast_log_handler(n, &payload); + break; + } + } + } + + thread_add_read(master, netlink_mcast_log_recv, 0, netlink_mcast_log_fd, + &netlink_mcast_log_thread); + + return 0; +} + +static void netlink_mcast_log_register(int fd, int group) +{ + struct nlmsghdr *n; + struct nfgenmsg *nf; + struct nfulnl_msg_config_cmd cmd; + struct zbuf *zb = zbuf_alloc(512); + + n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG, + NLM_F_REQUEST | NLM_F_ACK); + nf = znl_push(zb, sizeof(*nf)); + *nf = (struct nfgenmsg){ + .nfgen_family = AF_UNSPEC, + .version = NFNETLINK_V0, + .res_id = htons(group), + }; + cmd.command = NFULNL_CFG_CMD_BIND; + znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd)); + znl_nlmsg_complete(zb, n); + + zbuf_send(zb, fd); + zbuf_free(zb); +} + +void netlink_mcast_set_nflog_group(int nlgroup) +{ + if (netlink_mcast_log_fd >= 0) { + THREAD_OFF(netlink_mcast_log_thread); + close(netlink_mcast_log_fd); + netlink_mcast_log_fd = -1; + debugf(NHRP_DEBUG_COMMON, "De-register nflog group"); + } + netlink_mcast_nflog_group = nlgroup; + if (nlgroup) { + netlink_mcast_log_fd = znl_open(NETLINK_NETFILTER, 0); + if (netlink_mcast_log_fd < 0) + return; + + netlink_mcast_log_register(netlink_mcast_log_fd, nlgroup); + thread_add_read(master, netlink_mcast_log_recv, 0, + netlink_mcast_log_fd, + &netlink_mcast_log_thread); + debugf(NHRP_DEBUG_COMMON, "Register nflog group: %d", + netlink_mcast_nflog_group); + } +} + +static int nhrp_multicast_free(struct interface *ifp, + struct nhrp_multicast *mcast) +{ + list_del(&mcast->list_entry); + XFREE(MTYPE_NHRP_MULTICAST, mcast); + return 0; +} + +int nhrp_multicast_add(struct interface *ifp, afi_t afi, + union sockunion *nbma_addr) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_multicast *mcast; + + list_for_each_entry(mcast, &nifp->afi[afi].mcastlist_head, list_entry) + { + if (sockunion_same(&mcast->nbma_addr, nbma_addr)) + return NHRP_ERR_ENTRY_EXISTS; + } + + mcast = XMALLOC(MTYPE_NHRP_MULTICAST, sizeof(struct nhrp_multicast)); + + *mcast = (struct nhrp_multicast){ + .afi = afi, .ifp = ifp, .nbma_addr = *nbma_addr, + }; + list_add_tail(&mcast->list_entry, &nifp->afi[afi].mcastlist_head); + + debugf(NHRP_DEBUG_COMMON, "Adding multicast entry (%pSU)", nbma_addr); + + return NHRP_OK; +} + +int nhrp_multicast_del(struct interface *ifp, afi_t afi, + union sockunion *nbma_addr) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_multicast *mcast, *tmp; + + list_for_each_entry_safe(mcast, tmp, &nifp->afi[afi].mcastlist_head, + list_entry) + { + if (!sockunion_same(&mcast->nbma_addr, nbma_addr)) + continue; + + debugf(NHRP_DEBUG_COMMON, "Deleting multicast entry (%pSU)", + nbma_addr); + + nhrp_multicast_free(ifp, mcast); + + return NHRP_OK; + } + + return NHRP_ERR_ENTRY_NOT_FOUND; +} + +void nhrp_multicast_interface_del(struct interface *ifp) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_multicast *mcast, *tmp; + afi_t afi; + + for (afi = 0; afi < AFI_MAX; afi++) { + debugf(NHRP_DEBUG_COMMON, + "Cleaning up multicast entries (%d)", + !list_empty(&nifp->afi[afi].mcastlist_head)); + + list_for_each_entry_safe( + mcast, tmp, &nifp->afi[afi].mcastlist_head, list_entry) + { + nhrp_multicast_free(ifp, mcast); + } + } +} + +void nhrp_multicast_foreach(struct interface *ifp, afi_t afi, + void (*cb)(struct nhrp_multicast *, void *), + void *ctx) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_multicast *mcast; + + list_for_each_entry(mcast, &nifp->afi[afi].mcastlist_head, list_entry) + { + cb(mcast, ctx); + } +} diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c index 1689facbcf..9dfaf073d8 100644 --- a/nhrpd/nhrp_nhs.c +++ b/nhrpd/nhrp_nhs.c @@ -32,7 +32,8 @@ static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg) struct nhrp_cie_header *cie; struct nhrp_cache *c; struct zbuf extpl; - union sockunion cie_nbma, cie_proto, *proto; + union sockunion cie_nbma, cie_nbma_nhs, cie_proto, cie_proto_nhs, + *proto; int ok = 0, holdtime; unsigned short mtu = 0; @@ -66,6 +67,7 @@ static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg) /* Parse extensions */ sockunion_family(&nifp->nat_nbma) = AF_UNSPEC; + sockunion_family(&cie_nbma_nhs) = AF_UNSPEC; while ((ext = nhrp_ext_pull(&p->extensions, &extpl)) != NULL) { switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { case NHRP_EXTENSION_NAT_ADDRESS: @@ -79,6 +81,11 @@ static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg) ifp->name, &nifp->nbma); } break; + case NHRP_EXTENSION_RESPONDER_ADDRESS: + /* NHS adds its own record as responder address */ + nhrp_cie_pull(&extpl, p->hdr, &cie_nbma_nhs, + &cie_proto_nhs); + break; } } @@ -96,7 +103,8 @@ static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg) c = nhrp_cache_get(ifp, &p->dst_proto, 1); if (c) nhrp_cache_update_binding(c, NHRP_CACHE_NHS, holdtime, - nhrp_peer_ref(r->peer), mtu, NULL); + nhrp_peer_ref(r->peer), mtu, NULL, + &cie_nbma_nhs); } static int nhrp_reg_timeout(struct thread *t) @@ -111,7 +119,7 @@ static int nhrp_reg_timeout(struct thread *t) c = nhrp_cache_get(r->nhs->ifp, &r->proto_addr, 0); if (c) nhrp_cache_update_binding(c, NHRP_CACHE_NHS, -1, NULL, - 0, NULL); + 0, NULL, NULL); sockunion_family(&r->proto_addr) = AF_UNSPEC; } @@ -162,7 +170,7 @@ static int nhrp_reg_send_req(struct thread *t) struct interface *ifp = nhs->ifp; struct nhrp_interface *nifp = ifp->info; struct nhrp_afi_data *if_ad = &nifp->afi[nhs->afi]; - union sockunion *dst_proto; + union sockunion *dst_proto, nhs_proto; struct zbuf *zb; struct nhrp_packet_header *hdr; struct nhrp_extension_header *ext; @@ -207,17 +215,34 @@ static int nhrp_reg_send_req(struct thread *t) /* FIXME: push CIE for each local protocol address */ cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL); /* RFC2332 5.2.1 if unique is set then prefix length must be 0xff */ - cie->prefix_length = (if_ad->flags & NHRP_IFF_REG_NO_UNIQUE) ? 8 * sockunion_get_addrlen(dst_proto) : 0xff; + cie->prefix_length = (if_ad->flags & NHRP_IFF_REG_NO_UNIQUE) + ? 8 * sockunion_get_addrlen(dst_proto) + : 0xff; cie->holding_time = htons(if_ad->holdtime); cie->mtu = htons(if_ad->mtu); nhrp_ext_request(zb, hdr, ifp); /* Cisco NAT detection extension */ + if (sockunion_family(&r->proto_addr) != AF_UNSPEC) { + nhs_proto = r->proto_addr; + } else if (sockunion_family(&nhs->proto_addr) != AF_UNSPEC) { + nhs_proto = nhs->proto_addr; + } else { + /* cisco magic: If NHS is not known then use all 0s as + * client protocol address in NAT Extension header + */ + memset(&nhs_proto, 0, sizeof(nhs_proto)); + sockunion_family(&nhs_proto) = afi2family(nhs->afi); + } + hdr->flags |= htons(NHRP_FLAG_REGISTRATION_NAT); ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS); - cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &if_ad->addr); + /* push NHS details */ + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &r->peer->vc->remote.nbma, + &nhs_proto); cie->prefix_length = 8 * sockunion_get_addrlen(&if_ad->addr); + cie->mtu = htons(if_ad->mtu); nhrp_ext_complete(zb, ext); nhrp_packet_complete(zb, hdr); @@ -442,3 +467,29 @@ void nhrp_nhs_foreach(struct interface *ifp, afi_t afi, cb(nhs, 0, ctx); } } + +int nhrp_nhs_match_ip(union sockunion *in_ip, struct nhrp_interface *nifp) +{ + int i; + struct nhrp_nhs *nhs; + struct nhrp_registration *reg; + + for (i = 0; i < AFI_MAX; i++) { + list_for_each_entry(nhs, &nifp->afi[i].nhslist_head, + nhslist_entry) + { + if (!list_empty(&nhs->reglist_head)) { + list_for_each_entry(reg, &nhs->reglist_head, + reglist_entry) + { + if (!sockunion_cmp( + in_ip, + ®->peer->vc->remote + .nbma)) + return 1; + } + } + } + } + return 0; +} diff --git a/nhrpd/nhrp_packet.c b/nhrpd/nhrp_packet.c index a983aa71bc..fd77533c89 100644 --- a/nhrpd/nhrp_packet.c +++ b/nhrpd/nhrp_packet.c @@ -268,6 +268,7 @@ int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, &ad->addr); if (!cie) goto err; + cie->mtu = htons(ad->mtu); cie->holding_time = htons(ad->holdtime); break; default: diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index af352c68ee..5a7da703ac 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -47,6 +47,7 @@ static void nhrp_peer_check_delete(struct nhrp_peer *p) p->ref, &p->vc->remote.nbma, &p->vc->local.nbma); THREAD_OFF(p->t_fallback); + THREAD_OFF(p->t_timer); hash_release(nifp->peer_hash, p); nhrp_interface_notify_del(p->ifp, &p->ifp_notifier); nhrp_vc_notify_del(p->vc, &p->vc_notifier); @@ -182,8 +183,7 @@ static void *nhrp_peer_create(void *data) .ref = 0, .ifp = key->ifp, .vc = key->vc, - .notifier_list = - NOTIFIER_LIST_INITIALIZER(&p->notifier_list), + .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list), }; nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify); nhrp_interface_notify_add(p->ifp, &p->ifp_notifier, @@ -192,10 +192,10 @@ static void *nhrp_peer_create(void *data) return p; } -static void do_peer_hash_free(struct hash_bucket *hb, - void *arg __attribute__((__unused__))) +static void do_peer_hash_free(void *hb_data) { - struct nhrp_peer *p = hb->data; + struct nhrp_peer *p = (struct nhrp_peer *)hb_data; + nhrp_peer_check_delete(p); } @@ -207,9 +207,10 @@ void nhrp_peer_interface_del(struct interface *ifp) nifp->peer_hash ? nifp->peer_hash->count : 0); if (nifp->peer_hash) { - hash_iterate(nifp->peer_hash, do_peer_hash_free, NULL); + hash_clean(nifp->peer_hash, do_peer_hash_free); assert(nifp->peer_hash->count == 0); hash_free(nifp->peer_hash); + nifp->peer_hash = NULL; } } @@ -283,6 +284,30 @@ static int nhrp_peer_request_timeout(struct thread *t) return 0; } +static int nhrp_peer_defer_vici_request(struct thread *t) +{ + struct nhrp_peer *p = THREAD_ARG(t); + struct nhrp_vc *vc = p->vc; + struct interface *ifp = p->ifp; + struct nhrp_interface *nifp = ifp->info; + + THREAD_OFF(p->t_timer); + + if (p->online) { + debugf(NHRP_DEBUG_COMMON, + "IPsec connection to %pSU already established", + &vc->remote.nbma); + } else { + vici_request_vc(nifp->ipsec_profile, &vc->local.nbma, + &vc->remote.nbma, p->prio); + thread_add_timer( + master, nhrp_peer_request_timeout, p, + (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30, + &p->t_fallback); + } + return 0; +} + int nhrp_peer_check(struct nhrp_peer *p, int establish) { struct nhrp_vc *vc = p->vc; @@ -304,11 +329,25 @@ int nhrp_peer_check(struct nhrp_peer *p, int establish) p->prio = establish > 1; p->requested = 1; - vici_request_vc(nifp->ipsec_profile, &vc->local.nbma, &vc->remote.nbma, - p->prio); - thread_add_timer(master, nhrp_peer_request_timeout, p, - (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30, - &p->t_fallback); + + /* All NHRP registration requests are prioritized */ + if (p->prio) { + vici_request_vc(nifp->ipsec_profile, &vc->local.nbma, + &vc->remote.nbma, p->prio); + thread_add_timer( + master, nhrp_peer_request_timeout, p, + (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30, + &p->t_fallback); + } else { + /* Maximum timeout is 1 second */ + int r_time_ms = rand() % 1000; + + debugf(NHRP_DEBUG_COMMON, + "Initiating IPsec connection request to %pSU after %d ms:", + &vc->remote.nbma, r_time_ms); + thread_add_timer_msec(master, nhrp_peer_defer_vici_request, + p, r_time_ms, &p->t_timer); + } return 0; } @@ -337,10 +376,64 @@ void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb) os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex, sockunion_get_addr(&p->vc->remote.nbma), - sockunion_get_addrlen(&p->vc->remote.nbma)); + sockunion_get_addrlen(&p->vc->remote.nbma), ETH_P_NHRP); zbuf_reset(zb); } +static void nhrp_process_nat_extension(struct nhrp_packet_parser *pp, + union sockunion *proto, + union sockunion *cie_nbma) +{ + union sockunion cie_proto; + struct zbuf payload; + struct nhrp_extension_header *ext; + struct zbuf *extensions; + + if (!cie_nbma) + return; + + sockunion_family(cie_nbma) = AF_UNSPEC; + + if (!proto || sockunion_family(proto) == AF_UNSPEC) + return; + + /* Handle extensions */ + extensions = zbuf_alloc(zbuf_used(&pp->extensions)); + if (extensions) { + zbuf_copy_peek(extensions, &pp->extensions, + zbuf_used(&pp->extensions)); + while ((ext = nhrp_ext_pull(extensions, &payload)) != NULL) { + switch (htons(ext->type) + & ~NHRP_EXTENSION_FLAG_COMPULSORY) { + case NHRP_EXTENSION_NAT_ADDRESS: + /* Process the NBMA and proto address in NAT + * extension and update the cache without which + * the neighbor table in the kernel contains the + * source NBMA address which is not reachable + * since it is behind a NAT device + */ + debugf(NHRP_DEBUG_COMMON, + "shortcut res_resp: Processing NAT Extension for %pSU", + proto); + while (nhrp_cie_pull(&payload, pp->hdr, + cie_nbma, &cie_proto)) { + if (sockunion_family(&cie_proto) + == AF_UNSPEC) + continue; + + if (!sockunion_cmp(proto, &cie_proto)) { + debugf(NHRP_DEBUG_COMMON, + "cie_nbma for proto %pSU is %pSU", + proto, cie_nbma); + break; + } + } + } + } + zbuf_free(extensions); + } +} + static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp) { struct interface *ifp = pp->ifp; @@ -349,12 +442,12 @@ static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp) struct nhrp_cie_header *cie; struct nhrp_extension_header *ext; struct nhrp_cache *c; - union sockunion cie_nbma, cie_proto, *proto_addr, *nbma_addr; + union sockunion cie_nbma, cie_nbma_nat, cie_proto, *proto_addr, + *nbma_addr, *claimed_nbma_addr; int holdtime, prefix_len, hostprefix_len; struct nhrp_interface *nifp = ifp->info; struct nhrp_peer *peer; size_t paylen; - char buf[SU_ADDRSTRLEN]; if (!(pp->if_ad->flags & NHRP_IFF_SHORTCUT)) { debugf(NHRP_DEBUG_COMMON, "Shortcuts disabled"); @@ -406,9 +499,41 @@ static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp) proto_addr = (sockunion_family(&cie_proto) == AF_UNSPEC) ? &pp->src_proto : &cie_proto; - nbma_addr = (sockunion_family(&cie_nbma) == AF_UNSPEC) - ? &pp->src_nbma - : &cie_nbma; + + /* Check for this proto_addr in NHRP_NAT_EXTENSION */ + nhrp_process_nat_extension(pp, proto_addr, &cie_nbma_nat); + + if (sockunion_family(&cie_nbma_nat) == AF_UNSPEC) { + /* It may be possible that this resolution reply is + * coming directly from NATTED Spoke and there is not + * NAT Extension present + */ + debugf(NHRP_DEBUG_COMMON, + "shortcut res_rep: No NAT Extension for %pSU", + proto_addr); + + if (!sockunion_same(&pp->src_nbma, + &pp->peer->vc->remote.nbma) + && !nhrp_nhs_match_ip(&pp->peer->vc->remote.nbma, + nifp)) { + cie_nbma_nat = pp->peer->vc->remote.nbma; + debugf(NHRP_DEBUG_COMMON, + "shortcut res_rep: NAT detected using %pSU as cie_nbma", + &cie_nbma_nat); + } + } + + if (sockunion_family(&cie_nbma_nat) != AF_UNSPEC) + nbma_addr = &cie_nbma_nat; + else if (sockunion_family(&cie_nbma) != AF_UNSPEC) + nbma_addr = &cie_nbma; + else + nbma_addr = &pp->src_nbma; + + if (sockunion_family(&cie_nbma) != AF_UNSPEC) + claimed_nbma_addr = &cie_nbma; + else + claimed_nbma_addr = &pp->src_nbma; holdtime = htons(cie->holding_time); debugf(NHRP_DEBUG_COMMON, @@ -424,15 +549,14 @@ static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp) cie->code = NHRP_CODE_INSUFFICIENT_RESOURCES; continue; } - if (nbma_addr) - sockunion2str(nbma_addr, buf, sizeof(buf)); debugf(NHRP_DEBUG_COMMON, - "shortcut res_rep: updating binding for nmba addr %s", - nbma_addr ? buf : "(NULL)"); - if (!nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holdtime, - nhrp_peer_ref(pp->peer), - htons(cie->mtu), nbma_addr)) { + "shortcut res_rep: updating binding for nmba addr %pSU", + nbma_addr); + if (!nhrp_cache_update_binding( + c, NHRP_CACHE_DYNAMIC, holdtime, + nhrp_peer_get(pp->ifp, nbma_addr), htons(cie->mtu), + nbma_addr, claimed_nbma_addr)) { cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED; continue; } @@ -468,17 +592,23 @@ static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp) while ((ext = nhrp_ext_pull(&pp->extensions, &payload)) != NULL) { switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { case NHRP_EXTENSION_NAT_ADDRESS: - if (sockunion_family(&nifp->nat_nbma) == AF_UNSPEC) - break; ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS); if (!ext) goto err; - cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, - &nifp->nat_nbma, &pp->if_ad->addr); - if (!cie) - goto err; - nhrp_ext_complete(zb, ext); + if (sockunion_family(&nifp->nat_nbma) != AF_UNSPEC) { + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, + &nifp->nat_nbma, + &pp->if_ad->addr); + if (!cie) + goto err; + cie->prefix_length = + 8 * sockunion_get_addrlen( + &pp->if_ad->addr); + + cie->mtu = htons(pp->if_ad->mtu); + nhrp_ext_complete(zb, ext); + } break; default: if (nhrp_ext_reply(zb, hdr, ifp, ext, &payload) < 0) @@ -486,7 +616,6 @@ static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp) break; } } - nhrp_packet_complete(zb, hdr); nhrp_peer_send(peer, zb); err: @@ -559,7 +688,11 @@ static void nhrp_handle_registration_request(struct nhrp_packet_parser *p) : &cie_nbma; nbma_natoa = NULL; if (natted) { - nbma_natoa = nbma_addr; + nbma_natoa = + (sockunion_family(&p->peer->vc->remote.nbma) + == AF_UNSPEC) + ? nbma_addr + : &p->peer->vc->remote.nbma; } holdtime = htons(cie->holding_time); @@ -574,7 +707,8 @@ static void nhrp_handle_registration_request(struct nhrp_packet_parser *p) if (!nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holdtime, nhrp_peer_ref(p->peer), - htons(cie->mtu), nbma_natoa)) { + htons(cie->mtu), nbma_natoa, + nbma_addr)) { cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED; continue; } @@ -592,9 +726,13 @@ static void nhrp_handle_registration_request(struct nhrp_packet_parser *p) goto err; zbuf_copy(zb, &payload, zbuf_used(&payload)); if (natted) { - nhrp_cie_push(zb, NHRP_CODE_SUCCESS, - &p->peer->vc->remote.nbma, - &p->src_proto); + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, + &p->peer->vc->remote.nbma, + &p->src_proto); + cie->prefix_length = + 8 * sockunion_get_addrlen( + &p->if_ad->addr); + cie->mtu = htons(p->if_ad->mtu); } nhrp_ext_complete(zb, ext); break; @@ -794,20 +932,23 @@ static struct { static void nhrp_peer_forward(struct nhrp_peer *p, struct nhrp_packet_parser *pp) { - struct zbuf *zb, extpl; + struct zbuf *zb, *zb_copy, extpl; struct nhrp_packet_header *hdr; struct nhrp_extension_header *ext, *dst; struct nhrp_cie_header *cie; struct nhrp_interface *nifp = pp->ifp->info; struct nhrp_afi_data *if_ad = pp->if_ad; - union sockunion cie_nbma, cie_protocol; + union sockunion cie_nbma, cie_protocol, cie_protocol_mandatory, *proto; uint16_t type, len; + struct nhrp_cache *c; if (pp->hdr->hop_count == 0) return; /* Create forward packet - copy header */ zb = zbuf_alloc(1500); + zb_copy = zbuf_alloc(1500); + hdr = nhrp_packet_push(zb, pp->hdr->type, &pp->src_nbma, &pp->src_proto, &pp->dst_proto); hdr->flags = pp->hdr->flags; @@ -815,8 +956,13 @@ static void nhrp_peer_forward(struct nhrp_peer *p, hdr->u.request_id = pp->hdr->u.request_id; /* Copy payload */ + zbuf_copy_peek(zb_copy, &pp->payload, zbuf_used(&pp->payload)); zbuf_copy(zb, &pp->payload, zbuf_used(&pp->payload)); + /* Get CIE Extension from Mandatory part */ + sockunion_family(&cie_protocol_mandatory) = AF_UNSPEC; + nhrp_cie_pull(zb_copy, pp->hdr, &cie_nbma, &cie_protocol_mandatory); + /* Copy extensions */ while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) { type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY; @@ -848,19 +994,82 @@ static void nhrp_peer_forward(struct nhrp_peer *p, &nifp->nbma, &if_ad->addr); if (!cie) goto err; + cie->mtu = htons(if_ad->mtu); cie->holding_time = htons(if_ad->holdtime); } break; + case NHRP_EXTENSION_NAT_ADDRESS: + c = NULL; + proto = NULL; + + /* If NAT extension is empty then attempt to populate + * it with cached NBMA information + */ + if (len == 0) { + if (packet_types[hdr->type].type + == PACKET_REQUEST) { + debugf(NHRP_DEBUG_COMMON, + "Processing NHRP_EXTENSION_NAT_ADDRESS while forwarding the request packet"); + proto = &pp->src_proto; + } else if (packet_types[hdr->type].type + == PACKET_REPLY) { + debugf(NHRP_DEBUG_COMMON, + "Processing NHRP_EXTENSION_NAT_ADDRESS while forwarding the reply packet"); + /* For reply packet use protocol + * specified in CIE of mandatory part + * for cache lookup + */ + if (sockunion_family( + &cie_protocol_mandatory) + != AF_UNSPEC) + proto = &cie_protocol_mandatory; + } + } + + if (proto) { + debugf(NHRP_DEBUG_COMMON, "Proto is %pSU", + proto); + c = nhrp_cache_get(nifp->ifp, proto, 0); + } + + if (c) { + debugf(NHRP_DEBUG_COMMON, + "c->cur.remote_nbma_natoa is %pSU", + &c->cur.remote_nbma_natoa); + if (sockunion_family(&c->cur.remote_nbma_natoa) + != AF_UNSPEC) { + cie = nhrp_cie_push( + zb, + NHRP_CODE_SUCCESS, + &c->cur.remote_nbma_natoa, + proto); + if (!cie) + goto err; + } + } else { + if (proto) + debugf(NHRP_DEBUG_COMMON, + "No cache entry for proto %pSU", + proto); + /* Copy existing NAT extension to new packet if + * either it was already not-empty, or we do not + * have valid cache information + */ + zbuf_put(zb, extpl.head, len); + } + break; default: if (htons(ext->type) & NHRP_EXTENSION_FLAG_COMPULSORY) /* FIXME: RFC says to just copy, but not - * append our selves to the transit NHS list */ + * append our selves to the transit NHS list + */ goto err; /* fallthru */ case NHRP_EXTENSION_RESPONDER_ADDRESS: /* Supported compulsory extensions, and any * non-compulsory that is not explicitly handled, - * should be just copied. */ + * should be just copied. + */ zbuf_copy(zb, &extpl, len); break; } @@ -870,10 +1079,12 @@ static void nhrp_peer_forward(struct nhrp_peer *p, nhrp_packet_complete(zb, hdr); nhrp_peer_send(p, zb); zbuf_free(zb); + zbuf_free(zb_copy); return; err: nhrp_packet_debug(pp->pkt, "FWD-FAIL"); zbuf_free(zb); + zbuf_free(zb_copy); } static void nhrp_packet_debug(struct zbuf *zb, const char *dir) diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index 7a4c57b5d4..23fa0771ef 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -79,6 +79,24 @@ static void nhrp_route_update_zebra(const struct prefix *p, } } +static void nhrp_zebra_register_neigh(vrf_id_t vrf_id, afi_t afi, bool reg) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) + return; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, reg ? ZEBRA_NHRP_NEIGH_REGISTER : + ZEBRA_NHRP_NEIGH_UNREGISTER, + vrf_id); + stream_putw(s, afi); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} + void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp) { struct route_node *rn; @@ -344,6 +362,8 @@ static void nhrp_zebra_connected(struct zclient *zclient) ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP6, ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP, true); + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP6, true); } void nhrp_zebra_init(void) @@ -357,7 +377,9 @@ void nhrp_zebra_init(void) zclient->interface_address_delete = nhrp_interface_address_delete; zclient->redistribute_route_add = nhrp_route_read; zclient->redistribute_route_del = nhrp_route_read; - + zclient->neighbor_added = nhrp_neighbor_operation; + zclient->neighbor_removed = nhrp_neighbor_operation; + zclient->neighbor_get = nhrp_neighbor_operation; zclient_init(zclient, ZEBRA_ROUTE_NHRP, 0, &nhrpd_privs); } @@ -370,8 +392,47 @@ static void nhrp_table_node_cleanup(struct route_table *table, XFREE(MTYPE_NHRP_ROUTE, node->info); } +void nhrp_send_zebra_configure_arp(struct interface *ifp, int family) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) { + debugf(NHRP_DEBUG_COMMON, "%s() : zclient not ready", + __func__); + return; + } + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, + ZEBRA_CONFIGURE_ARP, + ifp->vrf_id); + stream_putc(s, family); + stream_putl(s, ifp->ifindex); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} + +void nhrp_send_zebra_nbr(union sockunion *in, + union sockunion *out, + struct interface *ifp) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) + return; + s = zclient->obuf; + stream_reset(s); + zclient_neigh_ip_encode(s, out ? ZEBRA_NEIGH_IP_ADD : + ZEBRA_NEIGH_IP_DEL, in, out, + ifp); + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); +} + void nhrp_zebra_terminate(void) { + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP, false); + nhrp_zebra_register_neigh(VRF_DEFAULT, AFI_IP6, false); zclient_stop(zclient); zclient_free(zclient); diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c index 8ce19cd4a3..56861551ea 100644 --- a/nhrpd/nhrp_shortcut.c +++ b/nhrpd/nhrp_shortcut.c @@ -203,17 +203,18 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, void *arg) { struct nhrp_packet_parser *pp = arg; + struct interface *ifp = pp->ifp; + struct nhrp_interface *nifp = ifp->info; struct nhrp_shortcut *s = container_of(reqid, struct nhrp_shortcut, reqid); struct nhrp_shortcut *ps; struct nhrp_extension_header *ext; struct nhrp_cie_header *cie; struct nhrp_cache *c = NULL; - struct nhrp_cache *c_dst_proto = NULL; + struct nhrp_cache *c_dst = NULL; union sockunion *proto, cie_proto, *nbma, cie_nbma, nat_nbma; struct prefix prefix, route_prefix; struct zbuf extpl; - char buf[4][SU_ADDRSTRLEN]; int holding_time = pp->if_ad->holdtime; nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); @@ -235,16 +236,6 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, return; } - /* Parse extensions */ - memset(&nat_nbma, 0, sizeof(nat_nbma)); - while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) { - switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { - case NHRP_EXTENSION_NAT_ADDRESS: - nhrp_cie_pull(&extpl, pp->hdr, &nat_nbma, &cie_proto); - break; - } - } - /* Minor sanity check */ prefix2sockunion(s->p, &cie_proto); if (!sockunion_same(&cie_proto, &pp->dst_proto)) { @@ -280,16 +271,58 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, prefix.prefixlen = route_prefix.prefixlen; } - debugf(NHRP_DEBUG_COMMON, - "Shortcut: %pFX is at proto %pSU dst_proto %pSU cie-nbma %pSU nat-nbma %pSU cie-holdtime %d", - &prefix, proto, &pp->dst_proto, &cie_nbma, &nat_nbma, - htons(cie->holding_time)); + /* Parse extensions */ + memset(&nat_nbma, 0, sizeof(nat_nbma)); + while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) { + switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { + case NHRP_EXTENSION_NAT_ADDRESS: { + struct nhrp_cie_header *cie_nat; + + do { + union sockunion cie_nat_proto, cie_nat_nbma; + + sockunion_family(&cie_nat_proto) = AF_UNSPEC; + sockunion_family(&cie_nat_nbma) = AF_UNSPEC; + cie_nat = nhrp_cie_pull(&extpl, pp->hdr, + &cie_nat_nbma, + &cie_nat_proto); + /* We are interested only in peer CIE */ + if (cie_nat + && sockunion_same(&cie_nat_proto, proto)) { + nat_nbma = cie_nat_nbma; + } + } while (cie_nat); + } break; + default: + break; + } + } /* Update cache entry for the protocol to nbma binding */ - if (sockunion_family(&nat_nbma) != AF_UNSPEC) + if (sockunion_family(&nat_nbma) != AF_UNSPEC) { + debugf(NHRP_DEBUG_COMMON, + "Shortcut: NAT detected (NAT extension) proto %pSU NBMA %pSU claimed-NBMA %pSU", + proto, &nat_nbma, &cie_nbma); nbma = &nat_nbma; - else + } + /* For NHRP resolution reply the cie_nbma in mandatory part is the + * address of the actual address of the sender + */ + else if (!sockunion_same(&cie_nbma, &pp->peer->vc->remote.nbma) + && !nhrp_nhs_match_ip(&pp->peer->vc->remote.nbma, nifp)) { + debugf(NHRP_DEBUG_COMMON, + "Shortcut: NAT detected (no NAT Extension) proto %pSU NBMA %pSU claimed-NBMA %pSU", + proto, &pp->peer->vc->remote.nbma, &cie_nbma); + nbma = &pp->peer->vc->remote.nbma; + nat_nbma = *nbma; + } else { nbma = &cie_nbma; + } + + debugf(NHRP_DEBUG_COMMON, + "Shortcut: %pFX is at proto %pSU dst_proto %pSU NBMA %pSU cie-holdtime %d", + &prefix, proto, &pp->dst_proto, nbma, + htons(cie->holding_time)); if (sockunion_family(nbma)) { c = nhrp_cache_get(pp->ifp, proto, 1); @@ -299,25 +332,31 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holding_time, nhrp_peer_get(pp->ifp, nbma), - htons(cie->mtu), nbma); + htons(cie->mtu), + nbma, + &cie_nbma); } else { debugf(NHRP_DEBUG_COMMON, - "Shortcut: no cache for nbma %s", buf[2]); + "Shortcut: no cache for proto %pSU", proto); } /* Update cache binding for dst_proto as well */ - if (proto != &pp->dst_proto) { - c_dst_proto = nhrp_cache_get(pp->ifp, &pp->dst_proto, 1); - if (c_dst_proto) { + if (sockunion_cmp(proto, &pp->dst_proto)) { + c_dst = nhrp_cache_get(pp->ifp, &pp->dst_proto, 1); + if (c_dst) { debugf(NHRP_DEBUG_COMMON, - "Shortcut: cache found, update binding"); - nhrp_cache_update_binding(c_dst_proto, NHRP_CACHE_DYNAMIC, + "Shortcut: cache found, update binding"); + nhrp_cache_update_binding(c_dst, + NHRP_CACHE_DYNAMIC, holding_time, nhrp_peer_get(pp->ifp, nbma), - htons(cie->mtu), nbma); + htons(cie->mtu), + nbma, + &cie_nbma); } else { debugf(NHRP_DEBUG_COMMON, - "Shortcut: no cache for nbma %s", buf[2]); + "Shortcut: no cache for proto %pSU", + &pp->dst_proto); } } } @@ -356,6 +395,7 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s) struct nhrp_afi_data *if_ad; struct nhrp_peer *peer; struct nhrp_cie_header *cie; + struct nhrp_extension_header *ext; if (nhrp_route_address(NULL, &s->addr, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP) @@ -399,7 +439,14 @@ static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s) /* Cisco NAT detection extension */ hdr->flags |= htons(NHRP_FLAG_RESOLUTION_NAT); - nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS); + ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS); + if (sockunion_family(&nifp->nat_nbma) != AF_UNSPEC) { + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nat_nbma, + &if_ad->addr); + cie->prefix_length = 8 * sockunion_get_addrlen(&if_ad->addr); + cie->mtu = htons(if_ad->mtu); + nhrp_ext_complete(zb, ext); + } nhrp_packet_complete(zb, hdr); diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c index 1ea4c5e64f..420ea12ec1 100644 --- a/nhrpd/nhrp_vty.c +++ b/nhrpd/nhrp_vty.c @@ -187,6 +187,9 @@ static int nhrp_config_write(struct vty *vty) if (netlink_nflog_group) { vty_out(vty, "nhrp nflog-group %d\n", netlink_nflog_group); } + if (netlink_mcast_nflog_group) + vty_out(vty, "nhrp multicast-nflog-group %d\n", + netlink_mcast_nflog_group); return 0; } @@ -257,6 +260,31 @@ DEFUN(no_nhrp_nflog_group, no_nhrp_nflog_group_cmd, return CMD_SUCCESS; } +DEFUN(nhrp_multicast_nflog_group, nhrp_multicast_nflog_group_cmd, + "nhrp multicast-nflog-group (1-65535)", + NHRP_STR + "Specify NFLOG group number for Multicast Packets\n" + "NFLOG group number\n") +{ + uint32_t nfgroup; + + nfgroup = strtoul(argv[2]->arg, NULL, 10); + netlink_mcast_set_nflog_group(nfgroup); + + return CMD_SUCCESS; +} + +DEFUN(no_nhrp_multicast_nflog_group, no_nhrp_multicast_nflog_group_cmd, + "no nhrp multicast-nflog-group [(1-65535)]", + NO_STR + NHRP_STR + "Specify NFLOG group number\n" + "NFLOG group number\n") +{ + netlink_mcast_set_nflog_group(0); + return CMD_SUCCESS; +} + DEFUN(tunnel_protection, tunnel_protection_cmd, "tunnel protection vici profile PROFILE [fallback-profile FALLBACK]", "NHRP/GRE integration\n" @@ -525,11 +553,11 @@ DEFUN(if_nhrp_map, if_nhrp_map_cmd, c->map = 1; if (type == NHRP_CACHE_LOCAL) nhrp_cache_update_binding(c, NHRP_CACHE_LOCAL, 0, NULL, 0, - NULL); + NULL, NULL); else nhrp_cache_update_binding(c, NHRP_CACHE_STATIC, 0, nhrp_peer_get(ifp, &nbma_addr), 0, - NULL); + NULL, NULL); return CMD_SUCCESS; } @@ -565,10 +593,58 @@ DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd, return CMD_SUCCESS; nhrp_cache_update_binding(c, c->cur.type, -1, - nhrp_peer_get(ifp, &nbma_addr), 0, NULL); + nhrp_peer_get(ifp, &nbma_addr), 0, NULL, + NULL); return CMD_SUCCESS; } +DEFUN(if_nhrp_map_multicast, if_nhrp_map_multicast_cmd, + AFI_CMD " nhrp map multicast <A.B.C.D|X:X::X:X|dynamic>", + AFI_STR + NHRP_STR + "Multicast NBMA Configuration\n" + "Use this NBMA mapping for multicasts\n" + "IPv4 NBMA address\n" + "IPv6 NBMA address\n" + "Dynamically learn destinations from client registrations on hub\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + afi_t afi = cmd_to_afi(argv[0]); + union sockunion nbma_addr; + int ret; + + if (str2sockunion(argv[4]->arg, &nbma_addr) < 0) + sockunion_family(&nbma_addr) = AF_UNSPEC; + + ret = nhrp_multicast_add(ifp, afi, &nbma_addr); + + return nhrp_vty_return(vty, ret); +} + +DEFUN(if_no_nhrp_map_multicast, if_no_nhrp_map_multicast_cmd, + "no " AFI_CMD " nhrp map multicast <A.B.C.D|X:X::X:X|dynamic>", + NO_STR + AFI_STR + NHRP_STR + "Multicast NBMA Configuration\n" + "Use this NBMA mapping for multicasts\n" + "IPv4 NBMA address\n" + "IPv6 NBMA address\n" + "Dynamically learn destinations from client registrations on hub\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + afi_t afi = cmd_to_afi(argv[1]); + union sockunion nbma_addr; + int ret; + + if (str2sockunion(argv[5]->arg, &nbma_addr) < 0) + sockunion_family(&nbma_addr) = AF_UNSPEC; + + ret = nhrp_multicast_del(ifp, afi, &nbma_addr); + + return nhrp_vty_return(vty, ret); +} + DEFUN(if_nhrp_nhs, if_nhrp_nhs_cmd, AFI_CMD " nhrp nhs <A.B.C.D|X:X::X:X|dynamic> nbma <A.B.C.D|FQDN>", AFI_STR @@ -629,7 +705,7 @@ static void show_ip_nhrp_cache(struct nhrp_cache *c, void *pctx) { struct info_ctx *ctx = pctx; struct vty *vty = ctx->vty; - char buf[2][SU_ADDRSTRLEN]; + char buf[3][SU_ADDRSTRLEN]; struct json_object *json = NULL; if (ctx->afi != family2afi(sockunion_family(&c->remote_addr))) @@ -637,17 +713,42 @@ static void show_ip_nhrp_cache(struct nhrp_cache *c, void *pctx) if (!ctx->count && !ctx->json) { - vty_out(vty, "%-8s %-8s %-24s %-24s %-6s %s\n", "Iface", "Type", - "Protocol", "NBMA", "Flags", "Identity"); + vty_out(vty, "%-8s %-8s %-24s %-24s %-24s %-6s %s\n", "Iface", + "Type", "Protocol", "NBMA", "Claimed NBMA", "Flags", + "Identity"); } ctx->count++; sockunion2str(&c->remote_addr, buf[0], sizeof(buf[0])); - if (c->cur.peer) - sockunion2str(&c->cur.peer->vc->remote.nbma, - buf[1], sizeof(buf[1])); - else - snprintf(buf[1], sizeof(buf[1]), "-"); + if (c->cur.type == NHRP_CACHE_LOCAL) { + struct nhrp_interface *nifp = c->ifp->info; + + if (sockunion_family(&nifp->nbma) != AF_UNSPEC) { + sockunion2str(&nifp->nbma, buf[1], sizeof(buf[1])); + sockunion2str(&nifp->nbma, buf[2], sizeof(buf[2])); + } else { + snprintf(buf[1], sizeof(buf[1]), "-"); + snprintf(buf[2], sizeof(buf[2]), "-"); + } + + /* if we are behind NAT then update NBMA field */ + if (sockunion_family(&nifp->nat_nbma) != AF_UNSPEC) + sockunion2str(&nifp->nat_nbma, buf[1], sizeof(buf[1])); + } else { + if (c->cur.peer) + sockunion2str(&c->cur.peer->vc->remote.nbma, + buf[1], sizeof(buf[1])); + else + snprintf(buf[1], sizeof(buf[1]), "-"); + + if (c->cur.peer + && sockunion_family(&c->cur.remote_nbma_claimed) + != AF_UNSPEC) + sockunion2str(&c->cur.remote_nbma_claimed, + buf[2], sizeof(buf[2])); + else + snprintf(buf[2], sizeof(buf[2]), "-"); + } if (ctx->json) { json = json_object_new_object(); @@ -656,6 +757,7 @@ static void show_ip_nhrp_cache(struct nhrp_cache *c, void *pctx) nhrp_cache_type_str[c->cur.type]); json_object_string_add(json, "protocol", buf[0]); json_object_string_add(json, "nbma", buf[1]); + json_object_string_add(json, "claimed_nbma", buf[2]); if (c->used) json_object_boolean_true_add(json, "used"); @@ -681,9 +783,10 @@ static void show_ip_nhrp_cache(struct nhrp_cache *c, void *pctx) json_object_array_add(ctx->json, json); return; } - vty_out(ctx->vty, "%-8s %-8s %-24s %-24s %c%c%c %s\n", c->ifp->name, + vty_out(ctx->vty, "%-8s %-8s %-24s %-24s %-24s %c%c%c %s\n", + c->ifp->name, nhrp_cache_type_str[c->cur.type], - buf[0], buf[1], + buf[0], buf[1], buf[2], c->used ? 'U' : ' ', c->t_timeout ? 'T' : ' ', c->t_auth ? 'A' : ' ', c->cur.peer ? c->cur.peer->vc->remote.id : "-"); @@ -704,8 +807,8 @@ static void show_ip_nhrp_nhs(struct nhrp_nhs *n, struct nhrp_registration *reg, ctx->count++; if (reg && reg->peer) - sockunion2str(®->peer->vc->remote.nbma, - buf[0], sizeof(buf[0])); + sockunion2str(®->peer->vc->remote.nbma, buf[0], + sizeof(buf[0])); else snprintf(buf[0], sizeof(buf[0]), "-"); sockunion2str(reg ? ®->proto_addr : &n->proto_addr, buf[1], @@ -970,7 +1073,8 @@ static void clear_nhrp_cache(struct nhrp_cache *c, void *data) { struct info_ctx *ctx = data; if (c->cur.type <= NHRP_CACHE_DYNAMIC) { - nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL); + nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL, + NULL); ctx->count++; } } @@ -1018,19 +1122,20 @@ struct write_map_ctx { const char *aficmd; }; -static void interface_config_write_nhrp_map(struct nhrp_cache_config *c, void *data) +static void interface_config_write_nhrp_map(struct nhrp_cache_config *c, + void *data) { struct write_map_ctx *ctx = data; struct vty *vty = ctx->vty; - char buf[2][SU_ADDRSTRLEN]; if (sockunion_family(&c->remote_addr) != ctx->family) return; - vty_out(vty, " %s nhrp map %s %s\n", ctx->aficmd, - sockunion2str(&c->remote_addr, buf[0], sizeof(buf[0])), - c->type == NHRP_CACHE_LOCAL - ? "local" : sockunion2str(&c->nbma, buf[1], sizeof(buf[1]))); + vty_out(vty, " %s nhrp map %pSU ", ctx->aficmd, &c->remote_addr); + if (c->type == NHRP_CACHE_LOCAL) + vty_out(vty, "local\n"); + else + vty_out(vty, "%pSU\n", &c->nbma); } static int interface_config_write(struct vty *vty) @@ -1040,9 +1145,9 @@ static int interface_config_write(struct vty *vty) struct interface *ifp; struct nhrp_interface *nifp; struct nhrp_nhs *nhs; + struct nhrp_multicast *mcast; const char *aficmd; afi_t afi; - char buf[SU_ADDRSTRLEN]; int i; FOR_ALL_INTERFACES (vrf, ifp) { @@ -1093,21 +1198,31 @@ static int interface_config_write(struct vty *vty) .family = afi2family(afi), .aficmd = aficmd, }; - nhrp_cache_config_foreach(ifp, interface_config_write_nhrp_map, - &mapctx); + nhrp_cache_config_foreach( + ifp, interface_config_write_nhrp_map, &mapctx); list_for_each_entry(nhs, &ad->nhslist_head, nhslist_entry) { - vty_out(vty, " %s nhrp nhs %s nbma %s\n", - aficmd, - sockunion_family(&nhs->proto_addr) - == AF_UNSPEC - ? "dynamic" - : sockunion2str( - &nhs->proto_addr, buf, - sizeof(buf)), - nhs->nbma_fqdn); + vty_out(vty, " %s nhrp nhs ", aficmd); + if (sockunion_family(&nhs->proto_addr) + == AF_UNSPEC) + vty_out(vty, "dynamic"); + else + vty_out(vty, "%pSU", &nhs->proto_addr); + vty_out(vty, "nbma %s\n", nhs->nbma_fqdn); + } + + list_for_each_entry(mcast, &ad->mcastlist_head, + list_entry) + { + vty_out(vty, " %s nhrp map multicast ", aficmd); + if (sockunion_family(&mcast->nbma_addr) + == AF_UNSPEC) + vty_out(vty, "dynamic\n"); + else + vty_out(vty, "%pSU\n", + &mcast->nbma_addr); } } @@ -1142,6 +1257,8 @@ void nhrp_config_init(void) install_element(CONFIG_NODE, &no_nhrp_event_socket_cmd); install_element(CONFIG_NODE, &nhrp_nflog_group_cmd); install_element(CONFIG_NODE, &no_nhrp_nflog_group_cmd); + install_element(CONFIG_NODE, &nhrp_multicast_nflog_group_cmd); + install_element(CONFIG_NODE, &no_nhrp_multicast_nflog_group_cmd); /* interface specific commands */ install_node(&nhrp_interface_node); @@ -1163,6 +1280,8 @@ void nhrp_config_init(void) install_element(INTERFACE_NODE, &if_no_nhrp_reg_flags_cmd); install_element(INTERFACE_NODE, &if_nhrp_map_cmd); install_element(INTERFACE_NODE, &if_no_nhrp_map_cmd); + install_element(INTERFACE_NODE, &if_nhrp_map_multicast_cmd); + install_element(INTERFACE_NODE, &if_no_nhrp_map_multicast_cmd); install_element(INTERFACE_NODE, &if_nhrp_nhs_cmd); install_element(INTERFACE_NODE, &if_no_nhrp_nhs_cmd); } diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h index 9c8c293b9e..730f9b7d13 100644 --- a/nhrpd/nhrpd.h +++ b/nhrpd/nhrpd.h @@ -88,7 +88,12 @@ static inline int notifier_active(struct notifier_list *l) void nhrp_zebra_init(void); void nhrp_zebra_terminate(void); - +void nhrp_send_zebra_configure_arp(struct interface *ifp, int family); +void nhrp_send_zebra_nbr(union sockunion *in, + union sockunion *out, + struct interface *ifp); +void nhrp_send_zebra_configure_arp(struct interface *ifp, + int family); struct zbuf; struct nhrp_vc; struct nhrp_cache; @@ -156,6 +161,7 @@ struct nhrp_peer { struct nhrp_vc *vc; struct thread *t_fallback; struct notifier_block vc_notifier, ifp_notifier; + struct thread *t_timer; }; struct nhrp_packet_parser { @@ -225,9 +231,11 @@ struct nhrp_cache { struct { enum nhrp_cache_type type; union sockunion remote_nbma_natoa; + union sockunion remote_nbma_claimed; struct nhrp_peer *peer; time_t expires; uint32_t mtu; + int holding_time; } cur, new; }; @@ -261,6 +269,13 @@ struct nhrp_nhs { struct list_head reglist_head; }; +struct nhrp_multicast { + struct interface *ifp; + struct list_head list_entry; + afi_t afi; + union sockunion nbma_addr; /* IP-address */ +}; + struct nhrp_registration { struct list_head reglist_entry; struct thread *t_register; @@ -306,6 +321,7 @@ struct nhrp_interface { unsigned short mtu; unsigned int holdtime; struct list_head nhslist_head; + struct list_head mcastlist_head; } afi[AFI_MAX]; }; @@ -323,6 +339,7 @@ int nhrp_interface_up(ZAPI_CALLBACK_ARGS); int nhrp_interface_down(ZAPI_CALLBACK_ARGS); int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS); int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS); +void nhrp_neighbor_operation(ZAPI_CALLBACK_ARGS); void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn); @@ -347,6 +364,16 @@ void nhrp_nhs_foreach(struct interface *ifp, afi_t afi, void *ctx); void nhrp_nhs_interface_del(struct interface *ifp); +int nhrp_multicast_add(struct interface *ifp, afi_t afi, + union sockunion *nbma_addr); +int nhrp_multicast_del(struct interface *ifp, afi_t afi, + union sockunion *nbma_addr); +void nhrp_multicast_interface_del(struct interface *ifp); +void nhrp_multicast_foreach(struct interface *ifp, afi_t afi, + void (*cb)(struct nhrp_multicast *, void *), + void *ctx); +void netlink_mcast_set_nflog_group(int nlgroup); + void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp); void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, @@ -383,7 +410,8 @@ void nhrp_cache_config_foreach(struct interface *ifp, void nhrp_cache_set_used(struct nhrp_cache *, int); int nhrp_cache_update_binding(struct nhrp_cache *, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, - uint32_t mtu, union sockunion *nbma_natoa); + uint32_t mtu, union sockunion *nbma_natoa, + union sockunion *claimed_nbma); void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *, notifier_fn_t); void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *); @@ -465,4 +493,6 @@ void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb); void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb); void nhrp_peer_send_indication(struct interface *ifp, uint16_t, struct zbuf *); +int nhrp_nhs_match_ip(union sockunion *in_ip, struct nhrp_interface *nifp); + #endif diff --git a/nhrpd/os.h b/nhrpd/os.h index dd65d3cbe1..2b9e07fa6e 100644 --- a/nhrpd/os.h +++ b/nhrpd/os.h @@ -1,7 +1,7 @@ int os_socket(void); int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, - size_t addrlen); + size_t addrlen, uint16_t protocol); int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, size_t *addrlen); int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af); diff --git a/nhrpd/subdir.am b/nhrpd/subdir.am index 61b1fe31b2..d00aecc1ea 100644 --- a/nhrpd/subdir.am +++ b/nhrpd/subdir.am @@ -22,6 +22,7 @@ nhrpd_nhrpd_SOURCES = \ nhrpd/nhrp_nhs.c \ nhrpd/nhrp_packet.c \ nhrpd/nhrp_peer.c \ + nhrpd/nhrp_multicast.c \ nhrpd/nhrp_route.c \ nhrpd/nhrp_shortcut.c \ nhrpd/nhrp_vc.c \ diff --git a/nhrpd/vici.c b/nhrpd/vici.c index 9b117ddf0d..c21e01601c 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -470,10 +470,44 @@ static void vici_register_event(struct vici_conn *vici, const char *name) vici_submit(vici, obuf); } +static bool vici_charon_filepath_done; +static bool vici_charon_not_found; + +static char *vici_get_charon_filepath(void) +{ + static char buff[1200]; + FILE *fp; + char *ptr; + char line[1024]; + + if (vici_charon_filepath_done) + return (char *)buff; + fp = popen("ipsec --piddir", "r"); + if (!fp) { + if (!vici_charon_not_found) { + flog_err(EC_NHRP_SWAN, + "VICI: Failed to retrieve charon file path"); + vici_charon_not_found = true; + } + return NULL; + } + /* last line of output is used to get vici path */ + while (fgets(line, sizeof(line), fp) != NULL) { + ptr = strchr(line, '\n'); + if (ptr) + *ptr = '\0'; + snprintf(buff, sizeof(buff), "%s/charon.vici", line); + } + pclose(fp); + vici_charon_filepath_done = true; + return buff; +} + static int vici_reconnect(struct thread *t) { struct vici_conn *vici = THREAD_ARG(t); int fd; + char *file_path; vici->t_reconnect = NULL; if (vici->fd >= 0) @@ -481,6 +515,11 @@ static int vici_reconnect(struct thread *t) fd = sock_open_unix(VICI_SOCKET); if (fd < 0) { + file_path = vici_get_charon_filepath(); + if (file_path) + fd = sock_open_unix(file_path); + } + if (fd < 0) { debugf(NHRP_DEBUG_VICI, "%s: failure connecting VICI socket: %s", __func__, strerror(errno)); diff --git a/nhrpd/zbuf.c b/nhrpd/zbuf.c index a78d827ea5..43ce974817 100644 --- a/nhrpd/zbuf.c +++ b/nhrpd/zbuf.c @@ -27,7 +27,7 @@ struct zbuf *zbuf_alloc(size_t size) { struct zbuf *zb; - zb = XMALLOC(MTYPE_ZBUF_DATA, sizeof(*zb) + size); + zb = XCALLOC(MTYPE_ZBUF_DATA, sizeof(*zb) + size); zbuf_init(zb, zb + 1, size, 0); zb->allocated = 1; @@ -230,3 +230,15 @@ void zbuf_copy(struct zbuf *zdst, struct zbuf *zsrc, size_t len) return; memcpy(dst, src, len); } + +void zbuf_copy_peek(struct zbuf *zdst, struct zbuf *zsrc, size_t len) +{ + const void *src; + void *dst; + + dst = zbuf_pushn(zdst, len); + src = zbuf_pulln(zsrc, 0); + if (!dst || !src) + return; + memcpy(dst, src, len); +} diff --git a/nhrpd/zbuf.h b/nhrpd/zbuf.h index e6f7101d63..d4a7c15a95 100644 --- a/nhrpd/zbuf.h +++ b/nhrpd/zbuf.h @@ -190,6 +190,7 @@ static inline void zbuf_put_be32(struct zbuf *zb, uint32_t val) } void zbuf_copy(struct zbuf *zb, struct zbuf *src, size_t len); +void zbuf_copy_peek(struct zbuf *zdst, struct zbuf *zsrc, size_t len); void zbufq_init(struct zbuf_queue *); void zbufq_reset(struct zbuf_queue *); diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index f6a246500b..27d4f0755e 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -46,6 +46,7 @@ #include "ospf6_flood.h" #include "ospf6_intra.h" +#include "ospf6_asbr.h" #include "ospf6_abr.h" #include "ospf6d.h" @@ -646,10 +647,24 @@ void ospf6_abr_defaults_to_stub(struct ospf6 *o) struct listnode *node, *nnode; struct ospf6_area *oa; struct ospf6_route *def, *route; + struct ospf6_redist *red; + int type = DEFAULT_ROUTE; + struct prefix_ipv6 p = {}; if (!o->backbone) return; + red = ospf6_redist_lookup(o, type, 0); + if (!red) + return; + + p.family = AF_INET6; + p.prefixlen = 0; + + route = ospf6_route_lookup((struct prefix *)&p, o->external_table); + if (!route) + return; + def = ospf6_route_create(); def->type = OSPF6_DEST_TYPE_NETWORK; def->prefix.family = AF_INET6; @@ -659,6 +674,8 @@ void ospf6_abr_defaults_to_stub(struct ospf6 *o) def->path.type = OSPF6_PATH_TYPE_INTER; def->path.subtype = OSPF6_PATH_SUBTYPE_DEFAULT_RT; def->path.area_id = o->backbone->area_id; + def->path.metric_type = metric_type(o, type, 0); + def->path.cost = metric_value(o, type, 0); for (ALL_LIST_ELEMENTS(o->area_list, node, nnode, oa)) { if (!IS_AREA_STUB(oa)) { @@ -1209,7 +1226,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) */ if (old_entry_updated == false) { if ((old == NULL) || (old->type != route->type) - || (old->path.type != route->path.type)) + || (old->path.type != route->path.type) + || (old->path.cost != route->path.cost)) add_route = true; } diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 9223083aef..6bf61b4804 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -46,6 +46,7 @@ #include "ospf6d.h" #include "lib/json.h" +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area"); DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name"); int ospf6_area_cmp(void *va, void *vb) diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 977d810be5..ffd6dc22c3 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -30,6 +30,7 @@ #include "plist.h" #include "thread.h" #include "linklist.h" +#include "lib/northbound_cli.h" #include "ospf6_proto.h" #include "ospf6_lsa.h" @@ -37,6 +38,7 @@ #include "ospf6_route.h" #include "ospf6_zebra.h" #include "ospf6_message.h" +#include "ospf6_spf.h" #include "ospf6_top.h" #include "ospf6_area.h" @@ -49,10 +51,18 @@ #include "ospf6d.h" #include "lib/json.h" +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DIST_ARGS, "OSPF6 Distribute arguments"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_REDISTRIBUTE, "OSPF6 Redistribute arguments"); + static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id); static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6, struct ospf6_redist *red, int type); +#ifndef VTYSH_EXTRACT_PL +#include "ospf6d/ospf6_asbr_clippy.c" +#endif + unsigned char conf_debug_ospf6_asbr = 0; #define ZROUTE_NAME(x) zebra_route_string(x) @@ -1043,6 +1053,7 @@ static struct ospf6_redist *ospf6_redist_add(struct ospf6 *ospf6, int type, ROUTEMAP(red) = NULL; listnode_add(ospf6->redist[type], red); + ospf6->redistribute++; return red; } @@ -1056,6 +1067,7 @@ static void ospf6_redist_del(struct ospf6 *ospf6, struct ospf6_redist *red, list_delete(&ospf6->redist[type]); } XFREE(MTYPE_OSPF6_REDISTRIBUTE, red); + ospf6->redistribute--; } } @@ -1092,8 +1104,10 @@ void ospf6_asbr_send_externals_to_area(struct ospf6_area *oa) for (ALL_LSDB(oa->ospf6->lsdb, lsa, lsanext)) { if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) { - zlog_debug("%s: Flooding AS-External LSA %s", - __func__, lsa->name); + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("%s: Flooding AS-External LSA %s", + __func__, lsa->name); + ospf6_flood_area(NULL, lsa, oa); } } @@ -1116,6 +1130,7 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa) /* if router is only in a stub area then purge AS-External LSAs */ iterend = ospf6_lsdb_head(ospf6->lsdb, 0, 0, 0, &lsa); while (lsa != NULL) { + assert(lsa->lock > 1); lsanext = ospf6_lsdb_next(iterend, lsa); if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) ospf6_lsdb_remove(lsa, ospf6->lsdb); @@ -1123,6 +1138,35 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa) } } +/* Update ASBR status. */ +static void ospf6_asbr_status_update(struct ospf6 *ospf6, uint8_t status) +{ + struct listnode *lnode, *lnnode; + struct ospf6_area *oa; + + zlog_info("ASBR[%s:Status:%d]: Update", ospf6->name, status); + + if (status) { + if (IS_OSPF6_ASBR(ospf6)) { + zlog_info("ASBR[%s:Status:%d]: Already ASBR", + ospf6->name, status); + return; + } + SET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR); + } else { + if (!IS_OSPF6_ASBR(ospf6)) { + zlog_info("ASBR[%s:Status:%d]: Already non ASBR", + ospf6->name, status); + return; + } + UNSET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR); + } + + ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE); + for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, lnnode, oa)) + OSPF6_ROUTER_LSA_SCHEDULE(oa); +} + void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, struct prefix *prefix, unsigned int nexthop_num, @@ -1137,8 +1181,6 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, struct prefix prefix_id; struct route_node *node; char ibuf[16]; - struct listnode *lnode, *lnnode; - struct ospf6_area *oa; struct ospf6_redist *red; red = ospf6_redist_lookup(ospf6, type, 0); @@ -1146,7 +1188,8 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, if (!red) return; - if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) + if ((type != DEFAULT_ROUTE) + && !ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) return; memset(&troute, 0, sizeof(troute)); @@ -1199,7 +1242,11 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, sizeof(struct in6_addr)); info->tag = tinfo.tag; } else { - /* If there is no route-map, simply update the tag */ + /* If there is no route-map, simply update the tag and + * metric fields + */ + match->path.metric_type = metric_type(ospf6, type, 0); + match->path.cost = metric_value(ospf6, type, 0); info->tag = tag; } @@ -1227,6 +1274,7 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, match->path.origin.id = htonl(info->id); ospf6_as_external_lsa_originate(match, ospf6); + ospf6_asbr_status_update(ospf6, ospf6->redistribute); return; } @@ -1251,7 +1299,11 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, sizeof(struct in6_addr)); info->tag = tinfo.tag; } else { - /* If there is no route-map, simply set the tag */ + /* If there is no route-map, simply update the tag and metric + * fields + */ + route->path.metric_type = metric_type(ospf6, type, 0); + route->path.cost = metric_value(ospf6, type, 0); info->tag = tag; } @@ -1280,10 +1332,7 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, route->path.origin.id = htonl(info->id); ospf6_as_external_lsa_originate(route, ospf6); - - /* Router-Bit (ASBR Flag) may have to be updated */ - for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, lnnode, oa)) - OSPF6_ROUTER_LSA_SCHEDULE(oa); + ospf6_asbr_status_update(ospf6, ospf6->redistribute); } void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, @@ -1295,8 +1344,6 @@ void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, struct ospf6_lsa *lsa; struct prefix prefix_id; char ibuf[16]; - struct listnode *lnode, *lnnode; - struct ospf6_area *oa; match = ospf6_route_lookup(prefix, ospf6->external_table); if (match == NULL) { @@ -1337,9 +1384,7 @@ void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, ospf6_route_remove(match, ospf6->external_table); XFREE(MTYPE_OSPF6_EXTERNAL_INFO, info); - /* Router-Bit (ASBR Flag) may have to be updated */ - for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, lnnode, oa)) - OSPF6_ROUTER_LSA_SCHEDULE(oa); + ospf6_asbr_status_update(ospf6, ospf6->redistribute); } DEFUN (ospf6_redistribute, @@ -1529,6 +1574,140 @@ static void ospf6_redistribute_show_config(struct vty *vty, struct ospf6 *ospf6, vty_out(vty, "Total %d routes\n", total); } +static void ospf6_redistribute_default_set(struct ospf6 *ospf6, int originate) +{ + struct prefix_ipv6 p = {}; + struct in6_addr nexthop = {}; + int cur_originate = ospf6->default_originate; + + p.family = AF_INET6; + p.prefixlen = 0; + + ospf6->default_originate = originate; + + switch (cur_originate) { + case DEFAULT_ORIGINATE_NONE: + break; + case DEFAULT_ORIGINATE_ZEBRA: + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, + zclient, AFI_IP6, ospf6->vrf_id); + ospf6_asbr_redistribute_remove(DEFAULT_ROUTE, 0, + (struct prefix *)&p, ospf6); + + break; + case DEFAULT_ORIGINATE_ALWAYS: + ospf6_asbr_redistribute_remove(DEFAULT_ROUTE, 0, + (struct prefix *)&p, ospf6); + break; + } + + switch (originate) { + case DEFAULT_ORIGINATE_NONE: + break; + case DEFAULT_ORIGINATE_ZEBRA: + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD, + zclient, AFI_IP6, ospf6->vrf_id); + + break; + case DEFAULT_ORIGINATE_ALWAYS: + ospf6_asbr_redistribute_add(DEFAULT_ROUTE, 0, + (struct prefix *)&p, 0, &nexthop, 0, + ospf6); + break; + } +} + +/* Default Route originate. */ +DEFPY (ospf6_default_route_originate, + ospf6_default_route_originate_cmd, + "default-information originate [{always$always|metric (0-16777214)$mval|metric-type (1-2)$mtype|route-map WORD$rtmap}]", + "Control distribution of default route\n" + "Distribute a default route\n" + "Always advertise default route\n" + "OSPFv3 default metric\n" + "OSPFv3 metric\n" + "OSPFv3 metric type for default routes\n" + "Set OSPFv3 External Type 1/2 metrics\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + int default_originate = DEFAULT_ORIGINATE_ZEBRA; + struct ospf6_redist *red; + bool sameRtmap = false; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + int cur_originate = ospf6->default_originate; + + OSPF6_CMD_CHECK_RUNNING(ospf6); + + red = ospf6_redist_add(ospf6, DEFAULT_ROUTE, 0); + + if (always != NULL) + default_originate = DEFAULT_ORIGINATE_ALWAYS; + + if (mval_str == NULL) + mval = -1; + + if (mtype_str == NULL) + mtype = -1; + + /* To check ,if user is providing same route map */ + if ((rtmap == ROUTEMAP_NAME(red)) + || (rtmap && ROUTEMAP_NAME(red) + && (strcmp(rtmap, ROUTEMAP_NAME(red)) == 0))) + sameRtmap = true; + + /* Don't allow if the same lsa is aleardy originated. */ + if ((sameRtmap) && (red->dmetric.type == mtype) + && (red->dmetric.value == mval) + && (cur_originate == default_originate)) + return CMD_SUCCESS; + + /* Updating Metric details */ + red->dmetric.type = mtype; + red->dmetric.value = mval; + + /* updating route map details */ + if (rtmap) + ospf6_asbr_routemap_set(red, rtmap); + else + ospf6_asbr_routemap_unset(red); + + ospf6_redistribute_default_set(ospf6, default_originate); + return CMD_SUCCESS; +} + +DEFPY (no_ospf6_default_information_originate, + no_ospf6_default_information_originate_cmd, + "no default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map WORD}]", + NO_STR + "Control distribution of default information\n" + "Distribute a default route\n" + "Always advertise default route\n" + "OSPFv3 default metric\n" + "OSPFv3 metric\n" + "OSPFv3 metric type for default routes\n" + "Set OSPFv3 External Type 1/2 metrics\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + struct ospf6_redist *red; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_CHECK_RUNNING(ospf6); + + red = ospf6_redist_lookup(ospf6, DEFAULT_ROUTE, 0); + if (!red) + return CMD_SUCCESS; + + ospf6_asbr_routemap_unset(red); + ospf6_redist_del(ospf6, red, DEFAULT_ROUTE); + + ospf6_redistribute_default_set(ospf6, DEFAULT_ORIGINATE_NONE); + return CMD_SUCCESS; +} /* Routemap Functions */ static enum route_map_cmd_result_t @@ -1746,99 +1925,83 @@ ospf6_routemap_rule_set_tag(void *rule, const struct prefix *p, void *object) return RMAP_OKAY; } -static const struct route_map_rule_cmd - ospf6_routemap_rule_set_tag_cmd = { +static const struct route_map_rule_cmd ospf6_routemap_rule_set_tag_cmd = { "tag", ospf6_routemap_rule_set_tag, route_map_rule_tag_compile, route_map_rule_tag_free, }; -static int route_map_command_status(struct vty *vty, enum rmap_compile_rets ret) -{ - switch (ret) { - case RMAP_RULE_MISSING: - vty_out(vty, "OSPF6 Can't find rule.\n"); - return CMD_WARNING_CONFIG_FAILED; - case RMAP_COMPILE_ERROR: - vty_out(vty, "OSPF6 Argument is malformed.\n"); - return CMD_WARNING_CONFIG_FAILED; - case RMAP_COMPILE_SUCCESS: - break; - } - - return CMD_SUCCESS; -} - /* add "set metric-type" */ -DEFUN (ospf6_routemap_set_metric_type, - ospf6_routemap_set_metric_type_cmd, - "set metric-type <type-1|type-2>", - "Set value\n" - "Type of metric\n" - "OSPF6 external type 1 metric\n" - "OSPF6 external type 2 metric\n") +DEFUN_YANG (ospf6_routemap_set_metric_type, ospf6_routemap_set_metric_type_cmd, + "set metric-type <type-1|type-2>", + "Set value\n" + "Type of metric\n" + "OSPF6 external type 1 metric\n" + "OSPF6 external type 2 metric\n") { - VTY_DECLVAR_CONTEXT(route_map_index, route_map_index); - int idx_external = 2; - enum rmap_compile_rets ret = route_map_add_set(route_map_index, - "metric-type", - argv[idx_external]->arg); + char *ext = argv[2]->text; + + const char *xpath = + "./set-action[action='frr-ospf-route-map:metric-type']"; + char xpath_value[XPATH_MAXLEN]; - return route_map_command_status(vty, ret); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-ospf-route-map:metric-type", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ext); + return nb_cli_apply_changes(vty, NULL); } /* delete "set metric-type" */ -DEFUN (ospf6_routemap_no_set_metric_type, - ospf6_routemap_no_set_metric_type_cmd, - "no set metric-type [<type-1|type-2>]", - NO_STR - "Set value\n" - "Type of metric\n" - "OSPF6 external type 1 metric\n" - "OSPF6 external type 2 metric\n") +DEFUN_YANG (ospf6_routemap_no_set_metric_type, ospf6_routemap_no_set_metric_type_cmd, + "no set metric-type [<type-1|type-2>]", + NO_STR + "Set value\n" + "Type of metric\n" + "OSPF6 external type 1 metric\n" + "OSPF6 external type 2 metric\n") { - VTY_DECLVAR_CONTEXT(route_map_index, route_map_index); - char *ext = (argc == 4) ? argv[3]->text : NULL; - enum rmap_compile_rets ret = route_map_delete_set(route_map_index, - "metric-type", ext); + const char *xpath = + "./set-action[action='frr-ospf-route-map:metric-type']"; - return route_map_command_status(vty, ret); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } /* add "set forwarding-address" */ -DEFUN (ospf6_routemap_set_forwarding, - ospf6_routemap_set_forwarding_cmd, - "set forwarding-address X:X::X:X", - "Set value\n" - "Forwarding Address\n" - "IPv6 Address\n") -{ - VTY_DECLVAR_CONTEXT(route_map_index, route_map_index); +DEFUN_YANG (ospf6_routemap_set_forwarding, ospf6_routemap_set_forwarding_cmd, + "set forwarding-address X:X::X:X", + "Set value\n" + "Forwarding Address\n" + "IPv6 Address\n") +{ int idx_ipv6 = 2; - enum rmap_compile_rets ret = route_map_add_set(route_map_index, - "forwarding-address", - argv[idx_ipv6]->arg); - - return route_map_command_status(vty, ret); + const char *xpath = + "./set-action[action='frr-ospf6-route-map:forwarding-address']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-ospf6-route-map:ipv6-address", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_ipv6]->arg); + return nb_cli_apply_changes(vty, NULL); } /* delete "set forwarding-address" */ -DEFUN (ospf6_routemap_no_set_forwarding, - ospf6_routemap_no_set_forwarding_cmd, - "no set forwarding-address X:X::X:X", - NO_STR - "Set value\n" - "Forwarding Address\n" - "IPv6 Address\n") +DEFUN_YANG (ospf6_routemap_no_set_forwarding, ospf6_routemap_no_set_forwarding_cmd, + "no set forwarding-address [X:X::X:X]", + NO_STR + "Set value\n" + "Forwarding Address\n" + "IPv6 Address\n") { - VTY_DECLVAR_CONTEXT(route_map_index, route_map_index); - int idx_ipv6 = 3; - enum rmap_compile_rets ret = route_map_delete_set(route_map_index, - "forwarding-address", - argv[idx_ipv6]->arg); + const char *xpath = + "./set-action[action='frr-ospf6-route-map:forwarding-address']"; - return route_map_command_status(vty, ret); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } static void ospf6_routemap_init(void) @@ -2111,6 +2274,9 @@ void ospf6_asbr_init(void) install_element(VIEW_NODE, &show_ipv6_ospf6_redistribute_cmd); + install_element(OSPF6_NODE, &ospf6_default_route_originate_cmd); + install_element(OSPF6_NODE, + &no_ospf6_default_information_originate_cmd); install_element(OSPF6_NODE, &ospf6_redistribute_cmd); install_element(OSPF6_NODE, &ospf6_redistribute_routemap_cmd); install_element(OSPF6_NODE, &no_ospf6_redistribute_cmd); @@ -2172,6 +2338,39 @@ int config_write_ospf6_debug_asbr(struct vty *vty) return 0; } +int ospf6_distribute_config_write(struct vty *vty, struct ospf6 *ospf6) +{ + struct ospf6_redist *red; + + if (ospf6) { + /* default-route print. */ + if (ospf6->default_originate != DEFAULT_ORIGINATE_NONE) { + vty_out(vty, " default-information originate"); + if (ospf6->default_originate + == DEFAULT_ORIGINATE_ALWAYS) + vty_out(vty, " always"); + + red = ospf6_redist_lookup(ospf6, DEFAULT_ROUTE, 0); + if (red) { + if (red->dmetric.value >= 0) + vty_out(vty, " metric %d", + red->dmetric.value); + + if (red->dmetric.type >= 0) + vty_out(vty, " metric-type %d", + red->dmetric.type); + + if (ROUTEMAP_NAME(red)) + vty_out(vty, " route-map %s", + ROUTEMAP_NAME(red)); + } + + vty_out(vty, "\n"); + } + } + return 0; +} + void install_element_ospf6_debug_asbr(void) { install_element(ENABLE_NODE, &debug_ospf6_asbr_cmd); diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index e4a4455a5c..4774ac435a 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -98,6 +98,7 @@ extern void ospf6_asbr_send_externals_to_area(struct ospf6_area *); extern void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa); extern int config_write_ospf6_debug_asbr(struct vty *vty); +extern int ospf6_distribute_config_write(struct vty *vty, struct ospf6 *ospf6); extern void install_element_ospf6_debug_asbr(void); extern void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, struct ospf6_route *route, diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 2d896546fa..5f4815fec1 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -463,6 +463,19 @@ static void ospf6_flood_process(struct ospf6_neighbor *from, struct ospf6_area *oa; for (ALL_LIST_ELEMENTS(process->area_list, node, nnode, oa)) { + + /* If unknown LSA and U-bit clear, treat as link local + * flooding scope + */ + if (!OSPF6_LSA_IS_KNOWN(lsa->header->type) + && !(ntohs(lsa->header->type) & OSPF6_LSTYPE_UBIT_MASK) + && (oa != OSPF6_INTERFACE(lsa->lsdb->data)->area)) { + + if (IS_OSPF6_DEBUG_FLOODING) + zlog_debug("Unknown LSA, do not flood"); + continue; + } + if (OSPF6_LSA_SCOPE(lsa->header->type) == OSPF6_SCOPE_AREA && oa != OSPF6_AREA(lsa->lsdb->data)) continue; diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 6f6bbc2b67..158b8dc483 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -45,6 +45,7 @@ #include "ospf6_zebra.h" #include "lib/json.h" +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_IF, "OSPF6 interface"); DEFINE_MTYPE_STATIC(OSPF6D, CFG_PLIST_NAME, "configured prefix list names"); DEFINE_QOBJ_TYPE(ospf6_interface); DEFINE_HOOK(ospf6_interface_change, diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index e1c3b4038c..f5f429b041 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -34,6 +34,8 @@ #include "ospf6_lsa.h" #include "ospf6_lsdb.h" #include "ospf6_message.h" +#include "ospf6_asbr.h" +#include "ospf6_zebra.h" #include "ospf6_top.h" #include "ospf6_area.h" @@ -43,6 +45,10 @@ #include "ospf6_flood.h" #include "ospf6d.h" +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_HEADER, "OSPF6 LSA header"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_SUMMARY, "OSPF6 LSA summary"); + vector ospf6_lsa_handler_vector; struct ospf6 *ospf6_get_by_lsdb(struct ospf6_lsa *lsa) @@ -159,6 +165,34 @@ uint8_t ospf6_lstype_debug(uint16_t type) return handler->lh_debug; } +int metric_type(struct ospf6 *ospf6, int type, uint8_t instance) +{ + struct ospf6_redist *red; + + red = ospf6_redist_lookup(ospf6, type, instance); + + return ((!red || red->dmetric.type < 0) ? DEFAULT_METRIC_TYPE + : red->dmetric.type); +} + +int metric_value(struct ospf6 *ospf6, int type, uint8_t instance) +{ + struct ospf6_redist *red; + + red = ospf6_redist_lookup(ospf6, type, instance); + if (!red || red->dmetric.value < 0) { + if (type == DEFAULT_ROUTE) { + if (ospf6->default_originate == DEFAULT_ORIGINATE_ZEBRA) + return DEFAULT_DEFAULT_ORIGINATE_METRIC; + else + return DEFAULT_DEFAULT_ALWAYS_METRIC; + } else + return DEFAULT_DEFAULT_METRIC; + } + + return red->dmetric.value; +} + /* RFC2328: Section 13.2 */ int ospf6_lsa_is_differ(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2) { @@ -657,27 +691,29 @@ void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, vty_out(vty, "\n"); } +struct ospf6_lsa *ospf6_lsa_alloc(size_t lsa_length) +{ + struct ospf6_lsa *lsa; + + lsa = XCALLOC(MTYPE_OSPF6_LSA, sizeof(struct ospf6_lsa)); + lsa->header = XMALLOC(MTYPE_OSPF6_LSA_HEADER, lsa_length); + + return lsa; +} + /* OSPFv3 LSA creation/deletion function */ struct ospf6_lsa *ospf6_lsa_create(struct ospf6_lsa_header *header) { struct ospf6_lsa *lsa = NULL; - struct ospf6_lsa_header *new_header = NULL; uint16_t lsa_size = 0; /* size of the entire LSA */ lsa_size = ntohs(header->length); /* XXX vulnerable */ - /* allocate memory for this LSA */ - new_header = XMALLOC(MTYPE_OSPF6_LSA_HEADER, lsa_size); + lsa = ospf6_lsa_alloc(lsa_size); /* copy LSA from original header */ - memcpy(new_header, header, lsa_size); - - /* LSA information structure */ - /* allocate memory */ - lsa = XCALLOC(MTYPE_OSPF6_LSA, sizeof(struct ospf6_lsa)); - - lsa->header = new_header; + memcpy(lsa->header, header, lsa_size); /* dump string */ ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name)); @@ -691,20 +727,11 @@ struct ospf6_lsa *ospf6_lsa_create(struct ospf6_lsa_header *header) struct ospf6_lsa *ospf6_lsa_create_headeronly(struct ospf6_lsa_header *header) { struct ospf6_lsa *lsa = NULL; - struct ospf6_lsa_header *new_header = NULL; - /* allocate memory for this LSA */ - new_header = XMALLOC(MTYPE_OSPF6_LSA_HEADER, - sizeof(struct ospf6_lsa_header)); + lsa = ospf6_lsa_alloc(sizeof(struct ospf6_lsa_header)); - /* copy LSA from original header */ - memcpy(new_header, header, sizeof(struct ospf6_lsa_header)); + memcpy(lsa->header, header, sizeof(struct ospf6_lsa_header)); - /* LSA information structure */ - /* allocate memory */ - lsa = XCALLOC(MTYPE_OSPF6_LSA, sizeof(struct ospf6_lsa)); - - lsa->header = new_header; SET_FLAG(lsa->flag, OSPF6_LSA_HEADERONLY); /* dump string */ @@ -856,11 +883,12 @@ int ospf6_lsa_refresh(struct thread *thread) void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6) { - struct listnode *node; + struct listnode *node, *nnode; struct ospf6_area *oa; struct ospf6_lsa *lsa; const struct route_node *end = NULL; uint32_t type, adv_router; + struct ospf6_interface *oi; ospf6->inst_shutdown = 1; @@ -875,6 +903,19 @@ void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6) lsa = ospf6_lsdb_next(end, lsa); } + + for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) { + end = ospf6_lsdb_head(oi->lsdb_self, 0, 0, + ospf6->router_id, &lsa); + while (lsa) { + /* RFC 2328 (14.1): Set MAXAGE */ + lsa->header->age = htons(OSPF_LSA_MAXAGE); + /* Flood MAXAGE LSA*/ + ospf6_flood(NULL, lsa); + + lsa = ospf6_lsdb_next(end, lsa); + } + } } type = htons(OSPF6_LSTYPE_AS_EXTERNAL); diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index 7fa9c5fe40..84c3b85631 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -29,6 +29,12 @@ #define OSPF6_LSA_DEBUG_EXAMIN 0x04 #define OSPF6_LSA_DEBUG_FLOOD 0x08 +/* OSPF LSA Default metric values */ +#define DEFAULT_DEFAULT_METRIC 20 +#define DEFAULT_DEFAULT_ORIGINATE_METRIC 10 +#define DEFAULT_DEFAULT_ALWAYS_METRIC 1 +#define DEFAULT_METRIC_TYPE 2 + #define IS_OSPF6_DEBUG_LSA(name) \ (ospf6_lstype_debug(htons(OSPF6_LSTYPE_##name)) & OSPF6_LSA_DEBUG) #define IS_OSPF6_DEBUG_ORIGINATE(name) \ @@ -197,6 +203,8 @@ extern vector ospf6_lsa_handler_vector; extern const char *ospf6_lstype_name(uint16_t type); extern const char *ospf6_lstype_short_name(uint16_t type); extern uint8_t ospf6_lstype_debug(uint16_t type); +extern int metric_type(struct ospf6 *ospf6, int type, uint8_t instance); +extern int metric_value(struct ospf6 *ospf6, int type, uint8_t instance); extern int ospf6_lsa_is_differ(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); extern int ospf6_lsa_is_changed(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); extern uint16_t ospf6_lsa_age_current(struct ospf6_lsa *); @@ -217,6 +225,7 @@ extern void ospf6_lsa_show_internal(struct vty *vty, struct ospf6_lsa *lsa, extern void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, json_object *json, bool use_json); +extern struct ospf6_lsa *ospf6_lsa_alloc(size_t lsa_length); extern struct ospf6_lsa *ospf6_lsa_create(struct ospf6_lsa_header *header); extern struct ospf6_lsa * ospf6_lsa_create_headeronly(struct ospf6_lsa_header *header); diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 9636e1a230..18f121e3a2 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -34,6 +34,8 @@ #include "ospf6d.h" #include "bitfield.h" +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSDB, "OSPF6 LSA database"); + struct ospf6_lsdb *ospf6_lsdb_create(void *data) { struct ospf6_lsdb *lsdb; diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index c601693a7e..cf61ca7a62 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -47,6 +47,7 @@ #include "ospf6_lsa.h" #include "ospf6_interface.h" #include "ospf6_zebra.h" +#include "ospf6_routemap_nb.h" /* Default configuration file name for ospf6d. */ #define OSPF6_DEFAULT_CONFIG "ospf6d.conf" @@ -172,6 +173,8 @@ static const struct frr_yang_module_info *const ospf6d_yang_modules[] = { &frr_interface_info, &frr_route_map_info, &frr_vrf_info, + &frr_ospf_route_map_info, + &frr_ospf6_route_map_info, }; FRR_DAEMON_INFO(ospf6d, OSPF6, .vty_port = OSPF6_VTY_PORT, diff --git a/ospf6d/ospf6_memory.c b/ospf6d/ospf6_memory.c deleted file mode 100644 index 05bad4936e..0000000000 --- a/ospf6d/ospf6_memory.c +++ /dev/null @@ -1,47 +0,0 @@ -/* ospf6d memory type definitions - * - * Copyright (C) 2015 David Lamparter - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "ospf6_memory.h" - -DEFINE_MGROUP(OSPF6D, "ospf6d"); -DEFINE_MTYPE(OSPF6D, OSPF6_TOP, "OSPF6 top"); -DEFINE_MTYPE(OSPF6D, OSPF6_AREA, "OSPF6 area"); -DEFINE_MTYPE(OSPF6D, OSPF6_IF, "OSPF6 interface"); -DEFINE_MTYPE(OSPF6D, OSPF6_NEIGHBOR, "OSPF6 neighbor"); -DEFINE_MTYPE(OSPF6D, OSPF6_ROUTE, "OSPF6 route"); -DEFINE_MTYPE(OSPF6D, OSPF6_PREFIX, "OSPF6 prefix"); -DEFINE_MTYPE(OSPF6D, OSPF6_MESSAGE, "OSPF6 message"); -DEFINE_MTYPE(OSPF6D, OSPF6_LSA, "OSPF6 LSA"); -DEFINE_MTYPE(OSPF6D, OSPF6_LSA_HEADER, "OSPF6 LSA header"); -DEFINE_MTYPE(OSPF6D, OSPF6_LSA_SUMMARY, "OSPF6 LSA summary"); -DEFINE_MTYPE(OSPF6D, OSPF6_LSDB, "OSPF6 LSA database"); -DEFINE_MTYPE(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex"); -DEFINE_MTYPE(OSPF6D, OSPF6_SPFTREE, "OSPF6 SPF tree"); -DEFINE_MTYPE(OSPF6D, OSPF6_NEXTHOP, "OSPF6 nexthop"); -DEFINE_MTYPE(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info"); -DEFINE_MTYPE(OSPF6D, OSPF6_PATH, "OSPF6 Path"); -DEFINE_MTYPE(OSPF6D, OSPF6_DIST_ARGS, "OSPF6 Distribute arguments"); -DEFINE_MTYPE(OSPF6D, OSPF6_OTHER, "OSPF6 other"); -DEFINE_MTYPE(OSPF6D, OSPF6_REDISTRIBUTE, "OSPF6 Redistribute arguments"); diff --git a/ospf6d/ospf6_memory.h b/ospf6d/ospf6_memory.h deleted file mode 100644 index 68a0363a15..0000000000 --- a/ospf6d/ospf6_memory.h +++ /dev/null @@ -1,48 +0,0 @@ -/* ospf6d memory type declarations - * - * Copyright (C) 2015 David Lamparter - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _QUAGGA_OSPF6_MEMORY_H -#define _QUAGGA_OSPF6_MEMORY_H - -#include "memory.h" - -DECLARE_MGROUP(OSPF6D); -DECLARE_MTYPE(OSPF6_TOP); -DECLARE_MTYPE(OSPF6_AREA); -DECLARE_MTYPE(OSPF6_IF); -DECLARE_MTYPE(OSPF6_NEIGHBOR); -DECLARE_MTYPE(OSPF6_ROUTE); -DECLARE_MTYPE(OSPF6_PREFIX); -DECLARE_MTYPE(OSPF6_MESSAGE); -DECLARE_MTYPE(OSPF6_LSA); -DECLARE_MTYPE(OSPF6_LSA_HEADER); -DECLARE_MTYPE(OSPF6_LSA_SUMMARY); -DECLARE_MTYPE(OSPF6_LSDB); -DECLARE_MTYPE(OSPF6_VERTEX); -DECLARE_MTYPE(OSPF6_SPFTREE); -DECLARE_MTYPE(OSPF6_NEXTHOP); -DECLARE_MTYPE(OSPF6_EXTERNAL_INFO); -DECLARE_MTYPE(OSPF6_PATH); -DECLARE_MTYPE(OSPF6_DIST_ARGS); -DECLARE_MTYPE(OSPF6_REDISTRIBUTE); -DECLARE_MTYPE(OSPF6_OTHER); - -#endif /* _QUAGGA_OSPF6_MEMORY_H */ diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index aebe43b9ec..7aedd3df45 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -49,6 +49,8 @@ #include <netinet/ip6.h> +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_MESSAGE, "OSPF6 message"); + unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0}; static const struct message ospf6_message_type_str[] = { {OSPF6_MESSAGE_TYPE_HELLO, "Hello"}, diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 15065c98eb..485bde4b7b 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -46,6 +46,8 @@ #include "ospf6_zebra.h" #include "lib/json.h" +DEFINE_MTYPE(OSPF6D, OSPF6_NEIGHBOR, "OSPF6 neighbor"); + DEFINE_HOOK(ospf6_neighbor_change, (struct ospf6_neighbor * on, int state, int next_state), (on, state, next_state)); diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index b77f968179..9770dd0444 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -38,6 +38,10 @@ #include "ospf6d.h" #include "ospf6_zebra.h" +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE, "OSPF6 route"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEXTHOP, "OSPF6 nexthop"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PATH, "OSPF6 Path"); + unsigned char conf_debug_ospf6_route = 0; static char *ospf6_route_table_name(struct ospf6_route_table *table) diff --git a/ospf6d/ospf6_routemap_nb.c b/ospf6d/ospf6_routemap_nb.c new file mode 100644 index 0000000000..b710fefbdf --- /dev/null +++ b/ospf6d/ospf6_routemap_nb.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "ospf6_routemap_nb.h" + +/* clang-format off */ +const struct frr_yang_module_info frr_ospf_route_map_info = { + .name = "frr-ospf-route-map", + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf-route-map:metric-type", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_metric_type_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; + +const struct frr_yang_module_info frr_ospf6_route_map_info = { + .name = "frr-ospf6-route-map", + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf6-route-map:ipv6-address", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/ospf6d/ospf6_routemap_nb.h b/ospf6d/ospf6_routemap_nb.h new file mode 100644 index 0000000000..181e71a8c5 --- /dev/null +++ b/ospf6d/ospf6_routemap_nb.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_OSPF6_ROUTEMAP_NB_H_ +#define _FRR_OSPF6_ROUTEMAP_NB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct frr_yang_module_info frr_ospf_route_map_info; +extern const struct frr_yang_module_info frr_ospf6_route_map_info; + +/* prototypes */ +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_metric_type_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy(struct nb_cb_destroy_args *args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ospf6d/ospf6_routemap_nb_config.c b/ospf6d/ospf6_routemap_nb_config.c new file mode 100644 index 0000000000..3c7741e473 --- /dev/null +++ b/ospf6d/ospf6_routemap_nb_config.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "ospf6_routemap_nb.h" + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf-route-map:metric-type + */ +int lib_route_map_entry_set_action_rmap_set_action_metric_type_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "metric-type"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "metric-type", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf6-route-map:ipv6-address + */ +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *ipv6_addr; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + ipv6_addr = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "forwarding-address"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "forwarding-address", + ipv6_addr, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index d1931055a2..7652d71c59 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -43,6 +43,8 @@ #include "ospf6d.h" #include "ospf6_abr.h" +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex"); + unsigned char conf_debug_ospf6_spf = 0; static void ospf6_spf_copy_nexthops_to_route(struct ospf6_route *rt, @@ -1021,13 +1023,8 @@ struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area, return NULL; } - /* Allocate memory for this LSA */ - new_header = XMALLOC(MTYPE_OSPF6_LSA_HEADER, total_lsa_length); - - /* LSA information structure */ - lsa = XCALLOC(MTYPE_OSPF6_LSA, sizeof(struct ospf6_lsa)); - - lsa->header = (struct ospf6_lsa_header *)new_header; + lsa = ospf6_lsa_alloc(total_lsa_length); + new_header = (uint8_t *)lsa->header; lsa->lsdb = area->temp_router_lsa_lsdb; diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index 523b318d5b..4660bfd05d 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -90,6 +90,7 @@ struct ospf6_vertex { #define OSPF6_SPF_FLAGS_ROUTER_LSA_ORIGINATED (1 << 6) #define OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED (1 << 7) #define OSPF6_SPF_FLAGS_CONFIG_CHANGE (1 << 8) +#define OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE (1 << 9) static inline void ospf6_set_spf_reason(struct ospf6 *ospf, unsigned int reason) { diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 2fed7c5d03..e2cd5c259d 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -53,6 +53,8 @@ #include "ospf6d.h" #include "lib/json.h" +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_TOP, "OSPF6 top"); + DEFINE_QOBJ_TYPE(ospf6); FRR_CFG_DEFAULT_BOOL(OSPF6_LOG_ADJACENCY_CHANGES, @@ -245,6 +247,8 @@ static struct ospf6 *ospf6_create(const char *name) o->spf_max_holdtime = OSPF_SPF_MAX_HOLDTIME_DEFAULT; o->spf_hold_multiplier = 1; + o->default_originate = DEFAULT_ORIGINATE_NONE; + o->redistribute = 0; /* LSA timers value init */ o->lsa_minarrival = OSPF_MIN_LS_ARRIVAL; @@ -1327,6 +1331,7 @@ static int config_write_ospf6(struct vty *vty) ospf6_area_config_write(vty, ospf6); ospf6_spf_config_write(vty, ospf6); ospf6_distance_config_write(vty, ospf6); + ospf6_distribute_config_write(vty, ospf6); for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, j, oa)) { for (ALL_LIST_ELEMENTS_RO(oa->if_list, k, oi)) diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 7980659e68..08b884f23a 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -40,6 +40,15 @@ enum { struct ospf6_redist { uint8_t instance; + + /* Redistribute metric info. */ + struct { + int type; /* External metric type (E1 or E2). */ + int value; /* Value for static metric (24-bit). + * -1 means metric value is not set. + */ + } dmetric; + /* For redistribute route map. */ struct { char *name; @@ -83,13 +92,18 @@ struct ospf6 { uint32_t external_id; /* OSPF6 redistribute configuration */ - struct list *redist[ZEBRA_ROUTE_MAX]; + struct list *redist[ZEBRA_ROUTE_MAX + 1]; uint8_t flag; + int redistribute; /* Num of redistributed protocols. */ + /* Configuration bitmask, refer to enum above */ uint8_t config_flags; - + int default_originate; /* Default information originate. */ +#define DEFAULT_ORIGINATE_NONE 0 +#define DEFAULT_ORIGINATE_ZEBRA 1 +#define DEFAULT_ORIGINATE_ALWAYS 2 /* LSA timer parameters */ unsigned int lsa_minarrival; /* LSA minimum arrival in milliseconds. */ @@ -138,6 +152,7 @@ DECLARE_QOBJ_TYPE(ospf6); #define OSPF6_DISABLED 0x01 #define OSPF6_STUB_ROUTER 0x02 +#define OSPF6_FLAG_ASBR 0x04 /* global pointer for OSPF top data structure */ extern struct ospf6 *ospf6; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 8d5e0f0a39..76e7172870 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -172,12 +172,23 @@ static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS) return 0; } +static int is_prefix_default(struct prefix_ipv6 *p) +{ + struct prefix_ipv6 q = {}; + + q.family = AF_INET6; + q.prefixlen = 0; + + return prefix_same((struct prefix *)p, (struct prefix *)&q); +} + static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; unsigned long ifindex; struct in6_addr *nexthop; struct ospf6 *ospf6; + struct prefix_ipv6 p; ospf6 = ospf6_lookup_by_vrf_id(vrf_id); @@ -205,6 +216,10 @@ static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS) zebra_route_string(api.type), &api.prefix, nexthop, ifindex, api.tag); + memcpy(&p, &api.prefix, sizeof(p)); + if (is_prefix_default(&p)) + api.type = DEFAULT_ROUTE; + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) ospf6_asbr_redistribute_add(api.type, ifindex, &api.prefix, api.nexthop_num, nexthop, api.tag, diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h index 5f340924b9..a3ccc3d38d 100644 --- a/ospf6d/ospf6_zebra.h +++ b/ospf6d/ospf6_zebra.h @@ -23,6 +23,8 @@ #include "zclient.h" +#define DEFAULT_ROUTE ZEBRA_ROUTE_MAX + /* Debug option */ extern unsigned char conf_debug_ospf6_zebra; #define OSPF6_DEBUG_ZEBRA_SEND 0x01 diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index 8d9c85fd08..91d427c78c 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -46,6 +46,8 @@ #include "ospf6_bfd.h" #include "lib/json.h" +DEFINE_MGROUP(OSPF6D, "ospf6d"); + struct route_node *route_prev(struct route_node *node) { struct route_node *end; diff --git a/ospf6d/ospf6d.conf.sample b/ospf6d/ospf6d.conf.sample deleted file mode 100644 index 0a6ddb7137..0000000000 --- a/ospf6d/ospf6d.conf.sample +++ /dev/null @@ -1,52 +0,0 @@ -! -! Zebra configuration saved from vty -! 2003/11/28 00:49:49 -! -hostname ospf6d@plant -password zebra -log stdout -service advanced-vty -! -debug ospf6 neighbor state -! -interface fxp0 - ipv6 ospf6 cost 1 - ipv6 ospf6 hello-interval 10 - ipv6 ospf6 dead-interval 40 - ipv6 ospf6 retransmit-interval 5 - ipv6 ospf6 priority 0 - ipv6 ospf6 transmit-delay 1 - ipv6 ospf6 instance-id 0 -! -interface lo0 - ipv6 ospf6 cost 1 - ipv6 ospf6 hello-interval 10 - ipv6 ospf6 dead-interval 40 - ipv6 ospf6 retransmit-interval 5 - ipv6 ospf6 priority 1 - ipv6 ospf6 transmit-delay 1 - ipv6 ospf6 instance-id 0 -! -router ospf6 - router-id 255.1.1.1 - redistribute static route-map static-ospf6 - interface fxp0 area 0.0.0.0 -! -access-list access4 permit 127.0.0.1/32 -! -ipv6 access-list access6 permit 3ffe:501::/32 -ipv6 access-list access6 permit 2001:200::/48 -ipv6 access-list access6 permit ::1/128 -! -ipv6 prefix-list test-prefix seq 1000 deny any -! -route-map static-ospf6 permit 10 - match ipv6 address prefix-list test-prefix - set metric-type type-2 - set metric 2000 -! -line vty - access-class access4 - ipv6 access-class access6 - exec-timeout 0 0 -! diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index d85ff40f32..dfac57aa2f 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -23,8 +23,9 @@ #include "libospf.h" #include "thread.h" +#include "memory.h" -#include "ospf6_memory.h" +DECLARE_MGROUP(OSPF6D); /* global variables */ extern struct thread_master *master; @@ -94,6 +95,7 @@ extern struct thread_master *master; return CMD_SUCCESS; \ } +#define IS_OSPF6_ASBR(O) ((O)->flag & OSPF6_FLAG_ASBR) extern struct zebra_privs_t ospf6d_privs; /* Function Prototypes */ diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index d9d4301df7..5ccae5b279 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -5,7 +5,6 @@ if OSPF6D noinst_LIBRARIES += ospf6d/libospf6.a sbin_PROGRAMS += ospf6d/ospf6d -dist_examples_DATA += ospf6d/ospf6d.conf.sample vtysh_scan += \ ospf6d/ospf6_abr.c \ ospf6d/ospf6_asbr.c \ @@ -34,13 +33,14 @@ ospf6d_libospf6_a_SOURCES = \ ospf6d/ospf6_abr.c \ ospf6d/ospf6_area.c \ ospf6d/ospf6_asbr.c \ + ospf6d/ospf6_routemap_nb.c \ + ospf6d/ospf6_routemap_nb_config.c \ ospf6d/ospf6_bfd.c \ ospf6d/ospf6_flood.c \ ospf6d/ospf6_interface.c \ ospf6d/ospf6_intra.c \ ospf6d/ospf6_lsa.c \ ospf6d/ospf6_lsdb.c \ - ospf6d/ospf6_memory.c \ ospf6d/ospf6_message.c \ ospf6d/ospf6_neighbor.c \ ospf6d/ospf6_network.c \ @@ -62,12 +62,12 @@ noinst_HEADERS += \ ospf6d/ospf6_intra.h \ ospf6d/ospf6_lsa.h \ ospf6d/ospf6_lsdb.h \ - ospf6d/ospf6_memory.h \ ospf6d/ospf6_message.h \ ospf6d/ospf6_neighbor.h \ ospf6d/ospf6_network.h \ ospf6d/ospf6_proto.h \ ospf6d/ospf6_route.h \ + ospf6d/ospf6_routemap_nb.h \ ospf6d/ospf6_spf.h \ ospf6d/ospf6_top.h \ ospf6d/ospf6_zebra.h \ @@ -83,3 +83,12 @@ ospf6d_ospf6d_snmp_la_SOURCES = ospf6d/ospf6_snmp.c ospf6d_ospf6d_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu11 ospf6d_ospf6d_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic ospf6d_ospf6d_snmp_la_LIBADD = lib/libfrrsnmp.la + +clippy_scan += \ + ospf6d/ospf6_asbr.c \ + # end + +nodist_ospf6d_ospf6d_SOURCES = \ + yang/frr-ospf-route-map.yang.c \ + yang/frr-ospf6-route-map.yang.c \ + # end diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index f3c4798906..b5c97eda3c 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -675,7 +675,8 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) * originate translated LSA */ - if (ospf_translated_nssa_originate(area->ospf, lsa) == NULL) { + if (ospf_translated_nssa_originate(area->ospf, lsa, old) + == NULL) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( "ospf_abr_translate_nssa(): Could not translate Type-7 for %pI4 to Type-5", diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c index a9bc9069d2..2ab7db68bd 100644 --- a/ospfd/ospf_bfd.c +++ b/ospfd/ospf_bfd.c @@ -23,6 +23,7 @@ #include <zebra.h> #include "command.h" +#include "json.h" #include "linklist.h" #include "memory.h" #include "prefix.h" @@ -44,48 +45,7 @@ #include "ospf_dump.h" #include "ospf_vty.h" -extern struct zclient *zclient; - -/* - * ospf_bfd_info_free - Free BFD info structure - */ -void ospf_bfd_info_free(void **bfd_info) -{ - bfd_info_free((struct bfd_info **)bfd_info); -} - -/* - * ospf_bfd_reg_dereg_nbr - Register/Deregister a neighbor with BFD through - * zebra for starting/stopping the monitoring of - * the neighbor rechahability. - */ -static void ospf_bfd_reg_dereg_nbr(struct ospf_neighbor *nbr, int command) -{ - struct ospf_interface *oi = nbr->oi; - struct interface *ifp = oi->ifp; - struct ospf_if_params *params; - struct bfd_info *bfd_info; - int cbit; - - /* Check if BFD is enabled */ - params = IF_DEF_PARAMS(ifp); - - /* Check if BFD is enabled */ - if (!params->bfd_info) - return; - bfd_info = (struct bfd_info *)params->bfd_info; - - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug("%s nbr (%pI4) with BFD. OSPF vrf %s", - bfd_get_command_dbg_str(command), - &nbr->src, - ospf_vrf_id_to_name(oi->ospf->vrf_id)); - - cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); - - bfd_peer_sendmsg(zclient, bfd_info, AF_INET, &nbr->src, NULL, ifp->name, - 0, 0, cbit, command, 0, oi->ospf->vrf_id); -} +DEFINE_MTYPE_STATIC(OSPFD, BFD_CONFIG, "BFD configuration data"); /* * ospf_bfd_trigger_event - Neighbor is registered/deregistered with BFD when @@ -94,293 +54,157 @@ static void ospf_bfd_reg_dereg_nbr(struct ospf_neighbor *nbr, int command) void ospf_bfd_trigger_event(struct ospf_neighbor *nbr, int old_state, int state) { if ((old_state < NSM_TwoWay) && (state >= NSM_TwoWay)) - ospf_bfd_reg_dereg_nbr(nbr, ZEBRA_BFD_DEST_REGISTER); + bfd_sess_install(nbr->bfd_session); else if ((old_state >= NSM_TwoWay) && (state < NSM_TwoWay)) - ospf_bfd_reg_dereg_nbr(nbr, ZEBRA_BFD_DEST_DEREGISTER); + bfd_sess_uninstall(nbr->bfd_session); } -/* - * ospf_bfd_reg_dereg_all_nbr - Register/Deregister all neighbors associated - * with a interface with BFD through - * zebra for starting/stopping the monitoring of - * the neighbor rechahability. - */ -static int ospf_bfd_reg_dereg_all_nbr(struct interface *ifp, int command) +static void ospf_bfd_session_change(struct bfd_session_params *bsp, + const struct bfd_session_status *bss, + void *arg) { - struct ospf_interface *oi; - struct route_table *nbrs; - struct ospf_neighbor *nbr; - struct route_node *irn; - struct route_node *nrn; - - for (irn = route_top(IF_OIFS(ifp)); irn; irn = route_next(irn)) { - if ((oi = irn->info) == NULL) - continue; + struct ospf_neighbor *nbr = arg; - if ((nbrs = oi->nbrs) == NULL) - continue; - - for (nrn = route_top(nbrs); nrn; nrn = route_next(nrn)) { - if ((nbr = nrn->info) == NULL || nbr == oi->nbr_self) - continue; - - if (command != ZEBRA_BFD_DEST_DEREGISTER) - ospf_bfd_info_nbr_create(oi, nbr); - else - bfd_info_free( - (struct bfd_info **)&nbr->bfd_info); - - if (nbr->state < NSM_TwoWay) - continue; + /* BFD peer went down. */ + if (bss->state == BFD_STATUS_DOWN + && bss->previous_state == BFD_STATUS_UP) { + if (IS_DEBUG_OSPF(bfd, BFD_LIB)) + zlog_debug("%s: NSM[%s:%pI4]: BFD Down", __func__, + IF_NAME(nbr->oi), &nbr->address.u.prefix4); - ospf_bfd_reg_dereg_nbr(nbr, command); - } + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer); } - return 0; + /* BFD peer went up. */ + if (bss->state == BSS_UP && bss->previous_state == BSS_DOWN) + if (IS_DEBUG_OSPF(bfd, BFD_LIB)) + zlog_debug("%s: NSM[%s:%pI4]: BFD Up", __func__, + IF_NAME(nbr->oi), &nbr->address.u.prefix4); } -/* - * ospf_bfd_nbr_replay - Replay all the neighbors that have BFD enabled - * to zebra - */ -static int ospf_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) +void ospf_neighbor_bfd_apply(struct ospf_neighbor *nbr) { - struct listnode *inode, *node, *onode; - struct ospf *ospf; - struct ospf_interface *oi; - struct route_table *nbrs; - struct route_node *rn; - struct ospf_neighbor *nbr; - struct ospf_if_params *params; + struct ospf_interface *oi = nbr->oi; + struct ospf_if_params *oip = IF_DEF_PARAMS(oi->ifp); - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) { - zlog_debug("Zebra: BFD Dest replay request"); + /* BFD configuration was removed. */ + if (oip->bfd_config == NULL) { + bfd_sess_free(&nbr->bfd_session); + return; } - /* Send the client registration */ - bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id); - - /* Replay the neighbor, if BFD is enabled in OSPF */ - for (ALL_LIST_ELEMENTS(om->ospf, node, onode, ospf)) { - for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi)) { - if ((nbrs = oi->nbrs) == NULL) - continue; - - params = IF_DEF_PARAMS(oi->ifp); - if (!params->bfd_info) - continue; - - for (rn = route_top(nbrs); rn; rn = route_next(rn)) { - if ((nbr = rn->info) == NULL - || nbr == oi->nbr_self) - continue; + /* New BFD session. */ + if (nbr->bfd_session == NULL) { + nbr->bfd_session = bfd_sess_new(ospf_bfd_session_change, nbr); + bfd_sess_set_ipv4_addrs(nbr->bfd_session, NULL, &nbr->src); + bfd_sess_set_interface(nbr->bfd_session, oi->ifp->name); + bfd_sess_set_vrf(nbr->bfd_session, oi->ospf->vrf_id); + bfd_sess_enable(nbr->bfd_session, true); + } - if (nbr->state < NSM_TwoWay) - continue; + /* Set new configuration. */ + bfd_sess_set_timers(nbr->bfd_session, + oip->bfd_config->detection_multiplier, + oip->bfd_config->min_rx, oip->bfd_config->min_tx); + bfd_sess_set_profile(nbr->bfd_session, oip->bfd_config->profile); - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug("Replaying nbr (%pI4) to BFD", - &nbr->src); + /* Don't start sessions on down OSPF sessions. */ + if (nbr->state < NSM_TwoWay) + return; - ospf_bfd_reg_dereg_nbr(nbr, - ZEBRA_BFD_DEST_UPDATE); - } - } - } - return 0; + bfd_sess_install(nbr->bfd_session); } -/* - * ospf_bfd_interface_dest_update - Find the neighbor for which the BFD status - * has changed and bring down the neighbor - * connectivity if the BFD status changed to - * down. - */ -static int ospf_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) +static void ospf_interface_bfd_apply(struct interface *ifp) { - struct interface *ifp; struct ospf_interface *oi; - struct ospf_if_params *params; - struct ospf_neighbor *nbr = NULL; - struct route_node *node; - struct route_node *n_node; - struct prefix p, src_p; - int status; - int old_status; - struct bfd_info *bfd_info; - struct timeval tv; - - ifp = bfd_get_peer_info(zclient->ibuf, &p, &src_p, &status, NULL, - vrf_id); - - if ((ifp == NULL) || (p.family != AF_INET)) - return 0; - - if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) - zlog_debug("Zebra: interface %s bfd destination %pFX %s", - ifp->name, &p, bfd_get_status_str(status)); - - params = IF_DEF_PARAMS(ifp); - if (!params->bfd_info) - return 0; - - for (node = route_top(IF_OIFS(ifp)); node; node = route_next(node)) { - if ((oi = node->info) == NULL) - continue; - - /* walk the neighbor list for point-to-point network */ - if (oi->type == OSPF_IFTYPE_POINTOPOINT) { - for (n_node = route_top(oi->nbrs); n_node; - n_node = route_next(n_node)) { - nbr = n_node->info; - if (nbr) { - /* skip myself */ - if (nbr == oi->nbr_self) { - nbr = NULL; - continue; - } - - /* Found the matching neighbor */ - if (nbr->src.s_addr == - p.u.prefix4.s_addr) - break; - } - } - } else { - nbr = ospf_nbr_lookup_by_addr(oi->nbrs, &p.u.prefix4); - } + struct route_table *nbrs; + struct ospf_neighbor *nbr; + struct route_node *irn; + struct route_node *nrn; - if (!nbr || !nbr->bfd_info) + /* Iterate over all interfaces and set neighbors BFD session. */ + for (irn = route_top(IF_OIFS(ifp)); irn; irn = route_next(irn)) { + if ((oi = irn->info) == NULL) continue; - - bfd_info = (struct bfd_info *)nbr->bfd_info; - if (bfd_info->status == status) + if ((nbrs = oi->nbrs) == NULL) continue; + for (nrn = route_top(nbrs); nrn; nrn = route_next(nrn)) { + if ((nbr = nrn->info) == NULL || nbr == oi->nbr_self) + continue; - old_status = bfd_info->status; - BFD_SET_CLIENT_STATUS(bfd_info->status, status); - monotime(&tv); - bfd_info->last_update = tv.tv_sec; - - if ((status == BFD_STATUS_DOWN) - && (old_status == BFD_STATUS_UP)) { - if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) - zlog_debug("NSM[%s:%pI4]: BFD Down", - IF_NAME(nbr->oi), - &nbr->address.u.prefix4); - - OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer); - } - if ((status == BFD_STATUS_UP) - && (old_status == BFD_STATUS_DOWN)) { - if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) - zlog_debug("NSM[%s:%pI4]: BFD Up", - IF_NAME(nbr->oi), - &nbr->address.u.prefix4); + ospf_neighbor_bfd_apply(nbr); } } - - return 0; } -/* - * ospf_bfd_info_nbr_create - Create/update BFD information for a neighbor. - */ -void ospf_bfd_info_nbr_create(struct ospf_interface *oi, - struct ospf_neighbor *nbr) +static void ospf_interface_enable_bfd(struct interface *ifp) { - struct bfd_info *oi_bfd_info; - struct bfd_info *nbr_bfd_info; - struct interface *ifp = oi->ifp; - struct ospf_if_params *params; - - /* Check if BFD is enabled */ - params = IF_DEF_PARAMS(ifp); + struct ospf_if_params *oip = IF_DEF_PARAMS(ifp); - /* Check if BFD is enabled */ - if (!params->bfd_info) + if (oip->bfd_config) return; - oi_bfd_info = (struct bfd_info *)params->bfd_info; - if (!nbr->bfd_info) - nbr->bfd_info = bfd_info_create(); + /* Allocate memory for configurations and set defaults. */ + oip->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*oip->bfd_config)); + oip->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT; + oip->bfd_config->min_rx = BFD_DEF_MIN_RX; + oip->bfd_config->min_tx = BFD_DEF_MIN_TX; +} - nbr_bfd_info = (struct bfd_info *)nbr->bfd_info; - nbr_bfd_info->detect_mult = oi_bfd_info->detect_mult; - nbr_bfd_info->desired_min_tx = oi_bfd_info->desired_min_tx; - nbr_bfd_info->required_min_rx = oi_bfd_info->required_min_rx; +void ospf_interface_disable_bfd(struct interface *ifp, + struct ospf_if_params *oip) +{ + XFREE(MTYPE_BFD_CONFIG, oip->bfd_config); + ospf_interface_bfd_apply(ifp); } /* * ospf_bfd_write_config - Write the interface BFD configuration. */ -void ospf_bfd_write_config(struct vty *vty, struct ospf_if_params *params) - +void ospf_bfd_write_config(struct vty *vty, const struct ospf_if_params *params + __attribute__((unused))) { #if HAVE_BFDD == 0 - struct bfd_info *bfd_info; -#endif /* ! HAVE_BFDD */ - - if (!params->bfd_info) - return; - -#if HAVE_BFDD == 0 - bfd_info = (struct bfd_info *)params->bfd_info; - - if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG)) - vty_out(vty, " ip ospf bfd %d %d %d\n", bfd_info->detect_mult, - bfd_info->required_min_rx, bfd_info->desired_min_tx); + if (params->bfd_config->detection_multiplier != BFD_DEF_DETECT_MULT + || params->bfd_config->min_rx != BFD_DEF_MIN_RX + || params->bfd_config->min_tx != BFD_DEF_MIN_TX) + vty_out(vty, " ip ospf bfd %d %d %d\n", + params->bfd_config->detection_multiplier, + params->bfd_config->min_rx, params->bfd_config->min_tx); else #endif /* ! HAVE_BFDD */ vty_out(vty, " ip ospf bfd\n"); -} -/* - * ospf_bfd_show_info - Show BFD info structure - */ -void ospf_bfd_show_info(struct vty *vty, void *bfd_info, json_object *json_obj, - bool use_json, int param_only) -{ - if (param_only) - bfd_show_param(vty, (struct bfd_info *)bfd_info, 1, 0, use_json, - json_obj); - else - bfd_show_info(vty, (struct bfd_info *)bfd_info, 0, 1, use_json, - json_obj); + if (params->bfd_config->profile[0]) + vty_out(vty, " ip ospf bfd profile %s\n", + params->bfd_config->profile); } -/* - * ospf_bfd_interface_show - Show the interface BFD configuration. - */ -void ospf_bfd_interface_show(struct vty *vty, struct interface *ifp, - json_object *json_interface_sub, bool use_json) +void ospf_interface_bfd_show(struct vty *vty, const struct interface *ifp, + struct json_object *json) { - struct ospf_if_params *params; - - params = IF_DEF_PARAMS(ifp); + struct ospf_if_params *params = IF_DEF_PARAMS(ifp); + struct bfd_configuration *bfd_config = params->bfd_config; + struct json_object *json_bfd; - ospf_bfd_show_info(vty, params->bfd_info, json_interface_sub, use_json, - 1); -} - -/* - * ospf_bfd_if_param_set - Set the configured BFD paramter values for - * interface. - */ -static void ospf_bfd_if_param_set(struct interface *ifp, uint32_t min_rx, - uint32_t min_tx, uint8_t detect_mult, - int defaults) -{ - struct ospf_if_params *params; - int command = 0; - - params = IF_DEF_PARAMS(ifp); + if (bfd_config == NULL) + return; - bfd_set_param((struct bfd_info **)&(params->bfd_info), min_rx, min_tx, - detect_mult, NULL, defaults, &command); - if (command) - ospf_bfd_reg_dereg_all_nbr(ifp, command); + if (json) { + json_bfd = json_object_new_object(); + json_object_int_add(json_bfd, "detectionMultiplier", + bfd_config->detection_multiplier); + json_object_int_add(json_bfd, "rxMinInterval", + bfd_config->min_rx); + json_object_int_add(json_bfd, "txMinInterval", + bfd_config->min_tx); + json_object_object_add(json, "peerBfdInfo", json_bfd); + } else + vty_out(vty, + " BFD: Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n", + bfd_config->detection_multiplier, bfd_config->min_rx, + bfd_config->min_tx); } DEFUN (ip_ospf_bfd, @@ -391,17 +215,8 @@ DEFUN (ip_ospf_bfd, "Enables BFD support\n") { VTY_DECLVAR_CONTEXT(interface, ifp); - struct ospf_if_params *params; - struct bfd_info *bfd_info; - - assert(ifp); - params = IF_DEF_PARAMS(ifp); - bfd_info = params->bfd_info; - - if (!bfd_info || !CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG)) - ospf_bfd_if_param_set(ifp, BFD_DEF_MIN_RX, BFD_DEF_MIN_TX, - BFD_DEF_DETECT_MULT, 1); - + ospf_interface_enable_bfd(ifp); + ospf_interface_bfd_apply(ifp); return CMD_SUCCESS; } @@ -421,23 +236,63 @@ DEFUN( "Desired min transmit interval\n") { VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; int idx_number = 3; int idx_number_2 = 4; int idx_number_3 = 5; - uint32_t rx_val; - uint32_t tx_val; - uint8_t dm_val; - int ret; - assert(ifp); + ospf_interface_enable_bfd(ifp); + + params = IF_DEF_PARAMS(ifp); + params->bfd_config->detection_multiplier = + strtol(argv[idx_number]->arg, NULL, 10); + params->bfd_config->min_rx = strtol(argv[idx_number_2]->arg, NULL, 10); + params->bfd_config->min_tx = strtol(argv[idx_number_3]->arg, NULL, 10); + + ospf_interface_bfd_apply(ifp); + + return CMD_SUCCESS; +} - if ((ret = bfd_validate_param( - vty, argv[idx_number]->arg, argv[idx_number_2]->arg, - argv[idx_number_3]->arg, &dm_val, &rx_val, &tx_val)) - != CMD_SUCCESS) - return ret; +DEFUN (ip_ospf_bfd_prof, + ip_ospf_bfd_prof_cmd, + "ip ospf bfd profile BFDPROF", + "IP Information\n" + "OSPF interface commands\n" + "Enables BFD support\n" + BFD_PROFILE_STR + BFD_PROFILE_NAME_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + int idx_prof = 4; - ospf_bfd_if_param_set(ifp, rx_val, tx_val, dm_val, 0); + ospf_interface_enable_bfd(ifp); + params = IF_DEF_PARAMS(ifp); + strlcpy(params->bfd_config->profile, argv[idx_prof]->arg, + sizeof(params->bfd_config->profile)); + ospf_interface_bfd_apply(ifp); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_ospf_bfd_prof, + no_ip_ospf_bfd_prof_cmd, + "no ip ospf bfd profile [BFDPROF]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Enables BFD support\n" + BFD_PROFILE_STR + BFD_PROFILE_NAME_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + + ospf_interface_enable_bfd(ifp); + params = IF_DEF_PARAMS(ifp); + params->bfd_config->profile[0] = 0; + ospf_interface_bfd_apply(ifp); return CMD_SUCCESS; } @@ -461,29 +316,18 @@ DEFUN (no_ip_ospf_bfd, ) { VTY_DECLVAR_CONTEXT(interface, ifp); - struct ospf_if_params *params; - - assert(ifp); - - params = IF_DEF_PARAMS(ifp); - if (params->bfd_info) { - ospf_bfd_reg_dereg_all_nbr(ifp, ZEBRA_BFD_DEST_DEREGISTER); - bfd_info_free(&(params->bfd_info)); - } - + ospf_interface_disable_bfd(ifp, IF_DEF_PARAMS(ifp)); return CMD_SUCCESS; } -void ospf_bfd_init(void) +void ospf_bfd_init(struct thread_master *tm) { - bfd_gbl_init(); - - /* Initialize BFD client functions */ - zclient->interface_bfd_dest_update = ospf_bfd_interface_dest_update; - zclient->bfd_dest_replay = ospf_bfd_nbr_replay; + bfd_protocol_integration_init(zclient, tm); /* Install BFD command */ install_element(INTERFACE_NODE, &ip_ospf_bfd_cmd); install_element(INTERFACE_NODE, &ip_ospf_bfd_param_cmd); + install_element(INTERFACE_NODE, &ip_ospf_bfd_prof_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_bfd_prof_cmd); install_element(INTERFACE_NODE, &no_ip_ospf_bfd_cmd); } diff --git a/ospfd/ospf_bfd.h b/ospfd/ospf_bfd.h index 74385d3268..9393c839f5 100644 --- a/ospfd/ospf_bfd.h +++ b/ospfd/ospf_bfd.h @@ -23,27 +23,34 @@ #ifndef _ZEBRA_OSPF_BFD_H #define _ZEBRA_OSPF_BFD_H +#include "ospfd/ospf_interface.h" #include "json.h" -extern void ospf_bfd_init(void); +extern void ospf_bfd_init(struct thread_master *tm); extern void ospf_bfd_write_config(struct vty *vty, - struct ospf_if_params *params); + const struct ospf_if_params *params); extern void ospf_bfd_trigger_event(struct ospf_neighbor *nbr, int old_state, int state); -extern void ospf_bfd_interface_show(struct vty *vty, struct interface *ifp, - json_object *json_interface_sub, - bool use_json); - -extern void ospf_bfd_info_nbr_create(struct ospf_interface *oi, - struct ospf_neighbor *nbr); +/** + * Legacy information: it is the peers who actually have this information + * and the protocol should not need to know about timers. + */ +extern void ospf_interface_bfd_show(struct vty *vty, + const struct interface *ifp, + struct json_object *json); -extern void ospf_bfd_show_info(struct vty *vty, void *bfd_info, - json_object *json_obj, bool use_json, - int param_only); +/** + * Disables interface BFD configuration and remove settings from all peers. + */ +extern void ospf_interface_disable_bfd(struct interface *ifp, + struct ospf_if_params *oip); -extern void ospf_bfd_info_free(void **bfd_info); +/** + * Create/update BFD session for this OSPF neighbor. + */ +extern void ospf_neighbor_bfd_apply(struct ospf_neighbor *nbr); #endif /* _ZEBRA_OSPF_BFD_H */ diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index 19829d4546..2442f2e781 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -21,6 +21,7 @@ #include <zebra.h> +#include "lib/bfd.h" #include "monotime.h" #include "linklist.h" #include "thread.h" @@ -60,6 +61,7 @@ unsigned long conf_debug_ospf_ti_lfa = 0; unsigned long conf_debug_ospf_defaultinfo = 0; unsigned long conf_debug_ospf_ldp_sync = 0; unsigned long conf_debug_ospf_gr = 0; +unsigned long conf_debug_ospf_bfd; /* Enable debug option variables -- valid only session. */ unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; @@ -76,6 +78,7 @@ unsigned long term_debug_ospf_ti_lfa = 0; unsigned long term_debug_ospf_defaultinfo; unsigned long term_debug_ospf_ldp_sync; unsigned long term_debug_ospf_gr = 0; +unsigned long term_debug_ospf_bfd; const char *ospf_redist_string(unsigned int route_type) { @@ -1563,6 +1566,31 @@ DEFPY (debug_ospf_gr, return CMD_SUCCESS; } +DEFPY(debug_ospf_bfd, debug_ospf_bfd_cmd, + "[no] debug ospf bfd", + NO_STR + DEBUG_STR + OSPF_STR + "Bidirection Forwarding Detection\n") +{ + if (vty->node == CONFIG_NODE) { + if (no) { + bfd_protocol_integration_set_debug(false); + CONF_DEBUG_OFF(bfd, BFD_LIB); + } else { + bfd_protocol_integration_set_debug(true); + CONF_DEBUG_ON(bfd, BFD_LIB); + } + } + + if (no) + TERM_DEBUG_OFF(bfd, BFD_LIB); + else + TERM_DEBUG_ON(bfd, BFD_LIB); + + return CMD_SUCCESS; +} + DEFUN (no_debug_ospf, no_debug_ospf_cmd, "no debug ospf", @@ -1594,6 +1622,10 @@ DEFUN (no_debug_ospf, DEBUG_OFF(defaultinfo, DEFAULTINFO); DEBUG_OFF(ldp_sync, LDP_SYNC); + /* BFD debugging is two parts: OSPF and library. */ + DEBUG_OFF(bfd, BFD_LIB); + bfd_protocol_integration_set_debug(false); + for (i = 0; i < 5; i++) DEBUG_PACKET_OFF(i, flag); } @@ -1621,6 +1653,7 @@ DEFUN (no_debug_ospf, TERM_DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO); TERM_DEBUG_OFF(ldp_sync, LDP_SYNC); + TERM_DEBUG_OFF(bfd, BFD_LIB); return CMD_SUCCESS; } @@ -1730,6 +1763,10 @@ static int show_debugging_ospf_common(struct vty *vty) if (IS_DEBUG_OSPF(gr, GR_HELPER) == OSPF_DEBUG_GR_HELPER) vty_out(vty, " OSPF Graceful Restart Helper debugging is on\n"); + if (IS_DEBUG_OSPF(bfd, BFD_LIB) == OSPF_DEBUG_BFD_LIB) + vty_out(vty, + " OSPF BFD integration library debugging is on\n"); + vty_out(vty, "\n"); return CMD_SUCCESS; @@ -1917,6 +1954,11 @@ static int config_write_debug(struct vty *vty) write = 1; } + if (IS_CONF_DEBUG_OSPF(bfd, BFD_LIB) == OSPF_DEBUG_BFD_LIB) { + vty_out(vty, "debug ospf%s bfd\n", str); + write = 1; + } + return write; } @@ -1949,6 +1991,7 @@ void ospf_debug_init(void) install_element(ENABLE_NODE, &no_debug_ospf_default_info_cmd); install_element(ENABLE_NODE, &no_debug_ospf_ldp_sync_cmd); install_element(ENABLE_NODE, &debug_ospf_gr_cmd); + install_element(ENABLE_NODE, &debug_ospf_bfd_cmd); install_element(ENABLE_NODE, &show_debugging_ospf_instance_cmd); install_element(ENABLE_NODE, &debug_ospf_packet_cmd); @@ -1992,6 +2035,7 @@ void ospf_debug_init(void) install_element(CONFIG_NODE, &no_debug_ospf_default_info_cmd); install_element(CONFIG_NODE, &no_debug_ospf_ldp_sync_cmd); install_element(CONFIG_NODE, &debug_ospf_gr_cmd); + install_element(CONFIG_NODE, &debug_ospf_bfd_cmd); install_element(CONFIG_NODE, &debug_ospf_instance_nsm_cmd); install_element(CONFIG_NODE, &debug_ospf_instance_lsa_cmd); diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h index c4c5606663..b1c1d02a51 100644 --- a/ospfd/ospf_dump.h +++ b/ospfd/ospf_dump.h @@ -67,6 +67,8 @@ #define OSPF_DEBUG_GR_HELPER 0x01 #define OSPF_DEBUG_GR 0x03 +#define OSPF_DEBUG_BFD_LIB 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) @@ -140,6 +142,7 @@ extern unsigned long term_debug_ospf_ti_lfa; extern unsigned long term_debug_ospf_defaultinfo; extern unsigned long term_debug_ospf_ldp_sync; extern unsigned long term_debug_ospf_gr; +extern unsigned long term_debug_ospf_bfd; /* Message Strings. */ extern char *ospf_lsa_type_str[]; diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 0161f05d71..334ed33ee0 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -35,6 +35,7 @@ #include "ldp_sync.h" #include "ospfd/ospfd.h" +#include "ospfd/ospf_bfd.h" #include "ospfd/ospf_spf.h" #include "ospfd/ospf_interface.h" #include "ospfd/ospf_ism.h" @@ -542,13 +543,16 @@ static struct ospf_if_params *ospf_new_if_params(void) oip->network_lsa_seqnum = htonl(OSPF_INITIAL_SEQUENCE_NUMBER); oip->is_v_wait_set = false; + oip->ptp_dmvpn = 0; + return oip; } -void ospf_del_if_params(struct ospf_if_params *oip) +static void ospf_del_if_params(struct interface *ifp, + struct ospf_if_params *oip) { list_delete(&oip->auth_crypt); - bfd_info_free(&(oip->bfd_info)); + ospf_interface_disable_bfd(ifp, oip); ldp_sync_info_free(&(oip->ldp_sync_info)); XFREE(MTYPE_OSPF_IF_PARAMS, oip); } @@ -582,7 +586,7 @@ void ospf_free_if_params(struct interface *ifp, struct in_addr addr) && !OSPF_IF_PARAM_CONFIGURED(oip, auth_type) && !OSPF_IF_PARAM_CONFIGURED(oip, if_area) && listcount(oip->auth_crypt) == 0) { - ospf_del_if_params(oip); + ospf_del_if_params(ifp, oip); rn->info = NULL; route_unlock_node(rn); } @@ -692,14 +696,20 @@ static int ospf_if_delete_hook(struct interface *ifp) struct route_node *rn; rc = ospf_opaque_del_if(ifp); + /* + * This function must be called before `route_table_finish` due to + * BFD integration need to iterate over the interface neighbors to + * remove all registrations. + */ + ospf_del_if_params(ifp, IF_DEF_PARAMS(ifp)); + route_table_finish(IF_OIFS(ifp)); for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) if (rn->info) - ospf_del_if_params(rn->info); + ospf_del_if_params(ifp, rn->info); route_table_finish(IF_OIFS_PARAMS(ifp)); - ospf_del_if_params((struct ospf_if_params *)IF_DEF_PARAMS(ifp)); XFREE(MTYPE_OSPF_IF_INFO, ifp->info); return rc; @@ -1270,12 +1280,27 @@ void ospf_if_interface(struct interface *ifp) hook_call(ospf_if_update, ifp); } -static int ospf_ifp_create(struct interface *ifp) +uint32_t ospf_if_count_area_params(struct interface *ifp) { - struct ospf *ospf = NULL; struct ospf_if_params *params; struct route_node *rn; uint32_t count = 0; + + params = IF_DEF_PARAMS(ifp); + if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) + count++; + + for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) + if ((params = rn->info) + && OSPF_IF_PARAM_CONFIGURED(params, if_area)) + count++; + + return count; +} + +static int ospf_ifp_create(struct interface *ifp) +{ + struct ospf *ospf = NULL; struct ospf_if_info *oii; if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) @@ -1301,18 +1326,8 @@ static int ospf_ifp_create(struct interface *ifp) if (!ospf) return 0; - params = IF_DEF_PARAMS(ifp); - if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) - if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - if (count > 0) { - ospf->if_ospf_cli_count += count; + if (ospf_if_count_area_params(ifp) > 0) ospf_interface_area_set(ospf, ifp); - } ospf_if_recalculate_output_cost(ifp); @@ -1380,9 +1395,7 @@ static int ospf_ifp_down(struct interface *ifp) static int ospf_ifp_destroy(struct interface *ifp) { struct ospf *ospf; - struct ospf_if_params *params; struct route_node *rn; - uint32_t count = 0; if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) zlog_debug( @@ -1395,18 +1408,8 @@ static int ospf_ifp_destroy(struct interface *ifp) ospf = ospf_lookup_by_vrf_id(ifp->vrf_id); if (ospf) { - params = IF_DEF_PARAMS(ifp); - if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) - if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - if (count > 0) { - ospf->if_ospf_cli_count -= count; + if (ospf_if_count_area_params(ifp) > 0) ospf_interface_area_unset(ospf, ifp); - } } for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 2f146c06f3..4a21147246 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -22,6 +22,7 @@ #ifndef _ZEBRA_OSPF_INTERFACE_H #define _ZEBRA_OSPF_INTERFACE_H +#include "lib/bfd.h" #include "qobj.h" #include "hook.h" #include "ospfd/ospf_packet.h" @@ -104,10 +105,22 @@ struct ospf_if_params { uint32_t network_lsa_seqnum; /* Network LSA seqnum */ /* BFD configuration */ - struct bfd_info *bfd_info; + struct bfd_configuration { + /** BFD session detection multiplier. */ + uint8_t detection_multiplier; + /** BFD session minimum required receive interval. */ + uint32_t min_rx; + /** BFD session minimum required transmission interval. */ + uint32_t min_tx; + /** BFD profile. */ + char profile[BFD_PROFILE_NAME_LEN]; + } *bfd_config; /* MPLS LDP-IGP Sync configuration */ struct ldp_sync_info *ldp_sync_info; + + /* point-to-point DMVPN configuration */ + uint8_t ptp_dmvpn; }; enum { MEMBER_ALLROUTERS = 0, @@ -170,6 +183,9 @@ struct ospf_interface { /* OSPF Network Type. */ uint8_t type; + /* point-to-point DMVPN configuration */ + uint8_t ptp_dmvpn; + /* State of Interface State Machine. */ uint8_t state; @@ -285,7 +301,6 @@ extern struct ospf_if_params *ospf_lookup_if_params(struct interface *, struct in_addr); extern struct ospf_if_params *ospf_get_if_params(struct interface *, struct in_addr); -extern void ospf_del_if_params(struct ospf_if_params *); extern void ospf_free_if_params(struct interface *, struct in_addr); extern void ospf_if_update_params(struct interface *, struct in_addr); @@ -329,6 +344,8 @@ extern void ospf_if_set_multicast(struct ospf_interface *); extern void ospf_if_interface(struct interface *ifp); +extern uint32_t ospf_if_count_area_params(struct interface *ifp); + DECLARE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd)); DECLARE_HOOK(ospf_vl_delete, (struct ospf_vl_data * vd), (vd)); diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 6bde5467b2..6e9df77fb8 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -469,6 +469,12 @@ char link_info_set(struct stream **s, struct in_addr id, struct in_addr data, } /* Describe Point-to-Point link (Section 12.4.1.1). */ + +/* Note: If the interface is configured as point-to-point dmvpn then the other + * end of link is dmvpn hub with point-to-multipoint ospf network type. The + * hub then expects this router to populate the stub network and also Link Data + * Field set to IP Address and not MIB-II ifIndex + */ static int lsa_link_ptop_set(struct stream **s, struct ospf_interface *oi) { int links = 0; @@ -482,7 +488,8 @@ static int lsa_link_ptop_set(struct stream **s, struct ospf_interface *oi) if ((nbr = ospf_nbr_lookup_ptop(oi))) if (nbr->state == NSM_Full) { if (CHECK_FLAG(oi->connected->flags, - ZEBRA_IFA_UNNUMBERED)) { + ZEBRA_IFA_UNNUMBERED) + && !oi->ptp_dmvpn) { /* For unnumbered point-to-point networks, the Link Data field should specify the interface's MIB-II ifIndex @@ -500,7 +507,8 @@ static int lsa_link_ptop_set(struct stream **s, struct ospf_interface *oi) } /* no need for a stub link for unnumbered interfaces */ - if (!CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) { + if (oi->ptp_dmvpn + || !CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) { /* Regardless of the state of the neighboring router, we must add a Type 3 link (stub network). N.B. Options 1 & 2 share basically the same logic. */ @@ -1765,7 +1773,14 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf, /* copy over Type-7 data to new */ extnew->e[0].tos = ext->e[0].tos; extnew->e[0].route_tag = ext->e[0].route_tag; - extnew->e[0].fwd_addr.s_addr = ext->e[0].fwd_addr.s_addr; + if (type7->area->suppress_fa) { + extnew->e[0].fwd_addr.s_addr = 0; + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "ospf_lsa_translated_nssa_new(): Suppress forwarding address for %pI4", + &ei.p.prefix); + } else + extnew->e[0].fwd_addr.s_addr = ext->e[0].fwd_addr.s_addr; new->data->ls_seqnum = type7->data->ls_seqnum; /* add translated flag, checksum and lock new lsa */ @@ -1777,7 +1792,8 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf, /* Originate Translated Type-5 for supplied Type-7 NSSA LSA */ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, - struct ospf_lsa *type7) + struct ospf_lsa *type7, + struct ospf_lsa *type5) { struct ospf_lsa *new; struct as_external_lsa *extnew; @@ -1796,6 +1812,10 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, extnew = (struct as_external_lsa *)new->data; + /* Update LSA sequence number from translated Type-5 LSA */ + if (type5) + new->data->ls_seqnum = lsa_seqnum_increment(type5); + if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: Could not install LSA id %pI4", __func__, @@ -1823,6 +1843,8 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, struct ospf_lsa *type5) { struct ospf_lsa *new = NULL; + struct as_external_lsa *extold = NULL; + uint32_t ls_seqnum = 0; /* Sanity checks. */ assert(type7 || type5); @@ -1887,6 +1909,12 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, return NULL; } + extold = (struct as_external_lsa *)type5->data; + if (type7->area->suppress_fa == 1) { + if (extold->e[0].fwd_addr.s_addr == 0) + ls_seqnum = ntohl(type5->data->ls_seqnum); + } + /* Delete LSA from neighbor retransmit-list. */ ospf_ls_retransmit_delete_nbr_as(ospf, type5); @@ -1899,6 +1927,11 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, return NULL; } + if (type7->area->suppress_fa == 1) { + if (extold->e[0].fwd_addr.s_addr == 0) + new->data->ls_seqnum = htonl(ls_seqnum + 1); + } + if (!(new = ospf_lsa_install(ospf, NULL, new))) { flog_warn( EC_OSPF_LSA_INSTALL_FAILURE, diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index f2a0d36e7e..3c1f94e628 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -341,11 +341,12 @@ extern char link_info_set(struct stream **s, struct in_addr id, extern struct in_addr ospf_get_nssa_ip(struct ospf_area *); extern int ospf_translated_nssa_compare(struct ospf_lsa *, struct ospf_lsa *); -extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *, - struct ospf_lsa *, - struct ospf_lsa *); -extern struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *, - struct ospf_lsa *); +extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, + struct ospf_lsa *type7, + struct ospf_lsa *type5); +extern struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, + struct ospf_lsa *type7, + struct ospf_lsa *type5); extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, int type); #endif /* _ZEBRA_OSPF_LSA_H */ diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 9ae2e8b043..91ba3044fe 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -22,6 +22,7 @@ #include <zebra.h> #include <lib/version.h> +#include "bfd.h" #include "getopt.h" #include "thread.h" #include "prefix.h" @@ -55,6 +56,7 @@ #include "ospfd/ospf_bfd.h" #include "ospfd/ospf_errors.h" #include "ospfd/ospf_ldp_sync.h" +#include "ospfd/ospf_routemap_nb.h" /* ospfd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, @@ -98,6 +100,7 @@ static void sighup(void) static void sigint(void) { zlog_notice("Terminating on signal"); + bfd_protocol_integration_set_shutdown(true); ospf_terminate(); exit(0); } @@ -132,6 +135,7 @@ static const struct frr_yang_module_info *const ospfd_yang_modules[] = { &frr_interface_info, &frr_route_map_info, &frr_vrf_info, + &frr_ospf_route_map_info, }; FRR_DAEMON_INFO(ospfd, OSPF, .vty_port = OSPF_VTY_PORT, @@ -214,7 +218,7 @@ int main(int argc, char **argv) ospf_vty_clear_init(); /* OSPF BFD init */ - ospf_bfd_init(); + ospf_bfd_init(master); /* OSPF LDP IGP Sync init */ ospf_ldp_sync_init(); diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c index 2fa43923ab..a1b35b2fcd 100644 --- a/ospfd/ospf_neighbor.c +++ b/ospfd/ospf_neighbor.c @@ -21,6 +21,7 @@ #include <zebra.h> +#include "lib/bfd.h" #include "linklist.h" #include "prefix.h" #include "memory.h" @@ -99,8 +100,6 @@ struct ospf_neighbor *ospf_nbr_new(struct ospf_interface *oi) nbr->crypt_seqnum = 0; - ospf_bfd_info_nbr_create(oi, nbr); - /* Initialize GR Helper info*/ nbr->gr_helper_info.recvd_grace_period = 0; nbr->gr_helper_info.actual_grace_period = 0; @@ -149,7 +148,7 @@ void ospf_nbr_free(struct ospf_neighbor *nbr) /* Cancel all events. */ /* Thread lookup cost would be negligible. */ thread_cancel_event(master, nbr); - ospf_bfd_info_free(&nbr->bfd_info); + bfd_sess_free(&nbr->bfd_session); OSPF_NSM_TIMER_OFF(nbr->gr_helper_info.t_grace_timer); @@ -458,6 +457,9 @@ static struct ospf_neighbor *ospf_nbr_add(struct ospf_interface *oi, if (ntohs(ospfh->auth_type) == OSPF_AUTH_CRYPTOGRAPHIC) nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; + /* Configure BFD if interface has it. */ + ospf_neighbor_bfd_apply(nbr); + if (IS_DEBUG_OSPF_EVENT) zlog_debug("NSM[%s:%pI4]: start", IF_NAME(oi), &nbr->router_id); diff --git a/ospfd/ospf_neighbor.h b/ospfd/ospf_neighbor.h index 758693e289..2ce6d6755c 100644 --- a/ospfd/ospf_neighbor.h +++ b/ospfd/ospf_neighbor.h @@ -88,7 +88,7 @@ struct ospf_neighbor { uint32_t state_change; /* NSM state change counter */ /* BFD information */ - void *bfd_info; + struct bfd_session_params *bfd_session; /* ospf graceful restart HELPER info */ struct ospf_helper_info gr_helper_info; diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index ca33fd4e18..006c4888ae 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -761,7 +761,8 @@ static void nsm_change_state(struct ospf_neighbor *nbr, int state) if (state == NSM_Down) nbr->crypt_seqnum = 0; - ospf_bfd_trigger_event(nbr, old_state, state); + if (nbr->bfd_session) + ospf_bfd_trigger_event(nbr, old_state, state); /* Preserve old status? */ } diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 0fd4803c79..aa98d7dd28 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -799,7 +799,13 @@ static int ospf_write(struct thread *thread) &iph.ip_dst, iph.ip_id, iph.ip_off, iph.ip_len, oi->ifp->name, oi->ifp->mtu); - if (ret < 0) + /* sendmsg will return EPERM if firewall is blocking sending. + * This is a normal situation when 'ip nhrp map multicast xxx' + * is being used to send multicast packets to DMVPN peers. In + * that case the original message is blocked with iptables rule + * causing the EPERM result + */ + if (ret < 0 && errno != EPERM) flog_err( EC_LIB_SOCKET, "*** sendmsg in ospf_write failed to %pI4, id %d, off %d, len %d, interface %s, mtu %u: %s", @@ -907,8 +913,11 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, /* Compare network mask. */ /* Checking is ignored for Point-to-Point and Virtual link. */ + /* Checking is also ignored for Point-to-Multipoint with /32 prefix */ if (oi->type != OSPF_IFTYPE_POINTOPOINT - && oi->type != OSPF_IFTYPE_VIRTUALLINK) + && oi->type != OSPF_IFTYPE_VIRTUALLINK + && !(oi->type == OSPF_IFTYPE_POINTOMULTIPOINT + && oi->address->prefixlen == IPV4_MAX_BITLEN)) if (oi->address->prefixlen != p.prefixlen) { flog_warn( EC_OSPF_PACKET, @@ -2427,6 +2436,11 @@ static int ospf_check_network_mask(struct ospf_interface *oi, || oi->type == OSPF_IFTYPE_VIRTUALLINK) return 1; + /* Ignore mask check for max prefix length (32) */ + if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT + && oi->address->prefixlen == IPV4_MAX_BITLEN) + return 1; + masklen2ip(oi->address->prefixlen, &mask); me.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index bdc65d23bf..d3b114840e 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -33,6 +33,7 @@ #include "plist.h" #include "vrf.h" #include "frrstr.h" +#include "northbound_cli.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_asbr.h" @@ -534,7 +535,7 @@ static const struct route_map_rule_cmd route_set_tag_cmd = { route_map_rule_tag_free, }; -DEFUN (set_metric_type, +DEFUN_YANG (set_metric_type, set_metric_type_cmd, "set metric-type <type-1|type-2>", SET_STR @@ -543,11 +544,19 @@ DEFUN (set_metric_type, "OSPF[6] external type 2 metric\n") { char *ext = argv[2]->text; - return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index), - "metric-type", ext); + + const char *xpath = + "./set-action[action='frr-ospf-route-map:metric-type']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-ospf-route-map:metric-type", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ext); + return nb_cli_apply_changes(vty, NULL); } -DEFUN (no_set_metric_type, +DEFUN_YANG (no_set_metric_type, no_set_metric_type_cmd, "no set metric-type [<type-1|type-2>]", NO_STR @@ -556,9 +565,11 @@ DEFUN (no_set_metric_type, "OSPF[6] external type 1 metric\n" "OSPF[6] external type 2 metric\n") { - char *ext = (argc == 4) ? argv[3]->text : NULL; - return generic_set_delete(vty, VTY_GET_CONTEXT(route_map_index), - "metric-type", ext); + const char *xpath = + "./set-action[action='frr-ospf-route-map:metric-type']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); } /* Route-map init */ diff --git a/pathd/path_memory.c b/ospfd/ospf_routemap_nb.c index 7a78c09cf9..1f6b0ef78c 100644 --- a/pathd/path_memory.c +++ b/ospfd/ospf_routemap_nb.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2020 NetDEF, Inc. + * Copyright (C) 2020 Vmware + * Sarita Patra * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -16,10 +17,23 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include <zebra.h> +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "ospf_routemap_nb.h" -#include <memory.h> - -#include "pathd/path_memory.h" - -DEFINE_MGROUP(PATHD, "pathd"); +/* clang-format off */ +const struct frr_yang_module_info frr_ospf_route_map_info = { + .name = "frr-ospf-route-map", + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf-route-map:metric-type", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_metric_type_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/staticd/static_memory.h b/ospfd/ospf_routemap_nb.h index 5348129ab2..17bcb4f5c3 100644 --- a/staticd/static_memory.h +++ b/ospfd/ospf_routemap_nb.h @@ -1,7 +1,6 @@ /* - * static memory code. - * Copyright (C) 2018 Cumulus Networks, Inc. - * Donald Sharp + * Copyright (C) 2020 Vmware + * Sarita Patra * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -17,14 +16,22 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef __STATIC_MEMORY_H__ -#include "memory.h" +#ifndef _FRR_OSPF_ROUTEMAP_NB_H_ +#define _FRR_OSPF_ROUTEMAP_NB_H_ -DECLARE_MGROUP(STATIC); +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct frr_yang_module_info frr_ospf_route_map_info; -DECLARE_MTYPE(STATIC_ROUTE); -DECLARE_MTYPE(STATIC_NEXTHOP); -DECLARE_MTYPE(STATIC_PATH); +/* prototypes */ +int lib_route_map_entry_set_action_rmap_set_action_metric_type_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy(struct nb_cb_destroy_args *args); + +#ifdef __cplusplus +} +#endif #endif diff --git a/ospfd/ospf_routemap_nb_config.c b/ospfd/ospf_routemap_nb_config.c new file mode 100644 index 0000000000..bfb18c5e08 --- /dev/null +++ b/ospfd/ospf_routemap_nb_config.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "ospf_routemap_nb.h" + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf-route-map:metric-type + */ +int lib_route_map_entry_set_action_rmap_set_action_metric_type_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "metric-type"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "metric-type", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index 8418bbf2b9..43b998ac5b 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -674,7 +674,7 @@ static struct ospf_area *ospfAreaLookup(struct variable *v, oid name[], if (area == NULL) return NULL; - oid_copy_addr(name + v->namelen, addr, sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, addr); *length = sizeof(struct in_addr) + v->namelen; return area; @@ -800,7 +800,7 @@ static struct ospf_area *ospfStubAreaLookup(struct variable *v, oid name[], if (area == NULL) return NULL; - oid_copy_addr(name + v->namelen, addr, sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, addr); /* Set TOS 0. */ name[v->namelen + sizeof(struct in_addr)] = 0; *length = v->namelen + sizeof(struct in_addr) + 1; @@ -1008,15 +1008,14 @@ static struct ospf_lsa *ospfLsdbLookup(struct variable *v, oid *name, /* Fill in value. */ offset = name + v->namelen; - oid_copy_addr(offset, area_id, IN_ADDR_SIZE); + oid_copy_in_addr(offset, area_id); offset += IN_ADDR_SIZE; *offset = lsa->data->type; offset++; - oid_copy_addr(offset, &lsa->data->id, - IN_ADDR_SIZE); + oid_copy_in_addr(offset, &lsa->data->id); offset += IN_ADDR_SIZE; - oid_copy_addr(offset, &lsa->data->adv_router, - IN_ADDR_SIZE); + oid_copy_in_addr(offset, + &lsa->data->adv_router); return lsa; } @@ -1170,9 +1169,9 @@ static struct ospf_area_range *ospfAreaRangeLookup(struct variable *v, /* Fill in value. */ offset = name + v->namelen; - oid_copy_addr(offset, area_id, IN_ADDR_SIZE); + oid_copy_in_addr(offset, area_id); offset += IN_ADDR_SIZE; - oid_copy_addr(offset, range_net, IN_ADDR_SIZE); + oid_copy_in_addr(offset, range_net); return range; } @@ -1573,7 +1572,7 @@ static struct ospf_interface *ospfIfLookup(struct variable *v, oid *name, if (oi) { *length = v->namelen + IN_ADDR_SIZE + 1; offset = name + v->namelen; - oid_copy_addr(offset, ifaddr, IN_ADDR_SIZE); + oid_copy_in_addr(offset, ifaddr); offset += IN_ADDR_SIZE; *offset = *ifindex; return oi; @@ -1717,7 +1716,7 @@ static struct ospf_interface *ospfIfMetricLookup(struct variable *v, oid *name, if (oi) { *length = v->namelen + IN_ADDR_SIZE + 1 + 1; offset = name + v->namelen; - oid_copy_addr(offset, ifaddr, IN_ADDR_SIZE); + oid_copy_in_addr(offset, ifaddr); offset += IN_ADDR_SIZE; *offset = *ifindex; offset++; @@ -1906,9 +1905,9 @@ ospfVirtIfLookup(struct variable *v, oid *name, size_t *length, if (vl_data) { *length = v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE; - oid_copy_addr(name + v->namelen, area_id, IN_ADDR_SIZE); - oid_copy_addr(name + v->namelen + IN_ADDR_SIZE, - neighbor, IN_ADDR_SIZE); + oid_copy_in_addr(name + v->namelen, area_id); + oid_copy_in_addr(name + v->namelen + IN_ADDR_SIZE, + neighbor); return vl_data; } } @@ -2083,8 +2082,7 @@ static struct ospf_neighbor *ospfNbrLookup(struct variable *v, oid *name, if (nbr) { *length = v->namelen + IN_ADDR_SIZE + 1; - oid_copy_addr(name + v->namelen, nbr_addr, - IN_ADDR_SIZE); + oid_copy_in_addr(name + v->namelen, nbr_addr); name[v->namelen + IN_ADDR_SIZE] = *ifindex; return nbr; } @@ -2307,10 +2305,9 @@ static struct ospf_lsa *ospfExtLsdbLookup(struct variable *v, oid *name, *offset = OSPF_AS_EXTERNAL_LSA; offset++; - oid_copy_addr(offset, &lsa->data->id, IN_ADDR_SIZE); + oid_copy_in_addr(offset, &lsa->data->id); offset += IN_ADDR_SIZE; - oid_copy_addr(offset, &lsa->data->adv_router, - IN_ADDR_SIZE); + oid_copy_in_addr(offset, &lsa->data->adv_router); return lsa; } @@ -2440,7 +2437,7 @@ static void ospfTrapNbrStateChange(struct ospf_neighbor *on) zlog_info("%s: trap sent: %pI4 now %s", __func__, &on->address.u.prefix4, msgbuf); - oid_copy_addr(index, &(on->address.u.prefix4), IN_ADDR_SIZE); + oid_copy_in_addr(index, &(on->address.u.prefix4)); index[IN_ADDR_SIZE] = 0; smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, @@ -2455,7 +2452,7 @@ static void ospfTrapVirtNbrStateChange(struct ospf_neighbor *on) zlog_info("ospfTrapVirtNbrStateChange trap sent"); - oid_copy_addr(index, &(on->address.u.prefix4), IN_ADDR_SIZE); + oid_copy_in_addr(index, &(on->address.u.prefix4)); index[IN_ADDR_SIZE] = 0; smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, @@ -2499,7 +2496,7 @@ static void ospfTrapIfStateChange(struct ospf_interface *oi) &oi->address->u.prefix4, lookup_msg(ospf_ism_state_msg, oi->state, NULL)); - oid_copy_addr(index, &(oi->address->u.prefix4), IN_ADDR_SIZE); + oid_copy_in_addr(index, &(oi->address->u.prefix4)); index[IN_ADDR_SIZE] = 0; smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, @@ -2514,7 +2511,7 @@ static void ospfTrapVirtIfStateChange(struct ospf_interface *oi) zlog_info("ospfTrapVirtIfStateChange trap sent"); - oid_copy_addr(index, &(oi->address->u.prefix4), IN_ADDR_SIZE); + oid_copy_in_addr(index, &(oi->address->u.prefix4)); index[IN_ADDR_SIZE] = 0; smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 1e0814764b..0164bfac67 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -905,7 +905,9 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, * somehow. */ if (area->ospf->ti_lfa_enabled - || (oi && oi->type == OSPF_IFTYPE_POINTOPOINT)) { + || (oi && oi->type == OSPF_IFTYPE_POINTOPOINT) + || (oi && oi->type == OSPF_IFTYPE_POINTOMULTIPOINT + && oi->address->prefixlen == IPV4_MAX_BITLEN)) { struct ospf_neighbor *nbr_w = NULL; /* Calculating node is root node, link @@ -1987,3 +1989,27 @@ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason) thread_add_timer_msec(master, ospf_spf_calculate_schedule_worker, ospf, delay, &ospf->t_spf_calc); } + +/* Restart OSPF SPF algorithm*/ +void ospf_restart_spf(struct ospf *ospf) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Restart SPF.", __PRETTY_FUNCTION__); + + /* Handling inter area and intra area routes*/ + if (ospf->new_table) { + ospf_route_delete(ospf, ospf->new_table); + ospf_route_table_free(ospf->new_table); + ospf->new_table = route_table_init(); + } + + /* Handling of TYPE-5 lsa(external routes) */ + if (ospf->old_external_route) { + ospf_route_delete(ospf, ospf->old_external_route); + ospf_route_table_free(ospf->old_external_route); + ospf->old_external_route = route_table_init(); + } + + /* Trigger SPF */ + ospf_spf_calculate_schedule(ospf, SPF_FLAG_CONFIG_CHANGE); +} diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h index 835caab288..4ff4a6d125 100644 --- a/ospfd/ospf_spf.h +++ b/ospfd/ospf_spf.h @@ -98,6 +98,6 @@ extern struct vertex_parent *ospf_spf_vertex_parent_find(struct in_addr id, extern int vertex_parent_cmp(void *aa, void *bb); extern void ospf_spf_print(struct vty *vty, struct vertex *v, int i); - +extern void ospf_restart_spf(struct ospf *ospf); /* void ospf_spf_calculate_timer_add (); */ #endif /* _QUAGGA_OSPF_SPF_H */ diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index 17ff9a1a46..f2842538a5 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -43,6 +43,9 @@ #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "network.h" +#include "link_state.h" +#include "zclient.h" +#include "printfrr.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -60,6 +63,9 @@ #include "ospfd/ospf_ase.h" #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_te.h" +#include "ospfd/ospf_sr.h" +#include "ospfd/ospf_ri.h" +#include "ospfd/ospf_ext.h" #include "ospfd/ospf_vty.h" #include "ospfd/ospf_errors.h" @@ -71,6 +77,7 @@ struct ospf_mpls_te OspfMplsTE; static const char *const mode2text[] = {"Off", "AS", "Area"}; + /*------------------------------------------------------------------------* * Followings are initialize/terminate functions for MPLS-TE handling. *------------------------------------------------------------------------*/ @@ -82,8 +89,11 @@ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_status); static void ospf_mpls_te_config_write_router(struct vty *vty); static void ospf_mpls_te_show_info(struct vty *vty, struct ospf_lsa *lsa); static int ospf_mpls_te_lsa_originate_area(void *arg); -static int ospf_mpls_te_lsa_originate_as(void *arg); +static int ospf_mpls_te_lsa_inter_as_as(void *arg); +static int ospf_mpls_te_lsa_inter_as_area(void *arg); static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa); +static int ospf_mpls_te_lsa_update(struct ospf_lsa *lsa); +static int ospf_mpls_te_lsa_delete(struct ospf_lsa *lsa); static void del_mpls_te_link(void *val); static void ospf_mpls_te_register_vty(void); @@ -92,79 +102,72 @@ int ospf_mpls_te_init(void) { int rc; + /* Register Opaque AREA LSA Type 1 for Traffic Engineering */ rc = ospf_register_opaque_functab( - OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, - ospf_mpls_te_new_if, ospf_mpls_te_del_if, - ospf_mpls_te_ism_change, ospf_mpls_te_nsm_change, + OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, + ospf_mpls_te_new_if, + ospf_mpls_te_del_if, + ospf_mpls_te_ism_change, + ospf_mpls_te_nsm_change, ospf_mpls_te_config_write_router, - NULL, /*ospf_mpls_te_config_write_if */ + NULL, /* ospf_mpls_te_config_write_if */ NULL, /* ospf_mpls_te_config_write_debug */ ospf_mpls_te_show_info, ospf_mpls_te_lsa_originate_area, - ospf_mpls_te_lsa_refresh, NULL, /* ospf_mpls_te_new_lsa_hook */ - NULL /* ospf_mpls_te_del_lsa_hook */); + ospf_mpls_te_lsa_refresh, + ospf_mpls_te_lsa_update, /* ospf_mpls_te_new_lsa_hook */ + ospf_mpls_te_lsa_delete /* ospf_mpls_te_del_lsa_hook */); if (rc != 0) { flog_warn( EC_OSPF_OPAQUE_REGISTRATION, - "ospf_mpls_te_init: Failed to register Traffic Engineering functions"); + "MPLS-TE (%s): Failed to register Traffic Engineering functions", + __func__); return rc; } - memset(&OspfMplsTE, 0, sizeof(struct ospf_mpls_te)); - OspfMplsTE.enabled = false; - OspfMplsTE.inter_as = Off; - OspfMplsTE.iflist = list_new(); - OspfMplsTE.iflist->del = del_mpls_te_link; - - ospf_mpls_te_register_vty(); - - return rc; -} - -/* Additional register for RFC5392 support */ -static int ospf_mpls_te_register(enum inter_as_mode mode) -{ - int rc = 0; - uint8_t scope; - - if (OspfMplsTE.inter_as != Off) + /* + * Wee need also to register Opaque LSA Type 6 i.e. Inter-AS RFC5392 for + * both AREA and AS at least to have the possibility to call the show() + * function when looking to the opaque LSA of the OSPF database. + */ + rc = ospf_register_opaque_functab(OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_INTER_AS_LSA, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + ospf_mpls_te_show_info, + ospf_mpls_te_lsa_inter_as_area, + ospf_mpls_te_lsa_refresh, NULL, NULL); + if (rc != 0) { + flog_warn( + EC_OSPF_OPAQUE_REGISTRATION, + "MPLS-TE (%s): Failed to register Inter-AS with Area scope", + __func__); return rc; + } - if (mode == AS) - scope = OSPF_OPAQUE_AS_LSA; - else - scope = OSPF_OPAQUE_AREA_LSA; - - rc = ospf_register_opaque_functab(scope, OPAQUE_TYPE_INTER_AS_LSA, NULL, + rc = ospf_register_opaque_functab(OSPF_OPAQUE_AS_LSA, + OPAQUE_TYPE_INTER_AS_LSA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ospf_mpls_te_show_info, - ospf_mpls_te_lsa_originate_as, + ospf_mpls_te_lsa_inter_as_as, ospf_mpls_te_lsa_refresh, NULL, NULL); - if (rc != 0) { flog_warn( EC_OSPF_OPAQUE_REGISTRATION, - "ospf_router_info_init: Failed to register Inter-AS functions"); + "MPLS-TE (%s): Failed to register Inter-AS with AS scope", + __func__); return rc; } - return rc; -} - -static int ospf_mpls_te_unregister(void) -{ - uint8_t scope; - - if (OspfMplsTE.inter_as == Off) - return 0; - - if (OspfMplsTE.inter_as == AS) - scope = OSPF_OPAQUE_AS_LSA; - else - scope = OSPF_OPAQUE_AREA_LSA; + memset(&OspfMplsTE, 0, sizeof(struct ospf_mpls_te)); + OspfMplsTE.enabled = false; + OspfMplsTE.export = false; + OspfMplsTE.inter_as = Off; + OspfMplsTE.iflist = list_new(); + OspfMplsTE.iflist->del = del_mpls_te_link; - ospf_delete_opaque_functab(scope, OPAQUE_TYPE_INTER_AS_LSA); + ospf_mpls_te_register_vty(); - return 0; + return rc; } void ospf_mpls_te_term(void) @@ -173,28 +176,28 @@ void ospf_mpls_te_term(void) ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA); + ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_INTER_AS_LSA); + ospf_delete_opaque_functab(OSPF_OPAQUE_AS_LSA, + OPAQUE_TYPE_INTER_AS_LSA); OspfMplsTE.enabled = false; - - ospf_mpls_te_unregister(); OspfMplsTE.inter_as = Off; + OspfMplsTE.export = false; return; } void ospf_mpls_te_finish(void) { - // list_delete_all_node(OspfMplsTE.iflist); - OspfMplsTE.enabled = false; - ospf_mpls_te_unregister(); OspfMplsTE.inter_as = Off; + OspfMplsTE.export = false; } /*------------------------------------------------------------------------* * Followings are control functions for MPLS-TE parameters management. *------------------------------------------------------------------------*/ - static void del_mpls_te_link(void *val) { XFREE(MTYPE_OSPF_MPLS_TE, val); @@ -235,8 +238,7 @@ static struct mpls_te_link *lookup_linkparams_by_instance(struct ospf_lsa *lsa) if (lp->instance == key) return lp; - zlog_info("lookup_linkparams_by_instance: Entry not found: key(%x)", - key); + ote_debug("MPLS-TE (%s): Entry not found: key(%x)", __func__, key); return NULL; } @@ -484,6 +486,13 @@ static void set_linkparams_inter_as(struct mpls_te_link *lp, lp->ras.header.type = htons(TE_LINK_SUBTLV_RAS); lp->ras.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); lp->ras.value = htonl(as); + + /* Set Type & Flooding flag accordingly */ + lp->type = INTER_AS; + if (OspfMplsTE.inter_as == AS) + SET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); + else + UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); } static void unset_linkparams_inter_as(struct mpls_te_link *lp) @@ -497,6 +506,10 @@ static void unset_linkparams_inter_as(struct mpls_te_link *lp) lp->ras.header.type = htons(0); lp->ras.header.length = htons(0); lp->ras.value = htonl(0); + + /* Reset Type & Flooding flag accordingly */ + lp->type = STD_TE; + UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); } void set_linkparams_llri(struct mpls_te_link *lp, uint32_t local, @@ -606,15 +619,15 @@ static void update_linkparams(struct mpls_te_link *lp) /* Get the Interface structure */ if ((ifp = lp->ifp) == NULL) { - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "OSPF MPLS-TE: Abort update TE parameters: no interface associated to Link Parameters"); + ote_debug( + "MPLS-TE (%s): Abort update TE parameters: no interface associated to Link Parameters", + __func__); return; } if (!HAS_LINK_PARAMS(ifp)) { - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "OSPF MPLS-TE: Abort update TE parameters: no Link Parameters for interface"); + ote_debug( + "MPLS-TE (%s): Abort update TE parameters: no Link Parameters for interface", + __func__); return; } @@ -688,17 +701,18 @@ static void update_linkparams(struct mpls_te_link *lp) /* Flush LSA if it engaged and was previously a STD_TE one */ if (IS_STD_TE(lp->type) && CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "OSPF MPLS-TE Update IF: Switch from Standard LSA to INTER-AS for %s[%d/%d]", - ifp->name, lp->flags, lp->type); + ote_debug( + "MPLS-TE (%s): Update IF: Switch from Standard LSA to INTER-AS for %s[%d/%d]", + __func__, ifp->name, lp->flags, lp->type); ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); /* Then, switch it to INTER-AS */ - if (OspfMplsTE.inter_as == AS) - lp->flags = INTER_AS | FLOOD_AS; - else { - lp->flags = INTER_AS | FLOOD_AREA; + if (OspfMplsTE.inter_as == AS) { + lp->type = INTER_AS; + SET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); + } else { + lp->type = INTER_AS; + UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); lp->area = ospf_area_lookup_by_area_id( ospf_lookup_by_vrf_id(VRF_DEFAULT), OspfMplsTE.interas_areaid); @@ -707,10 +721,9 @@ static void update_linkparams(struct mpls_te_link *lp) set_linkparams_inter_as(lp, ifp->link_params->rmt_ip, ifp->link_params->rmt_as); } else { - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "OSPF MPLS-TE Update IF: Switch from INTER-AS LSA to Standard for %s[%d/%d]", - ifp->name, lp->flags, lp->type); + ote_debug( + "MPLS-TE (%s): Update IF: Switch from INTER-AS LSA to Standard for %s[%d/%d]", + __func__, ifp->name, lp->flags, lp->type); /* reset inter-as TE params */ /* Flush LSA if it engaged and was previously an INTER_AS one */ @@ -718,7 +731,8 @@ static void update_linkparams(struct mpls_te_link *lp) && CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); /* Then, switch it to Standard TE */ - lp->flags = STD_TE | FLOOD_AREA; + lp->flags = STD_TE; + UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); } unset_linkparams_inter_as(lp); } @@ -730,10 +744,8 @@ static void initialize_linkparams(struct mpls_te_link *lp) struct ospf_interface *oi = NULL; struct route_node *rn; - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "MPLS-TE(initialize_linkparams) Initialize Link Parameters for interface %s", - ifp->name); + ote_debug("MPLS-TE (%s): Initialize Link Parameters for interface %s", + __func__, ifp->name); /* Search OSPF Interface parameters for this interface */ for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { @@ -746,10 +758,9 @@ static void initialize_linkparams(struct mpls_te_link *lp) } if ((oi == NULL) || (oi->ifp != ifp)) { - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "MPLS-TE(initialize_linkparams) Could not find corresponding OSPF Interface for %s", - ifp->name); + ote_debug( + "MPLS-TE (%s): Could not find corresponding OSPF Interface for %s", + __func__, ifp->name); return; } @@ -780,21 +791,20 @@ static int is_mandated_params_set(struct mpls_te_link *lp) int rc = 0; if (ntohs(OspfMplsTE.router_addr.header.type) == 0) { - flog_warn( - EC_OSPF_TE_UNEXPECTED, - "MPLS-TE(is_mandated_params_set) Missing Router Address"); + flog_warn(EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Missing Router Address", __func__); return rc; } if (ntohs(lp->link_type.header.type) == 0) { flog_warn(EC_OSPF_TE_UNEXPECTED, - "MPLS-TE(is_mandated_params_set) Missing Link Type"); + "MPLS-TE (%s): Missing Link Type", __func__); return rc; } if (!IS_INTER_AS(lp->type) && (ntohs(lp->link_id.header.type) == 0)) { - flog_warn(EC_OSPF_TE_UNEXPECTED, - "MPLS-TE(is_mandated_params_set) Missing Link ID"); + flog_warn(EC_OSPF_TE_UNEXPECTED, "MPLS-TE (%s) Missing Link ID", + __func__); return rc; } @@ -810,10 +820,9 @@ static int ospf_mpls_te_new_if(struct interface *ifp) { struct mpls_te_link *new; - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "MPLS-TE(ospf_mpls_te_new_if) Add new %s interface %s to MPLS-TE list", - ifp->link_params ? "Active" : "Inactive", ifp->name); + ote_debug("MPLS-TE (%s): Add new %s interface %s to MPLS-TE list", + __func__, ifp->link_params ? "Active" : "Inactive", + ifp->name); if (lookup_linkparams_by_ifp(ifp) != NULL) return 0; @@ -826,7 +835,7 @@ static int ospf_mpls_te_new_if(struct interface *ifp) * active */ /* This default behavior will be adapted with call to * ospf_mpls_te_update_if() */ - new->type = STD_TE | FLOOD_AREA; + new->type = STD_TE; new->flags = LPFLG_LSA_INACTIVE; /* Initialize Link Parameters from Interface */ @@ -838,10 +847,8 @@ static int ospf_mpls_te_new_if(struct interface *ifp) /* Add Link Parameters structure to the list */ listnode_add(OspfMplsTE.iflist, new); - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "OSPF MPLS-TE New IF: Add new LP context for %s[%d/%d]", - ifp->name, new->flags, new->type); + ote_debug("MPLS-TE (%s): Add new LP context for %s[%d/%d]", __func__, + ifp->name, new->flags, new->type); /* Schedule Opaque-LSA refresh. */ /* XXX */ return 0; @@ -874,17 +881,15 @@ void ospf_mpls_te_update_if(struct interface *ifp) { struct mpls_te_link *lp; - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "OSPF MPLS-TE: Update LSA parameters for interface %s [%s]", - ifp->name, HAS_LINK_PARAMS(ifp) ? "ON" : "OFF"); + ote_debug("MPLS-TE (%s): Update LSA parameters for interface %s [%s]", + __func__, ifp->name, HAS_LINK_PARAMS(ifp) ? "ON" : "OFF"); /* Get Link context from interface */ if ((lp = lookup_linkparams_by_ifp(ifp)) == NULL) { flog_warn( EC_OSPF_TE_UNEXPECTED, - "OSPF MPLS-TE Update: Did not find Link Parameters context for interface %s", - ifp->name); + "MPLS-TE (%s): Did not find Link Parameters context for interface %s", + __func__, ifp->name); return; } @@ -949,9 +954,6 @@ static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state) /* Keep Area information in combination with linkparams. */ lp->area = oi->area; - /* Keep interface MPLS-TE status */ - lp->flags = HAS_LINK_PARAMS(oi->ifp); - switch (oi->state) { case ISM_PointToPoint: case ISM_DROther: @@ -962,17 +964,22 @@ static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state) set_linkparams_lclif_ipaddr(lp, oi->address->u.prefix4); break; - default: - /* State is undefined: Flush LSA if engaged */ - if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + case ISM_Down: + /* Interface goes Down: Flush LSA if engaged */ + if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { + ote_debug( + "MPLS-TE (%s): Interface %s goes down: flush LSA", + __func__, IF_NAME(oi)); ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + return; + } + break; + default: break; } - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "MPLS-TE(%s): Update Link parameters for interface %s", - __func__, IF_NAME(oi)); + ote_debug("MPLS-TE (%s): Update Link parameters for interface %s", + __func__, IF_NAME(oi)); return; } @@ -1009,12 +1016,21 @@ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state) return; } + /* Flush TE Opaque LSA if Neighbor State goes Down or Deleted */ + if (OspfMplsTE.enabled + && (nbr->state == NSM_Down || nbr->state == NSM_Deleted)) { + if (CHECK_FLAG(lp->flags, EXT_LPFLG_LSA_ENGAGED)) { + ote_debug( + "MPLS-TE (%s): Interface %s goes down: flush LSA", + __func__, IF_NAME(oi)); + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + } + return; + } + /* Keep Area information in combination with SR info. */ lp->area = oi->area; - /* Keep interface MPLS-TE status */ - lp->flags = HAS_LINK_PARAMS(oi->ifp); - /* * The Link ID is identical to the contents of the Link ID field * in the Router LSA for these link types. @@ -1034,18 +1050,22 @@ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state) set_linkparams_link_id(lp, DR(oi)); break; - default: - /* State is undefined: Flush LSA if engaged */ - if (OspfMplsTE.enabled && - CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + case ISM_Down: + /* State goes Down: Flush LSA if engaged */ + if (OspfMplsTE.enabled + && CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { + ote_debug( + "MPLS-TE (%s): Interface %s goes down: flush LSA", + __func__, IF_NAME(oi)); ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + } return; + default: + break; } - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "MPLS-TE (%s): Add Link-ID %pI4 for interface %s ", - __func__, &lp->link_id.value, oi->ifp->name); + ote_debug("MPLS-TE (%s): Add Link-ID %pI4 for interface %s ", __func__, + &lp->link_id.value, oi->ifp->name); /* Try to Schedule LSA */ if (OspfMplsTE.enabled) { @@ -1058,7 +1078,7 @@ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state) } /*------------------------------------------------------------------------* - * Followings are OSPF protocol processing functions for MPLS-TE. + * Followings are OSPF protocol processing functions for MPLS-TE LSA. *------------------------------------------------------------------------*/ static void build_tlv_header(struct stream *s, struct tlv_header *tlvh) @@ -1119,11 +1139,12 @@ static void build_link_tlv(struct stream *s, struct mpls_te_link *lp) static void ospf_mpls_te_lsa_body_set(struct stream *s, struct mpls_te_link *lp) { /* - * The router address TLV is type 1, and ... - * It must appear in exactly one - * Traffic Engineering LSA originated by a router. + * The router address TLV is type 1, and ... It must appear in exactly + * one Traffic Engineering LSA originated by a router but not in + * Inter-AS TLV. */ - build_router_tlv(s); + if (!IS_INTER_AS(lp->type)) + build_router_tlv(s); /* * Only one Link TLV shall be carried in each LSA, allowing for fine @@ -1154,7 +1175,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf, /* Set opaque-LSA header fields depending of the type of RFC */ if (IS_INTER_AS(lp->type)) { - if (IS_FLOOD_AS(lp->type)) { + if (IS_FLOOD_AS(lp->flags)) { /* Enable AS external as we flood Inter-AS with Opaque * Type 11 */ @@ -1186,10 +1207,9 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf, area->ospf->router_id); } - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) - zlog_debug( - "LSA[Type%d:%pI4]: Create an Opaque-LSA/MPLS-TE instance", - lsa_type, &lsa_id); + ote_debug( + "MPLS-TE (%s): LSA[Type%d:%pI4]: Create an Opaque-LSA/MPLS-TE instance", + __func__, lsa_type, &lsa_id); /* Set opaque-LSA body fields. */ ospf_mpls_te_lsa_body_set(s, lp); @@ -1221,16 +1241,15 @@ static int ospf_mpls_te_lsa_originate1(struct ospf_area *area, /* Create new Opaque-LSA/MPLS-TE instance. */ new = ospf_mpls_te_lsa_new(area->ospf, area, lp); if (new == NULL) { - flog_warn( - EC_OSPF_TE_UNEXPECTED, - "ospf_mpls_te_lsa_originate1: ospf_mpls_te_lsa_new() ?"); + flog_warn(EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): ospf_mpls_te_lsa_new() ?", __func__); return rc; } /* Install this LSA into LSDB. */ if (ospf_lsa_install(area->ospf, NULL /*oi*/, new) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, - "ospf_mpls_te_lsa_originate1: ospf_lsa_install() ?"); + "MPLS-TE (%s): ospf_lsa_install() ?", __func__); ospf_lsa_unlock(&new); return rc; } @@ -1243,13 +1262,12 @@ static int ospf_mpls_te_lsa_originate1(struct ospf_area *area, /* Flood new LSA through area. */ ospf_flood_through_area(area, NULL /*nbr*/, new); - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - zlog_debug( - "LSA[Type%d:%pI4]: Originate Opaque-LSA/MPLS-TE: Area(%pI4), Link(%s)", - new->data->type, &new->data->id, &area->area_id, - lp->ifp->name); + ote_debug( + "MPLS-TE (%s): LSA[Type%d:%pI4]: Originate Opaque-LSA/MPLS-TE: Area(%pI4), Link(%s)", + __func__, new->data->type, &new->data->id, &area->area_id, + lp->ifp->name); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } rc = 0; return rc; @@ -1263,8 +1281,7 @@ static int ospf_mpls_te_lsa_originate_area(void *arg) int rc = -1; if (!OspfMplsTE.enabled) { - zlog_info( - "ospf_mpls_te_lsa_originate_area: MPLS-TE is disabled now."); + ote_debug("MPLS-TE (%s): MPLS-TE is disabled now.", __func__); rc = 0; /* This is not an error case. */ return rc; } @@ -1272,7 +1289,7 @@ static int ospf_mpls_te_lsa_originate_area(void *arg) for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) { /* Process only enabled LSA with area scope flooding */ if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE) - || IS_FLOOD_AS(lp->type)) + || IS_FLOOD_AS(lp->flags)) continue; if (lp->area == NULL) @@ -1284,26 +1301,26 @@ static int ospf_mpls_te_lsa_originate_area(void *arg) if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { if (CHECK_FLAG(lp->flags, LPFLG_LSA_FORCED_REFRESH)) { UNSET_FLAG(lp->flags, LPFLG_LSA_FORCED_REFRESH); - zlog_info( - "OSPF MPLS-TE (ospf_mpls_te_lsa_originate_area): Refresh instead of Originate"); + ote_debug( + "MPLS-TE (%s): Refresh instead of Originate", + __func__); ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA); } continue; } if (!is_mandated_params_set(lp)) { - zlog_info( - "ospf_mpls_te_lsa_originate_area: Link(%s) lacks some mandated MPLS-TE parameters.", - lp->ifp ? lp->ifp->name : "?"); + ote_debug( + "MPLS-TE (%s): Link(%s) lacks some mandated MPLS-TE parameters.", + __func__, lp->ifp ? lp->ifp->name : "?"); continue; } /* Ok, let's try to originate an LSA for this area and Link. */ - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "MPLS-TE(ospf_mpls_te_lsa_originate_area) Let's finally reoriginate the LSA %d through the Area %pI4 for Link %s", - lp->instance, &area->area_id, - lp->ifp ? lp->ifp->name : "?"); + ote_debug( + "MPLS-TE (%s): Let's finally reoriginate the LSA %d through the Area %pI4 for Link %s", + __func__, lp->instance, &area->area_id, + lp->ifp ? lp->ifp->name : "?"); if (ospf_mpls_te_lsa_originate1(area, lp) != 0) return rc; } @@ -1321,9 +1338,9 @@ static int ospf_mpls_te_lsa_originate2(struct ospf *top, /* Create new Opaque-LSA/Inter-AS instance. */ new = ospf_mpls_te_lsa_new(top, NULL, lp); if (new == NULL) { - flog_warn( - EC_OSPF_LSA_UNEXPECTED, - "ospf_mpls_te_lsa_originate2: ospf_router_info_lsa_new() ?"); + flog_warn(EC_OSPF_LSA_UNEXPECTED, + "MPLS-TE (%s): ospf_router_info_lsa_new() ?", + __func__); return rc; } new->vrf_id = top->vrf_id; @@ -1331,7 +1348,7 @@ static int ospf_mpls_te_lsa_originate2(struct ospf *top, /* Install this LSA into LSDB. */ if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, - "ospf_mpls_te_lsa_originate2: ospf_lsa_install() ?"); + "MPLS-TE (%s): ospf_lsa_install() ?", __func__); ospf_lsa_unlock(&new); return rc; } @@ -1344,12 +1361,12 @@ static int ospf_mpls_te_lsa_originate2(struct ospf *top, /* Flood new LSA through AS. */ ospf_flood_through_as(top, NULL /*nbr */, new); - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - zlog_debug( - "LSA[Type%d:%pI4]: Originate Opaque-LSA/MPLS-TE Inter-AS", - new->data->type, &new->data->id); + ote_debug( + "MPLS-TE (%s): LSA[Type%d:%pI4]: Originate Opaque-LSA/MPLS-TE Inter-AS", + __func__, new->data->type, &new->data->id); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } + rc = 0; return rc; @@ -1364,8 +1381,8 @@ static int ospf_mpls_te_lsa_originate_as(void *arg) int rc = -1; if ((!OspfMplsTE.enabled) || (OspfMplsTE.inter_as == Off)) { - zlog_info( - "ospf_mpls_te_lsa_originate_as: MPLS-TE Inter-AS is disabled for now."); + ote_debug("MPLS-TE (%s): Inter-AS is disabled for now", + __func__); rc = 0; /* This is not an error case. */ return rc; } @@ -1373,6 +1390,7 @@ static int ospf_mpls_te_lsa_originate_as(void *arg) for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) { /* Process only enabled INTER_AS Links or Pseudo-Links */ if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE) + || !CHECK_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS) || !IS_INTER_AS(lp->type)) continue; @@ -1387,20 +1405,19 @@ static int ospf_mpls_te_lsa_originate_as(void *arg) if (!is_mandated_params_set(lp)) { flog_warn( EC_OSPF_TE_UNEXPECTED, - "ospf_mpls_te_lsa_originate_as: Link(%s) lacks some mandated MPLS-TE parameters.", - lp->ifp ? lp->ifp->name : "?"); + "MPLS-TE (%s): Link(%s) lacks some mandated MPLS-TE parameters.", + __func__, lp->ifp ? lp->ifp->name : "?"); continue; } /* Ok, let's try to originate an LSA for this AS and Link. */ - if (IS_DEBUG_OSPF_TE) - zlog_debug( - "MPLS-TE(ospf_mpls_te_lsa_originate_as) Let's finally re-originate the Inter-AS LSA %d through the %s for Link %s", - lp->instance, - IS_FLOOD_AS(lp->type) ? "AS" : "Area", - lp->ifp ? lp->ifp->name : "Unknown"); + ote_debug( + "MPLS-TE (%s): Let's finally re-originate the Inter-AS LSA %d through the %s for Link %s", + __func__, lp->instance, + IS_FLOOD_AS(lp->flags) ? "AS" : "Area", + lp->ifp ? lp->ifp->name : "Unknown"); - if (IS_FLOOD_AS(lp->type)) { + if (IS_FLOOD_AS(lp->flags)) { top = (struct ospf *)arg; ospf_mpls_te_lsa_originate2(top, lp); } else { @@ -1413,6 +1430,30 @@ static int ospf_mpls_te_lsa_originate_as(void *arg) return rc; } +/* + * As Inter-AS LSA must be registered with both AREA and AS flooding, and + * because all origination callback functions are call (disregarding the Opaque + * LSA type and Flooding scope) it is necessary to determine which flooding + * scope is associated with the LSA origination as parameter is of type void and + * must be cast to struct *ospf for AS flooding and to struct *ospf_area for + * Area flooding. + */ +static int ospf_mpls_te_lsa_inter_as_as(void *arg) +{ + if (OspfMplsTE.inter_as == AS) + return ospf_mpls_te_lsa_originate_as(arg); + else + return 0; +} + +static int ospf_mpls_te_lsa_inter_as_area(void *arg) +{ + if (OspfMplsTE.inter_as == Area) + return ospf_mpls_te_lsa_originate_area(arg); + else + return 0; +} + static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa) { struct mpls_te_link *lp; @@ -1426,7 +1467,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa) * change. * It seems a slip among routers in the routing domain. */ - zlog_info("ospf_mpls_te_lsa_refresh: MPLS-TE is disabled now."); + ote_debug("MPLS-TE (%s): MPLS-TE is disabled now", __func__); lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */ } @@ -1434,7 +1475,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa) /* At first, resolve lsa/lp relationship. */ if ((lp = lookup_linkparams_by_instance(lsa)) == NULL) { flog_warn(EC_OSPF_TE_UNEXPECTED, - "ospf_mpls_te_lsa_refresh: Invalid parameter?"); + "MPLS-TE (%s): Invalid parameter?", __func__); lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */ ospf_opaque_lsa_flush_schedule(lsa); @@ -1443,9 +1484,8 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa) /* Check if lp was not disable in the interval */ if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE)) { - flog_warn( - EC_OSPF_TE_UNEXPECTED, - "ospf_mpls_te_lsa_refresh: lp was disabled: Flush it!"); + flog_warn(EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): lp was disabled: Flush it!", __func__); lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */ } @@ -1461,7 +1501,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa) new = ospf_mpls_te_lsa_new(top, area, lp); if (new == NULL) { flog_warn(EC_OSPF_TE_UNEXPECTED, - "ospf_mpls_te_lsa_refresh: ospf_mpls_te_lsa_new() ?"); + "MPLS-TE (%s): ospf_mpls_te_lsa_new() ?", __func__); return NULL; } new->data->ls_seqnum = lsa_seqnum_increment(lsa); @@ -1475,24 +1515,23 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa) if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, - "ospf_mpls_te_lsa_refresh: ospf_lsa_install() ?"); + "MPLS-TE (%s): ospf_lsa_install() ?", __func__); ospf_lsa_unlock(&new); return NULL; } /* Flood updated LSA through AS or Area depending of the RFC of the link */ - if (IS_FLOOD_AS(lp->type)) + if (IS_FLOOD_AS(lp->flags)) ospf_flood_through_as(top, NULL, new); else ospf_flood_through_area(area, NULL /*nbr*/, new); /* Debug logging. */ - if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { - zlog_debug("LSA[Type%d:%pI4]: Refresh Opaque-LSA/MPLS-TE", - new->data->type, &new->data->id); + ote_debug("MPLS-TE (%s): LSA[Type%d:%pI4]: Refresh Opaque-LSA/MPLS-TE", + __func__, new->data->type, &new->data->id); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) ospf_lsa_header_dump(new->data); - } return new; } @@ -1509,14 +1548,19 @@ void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode) top = ospf_lookup_by_vrf_id(VRF_DEFAULT); /* Check if the pseudo link is ready to flood */ - if (!(CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE)) - || !(IS_FLOOD_AREA(lp->type) || IS_FLOOD_AS(lp->type))) { + if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE)) return; - } + + ote_debug("MPLS-TE (%s): Schedule %s%s%s LSA for interface %s", + __func__, + opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", + opcode == REFRESH_THIS_LSA ? "Refresh" : "", + opcode == FLUSH_THIS_LSA ? "Flush" : "", + lp->ifp ? lp->ifp->name : "-"); lsa.area = lp->area; lsa.data = &lsah; - if (IS_FLOOD_AS(lp->type)) { + if (IS_FLOOD_AS(lp->flags)) { lsah.type = OSPF_OPAQUE_AS_LSA; tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_INTER_AS_LSA, lp->instance); lsah.id.s_addr = htonl(tmp); @@ -1531,7 +1575,8 @@ void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode) if (lp->area == NULL) { flog_warn( EC_OSPF_TE_UNEXPECTED, - "MPLS-TE(ospf_mpls_te_lsa_schedule) Area context is null. Abort !"); + "MPLS-TE (%s): Area context is null. Abort !", + __func__); return; } tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_INTER_AS_LSA, @@ -1545,14 +1590,11 @@ void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode) switch (opcode) { case REORIGINATE_THIS_LSA: - if (IS_FLOOD_AS(lp->type)) { + if (IS_FLOOD_AS(lp->flags)) { ospf_opaque_lsa_reoriginate_schedule( (void *)top, OSPF_OPAQUE_AS_LSA, OPAQUE_TYPE_INTER_AS_LSA); - break; - } - - if (IS_FLOOD_AREA(lp->type)) { + } else { if (IS_INTER_AS(lp->type)) ospf_opaque_lsa_reoriginate_schedule( (void *)lp->area, OSPF_OPAQUE_AREA_LSA, @@ -1561,7 +1603,6 @@ void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode) ospf_opaque_lsa_reoriginate_schedule( (void *)lp->area, OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA); - break; } break; case REFRESH_THIS_LSA: @@ -1574,14 +1615,1532 @@ void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode) break; default: flog_warn(EC_OSPF_TE_UNEXPECTED, - "ospf_mpls_te_lsa_schedule: Unknown opcode (%u)", + "MPLS-TE (%s): Unknown opcode (%u)", __func__, opcode); break; } +} - return; +/** + * ------------------------------------------------------ + * Followings are Link State Data Base control functions. + * ------------------------------------------------------ + */ + +/** + * Get Vertex from TED by the router which advertised the LSA. A new Vertex and + * associated Link State Node are created if Vertex is not found. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return Link State Vertex + */ +static struct ls_vertex *get_vertex(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_node_id lnid; + struct ls_node *lnode; + struct ls_vertex *vertex; + + /* Sanity Check */ + if (!ted || !lsa || !lsa->data || !lsa->area) + return NULL; + + /* Search if a Link State Vertex already exist */ + lnid.origin = OSPFv2; + lnid.id.ip.addr = lsa->data->adv_router; + lnid.id.ip.area_id = lsa->area->area_id; + vertex = ls_find_vertex_by_id(ted, lnid); + + /* Create Node & Vertex in the Link State Date Base if not found */ + if (!vertex) { + const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; + + lnode = ls_node_new(lnid, inaddr_any, in6addr_any); + snprintfrr(lnode->name, MAX_NAME_LENGTH, "%pI4", + &lnid.id.ip.addr); + vertex = ls_vertex_add(ted, lnode); + } + + if (IS_LSA_SELF(lsa)) + ted->self = vertex; + + return vertex; +} + +/** + * Get Edge from TED by Link State Attribute ID. A new Edge and associated Link + * State Attributes are created if not found. + * + * @param ted Link State Traffic Engineering Database + * @param adv Link State Node ID of router which advertised Edge + * @param link_id Link State Attribute ID + * + * @return Link State Edge + */ +static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_node_id adv, + struct in_addr link_id) +{ + uint64_t key; + struct ls_edge *edge; + struct ls_attributes *attr; + + /* Search Edge that corresponds to the Link ID */ + key = ((uint64_t)ntohl(link_id.s_addr)) & 0xffffffff; + edge = ls_find_edge_by_key(ted, key); + + /* Create new one if not exist */ + if (!edge) { + attr = ls_attributes_new(adv, link_id, in6addr_any, 0); + edge = ls_edge_add(ted, attr); + } + + return edge; } +/** + * Export Link State information to consumer daemon through ZAPI Link State + * Opaque Message. + * + * @param type Type of Link State Element i.e. Vertex, Edge or Subnet + * @param link_state Pointer to Link State Vertex, Edge or Subnet + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_export(uint8_t type, void *link_state) +{ + struct ls_message msg = {}; + int rc = 0; + + if (!OspfMplsTE.export) + return rc; + + switch (type) { + case LS_MSG_TYPE_NODE: + ls_vertex2msg(&msg, (struct ls_vertex *)link_state); + rc = ls_send_msg(zclient, &msg, NULL); + break; + case LS_MSG_TYPE_ATTRIBUTES: + ls_edge2msg(&msg, (struct ls_edge *)link_state); + rc = ls_send_msg(zclient, &msg, NULL); + break; + case LS_MSG_TYPE_PREFIX: + ls_subnet2msg(&msg, (struct ls_subnet *)link_state); + rc = ls_send_msg(zclient, &msg, NULL); + break; + default: + rc = -1; + break; + } + + return rc; +} + +/** + * Update Link State Edge & Attributes from the given Link State Attributes ID + * and metric. This function is called when parsing Router LSA. + * + * @param ted Link State Traffic Engineering Database + * @param vertex Vertex where the Edge is attached as source + * @param link_data Link State Edge ID + * @param metric Standard metric attached to this Edge + */ +static void ospf_te_update_link(struct ls_ted *ted, struct ls_vertex *vertex, + struct in_addr link_data, uint8_t metric) +{ + struct ls_edge *edge; + struct ls_attributes *attr; + + /* Sanity check */ + if (!ted || !vertex || !vertex->node) + return; + + /* Get Corresponding Edge from Link State Data Base */ + edge = get_edge(ted, vertex->node->adv, link_data); + attr = edge->attributes; + + /* re-attached edge to vertex if needed */ + if (!edge->source) + edge->source = vertex; + + /* Check if it is just an LSA refresh */ + if ((CHECK_FLAG(attr->flags, LS_ATTR_METRIC) + && (attr->metric == metric))) { + edge->status = SYNC; + return; + } + + /* Update metric value */ + attr->metric = metric; + SET_FLAG(attr->flags, LS_ATTR_METRIC); + if (edge->status != NEW) + edge->status = UPDATE; + + ote_debug(" |- %s Edge %pI4 with metric %d", + edge->status == NEW ? "Add" : "Update", &attr->standard.local, + attr->metric); + + /* Export Link State Edge */ + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + edge->status = SYNC; +} + +/** + * Update Link State Subnet & Prefix from the given prefix and metric. This + * function is called when parsing Router LSA. + * + * @param ted Link State Traffic Engineering Database + * @param vertex Vertex where the Edge is attached as source + * @param p Prefix associated to the Subnet + * @param metric Standard metric attached to this Edge + */ +static void ospf_te_update_subnet(struct ls_ted *ted, struct ls_vertex *vertex, + struct prefix p, uint8_t metric) +{ + struct ls_subnet *subnet; + struct ls_prefix *ls_pref; + + /* Search if there is a Subnet for this prefix */ + subnet = ls_find_subnet(ted, p); + + /* If found a Subnet, check if it is attached to this Vertex */ + if (subnet) { + /* Re-attach the subnet to the vertex if necessary */ + if (subnet->vertex != vertex) { + subnet->vertex = vertex; + listnode_add_sort_nodup(vertex->prefixes, subnet); + } + /* Check if it is a simple refresh */ + ls_pref = subnet->ls_pref; + if ((CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC)) + && (ls_pref->metric == metric)) { + subnet->status = SYNC; + return; + } + ls_pref->metric = metric; + SET_FLAG(ls_pref->flags, LS_PREF_METRIC); + subnet->status = UPDATE; + } else { + /* Create new Link State Prefix */ + ls_pref = ls_prefix_new(vertex->node->adv, p); + ls_pref->metric = metric; + SET_FLAG(ls_pref->flags, LS_PREF_METRIC); + /* and add it to the TED */ + subnet = ls_subnet_add(ted, ls_pref); + } + + ote_debug(" |- %s subnet %pFX with metric %d", + subnet->status == NEW ? "Add" : "Update", &subnet->key, + ls_pref->metric); + + /* Export Link State Subnet */ + ospf_te_export(LS_MSG_TYPE_PREFIX, subnet); + subnet->status = SYNC; +} + +/** + * Delete Subnet that correspond to the given IPv4 address and export deletion + * information before removal. Prefix length is fixed to IPV4_MAX_PREFIXLEN. + * + * @param ted Links State Database + * @param addr IPv4 address + */ +static void ospf_te_delete_subnet(struct ls_ted *ted, struct in_addr addr) +{ + struct prefix p; + struct ls_subnet *subnet; + + /* Search subnet that correspond to the address/32 as prefix */ + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.u.prefix4 = addr; + subnet = ls_find_subnet(ted, p); + + /* Remove subnet if found */ + if (subnet) { + subnet->status = DELETE; + ospf_te_export(LS_MSG_TYPE_PREFIX, subnet); + ls_subnet_del_all(ted, subnet); + } +} + +/** + * Parse Router LSA. This function will create or update corresponding Vertex, + * Edge and Subnet. It also remove Edge and Subnet if they are marked as Orphan + * once Router LSA is parsed. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct router_lsa *rl; + enum ls_node_type type; + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + struct listnode *node; + int len; + + /* Sanity Check */ + if (!ted || !lsa || !lsa->data) + return -1; + + ote_debug("MPLS-TE (%s): Parse Router LSA[%pI4] from Router[%pI4]", + __func__, &lsa->data->id, &lsa->data->adv_router); + + /* Get vertex from LSA Advertise Router ID */ + vertex = get_vertex(ted, lsa); + + /* Set Node type information if it has changed */ + rl = (struct router_lsa *)lsa->data; + if (IS_ROUTER_LSA_VIRTUAL(rl)) + type = PSEUDO; + else if (IS_ROUTER_LSA_EXTERNAL(rl)) + type = ASBR; + else if (IS_ROUTER_LSA_BORDER(rl)) + type = ABR; + else + type = STANDARD; + + if (vertex->status == NEW) { + vertex->node->type = type; + SET_FLAG(vertex->node->flags, LS_NODE_TYPE); + } else if (vertex->node->type != type) { + vertex->node->type = type; + vertex->status = UPDATE; + } + + /* Check if Vertex has been modified */ + if (vertex->status != SYNC) { + ote_debug(" |- %s Vertex %pI4", + vertex->status == NEW ? "Add" : "Update", + &vertex->node->router_id); + + /* Vertex is out of sync: export it */ + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + vertex->status = SYNC; + } + + /* Mark outgoing Edge and Subnet as ORPHAN to detect deletion */ + for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) + edge->status = ORPHAN; + + for (ALL_LIST_ELEMENTS_RO(vertex->prefixes, node, subnet)) + subnet->status = ORPHAN; + + /* Then, process Link Information */ + len = ntohs(rl->header.length) - 4; + for (int i = 0; i < ntohs(rl->links) && len > 0; len -= 12, i++) { + struct prefix p; + uint32_t metric; + + switch (rl->link[i].type) { + case LSA_LINK_TYPE_POINTOPOINT: + ospf_te_update_link(ted, vertex, rl->link[i].link_data, + ntohs(rl->link[i].metric)); + /* Add corresponding subnet */ + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.u.prefix4 = rl->link[i].link_data; + metric = ntohs(rl->link[i].metric); + ospf_te_update_subnet(ted, vertex, p, metric); + break; + case LSA_LINK_TYPE_STUB: + /* Keep only /32 prefix */ + p.prefixlen = ip_masklen(rl->link[i].link_data); + if (p.prefixlen == IPV4_MAX_PREFIXLEN) { + p.family = AF_INET; + p.u.prefix4 = rl->link[i].link_id; + metric = ntohs(rl->link[i].metric); + ospf_te_update_subnet(ted, vertex, p, metric); + } + break; + default: + break; + } + } + /* Clean remaining Orphan Edges or Subnets */ + if (OspfMplsTE.export) + ls_vertex_clean(ted, vertex, zclient); + else + ls_vertex_clean(ted, vertex, NULL); + + return 0; +} + +/** + * Delete Vertex, Edge and Subnet associated to this Router LSA. This function + * is called when the router received such LSA with MAX_AGE (Flush) or when the + * router stop OSPF. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_node_id lnid; + struct ls_vertex *vertex; + + /* Sanity Check */ + if (!ted || !lsa || !lsa->data) + return -1; + + /* Search Vertex that corresponds to this LSA */ + lnid.origin = OSPFv2; + lnid.id.ip.addr = lsa->data->adv_router; + lnid.id.ip.area_id = lsa->area->area_id; + vertex = ls_find_vertex_by_id(ted, lnid); + if (!vertex) + return -1; + + ote_debug("MPLS-TE (%s): Delete Vertex %pI4 from Router LSA[%pI4]", + __func__, &vertex->node->router_id, &lsa->data->id); + + /* Export deleted vertex ... */ + vertex->status = DELETE; + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + + /* ... and remove Node & Vertex from Link State Date Base */ + ls_vertex_del_all(ted, vertex); + + return 0; +} + +/** + * Create or update Remote Vertex that corresponds to the remote ASBR of the + * foreign network if Edge is associated to an Inter-AS LSA (Type 6). + * + * @param ted Link State Traffic Engineering Database + * @param edge Link State Edge + */ +static void ospf_te_update_remote_asbr(struct ls_ted *ted, struct ls_edge *edge) +{ + struct ls_node_id lnid; + struct ls_vertex *vertex; + struct ls_node *lnode; + struct ls_attributes *attr; + struct prefix p; + + /* Sanity Check */ + if (!ted || !edge) + return; + + /* Search if a Link State Vertex already exist */ + attr = edge->attributes; + lnid.origin = OSPFv2; + lnid.id.ip.addr = attr->standard.remote_addr; + lnid.id.ip.area_id = attr->adv.id.ip.area_id; + vertex = ls_find_vertex_by_id(ted, lnid); + + /* Create Node & Vertex in the Link State Date Base if not found */ + if (!vertex) { + const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; + + lnode = ls_node_new(lnid, inaddr_any, in6addr_any); + snprintfrr(lnode->name, MAX_NAME_LENGTH, "%pI4", + &lnid.id.ip.addr); + vertex = ls_vertex_add(ted, lnode); + } + + /* Update Node information */ + lnode = vertex->node; + if (CHECK_FLAG(lnode->flags, LS_NODE_TYPE)) { + if (lnode->type != RMT_ASBR) { + lnode->type = RMT_ASBR; + if (vertex->status != NEW) + vertex->status = UPDATE; + } + } else { + lnode->type = RMT_ASBR; + SET_FLAG(lnode->flags, LS_NODE_TYPE); + if (vertex->status != NEW) + vertex->status = UPDATE; + } + if (CHECK_FLAG(lnode->flags, LS_NODE_AS_NUMBER)) { + if (lnode->as_number != attr->standard.remote_as) { + lnode->as_number = attr->standard.remote_as; + if (vertex->status != NEW) + vertex->status = UPDATE; + } + } else { + lnode->as_number = attr->standard.remote_as; + SET_FLAG(lnode->flags, LS_NODE_AS_NUMBER); + if (vertex->status != NEW) + vertex->status = UPDATE; + } + + /* Export Link State Vertex if needed */ + if (vertex->status == NEW || vertex->status == UPDATE) { + ote_debug(" |- %s Remote Vertex %pI4 for AS %u", + vertex->status == NEW ? "Add" : "Update", + &lnode->router_id, lnode->as_number); + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + vertex->status = SYNC; + } + + /* Update corresponding Subnets */ + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.u.prefix4 = attr->standard.local; + ospf_te_update_subnet(ted, edge->source, p, attr->standard.te_metric); + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.u.prefix4 = attr->standard.remote_addr; + ospf_te_update_subnet(ted, vertex, p, attr->standard.te_metric); + + /* Connect Edge to the remote Vertex */ + if (edge->destination == NULL) { + edge->destination = vertex; + listnode_add_sort_nodup(vertex->incoming_edges, edge); + } + + /* Finally set type to ASBR the node that advertised this Edge ... */ + vertex = edge->source; + lnode = vertex->node; + if (CHECK_FLAG(lnode->flags, LS_NODE_TYPE)) { + if (lnode->type != ASBR) { + lnode->type = ASBR; + if (vertex->status != NEW) + vertex->status = UPDATE; + } + } else { + lnode->type = ASBR; + SET_FLAG(lnode->flags, LS_NODE_TYPE); + if (vertex->status != NEW) + vertex->status = UPDATE; + } + + /* ... and Export it if needed */ + if (vertex->status == NEW || vertex->status == UPDATE) { + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + vertex->status = SYNC; + } +} + +/** + * Parse Opaque Traffic Engineering LSA (Type 1) TLVs and create or update the + * corresponding Link State Edge and Attributes. Vertex connections are also + * updated if needed based on the remote IP address of the Edge and existing + * reverse Edge. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_te(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_edge *edge; + struct ls_vertex *vertex; + struct ls_attributes *old, attr = {}; + struct tlv_header *tlvh; + void *value; + uint16_t len, sum; + uint8_t lsa_id; + + /* Initialize Attribute */ + attr.adv.origin = OSPFv2; + attr.adv.id.ip.addr = lsa->data->adv_router; + if (lsa->data->type != OSPF_OPAQUE_AS_LSA) + attr.adv.id.ip.area_id = lsa->area->area_id; + + /* Initialize TLV browsing */ + tlvh = TLV_HDR_TOP(lsa->data); + + /* Skip TE Router-ID if present */ + if (ntohs(tlvh->type) == TE_TLV_ROUTER_ADDR) + tlvh = TLV_HDR_NEXT(tlvh); + + /* Check if we have a TE Link TLV */ + len = TLV_BODY_SIZE(tlvh); + if ((len == 0) || (ntohs(tlvh->type) != TE_TLV_LINK)) + return 0; + + sum = 0; + /* Browse sub-TLV and fulfill Link State Attributes */ + for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { + uint32_t val32, tab32[2]; + float valf, tabf[8]; + struct in_addr addr; + + value = TLV_DATA(tlvh); + switch (ntohs(tlvh->type)) { + case TE_LINK_SUBTLV_LCLIF_IPADDR: + memcpy(&addr, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.local = addr; + SET_FLAG(attr.flags, LS_ATTR_LOCAL_ADDR); + break; + case TE_LINK_SUBTLV_RMTIF_IPADDR: + memcpy(&addr, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.remote = addr; + SET_FLAG(attr.flags, LS_ATTR_NEIGH_ADDR); + break; + case TE_LINK_SUBTLV_TE_METRIC: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.te_metric = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_TE_METRIC); + break; + case TE_LINK_SUBTLV_MAX_BW: + memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.max_bw = ntohf(valf); + SET_FLAG(attr.flags, LS_ATTR_MAX_BW); + break; + case TE_LINK_SUBTLV_MAX_RSV_BW: + memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.max_rsv_bw = ntohf(valf); + SET_FLAG(attr.flags, LS_ATTR_MAX_RSV_BW); + break; + case TE_LINK_SUBTLV_UNRSV_BW: + memcpy(tabf, value, TE_LINK_SUBTLV_UNRSV_SIZE); + for (int i = 0; i < MAX_CLASS_TYPE; i++) + attr.standard.unrsv_bw[i] = ntohf(tabf[i]); + SET_FLAG(attr.flags, LS_ATTR_UNRSV_BW); + break; + case TE_LINK_SUBTLV_RSC_CLSCLR: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.admin_group = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_ADM_GRP); + break; + case TE_LINK_SUBTLV_LLRI: + memcpy(tab32, value, TE_LINK_SUBTLV_LLRI_SIZE); + attr.standard.local_id = ntohl(tab32[0]); + attr.standard.remote_id = ntohl(tab32[1]); + SET_FLAG(attr.flags, LS_ATTR_LOCAL_ID); + SET_FLAG(attr.flags, LS_ATTR_NEIGH_ID); + break; + case TE_LINK_SUBTLV_RIP: + memcpy(&addr, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.remote_addr = addr; + SET_FLAG(attr.flags, LS_ATTR_REMOTE_ADDR); + break; + case TE_LINK_SUBTLV_RAS: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.remote_as = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_REMOTE_AS); + break; + case TE_LINK_SUBTLV_AV_DELAY: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.delay = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_DELAY); + break; + case TE_LINK_SUBTLV_MM_DELAY: + memcpy(tab32, value, TE_LINK_SUBTLV_MM_DELAY_SIZE); + attr.extended.min_delay = ntohl(tab32[0]); + attr.extended.max_delay = ntohl(tab32[1]); + SET_FLAG(attr.flags, LS_ATTR_MIN_MAX_DELAY); + break; + case TE_LINK_SUBTLV_DELAY_VAR: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.jitter = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_JITTER); + break; + case TE_LINK_SUBTLV_PKT_LOSS: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.pkt_loss = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_PACKET_LOSS); + break; + case TE_LINK_SUBTLV_RES_BW: + memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.rsv_bw = ntohf(valf); + SET_FLAG(attr.flags, LS_ATTR_RSV_BW); + break; + case TE_LINK_SUBTLV_AVA_BW: + memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.ava_bw = ntohf(valf); + SET_FLAG(attr.flags, LS_ATTR_AVA_BW); + break; + case TE_LINK_SUBTLV_USE_BW: + memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.used_bw = ntohf(valf); + SET_FLAG(attr.flags, LS_ATTR_USE_BW); + break; + default: + break; + } + sum += TLV_SIZE(tlvh); + } + + /* Get corresponding Edge from Link State Data Base */ + edge = get_edge(ted, attr.adv, attr.standard.local); + old = edge->attributes; + + ote_debug(" |- Process Traffic Engineering LSA %pI4 for Edge %pI4", + &lsa->data->id, &attr.standard.local); + + /* Update standard fields */ + len = sizeof(struct ls_standard); + if ((attr.flags & 0x0FFFF) == (old->flags & 0x0FFFF)) { + if (memcmp(&attr.standard, &old->standard, len) != 0) { + memcpy(&old->standard, &attr.standard, len); + if (edge->status != NEW) + edge->status = UPDATE; + } + } else { + memcpy(&old->standard, &attr.standard, len); + old->flags |= attr.flags & 0x0FFFF; + if (edge->status != NEW) + edge->status = UPDATE; + } + /* Update extended fields */ + len = sizeof(struct ls_extended); + if ((attr.flags & 0x0FF0000) == (old->flags & 0x0FF0000)) { + if (memcmp(&attr.extended, &old->extended, len) != 0) { + memcpy(&old->extended, &attr.extended, len); + if (edge->status != NEW) + edge->status = UPDATE; + } + } else { + memcpy(&old->extended, &attr.extended, len); + old->flags |= attr.flags & 0x0FF0000; + if (edge->status != NEW) + edge->status = UPDATE; + } + + /* If LSA is an Opaque Inter-AS, Add Node and Subnet */ + lsa_id = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + if (lsa_id == OPAQUE_TYPE_INTER_AS_LSA) + ospf_te_update_remote_asbr(ted, edge); + + /* Update remote Link if remote IP addr is known */ + if (CHECK_FLAG(old->flags, LS_ATTR_NEIGH_ADDR)) { + struct ls_edge *dst; + + dst = ls_find_edge_by_destination(ted, old); + /* Attach remote link if not set */ + if (dst && edge->source && dst->destination == NULL) { + vertex = edge->source; + if (vertex->incoming_edges) + listnode_add_sort_nodup(vertex->incoming_edges, + dst); + dst->destination = vertex; + } + /* and destination vertex to this edge */ + if (dst && dst->source && edge->destination == NULL) { + vertex = dst->source; + if (vertex->incoming_edges) + listnode_add_sort_nodup(vertex->incoming_edges, + edge); + edge->destination = vertex; + } + } + + /* Export Link State Edge if needed */ + if (edge->status == NEW || edge->status == UPDATE) { + ote_debug(" |- %s TE info. for Edge %pI4", + edge->status == NEW ? "Add" : "Update", + &edge->attributes->standard.local); + + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + edge->status = SYNC; + } + + return 0; +} + +/** + * Delete Link State Attributes information that correspond to the Opaque + * Traffic Engineering LSA (Type 1) TLVs. Note that the Edge is not removed. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_edge *edge; + struct ls_attributes *attr; + struct tlv_header *tlvh; + struct in_addr addr; + uint64_t key = 0; + uint16_t len, sum; + uint8_t lsa_id; + + /* Initialize TLV browsing */ + tlvh = TLV_HDR_TOP(lsa->data); + /* Skip Router TE ID if present */ + if (ntohs(tlvh->type) == TE_TLV_ROUTER_ADDR) + tlvh = TLV_HDR_NEXT(tlvh); + len = TLV_BODY_SIZE(tlvh); + sum = 0; + + /* Browse sub-TLV to find Link ID */ + for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { + if (ntohs(tlvh->type) == TE_LINK_SUBTLV_LCLIF_IPADDR) { + memcpy(&addr, TLV_DATA(tlvh), TE_LINK_SUBTLV_DEF_SIZE); + key = ((uint64_t)ntohl(addr.s_addr)) & 0xffffffff; + break; + } + sum += TLV_SIZE(tlvh); + } + if (key == 0) + return 0; + + /* Search Edge that corresponds to the Link ID */ + edge = ls_find_edge_by_key(ted, key); + if (!edge || !edge->attributes) + return 0; + attr = edge->attributes; + + /* First, remove Remote ASBR and associated Edge & Subnet if any */ + lsa_id = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + if (lsa_id == OPAQUE_TYPE_INTER_AS_LSA) { + ote_debug(" |- Delete remote ASBR, Edge and Subnet"); + + if (edge->destination) { + edge->destination->status = DELETE; + ospf_te_export(LS_MSG_TYPE_NODE, edge->destination); + ls_vertex_del_all(ted, edge->destination); + } + + ospf_te_delete_subnet(ted, attr->standard.local); + + edge->status = DELETE; + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + ls_edge_del_all(ted, edge); + + return 0; + } + + ote_debug(" |- Delete TE info. for Edge %pI4", + &edge->attributes->standard.local); + + /* Remove Link State Attributes TE information */ + memset(&attr->standard, 0, sizeof(struct ls_standard)); + attr->flags &= 0x0FFFF; + memset(&attr->extended, 0, sizeof(struct ls_extended)); + attr->flags &= 0x0FF0000; + ls_attributes_srlg_del(attr); + + /* Export Edge that has been updated */ + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID) + || CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) { + edge->status = UPDATE; + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + edge->status = SYNC; + } else { + /* Remove completely the Edge if Segment Routing is not set */ + ospf_te_delete_subnet(ted, attr->standard.local); + edge->status = DELETE; + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + ls_edge_del_all(ted, edge); + } + + return 0; +} + +/** + * Parse Opaque Router Information LSA (Type 4) TLVs and update the + * corresponding Link State Vertex with these information (Segment Routing). + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_ri(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_vertex *vertex; + struct ls_node *node; + struct lsa_header *lsah = lsa->data; + struct tlv_header *tlvh; + uint16_t len = 0, sum = 0; + + /* Get vertex / Node from LSA Advertised Router ID */ + vertex = get_vertex(ted, lsa); + node = vertex->node; + + ote_debug(" |- Process Router Information LSA %pI4 for Vertex %pI4", + &lsa->data->id, &node->router_id); + + /* Initialize TLV browsing */ + len = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE; + for (tlvh = TLV_HDR_TOP(lsah); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { + struct ri_sr_tlv_sr_algorithm *algo; + struct ri_sr_tlv_sid_label_range *range; + struct ri_sr_tlv_node_msd *msd; + uint32_t size, lower; + + switch (ntohs(tlvh->type)) { + case RI_SR_TLV_SR_ALGORITHM: + algo = (struct ri_sr_tlv_sr_algorithm *)tlvh; + + for (int i = 0; i < ntohs(algo->header.length); i++) { + if (CHECK_FLAG(node->flags, LS_NODE_SR) + && (node->algo[i] == algo->value[i])) + continue; + + node->algo[i] = algo->value[i]; + SET_FLAG(node->flags, LS_NODE_SR); + if (vertex->status != NEW) + vertex->status = UPDATE; + } + + /* Reset other Algorithms */ + for (int i = ntohs(algo->header.length); i < 2; i++) { + if (vertex->status != NEW + && node->algo[i] != SR_ALGORITHM_UNSET) + vertex->status = UPDATE; + node->algo[i] = SR_ALGORITHM_UNSET; + } + + break; + + case RI_SR_TLV_SRGB_LABEL_RANGE: + range = (struct ri_sr_tlv_sid_label_range *)tlvh; + size = GET_RANGE_SIZE(ntohl(range->size)); + lower = GET_LABEL(ntohl(range->lower.value)); + if ((CHECK_FLAG(node->flags, LS_NODE_SR)) + && ((node->srgb.range_size == size) + && (node->srgb.lower_bound == lower))) + break; + + node->srgb.range_size = size; + node->srgb.lower_bound = lower; + SET_FLAG(node->flags, LS_NODE_SR); + if (vertex->status != NEW) + vertex->status = UPDATE; + + break; + + case RI_SR_TLV_SRLB_LABEL_RANGE: + range = (struct ri_sr_tlv_sid_label_range *)tlvh; + size = GET_RANGE_SIZE(ntohl(range->size)); + lower = GET_LABEL(ntohl(range->lower.value)); + if ((CHECK_FLAG(node->flags, LS_NODE_SRLB)) + && ((node->srlb.range_size == size) + && (node->srlb.lower_bound == lower))) + break; + + node->srlb.range_size = size; + node->srlb.lower_bound = lower; + SET_FLAG(node->flags, LS_NODE_SRLB); + if (vertex->status != NEW) + vertex->status = UPDATE; + + break; + + case RI_SR_TLV_NODE_MSD: + msd = (struct ri_sr_tlv_node_msd *)tlvh; + if ((CHECK_FLAG(node->flags, LS_NODE_MSD)) + && (node->msd == msd->value)) + break; + + node->msd = msd->value; + SET_FLAG(node->flags, LS_NODE_MSD); + if (vertex->status != NEW) + vertex->status = UPDATE; + + break; + + default: + break; + } + sum += TLV_SIZE(tlvh); + } + + /* Vertex has been created or updated: export it */ + if (vertex->status == NEW || vertex->status == UPDATE) { + ote_debug(" |- %s SR info - SRGB[%d/%d] for Vertex %pI4", + vertex->status == NEW ? "Add" : "Update", + vertex->node->srgb.lower_bound, + vertex->node->srgb.range_size, + &vertex->node->router_id); + + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + vertex->status = SYNC; + } + + return 0; +} + +/** + * Delete Link State Node information (Segment Routing) that correspond to the + * Opaque Router Information LSA (Type 4) TLVs. Note that the Vertex is not + * removed. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_ri(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_node_id lnid; + struct ls_vertex *vertex; + struct ls_node *node; + + /* Search if a Link State Vertex already exist */ + lnid.origin = OSPFv2; + lnid.id.ip.addr = lsa->data->adv_router; + lnid.id.ip.area_id = lsa->area->area_id; + vertex = ls_find_vertex_by_id(ted, lnid); + if (!vertex) + return -1; + + /* Remove Segment Routing Information if any */ + node = vertex->node; + UNSET_FLAG(node->flags, LS_NODE_SR); + memset(&node->srgb, 0, sizeof(struct ls_srgb)); + node->algo[0] = SR_ALGORITHM_UNSET; + node->algo[1] = SR_ALGORITHM_UNSET; + UNSET_FLAG(node->flags, LS_NODE_SRLB); + memset(&node->srlb, 0, sizeof(struct ls_srlb)); + UNSET_FLAG(node->flags, LS_NODE_MSD); + node->msd = 0; + vertex->status = UPDATE; + + ote_debug(" |- Delete SR info. for Vertex %pI4", + &vertex->node->router_id); + + /* Vertex has been updated: export it */ + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + vertex->status = SYNC; + + return 0; +} + +/** + * Parse Opaque Extended Prefix LSA (Type 7) TLVs and update the corresponding + * Link State Subnet with these information (Segment Routing ID). + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_node_id lnid; + struct ls_subnet *subnet; + struct ls_prefix *ls_pref; + struct prefix pref; + struct ext_tlv_prefix *ext; + struct ext_subtlv_prefix_sid *pref_sid; + uint32_t label; + + /* Get corresponding Subnet from Link State Data Base */ + ext = (struct ext_tlv_prefix *)TLV_HDR_TOP(lsa->data); + pref.family = AF_INET; + pref.prefixlen = ext->pref_length; + pref.u.prefix4 = ext->address; + subnet = ls_find_subnet(ted, pref); + + /* Create new Link State Prefix if not found */ + if (!subnet) { + lnid.origin = OSPFv2; + lnid.id.ip.addr = lsa->data->adv_router; + lnid.id.ip.area_id = lsa->area->area_id; + ls_pref = ls_prefix_new(lnid, pref); + /* and add it to the TED */ + subnet = ls_subnet_add(ted, ls_pref); + } + + ote_debug(" |- Process Extended Prefix LSA %pI4 for subnet %pFX", + &lsa->data->id, &pref); + + /* Initialize TLV browsing */ + ls_pref = subnet->ls_pref; + pref_sid = (struct ext_subtlv_prefix_sid *)((char *)(ext) + TLV_HDR_SIZE + + EXT_TLV_PREFIX_SIZE); + label = CHECK_FLAG(pref_sid->flags, EXT_SUBTLV_PREFIX_SID_VFLG) + ? GET_LABEL(ntohl(pref_sid->value)) + : ntohl(pref_sid->value); + + /* Check if it is a simple refresh */ + if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR) + && ls_pref->sr.algo == pref_sid->algorithm + && ls_pref->sr.sid_flag == pref_sid->flags + && ls_pref->sr.sid == label) + return 0; + + /* Fulfill SR information */ + ls_pref->sr.algo = pref_sid->algorithm; + ls_pref->sr.sid_flag = pref_sid->flags; + ls_pref->sr.sid = label; + SET_FLAG(ls_pref->flags, LS_PREF_SR); + if (subnet->status != NEW) + subnet->status = UPDATE; + + /* Export Subnet if needed */ + if (subnet->status == NEW || subnet->status == UPDATE) { + ote_debug(" |- %s SID %d to subnet %pFX", + subnet->status == NEW ? "Add" : "Update", + ls_pref->sr.sid, &ls_pref->pref); + + ospf_te_export(LS_MSG_TYPE_PREFIX, subnet); + subnet->status = SYNC; + } + + return 0; +} + +/** + * Delete Link State Subnet information (Segment Routing ID) that correspond to + * the Opaque Extended Prefix LSA (Type 7) TLVs. Note that the Subnet is not + * removed. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_subnet *subnet; + struct ls_prefix *ls_pref; + struct prefix pref; + struct ext_tlv_prefix *ext; + + /* Get corresponding Subnet from Link State Data Base */ + ext = (struct ext_tlv_prefix *)TLV_HDR_TOP(lsa->data); + pref.family = AF_INET; + pref.prefixlen = ext->pref_length; + pref.u.prefix4 = ext->address; + subnet = ls_find_subnet(ted, pref); + + /* Check if there is a corresponding subnet */ + if (!subnet) + return -1; + + ote_debug(" |- Delete SID %d to subnet %pFX", subnet->ls_pref->sr.sid, + &subnet->ls_pref->pref); + + /* Remove Segment Routing information */ + ls_pref = subnet->ls_pref; + UNSET_FLAG(ls_pref->flags, LS_PREF_SR); + memset(&ls_pref->sr, 0, sizeof(struct ls_sid)); + subnet->status = UPDATE; + + /* Subnet has been updated: export it */ + ospf_te_export(LS_MSG_TYPE_PREFIX, subnet); + subnet->status = SYNC; + + return 0; +} + +/** + * Parse Opaque Extended Link LSA (Type 8) TLVs and update the corresponding + * Link State Edge with these information (Segment Routing Adjacency). + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_node_id lnid; + struct tlv_header *tlvh; + struct ext_tlv_link *ext; + struct ls_edge *edge; + struct ls_attributes *atr; + uint16_t len = 0, sum = 0, i; + uint32_t label; + + /* Get corresponding Edge from Link State Data Base */ + lnid.origin = OSPFv2; + lnid.id.ip.addr = lsa->data->adv_router; + lnid.id.ip.area_id = lsa->area->area_id; + ext = (struct ext_tlv_link *)TLV_HDR_TOP(lsa->data); + edge = get_edge(ted, lnid, ext->link_data); + atr = edge->attributes; + + ote_debug(" |- Process Extended Link LSA %pI4 for edge %pI4", + &lsa->data->id, &edge->attributes->standard.local); + + /* Initialize TLV browsing */ + len = TLV_BODY_SIZE(&ext->header); + tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE + + EXT_TLV_LINK_SIZE); + for (; sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { + struct ext_subtlv_adj_sid *adj; + struct ext_subtlv_lan_adj_sid *ladj; + struct ext_subtlv_rmt_itf_addr *rmt; + + switch (ntohs(tlvh->type)) { + case EXT_SUBTLV_ADJ_SID: + adj = (struct ext_subtlv_adj_sid *)tlvh; + label = CHECK_FLAG(adj->flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? GET_LABEL(ntohl(adj->value)) + : ntohl(adj->value); + i = CHECK_FLAG(adj->flags, + EXT_SUBTLV_LINK_ADJ_SID_BFLG) ? 1 : 0; + if (((i && CHECK_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID)) + || (!i && CHECK_FLAG(atr->flags, LS_ATTR_ADJ_SID))) + && atr->adj_sid[i].flags == adj->flags + && atr->adj_sid[i].sid == label + && atr->adj_sid[i].weight == adj->weight) + break; + + atr->adj_sid[i].flags = adj->flags; + atr->adj_sid[i].sid = label; + atr->adj_sid[i].weight = adj->weight; + if (i == 0) + SET_FLAG(atr->flags, LS_ATTR_ADJ_SID); + else + SET_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID); + if (edge->status != NEW) + edge->status = UPDATE; + + break; + case EXT_SUBTLV_LAN_ADJ_SID: + ladj = (struct ext_subtlv_lan_adj_sid *)tlvh; + label = CHECK_FLAG(ladj->flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? GET_LABEL(ntohl(ladj->value)) + : ntohl(ladj->value); + i = CHECK_FLAG(ladj->flags, + EXT_SUBTLV_LINK_ADJ_SID_BFLG) ? 1 : 0; + if (((i && CHECK_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID)) + || (!i && CHECK_FLAG(atr->flags, LS_ATTR_ADJ_SID))) + && atr->adj_sid[i].flags == ladj->flags + && atr->adj_sid[i].sid == label + && atr->adj_sid[i].weight == ladj->weight + && IPV4_ADDR_SAME(&atr->adj_sid[1].neighbor.addr, + &ladj->neighbor_id)) + break; + + atr->adj_sid[i].flags = ladj->flags; + atr->adj_sid[i].sid = label; + atr->adj_sid[i].weight = ladj->weight; + atr->adj_sid[i].neighbor.addr = ladj->neighbor_id; + if (i == 0) + SET_FLAG(atr->flags, LS_ATTR_ADJ_SID); + else + SET_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID); + if (edge->status != NEW) + edge->status = UPDATE; + + break; + case EXT_SUBTLV_RMT_ITF_ADDR: + rmt = (struct ext_subtlv_rmt_itf_addr *)tlvh; + if (CHECK_FLAG(atr->flags, LS_ATTR_NEIGH_ADDR) + && IPV4_ADDR_SAME(&atr->standard.remote, + &rmt->value)) + break; + + atr->standard.remote = rmt->value; + SET_FLAG(atr->flags, LS_ATTR_NEIGH_ADDR); + if (edge->status != NEW) + edge->status = UPDATE; + + break; + default: + break; + } + sum += TLV_SIZE(tlvh); + } + + /* Export Link State Edge if needed */ + if (edge->status == NEW || edge->status == UPDATE) { + ote_debug(" |- %s Adj-SID %d & %d to edge %pI4", + edge->status == NEW ? "Add" : "Update", + edge->attributes->adj_sid[0].sid, + edge->attributes->adj_sid[1].sid, + &edge->attributes->standard.local); + + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + edge->status = SYNC; + } + + return 0; +} + +/** + * Delete Link State Edge information (Segment Routing Adjacency) that + * correspond to the Opaque Extended Link LSA (Type 8) TLVs. Note that the Edge + * is not removed. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_edge *edge; + struct ls_attributes *atr; + struct ext_tlv_link *ext; + uint64_t key; + + /* Search for corresponding Edge from Link State Data Base */ + ext = (struct ext_tlv_link *)TLV_HDR_TOP(lsa->data); + key = ((uint64_t)ntohl(ext->link_data.s_addr)) & 0xffffffff; + edge = ls_find_edge_by_key(ted, key); + + /* Check if there is a corresponding Edge */ + if (!edge) + return -1; + + ote_debug(" |- Delete Adj-SID %d to edge %pI4", + edge->attributes->adj_sid[0].sid, + &edge->attributes->standard.local); + + /* Remove Segment Routing information */ + atr = edge->attributes; + UNSET_FLAG(atr->flags, LS_ATTR_ADJ_SID); + UNSET_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID); + memset(atr->adj_sid, 0, 2 * sizeof(struct ls_sid)); + edge->status = UPDATE; + + /* Edge has been updated: export it */ + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + edge->status = SYNC; + + return 0; +} + +/** + * Parse Opaque LSA Type and call corresponding parser. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_opaque_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + uint8_t key = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + int rc = -1; + + ote_debug("MPLS-TE (%s): Parse Opaque LSA[%pI4] from Router[%pI4]", + __func__, &lsa->data->id, &lsa->data->adv_router); + + switch (key) { + case OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA: + case OPAQUE_TYPE_INTER_AS_LSA: + rc = ospf_te_parse_te(ted, lsa); + break; + case OPAQUE_TYPE_ROUTER_INFORMATION_LSA: + rc = ospf_te_parse_ri(ted, lsa); + break; + case OPAQUE_TYPE_EXTENDED_PREFIX_LSA: + rc = ospf_te_parse_ext_pref(ted, lsa); + break; + case OPAQUE_TYPE_EXTENDED_LINK_LSA: + rc = ospf_te_parse_ext_link(ted, lsa); + break; + default: + break; + } + + return rc; +} + +/** + * Parse Opaque LSA Type and call corresponding deletion function. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_opaque_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + uint8_t key = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + int rc = -1; + + ote_debug("MPLS-TE (%s): Parse Opaque LSA[%pI4] from Router[%pI4]", + __func__, &lsa->data->id, &lsa->data->adv_router); + + switch (key) { + case OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA: + case OPAQUE_TYPE_INTER_AS_LSA: + rc = ospf_te_delete_te(ted, lsa); + break; + case OPAQUE_TYPE_ROUTER_INFORMATION_LSA: + rc = ospf_te_delete_ri(ted, lsa); + break; + case OPAQUE_TYPE_EXTENDED_PREFIX_LSA: + rc = ospf_te_delete_ext_pref(ted, lsa); + break; + case OPAQUE_TYPE_EXTENDED_LINK_LSA: + rc = ospf_te_delete_ext_link(ted, lsa); + break; + default: + break; + } + + return rc; +} + +/** + * Update Traffic Engineering Database Elements that correspond to the received + * OSPF LSA. If LSA age is equal to MAX_AGE, call deletion function instead. + * + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_mpls_te_lsa_update(struct ospf_lsa *lsa) +{ + + uint8_t rc; + + /* Check that MPLS-TE is active */ + if (!OspfMplsTE.enabled || !OspfMplsTE.ted) + return 0; + + /* Sanity Check */ + if (lsa == NULL) { + flog_warn(EC_OSPF_LSA_NULL, "TE (%s): Abort! LSA is NULL", + __func__); + 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; + } + } + + return rc; +} + +/** + * Delete Traffic Engineering Database element from OSPF LSA. This function + * process only self LSA (i.e. advertised by the router) which reach MAX_AGE + * as LSA deleted by neighbor routers are Flushed (i.e. advertised with + * age == MAX_AGE) and processed by ospf_mpls_te_lsa_update() function. + * + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_mpls_te_lsa_delete(struct ospf_lsa *lsa) +{ + + uint8_t rc; + + /* Check that MPLS-TE is active */ + if (!OspfMplsTE.enabled || !OspfMplsTE.ted) + return 0; + + /* Sanity Check */ + if (lsa == NULL) { + flog_warn(EC_OSPF_LSA_NULL, "TE (%s): Abort! LSA is NULL", + __func__); + 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: + 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; + } + + return rc; +} + +/** + * Send the whole Link State Traffic Engineering Database to the consumer that + * request it through a ZAPI Link State Synchronous Opaque Message. + * + * @param info ZAPI Opaque message + * + * @return 0 if success, -1 otherwise + */ +int ospf_te_sync_ted(struct zapi_opaque_reg_info dst) +{ + int rc = -1; + + /* Check that MPLS-TE and TE distribution are enabled */ + if (!OspfMplsTE.enabled || !OspfMplsTE.export) + return rc; + + rc = ls_sync_ted(OspfMplsTE.ted, zclient, &dst); + + return rc; +} + +/** + * Initialize Traffic Engineering Database from the various OSPF Link State + * Database (LSDB). + * + * @param ted Link State Traffice Engineering Database + * @param ospf OSPF main structure + */ +static void ospf_te_init_ted(struct ls_ted *ted, struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct route_node *rn; + struct ospf_area *area; + struct ospf_lsa *lsa; + + /* Iterate over all areas. */ + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + if (!area->lsdb) + continue; + + /* Parse all Router LSAs from the area LSDB */ + LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) + ospf_te_parse_router_lsa(ted, lsa); + + /* Parse all Opaque LSAs from the area LSDB */ + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + ospf_te_parse_opaque_lsa(ted, lsa); + } + + /* Parse AS-external opaque LSAs from OSPF LSDB */ + if (ospf->lsdb) { + LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) + ospf_te_parse_opaque_lsa(ted, lsa); + } + +} /*------------------------------------------------------------------------* * Followings are vty session control functions. @@ -2157,14 +3716,15 @@ static void ospf_mpls_te_config_write_router(struct vty *vty) vty_out(vty, " mpls-te on\n"); vty_out(vty, " mpls-te router-address %pI4\n", &OspfMplsTE.router_addr.value); - } - - if (OspfMplsTE.inter_as == AS) - vty_out(vty, " mpls-te inter-as as\n"); - if (OspfMplsTE.inter_as == Area) - vty_out(vty, " mpls-te inter-as area %pI4 \n", - &OspfMplsTE.interas_areaid); + if (OspfMplsTE.inter_as == AS) + vty_out(vty, " mpls-te inter-as as\n"); + if (OspfMplsTE.inter_as == Area) + vty_out(vty, " mpls-te inter-as area %pI4 \n", + &OspfMplsTE.interas_areaid); + if (OspfMplsTE.export) + vty_out(vty, " mpls-te export\n"); + } return; } @@ -2185,8 +3745,7 @@ DEFUN (ospf_mpls_te_on, if (OspfMplsTE.enabled) return CMD_SUCCESS; - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("MPLS-TE: OFF -> ON"); + ote_debug("MPLS-TE: OFF -> ON"); OspfMplsTE.enabled = true; @@ -2204,6 +3763,14 @@ DEFUN (ospf_mpls_te_on, } } + /* Create TED and initialize it */ + OspfMplsTE.ted = ls_ted_new(1, "OSPF", 0); + if (!OspfMplsTE.ted) { + vty_out(vty, "Unable to create Link State Data Base\n"); + return CMD_WARNING; + } + ospf_te_init_ted(OspfMplsTE.ted, ospf); + return CMD_SUCCESS; } @@ -2221,11 +3788,13 @@ DEFUN (no_ospf_mpls_te, if (!OspfMplsTE.enabled) return CMD_SUCCESS; - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("MPLS-TE: ON -> OFF"); + ote_debug("MPLS-TE: ON -> OFF"); + /* Remove TED */ + ls_ted_del_all(OspfMplsTE.ted); OspfMplsTE.enabled = false; + /* Flush all TE Opaque LSAs */ for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); @@ -2235,16 +3804,11 @@ DEFUN (no_ospf_mpls_te, * This is to avoid having an inter-as value different from * Off when mpls-te gets restarted (after being removed) */ - if (OspfMplsTE.inter_as != Off) { - /* Deregister the Callbacks for Inter-AS support */ - ospf_mpls_te_unregister(); - OspfMplsTE.inter_as = Off; - } + OspfMplsTE.inter_as = Off; return CMD_SUCCESS; } - DEFUN (ospf_mpls_te_router_addr, ospf_mpls_te_router_addr_cmd, "mpls-te router-address A.B.C.D", @@ -2274,7 +3838,7 @@ DEFUN (ospf_mpls_te_router_addr, return CMD_SUCCESS; for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) { - if ((lp->area == NULL) || IS_FLOOD_AS(lp->type)) + if ((lp->area == NULL) || IS_FLOOD_AS(lp->flags)) continue; if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { @@ -2284,7 +3848,7 @@ DEFUN (ospf_mpls_te_router_addr, } for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) { - if ((lp->area == NULL) || IS_FLOOD_AS(lp->type)) + if ((lp->area == NULL) || IS_FLOOD_AS(lp->flags)) continue; if (need_to_reoriginate) @@ -2324,18 +3888,9 @@ static int set_inter_as_mode(struct vty *vty, const char *mode_name, return CMD_WARNING; } - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "MPLS-TE: Inter-AS enable with %s flooding support", - mode2text[mode]); - - /* Register new callbacks regarding the flooding scope (AS or - * Area) */ - if (ospf_mpls_te_register(mode) < 0) { - vty_out(vty, - "Internal error: Unable to register Inter-AS functions\n"); - return CMD_WARNING; - } + ote_debug( + "MPLS-TE (%s): Inter-AS enable with %s flooding support", + __func__, mode2text[mode]); /* Enable mode and re-originate LSA if needed */ if ((OspfMplsTE.inter_as == Off) @@ -2346,9 +3901,11 @@ static int set_inter_as_mode(struct vty *vty, const char *mode_name, lp)) { if (IS_INTER_AS(lp->type)) { if (mode == AS) - lp->type |= FLOOD_AS; + SET_FLAG(lp->flags, + LPFLG_LSA_FLOOD_AS); else - lp->type |= FLOOD_AREA; + UNSET_FLAG(lp->flags, + LPFLG_LSA_FLOOD_AS); ospf_mpls_te_lsa_schedule( lp, REORIGINATE_THIS_LSA); } @@ -2401,8 +3958,7 @@ DEFUN (no_ospf_mpls_te_inter_as, struct listnode *node, *nnode; struct mpls_te_link *lp; - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("MPLS-TE: Inter-AS support OFF"); + ote_debug("MPLS-TE: Inter-AS support OFF"); if ((OspfMplsTE.enabled) && (OspfMplsTE.inter_as != Off)) { /* Flush all Inter-AS LSA */ @@ -2411,14 +3967,55 @@ DEFUN (no_ospf_mpls_te_inter_as, && CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); - /* Deregister the Callbacks for Inter-AS support */ - ospf_mpls_te_unregister(); OspfMplsTE.inter_as = Off; } return CMD_SUCCESS; } +DEFUN (ospf_mpls_te_export, + ospf_mpls_te_export_cmd, + "mpls-te export", + MPLS_TE_STR + "Export the MPLS-TE information as Link State\n") +{ + + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (OspfMplsTE.enabled) { + if (ls_register(zclient, true) != 0) { + vty_out(vty, "Unable to register Link State\n"); + return CMD_WARNING; + } + OspfMplsTE.export = true; + } else { + vty_out(vty, "mpls-te has not been turned on\n"); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + + +DEFUN (no_ospf_mpls_te_export, + no_ospf_mpls_te_export_cmd, + "no mpls-te export", + NO_STR + MPLS_TE_STR + "Stop export of the MPLS-TE information as Link State\n") +{ + + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (OspfMplsTE.export) { + if (ls_unregister(zclient, true) != 0) { + vty_out(vty, "Unable to unregister Link State\n"); + return CMD_WARNING; + } + OspfMplsTE.export = false; + } + return CMD_SUCCESS; +} + DEFUN (show_ip_ospf_mpls_te_router, show_ip_ospf_mpls_te_router_cmd, "show ip ospf mpls-te router", @@ -2435,7 +4032,9 @@ DEFUN (show_ip_ospf_mpls_te_router, show_vty_router_addr(vty, &OspfMplsTE.router_addr.header); else - vty_out(vty, " N/A\n"); + vty_out(vty, " Router address is not set\n"); + vty_out(vty, " Link State distribution is %s\n", + OspfMplsTE.export ? "Active" : "Inactive"); } return CMD_SUCCESS; } @@ -2592,10 +4191,142 @@ DEFUN (show_ip_ospf_mpls_te_link, return CMD_SUCCESS; } +DEFUN (show_ip_ospf_mpls_te_db, + show_ip_ospf_mpls_te_db_cmd, + "show ip ospf mpls-te database [<vertex [<self-originate|adv-router A.B.C.D>]|edge [A.B.C.D]|subnet [A.B.C.D/M]>] [verbose|json]", + SHOW_STR + IP_STR + OSPF_STR + "MPLS-TE information\n" + "MPLS-TE database\n" + "MPLS-TE Vertex\n" + "Self-originated MPLS-TE router\n" + "Advertised MPLS-TE router\n" + "MPLS-TE router ID (as an IP address)\n" + "MPLS-TE Edge\n" + "MPLS-TE Edge ID (as an IP address)\n" + "MPLS-TE Subnet\n" + "MPLS-TE Subnet ID (as an IP prefix)\n" + "Verbose output\n" + JSON_STR) +{ + int idx = 0; + struct in_addr ip_addr; + struct prefix pref; + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + uint64_t key; + bool verbose = false; + bool uj = use_json(argc, argv); + json_object *json = NULL; + + if (!OspfMplsTE.enabled || !OspfMplsTE.ted) { + vty_out(vty, "MPLS-TE database is not enabled\n"); + return CMD_WARNING; + } + + if (uj) + json = json_object_new_object(); + + if (argv[argc - 1]->arg && strmatch(argv[argc - 1]->text, "verbose")) + verbose = true; + + idx = 5; + if (argv_find(argv, argc, "vertex", &idx)) { + /* Show Vertex */ + if (argv_find(argv, argc, "self-originate", &idx)) + vertex = OspfMplsTE.ted->self; + else if (argv_find(argv, argc, "adv-router", &idx)) { + if (!inet_aton(argv[idx + 1]->arg, &ip_addr)) { + vty_out(vty, + "Specified Router ID %s is invalid\n", + argv[idx + 1]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Vertex from the Link State Database */ + key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff; + vertex = ls_find_vertex_by_key(OspfMplsTE.ted, key); + if (!vertex) { + vty_out(vty, "No vertex found for ID %pI4\n", + &ip_addr); + return CMD_WARNING; + } + } else + vertex = NULL; + + if (vertex) + ls_show_vertex(vertex, vty, json, verbose); + else + ls_show_vertices(OspfMplsTE.ted, vty, json, verbose); + + } else if (argv_find(argv, argc, "edge", &idx)) { + /* Show Edge */ + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &ip_addr)) { + vty_out(vty, + "Specified Edge ID %s is invalid\n", + argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Edge from the Link State Database */ + key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff; + edge = ls_find_edge_by_key(OspfMplsTE.ted, key); + if (!edge) { + vty_out(vty, "No edge found for ID %pI4\n", + &ip_addr); + return CMD_WARNING; + } + } else + edge = NULL; + + if (edge) + ls_show_edge(edge, vty, json, verbose); + else + ls_show_edges(OspfMplsTE.ted, vty, json, verbose); + + } else if (argv_find(argv, argc, "subnet", &idx)) { + /* Show Subnet */ + if (argv_find(argv, argc, "A.B.C.D/M", &idx)) { + if (!str2prefix(argv[idx]->arg, &pref)) { + vty_out(vty, "Invalid prefix format %s\n", + argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Subnet from the Link State Database */ + subnet = ls_find_subnet(OspfMplsTE.ted, pref); + if (!subnet) { + vty_out(vty, "No subnet found for ID %pFX\n", + &pref); + return CMD_WARNING; + } + } else + subnet = NULL; + + if (subnet) + ls_show_subnet(subnet, vty, json, verbose); + else + ls_show_subnets(OspfMplsTE.ted, vty, json, verbose); + + } else { + /* Show the complete TED */ + ls_show_ted(OspfMplsTE.ted, vty, json, verbose); + } + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; +} + static void ospf_mpls_te_register_vty(void) { install_element(VIEW_NODE, &show_ip_ospf_mpls_te_router_cmd); install_element(VIEW_NODE, &show_ip_ospf_mpls_te_link_cmd); + install_element(VIEW_NODE, &show_ip_ospf_mpls_te_db_cmd); install_element(OSPF_NODE, &ospf_mpls_te_on_cmd); install_element(OSPF_NODE, &no_ospf_mpls_te_cmd); @@ -2603,6 +4334,8 @@ static void ospf_mpls_te_register_vty(void) install_element(OSPF_NODE, &ospf_mpls_te_inter_as_cmd); install_element(OSPF_NODE, &ospf_mpls_te_inter_as_area_cmd); install_element(OSPF_NODE, &no_ospf_mpls_te_inter_as_cmd); + install_element(OSPF_NODE, &ospf_mpls_te_export_cmd); + install_element(OSPF_NODE, &no_ospf_mpls_te_export_cmd); return; } diff --git a/ospfd/ospf_te.h b/ospfd/ospf_te.h index 06c17f07fe..fc9ca20780 100644 --- a/ospfd/ospf_te.h +++ b/ospfd/ospf_te.h @@ -74,25 +74,29 @@ #define GMPLS 0x02 #define INTER_AS 0x04 #define PSEUDO_TE 0x08 -#define FLOOD_AREA 0x10 -#define FLOOD_AS 0x20 -#define EMULATED 0x80 +#define EMULATED 0x10 -#define IS_STD_TE(x) (x & STD_TE) +#define IS_STD_TE(x) (x & STD_TE) #define IS_PSEUDO_TE(x) (x & PSEUDO_TE) #define IS_INTER_AS(x) (x & INTER_AS) #define IS_EMULATED(x) (x & EMULATED) -#define IS_FLOOD_AREA(x) (x & FLOOD_AREA) -#define IS_FLOOD_AS(x) (x & FLOOD_AS) -#define IS_INTER_AS_EMU(x) (x & INTER_AS & EMULATED) -#define IS_INTER_AS_AS(x) (x & INTER_AS & FLOOD_AS) /* Flags to manage TE Link LSA */ -#define LPFLG_LSA_INACTIVE 0x0 -#define LPFLG_LSA_ACTIVE 0x1 -#define LPFLG_LSA_ENGAGED 0x2 -#define LPFLG_LOOKUP_DONE 0x4 -#define LPFLG_LSA_FORCED_REFRESH 0x8 +#define LPFLG_LSA_INACTIVE 0x00 +#define LPFLG_LSA_ACTIVE 0x01 +#define LPFLG_LSA_ENGAGED 0x02 +#define LPFLG_LOOKUP_DONE 0x04 +#define LPFLG_LSA_FORCED_REFRESH 0x08 +#define LPFLG_LSA_FLOOD_AS 0x10 + +#define IS_FLOOD_AS(x) (x & LPFLG_LSA_FLOOD_AS) + +/* Macro to log debug message */ +#define ote_debug(...) \ + do { \ + if (IS_DEBUG_OSPF_TE) \ + zlog_debug(__VA_ARGS__); \ + } while (0) /* * Following section defines TLV body parts. @@ -336,9 +340,13 @@ struct te_link_subtlv { /* Following structure are internal use only. */ struct ospf_mpls_te { - /* Status of MPLS-TE: enable or disbale */ + /* Status of MPLS-TE: enable or disable */ bool enabled; + /* Traffic Engineering Database i.e. Link State */ + struct ls_ted *ted; + bool export; + /* RFC5392 */ enum inter_as_mode inter_as; struct in_addr interas_areaid; @@ -414,4 +422,15 @@ extern void set_linkparams_llri(struct mpls_te_link *, uint32_t, uint32_t); extern void set_linkparams_lrrid(struct mpls_te_link *, struct in_addr, struct in_addr); +struct zapi_opaque_reg_info; +/** + * Call when a client send a Link State Sync message. In turn, OSPF will send + * the contain of the Link State Data base. + * + * @param info ZAPI Opaque message information + * + * @return 0 on success, -1 otherwise + */ +extern int ospf_te_sync_ted(struct zapi_opaque_reg_info dst); + #endif /* _ZEBRA_OSPF_MPLS_TE_H */ diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 6e42169b3a..be446705ea 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -577,6 +577,7 @@ DEFUN (ospf_network_area, struct prefix_ipv4 p; struct in_addr area_id; int ret, format; + uint32_t count; if (ospf->instance) { vty_out(vty, @@ -584,14 +585,15 @@ DEFUN (ospf_network_area, return CMD_WARNING_CONFIG_FAILED; } - if (ospf->if_ospf_cli_count > 0) { + count = ospf_count_area_params(ospf); + if (count > 0) { vty_out(vty, "Please remove all ip ospf area x.x.x.x commands first.\n"); if (IS_DEBUG_OSPF_EVENT) zlog_debug( "%s ospf vrf %s num of %u ip ospf area x config", __func__, ospf->name ? ospf->name : "NIL", - ospf->if_ospf_cli_count); + count); return CMD_WARNING_CONFIG_FAILED; } @@ -1574,6 +1576,58 @@ DEFUN (ospf_area_nssa, return ospf_area_nssa_cmd_handler(vty, argc, argv, 0, 0); } +DEFUN(ospf_area_nssa_suppress_fa, ospf_area_nssa_suppress_fa_cmd, + "area <A.B.C.D|(0-4294967295)> nssa suppress-fa", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Suppress forwarding address\n") +{ + int idx_ipv4_number = 1; + struct in_addr area_id; + int format; + + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, + argv[idx_ipv4_number]->arg); + + ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), + format); + ospf_area_nssa_suppress_fa_set(ospf, area_id); + + ospf_schedule_abr_task(ospf); + + return CMD_SUCCESS; +} + +DEFUN(no_ospf_area_nssa_suppress_fa, no_ospf_area_nssa_suppress_fa_cmd, + "no area <A.B.C.D|(0-4294967295)> nssa suppress-fa", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Suppress forwarding address\n") +{ + int idx_ipv4_number = 2; + struct in_addr area_id; + int format; + + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + VTY_GET_OSPF_AREA_ID_NO_BB("nssa", area_id, format, + argv[idx_ipv4_number]->arg); + + ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), + format); + ospf_area_nssa_suppress_fa_unset(ospf, area_id); + + ospf_schedule_abr_task(ospf); + + return CMD_SUCCESS; +} + DEFUN (ospf_area_nssa_no_summary, ospf_area_nssa_no_summary_cmd, "area <A.B.C.D|(0-4294967295)> nssa no-summary", @@ -2645,6 +2699,50 @@ DEFUN(no_ospf_ti_lfa, no_ospf_ti_lfa_cmd, return CMD_SUCCESS; } +static void ospf_maxpath_set(struct vty *vty, struct ospf *ospf, uint16_t paths) +{ + if (ospf->max_multipath == paths) + return; + + ospf->max_multipath = paths; + + /* Send deletion notification to zebra to delete all + * ospf specific routes and reinitiat SPF to reflect + * the new max multipath. + */ + ospf_restart_spf(ospf); +} + +/* Ospf Maximum multiple paths config support */ +DEFUN (ospf_max_multipath, + ospf_max_multipath_cmd, + "maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM), + "Max no of multiple paths for ECMP support\n" + "Number of paths\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_number = 1; + uint16_t maxpaths; + + maxpaths = strtol(argv[idx_number]->arg, NULL, 10); + + ospf_maxpath_set(vty, ospf, maxpaths); + return CMD_SUCCESS; +} + +DEFUN (no_ospf_max_multipath, + no_ospf_max_multipath_cmd, + "no maximum-paths", + NO_STR + "Max no of multiple paths for ECMP support\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + uint16_t maxpaths = MULTIPATH_NUM; + + ospf_maxpath_set(vty, ospf, maxpaths); + return CMD_SUCCESS; +} + static const char *const ospf_abr_type_descr_str[] = { "Unknown", "Standard (RFC2328)", "Alternative IBM", "Alternative Cisco", "Alternative Shortcut" @@ -3174,6 +3272,10 @@ static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf, /* Show refresh parameters. */ vty_out(vty, " Refresh timer %d secs\n", ospf->lsa_refresh_interval); + + /* show max multipath */ + vty_out(vty, " Maximum multiple paths(ECMP) supported %d\n", + ospf->max_multipath); } /* Show ABR/ASBR flags. */ @@ -3776,7 +3878,8 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, " Neighbor Count is %d, Adjacent neighbor count is %d\n", ospf_nbr_count(oi, 0), ospf_nbr_count(oi, NSM_Full)); - ospf_bfd_interface_show(vty, ifp, json_interface_sub, use_json); + + ospf_interface_bfd_show(vty, ifp, json_interface_sub); /* OSPF Authentication information */ ospf_interface_auth_show(vty, oi, json_interface_sub, use_json); @@ -5282,7 +5385,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, .helper_exit_reason)); } - ospf_bfd_show_info(vty, nbr->bfd_info, json_neigh, use_json, 0); + bfd_sess_show(vty, json_neigh, nbr->bfd_session); if (use_json) json_object_array_add(json_neigh_array, json_neigh); @@ -7109,14 +7212,14 @@ DEFUN (show_ip_ospf_database_max, return ret; } -DEFUN (show_ip_ospf_instance_database, - show_ip_ospf_instance_database_cmd, - "show ip ospf [{(1-65535)|vrf NAME}] database [<asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as> [A.B.C.D [<self-originate|adv-router A.B.C.D>]]] [json]", +ALIAS (show_ip_ospf_database_max, + show_ip_ospf_database_cmd, + "show ip ospf [vrf <NAME|all>] database [<asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as> [A.B.C.D [<self-originate|adv-router A.B.C.D>]]] [json]", SHOW_STR IP_STR "OSPF information\n" - "Instance ID\n" VRF_CMD_HELP_STR + "All VRFs\n" "Database summary\n" OSPF_LSA_TYPES_DESC "Link State ID (as an IP address)\n" @@ -7124,78 +7227,6 @@ DEFUN (show_ip_ospf_instance_database, "Advertising Router link states\n" "Advertising Router (as an IP address)\n" JSON_STR) -{ - struct ospf *ospf; - unsigned short instance = 0; - struct listnode *node = NULL; - char *vrf_name = NULL; - bool all_vrf = false; - int ret = CMD_SUCCESS; - int inst = 0; - int idx = 0; - uint8_t use_vrf = 0; - bool uj = use_json(argc, argv); - json_object *json = NULL; - - if (uj) - json = json_object_new_object(); - - if (argv_find(argv, argc, "(1-65535)", &idx)) { - instance = strtoul(argv[idx]->arg, NULL, 10); - if (instance != ospf_instance) - return CMD_NOT_MY_INSTANCE; - - ospf = ospf_lookup_instance(instance); - if (!ospf || !ospf->oi_running) - return CMD_SUCCESS; - - return (show_ip_ospf_database_common( - vty, ospf, idx ? 1 : 0, argc, argv, use_vrf, json, uj)); - } else if (argv_find(argv, argc, "vrf", &idx)) { - vrf_name = argv[++idx]->arg; - all_vrf = strmatch(vrf_name, "all"); - } - - if (vrf_name) { - use_vrf = 1; - if (all_vrf) { - for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { - if (!ospf->oi_running) - continue; - ret = (show_ip_ospf_database_common( - vty, ospf, idx ? 2 : 0, argc, argv, - use_vrf, json, uj)); - } - } else { - ospf = ospf_lookup_by_inst_name(inst, vrf_name); - if ((ospf == NULL) || !ospf->oi_running) { - vty_out(vty, "%% OSPF instance not found\n"); - return CMD_SUCCESS; - } - - ret = (show_ip_ospf_database_common( - vty, ospf, idx ? 2 : 0, argc, argv, use_vrf, - json, uj)); - } - } else { - /* Display default ospf (instance 0) info */ - ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); - if (ospf == NULL || !ospf->oi_running) { - vty_out(vty, "%% OSPF instance not found\n"); - return CMD_SUCCESS; - } - - ret = (show_ip_ospf_database_common(vty, ospf, 0, argc, argv, - use_vrf, json, uj)); - } - - if (uj) { - vty_out(vty, "%s\n", json_object_to_json_string(json)); - json_object_free(json); - } - - return ret; -} DEFUN (show_ip_ospf_instance_database_max, show_ip_ospf_instance_database_max_cmd, @@ -7238,6 +7269,20 @@ DEFUN (show_ip_ospf_instance_database_max, return CMD_SUCCESS; } +ALIAS (show_ip_ospf_instance_database_max, + show_ip_ospf_instance_database_cmd, + "show ip ospf (1-65535) database [<asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as> [A.B.C.D [<self-originate|adv-router A.B.C.D>]]] [json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Database summary\n" + OSPF_LSA_TYPES_DESC + "Link State ID (as an IP address)\n" + "Self-originated link states\n" + "Advertising Router link states\n" + "Advertising Router (as an IP address)\n" + JSON_STR) static int show_ip_ospf_database_type_adv_router_common(struct vty *vty, struct ospf *ospf, @@ -7327,14 +7372,14 @@ static int show_ip_ospf_database_type_adv_router_common(struct vty *vty, return CMD_SUCCESS; } -DEFUN (show_ip_ospf_instance_database_type_adv_router, - show_ip_ospf_instance_database_type_adv_router_cmd, - "show ip ospf [{(1-65535)|vrf NAME}] database <asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as> <adv-router A.B.C.D|self-originate> [json]", +DEFUN (show_ip_ospf_database_type_adv_router, + show_ip_ospf_database_type_adv_router_cmd, + "show ip ospf [vrf <NAME|all>] database <asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as> <adv-router A.B.C.D|self-originate> [json]", SHOW_STR IP_STR "OSPF information\n" - "Instance ID\n" VRF_CMD_HELP_STR + "All VRFs\n" "Database summary\n" OSPF_LSA_TYPES_DESC "Advertising Router link states\n" @@ -7343,13 +7388,12 @@ DEFUN (show_ip_ospf_instance_database_type_adv_router, JSON_STR) { struct ospf *ospf = NULL; - unsigned short instance = 0; struct listnode *node = NULL; char *vrf_name = NULL; bool all_vrf = false; int ret = CMD_SUCCESS; int inst = 0; - int idx = 0, idx_vrf = 0; + int idx_vrf = 0; uint8_t use_vrf = 0; bool uj = use_json(argc, argv); json_object *json = NULL; @@ -7357,19 +7401,6 @@ DEFUN (show_ip_ospf_instance_database_type_adv_router, if (uj) json = json_object_new_object(); - if (argv_find(argv, argc, "(1-65535)", &idx)) { - instance = strtoul(argv[idx]->arg, NULL, 10); - if (instance != ospf_instance) - return CMD_NOT_MY_INSTANCE; - - ospf = ospf_lookup_instance(instance); - if (!ospf || !ospf->oi_running) - return CMD_SUCCESS; - - return (show_ip_ospf_database_type_adv_router_common( - vty, ospf, idx ? 1 : 0, argc, argv, use_vrf, json, uj)); - } - OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); if (vrf_name) { @@ -7407,7 +7438,7 @@ DEFUN (show_ip_ospf_instance_database_type_adv_router, } ret = show_ip_ospf_database_type_adv_router_common( - vty, ospf, idx ? 1 : 0, argc, argv, use_vrf, json, uj); + vty, ospf, 0, argc, argv, use_vrf, json, uj); } if (uj) { @@ -7416,8 +7447,50 @@ DEFUN (show_ip_ospf_instance_database_type_adv_router, } return ret; - /*return (show_ip_ospf_database_type_adv_router_common( - vty, ospf, idx ? 1 : 0, argc, argv));*/ +} + +DEFUN (show_ip_ospf_instance_database_type_adv_router, + show_ip_ospf_instance_database_type_adv_router_cmd, + "show ip ospf (1-65535) database <asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as> <adv-router A.B.C.D|self-originate> [json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Database summary\n" + OSPF_LSA_TYPES_DESC + "Advertising Router link states\n" + "Advertising Router (as an IP address)\n" + "Self-originated link states\n" + JSON_STR) +{ + int idx_number = 3; + struct ospf *ospf; + unsigned short instance = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + show_ip_ospf_database_type_adv_router_common(vty, ospf, 1, argc, argv, + 0, json, uj); + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + + return CMD_SUCCESS; } DEFUN (ip_ospf_authentication_args, @@ -7792,10 +7865,9 @@ DEFUN (ip_ospf_message_digest_key, } key_id = strtol(keyid, NULL, 10); - if (ospf_crypt_key_lookup(params->auth_crypt, key_id) != NULL) { - vty_out(vty, "OSPF: Key %d already exists\n", key_id); - return CMD_WARNING; - } + + /* Remove existing key, if any */ + ospf_crypt_key_delete(params->auth_crypt, key_id); ck = ospf_crypt_key_new(); ck->key_id = (uint8_t)key_id; @@ -8319,6 +8391,7 @@ DEFUN (no_ip_ospf_hello_interval, continue; oi->type = IF_DEF_PARAMS(ifp)->type; + oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn; if (oi->state > ISM_Down) { OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); @@ -8346,20 +8419,21 @@ DEFUN_HIDDEN (no_ospf_hello_interval, return no_ip_ospf_hello_interval(self, vty, argc, argv); } -DEFUN (ip_ospf_network, - ip_ospf_network_cmd, - "ip ospf network <broadcast|non-broadcast|point-to-multipoint|point-to-point>", - "IP Information\n" - "OSPF interface commands\n" - "Network type\n" - "Specify OSPF broadcast multi-access network\n" - "Specify OSPF NBMA network\n" - "Specify OSPF point-to-multipoint network\n" - "Specify OSPF point-to-point network\n") +DEFUN(ip_ospf_network, ip_ospf_network_cmd, + "ip ospf network <broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn]>", + "IP Information\n" + "OSPF interface commands\n" + "Network type\n" + "Specify OSPF broadcast multi-access network\n" + "Specify OSPF NBMA network\n" + "Specify OSPF point-to-multipoint network\n" + "Specify OSPF point-to-point network\n" + "Specify OSPF point-to-point DMVPN network\n") { VTY_DECLVAR_CONTEXT(interface, ifp); int idx = 0; int old_type = IF_DEF_PARAMS(ifp)->type; + uint8_t old_ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn; struct route_node *rn; if (old_type == OSPF_IFTYPE_LOOPBACK) { @@ -8368,16 +8442,22 @@ DEFUN (ip_ospf_network, return CMD_WARNING_CONFIG_FAILED; } + IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0; + if (argv_find(argv, argc, "broadcast", &idx)) IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_BROADCAST; else if (argv_find(argv, argc, "non-broadcast", &idx)) IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_NBMA; else if (argv_find(argv, argc, "point-to-multipoint", &idx)) IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT; - else if (argv_find(argv, argc, "point-to-point", &idx)) + else if (argv_find(argv, argc, "point-to-point", &idx)) { IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOPOINT; + if (argv_find(argv, argc, "dmvpn", &idx)) + IF_DEF_PARAMS(ifp)->ptp_dmvpn = 1; + } - if (IF_DEF_PARAMS(ifp)->type == old_type) + if (IF_DEF_PARAMS(ifp)->type == old_type + && IF_DEF_PARAMS(ifp)->ptp_dmvpn == old_ptp_dmvpn) return CMD_SUCCESS; SET_IF_PARAM(IF_DEF_PARAMS(ifp), type); @@ -8429,6 +8509,7 @@ DEFUN (no_ip_ospf_network, struct route_node *rn; IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp); + IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0; if (IF_DEF_PARAMS(ifp)->type == old_type) return CMD_SUCCESS; @@ -8850,10 +8931,8 @@ DEFUN (ip_ospf_area, if (count > 0) { ospf = ospf_lookup_by_vrf_id(ifp->vrf_id); - if (ospf) { + if (ospf) ospf_interface_area_unset(ospf, ifp); - ospf->if_ospf_cli_count -= count; - } } return CMD_NOT_MY_INSTANCE; @@ -8897,9 +8976,12 @@ DEFUN (ip_ospf_area, // update/create address-level params params = ospf_get_if_params((ifp), (addr)); if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) { - vty_out(vty, - "Must remove previous area/address config before changing ospf area\n"); - return CMD_WARNING_CONFIG_FAILED; + if (!IPV4_ADDR_SAME(¶ms->if_area, &area_id)) { + vty_out(vty, + "Must remove previous area/address config before changing ospf area\n"); + return CMD_WARNING_CONFIG_FAILED; + } else + return CMD_SUCCESS; } ospf_if_update_params((ifp), (addr)); } @@ -8911,10 +8993,8 @@ DEFUN (ip_ospf_area, params->if_area_id_fmt = format; } - if (ospf) { + if (ospf) ospf_interface_area_set(ospf, ifp); - ospf->if_ospf_cli_count++; - } return CMD_SUCCESS; } @@ -8980,7 +9060,6 @@ DEFUN (no_ip_ospf_area, if (ospf) { ospf_interface_area_unset(ospf, ifp); - ospf->if_ospf_cli_count--; ospf_area_check_free(ospf, area_id); } @@ -11573,6 +11652,10 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) vty_out(vty, " ip ospf network %s", ospf_int_type_str [params->type]); + if (params->type + == OSPF_IFTYPE_POINTOPOINT + && params->ptp_dmvpn) + vty_out(vty, " dmvpn"); if (params != IF_DEF_PARAMS(ifp) && rn) vty_out(vty, " %pI4", &rn->p.u.prefix4); @@ -11640,6 +11723,7 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) /* Router Dead Interval print. */ if (OSPF_IF_PARAM_CONFIGURED(params, v_wait) + && params->is_v_wait_set && params->v_wait != OSPF_ROUTER_DEAD_INTERVAL_DEFAULT) { vty_out(vty, " ip ospf dead-interval "); @@ -11716,7 +11800,7 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) } /* bfd print. */ - if (params && params->bfd_info) + if (params && params->bfd_config) ospf_bfd_write_config(vty, params); /* MTU ignore print. */ @@ -11852,6 +11936,10 @@ static int config_write_ospf_area(struct vty *vty, struct ospf *ospf) vty_out(vty, " area %s nssa no-summary\n", buf); + if (area->suppress_fa) + vty_out(vty, + " area %s nssa suppress-fa\n", + buf); } if (area->default_cost != 1) @@ -12250,6 +12338,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) vty_out(vty, " ospf write-multiplier %d\n", ospf->write_oi_count); + if (ospf->max_multipath != MULTIPATH_NUM) + vty_out(vty, " maximum-paths %d\n", ospf->max_multipath); + /* Max-metric router-lsa print */ config_write_stub_router(vty, ospf); @@ -12375,8 +12466,10 @@ void ospf_vty_show_init(void) install_element(VIEW_NODE, &show_ip_ospf_instance_cmd); /* "show ip ospf database" commands. */ + install_element(VIEW_NODE, &show_ip_ospf_database_cmd); install_element(VIEW_NODE, &show_ip_ospf_database_max_cmd); - + install_element(VIEW_NODE, + &show_ip_ospf_database_type_adv_router_cmd); install_element(VIEW_NODE, &show_ip_ospf_instance_database_type_adv_router_cmd); install_element(VIEW_NODE, &show_ip_ospf_instance_database_cmd); @@ -12711,6 +12804,8 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &ospf_area_nssa_translate_cmd); install_element(OSPF_NODE, &ospf_area_nssa_no_summary_cmd); install_element(OSPF_NODE, &no_ospf_area_nssa_no_summary_cmd); + install_element(OSPF_NODE, &ospf_area_nssa_suppress_fa_cmd); + install_element(OSPF_NODE, &no_ospf_area_nssa_suppress_fa_cmd); install_element(OSPF_NODE, &no_ospf_area_nssa_cmd); install_element(OSPF_NODE, &ospf_area_default_cost_cmd); @@ -12774,6 +12869,10 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &ospf_ti_lfa_cmd); install_element(OSPF_NODE, &no_ospf_ti_lfa_cmd); + /* Max path configurations */ + install_element(OSPF_NODE, &ospf_max_multipath_cmd); + install_element(OSPF_NODE, &no_ospf_max_multipath_cmd); + /* Init interface related vty commands. */ ospf_vty_if_init(); diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 56b2f8d660..a2ce4d1ce7 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -297,7 +297,7 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p, } for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) { - if (api.nexthop_num >= MULTIPATH_NUM) + if (api.nexthop_num >= ospf->max_multipath) break; ospf_zebra_add_nexthop(ospf, path, &api); @@ -2043,6 +2043,7 @@ static int ospf_opaque_msg_handler(ZAPI_CALLBACK_ARGS) struct zapi_opaque_msg info; struct ldp_igp_sync_if_state state; struct ldp_igp_sync_announce announce; + struct zapi_opaque_reg_info dst; int ret = 0; s = zclient->ibuf; @@ -2051,6 +2052,13 @@ static int ospf_opaque_msg_handler(ZAPI_CALLBACK_ARGS) return -1; switch (info.type) { + case LINK_STATE_SYNC: + STREAM_GETC(s, dst.proto); + STREAM_GETW(s, dst.instance); + STREAM_GETL(s, dst.session_id); + dst.type = LINK_STATE_SYNC; + ret = ospf_te_sync_ted(dst); + break; case LDP_IGP_SYNC_IF_STATE_UPDATE: STREAM_GET(&state, s, sizeof(state)); ret = ospf_ldp_sync_state_update(state); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 1a1861fc58..9949a78336 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -42,6 +42,7 @@ #include "ldp_sync.h" #include "ospfd/ospfd.h" +#include "ospfd/ospf_bfd.h" #include "ospfd/ospf_network.h" #include "ospfd/ospf_interface.h" #include "ospfd/ospf_ism.h" @@ -366,6 +367,9 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name) new->maxage_lsa = route_table_init(); new->t_maxage_walker = NULL; + /* Max paths initialization */ + new->max_multipath = MULTIPATH_NUM; + /* Distance table init. */ new->distance_table = route_table_init(); @@ -475,41 +479,11 @@ struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name) static void ospf_init(struct ospf *ospf) { - struct vrf *vrf; - struct interface *ifp; - ospf_opaque_type11_lsa_init(ospf); if (ospf->vrf_id != VRF_UNKNOWN) ospf->oi_running = 1; - /* Activate 'ip ospf area x' configured interfaces for given - * vrf. Activate area on vrf x aware interfaces. - * vrf_enable callback calls router_id_update which - * internally will call ospf_if_update to trigger - * network_run_state - */ - vrf = vrf_lookup_by_id(ospf->vrf_id); - - FOR_ALL_INTERFACES (vrf, ifp) { - struct ospf_if_params *params; - struct route_node *rn; - uint32_t count = 0; - - params = IF_DEF_PARAMS(ifp); - if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) - if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area)) - count++; - - if (count > 0) { - ospf_interface_area_set(ospf, ifp); - ospf->if_ospf_cli_count += count; - } - } - ospf_router_id_update(ospf); } @@ -553,6 +527,23 @@ struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id) return (vrf->info) ? (struct ospf *)vrf->info : NULL; } +uint32_t ospf_count_area_params(struct ospf *ospf) +{ + struct vrf *vrf; + struct interface *ifp; + uint32_t count = 0; + + if (ospf->vrf_id != VRF_UNKNOWN) { + vrf = vrf_lookup_by_id(ospf->vrf_id); + + FOR_ALL_INTERFACES (vrf, ifp) { + count += ospf_if_count_area_params(ifp); + } + } + + return count; +} + /* It should only be used when processing incoming info update from zebra. * Other situations, it is not sufficient to lookup the ospf instance by * vrf_name only without using the instance number. @@ -899,6 +890,7 @@ static void ospf_finish_final(struct ospf *ospf) close(ospf->fd); stream_free(ospf->ibuf); ospf->fd = -1; + ospf->max_multipath = MULTIPATH_NUM; ospf_delete(ospf); if (ospf->name) { @@ -1084,6 +1076,7 @@ struct ospf_interface *add_ospf_interface(struct connected *co, /* If network type is specified previously, skip network type setting. */ oi->type = IF_DEF_PARAMS(co->ifp)->type; + oi->ptp_dmvpn = IF_DEF_PARAMS(co->ifp)->ptp_dmvpn; /* Add pseudo neighbor. */ ospf_nbr_self_reset(oi, oi->ospf->router_id); @@ -1671,6 +1664,7 @@ int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id) /* set NSSA area defaults */ area->no_summary = 0; + area->suppress_fa = 0; area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; area->NSSATranslatorStabilityInterval = @@ -1692,6 +1686,7 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc) ospf->anyNSSA--; /* set NSSA area defaults */ area->no_summary = 0; + area->suppress_fa = 0; area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; area->NSSATranslatorStabilityInterval = @@ -1707,6 +1702,32 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc) return 1; } +int ospf_area_nssa_suppress_fa_set(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return 0; + + area->suppress_fa = 1; + + return 1; +} + +int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return 0; + + area->suppress_fa = 0; + + return 1; +} + int ospf_area_nssa_translator_role_set(struct ospf *ospf, struct in_addr area_id, int role) { @@ -1931,6 +1952,9 @@ static void ospf_nbr_nbma_add(struct ospf_nbr_nbma *nbr_nbma, nbr_nbma->nbr = nbr; + /* Configure BFD if interface has it. */ + ospf_neighbor_bfd_apply(nbr); + OSPF_NSM_EVENT_EXECUTE(nbr, NSM_Start); } } diff --git a/ospfd/ospfd.conf.sample b/ospfd/ospfd.conf.sample deleted file mode 100644 index 0e8ac67bba..0000000000 --- a/ospfd/ospfd.conf.sample +++ /dev/null @@ -1,13 +0,0 @@ -! -*- ospf -*- -! -! OSPFd sample configuration file -! -! -hostname ospfd -password zebra -!enable password please-set-at-here -! -!router ospf -! network 192.168.1.0/24 area 0 -! -log stdout diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 5d64ee9ba2..2093eb2e42 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -310,11 +310,6 @@ struct ospf { /* Statistics for LSA used for new instantiation. */ uint32_t rx_lsa_count; - /* Counter of "ip ospf area x.x.x.x" used - * for mutual exclusion of network command under - * router ospf or ip ospf area x under interface. */ - uint32_t if_ospf_cli_count; - struct route_table *distance_table; /* Used during ospf instance going down send LSDB @@ -379,6 +374,11 @@ struct ospf { */ int aggr_action; + /* Max number of multiple paths + * to support ECMP. + */ + uint16_t max_multipath; + /* MPLS LDP-IGP Sync */ struct ldp_sync_info_cmd ldp_sync_cmd; @@ -476,7 +476,7 @@ struct ospf_area { int shortcut_capability; /* Other ABRs agree on S-bit */ uint32_t default_cost; /* StubDefaultCost. */ int auth_type; /* Authentication type. */ - + int suppress_fa; /* Suppress forwarding address in NSSA ABR */ uint8_t NSSATranslatorRole; /* NSSA configured role */ #define OSPF_NSSA_ROLE_NEVER 0 @@ -650,6 +650,7 @@ extern struct ospf *ospf_new_alloc(unsigned short instance, const char *name); extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name); extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id); +extern uint32_t ospf_count_area_params(struct ospf *ospf); extern void ospf_finish(struct ospf *); extern void ospf_process_refresh_data(struct ospf *ospf, bool reset); extern void ospf_router_id_update(struct ospf *ospf); @@ -668,6 +669,10 @@ extern int ospf_area_no_summary_set(struct ospf *, struct in_addr); extern int ospf_area_no_summary_unset(struct ospf *, struct in_addr); extern int ospf_area_nssa_set(struct ospf *, struct in_addr); extern int ospf_area_nssa_unset(struct ospf *, struct in_addr, int); +extern int ospf_area_nssa_suppress_fa_set(struct ospf *ospf, + struct in_addr area_id); +extern int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf, + struct in_addr area_id); extern int ospf_area_nssa_translator_role_set(struct ospf *, struct in_addr, int); extern int ospf_area_export_list_set(struct ospf *, struct ospf_area *, @@ -727,4 +732,5 @@ extern int p_spaces_compare_func(const struct p_space *a, const struct p_space *b); extern int q_spaces_compare_func(const struct q_space *a, const struct q_space *b); + #endif /* _ZEBRA_OSPFD_H */ diff --git a/ospfd/subdir.am b/ospfd/subdir.am index 25ddef358f..f592a9eec8 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -5,7 +5,6 @@ if OSPFD noinst_LIBRARIES += ospfd/libfrrospf.a sbin_PROGRAMS += ospfd/ospfd -dist_examples_DATA += ospfd/ospfd.conf.sample vtysh_scan += \ ospfd/ospf_bfd.c \ ospfd/ospf_dump.c \ @@ -51,6 +50,8 @@ ospfd_libfrrospf_a_SOURCES = \ ospfd/ospf_ri.c \ ospfd/ospf_route.c \ ospfd/ospf_routemap.c \ + ospfd/ospf_routemap_nb.c \ + ospfd/ospf_routemap_nb_config.c \ ospfd/ospf_spf.c \ ospfd/ospf_ti_lfa.c \ ospfd/ospf_sr.c \ @@ -100,6 +101,7 @@ noinst_HEADERS += \ ospfd/ospf_packet.h \ ospfd/ospf_ri.h \ ospfd/ospf_route.h \ + ospfd/ospf_routemap_nb.h \ ospfd/ospf_spf.h \ ospfd/ospf_ti_lfa.h \ ospfd/ospf_sr.h \ @@ -120,3 +122,7 @@ ospfd_ospfd_snmp_la_LIBADD = lib/libfrrsnmp.la EXTRA_DIST += \ ospfd/ChangeLog.opaque.txt \ # end + +nodist_ospfd_ospfd_SOURCES = \ + yang/frr-ospf-route-map.yang.c \ + # end diff --git a/pathd/path_cli.c b/pathd/path_cli.c index a46ec5fac4..cf14aa8c61 100644 --- a/pathd/path_cli.c +++ b/pathd/path_cli.c @@ -20,6 +20,7 @@ #include <math.h> #include <zebra.h> +#include "memory.h" #include "log.h" #include "command.h" #include "mpls.h" @@ -28,7 +29,6 @@ #include "pathd/pathd.h" #include "pathd/path_nb.h" -#include "pathd/path_memory.h" #ifndef VTYSH_EXTRACT_PL #include "pathd/path_cli_clippy.c" #endif diff --git a/pathd/path_memory.h b/pathd/path_memory.h deleted file mode 100644 index a0896cdad8..0000000000 --- a/pathd/path_memory.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2020 NetDEF, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _FRR_PATH_MEMORY_H_ -#define _FRR_PATH_MEMORY_H_ - -#include "memory.h" - -DECLARE_MGROUP(PATHD); - -#endif /* _FRR_PATH_MEMORY_H_ */ diff --git a/pathd/path_nb_config.c b/pathd/path_nb_config.c index 669db169ae..af54f5bce2 100644 --- a/pathd/path_nb_config.c +++ b/pathd/path_nb_config.c @@ -302,7 +302,6 @@ int pathd_srte_policy_binding_sid_modify(struct nb_cb_modify_args *args) struct srte_policy *policy; mpls_label_t binding_sid; - policy = nb_running_get_entry(args->dnode, NULL, true); binding_sid = yang_dnode_get_uint32(args->dnode, NULL); switch (args->event) { @@ -315,6 +314,7 @@ int pathd_srte_policy_binding_sid_modify(struct nb_cb_modify_args *args) case NB_EV_ABORT: break; case NB_EV_APPLY: + policy = nb_running_get_entry(args->dnode, NULL, true); srte_policy_update_binding_sid(policy, binding_sid); SET_FLAG(policy->flags, F_POLICY_MODIFIED); break; @@ -668,12 +668,12 @@ int pathd_srte_policy_candidate_path_segment_list_name_modify( struct srte_candidate *candidate; const char *segment_list_name; - candidate = nb_running_get_entry(args->dnode, NULL, true); - segment_list_name = yang_dnode_get_string(args->dnode, NULL); - if (args->event != NB_EV_APPLY) return NB_OK; + candidate = nb_running_get_entry(args->dnode, NULL, true); + segment_list_name = yang_dnode_get_string(args->dnode, NULL); + candidate->segment_list = srte_segment_list_find(segment_list_name); candidate->lsp->segment_list = candidate->segment_list; assert(candidate->segment_list); diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c index 5dcba965a2..d6cd48ecdb 100644 --- a/pathd/path_pcep.c +++ b/pathd/path_pcep.c @@ -19,6 +19,7 @@ #include <zebra.h> #include "pceplib/pcep_utils_counters.h" +#include "memory.h" #include "log.h" #include "command.h" #include "libfrr.h" @@ -31,13 +32,13 @@ #include "pathd/pathd.h" #include "pathd/path_errors.h" -#include "pathd/path_pcep_memory.h" #include "pathd/path_pcep.h" #include "pathd/path_pcep_cli.h" #include "pathd/path_pcep_controller.h" #include "pathd/path_pcep_lib.h" #include "pathd/path_pcep_config.h" +DEFINE_MTYPE(PATHD, PCEP, "PCEP module"); /* * Globals. diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h index b131b31445..d0af674ff9 100644 --- a/pathd/path_pcep.h +++ b/pathd/path_pcep.h @@ -22,11 +22,13 @@ #include <stdbool.h> #include <debug.h> #include <netinet/tcp.h> +#include "memory.h" #include "pceplib/pcep_utils_logging.h" #include "pceplib/pcep_pcc_api.h" #include "mpls.h" #include "pathd/pathd.h" -#include "pathd/path_pcep_memory.h" + +DECLARE_MTYPE(PCEP); #define PCEP_DEFAULT_PORT 4189 #define MAX_PCC 32 @@ -82,7 +84,7 @@ struct pcep_config_group_opts { char name[64]; - char tcp_md5_auth[TCP_MD5SIG_MAXKEYLEN]; + char tcp_md5_auth[PCEP_MD5SIG_MAXKEYLEN]; struct ipaddr source_ip; short source_port; bool draft07; diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c index e2b33b8ff2..14404b1d08 100644 --- a/pathd/path_pcep_cli.c +++ b/pathd/path_pcep_cli.c @@ -33,7 +33,6 @@ #include "pathd/pathd.h" #include "pathd/path_errors.h" -#include "pathd/path_pcep_memory.h" #include "pathd/path_pcep.h" #include "pathd/path_pcep_cli.h" #include "pathd/path_pcep_controller.h" diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c index 1d2f25889e..e9d699de47 100644 --- a/pathd/path_pcep_lib.c +++ b/pathd/path_pcep_lib.c @@ -18,15 +18,18 @@ #include <zebra.h> +#include "memory.h" + #include <debug.h> #include "pceplib/pcep_utils_counters.h" #include "pceplib/pcep_timers.h" #include "pathd/path_errors.h" -#include "pathd/path_memory.h" #include "pathd/path_pcep.h" #include "pathd/path_pcep_lib.h" #include "pathd/path_pcep_debug.h" -#include "pathd/path_pcep_memory.h" + +DEFINE_MTYPE_STATIC(PATHD, PCEPLIB_INFRA, "PCEPlib Infrastructure"); +DEFINE_MTYPE_STATIC(PATHD, PCEPLIB_MESSAGES, "PCEPlib PCEP Messages"); #define CLASS_TYPE(CLASS, TYPE) (((CLASS) << 16) | (TYPE)) #define DEFAULT_LSAP_SETUP_PRIO 4 diff --git a/pathd/path_pcep_memory.c b/pathd/path_pcep_memory.c deleted file mode 100644 index 5cb5fb33ec..0000000000 --- a/pathd/path_pcep_memory.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2020 NetDEF, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <zebra.h> - -#include <memory.h> - -#include "pathd/path_pcep_memory.h" - -DEFINE_MTYPE(PATHD, PCEP, "PCEP module"); -DEFINE_MTYPE(PATHD, PCEPLIB_INFRA, "PCEPlib Infrastructure"); -DEFINE_MTYPE(PATHD, PCEPLIB_MESSAGES, "PCEPlib PCEP Messages"); diff --git a/pathd/path_pcep_memory.h b/pathd/path_pcep_memory.h deleted file mode 100644 index a44d178b2b..0000000000 --- a/pathd/path_pcep_memory.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2020 NetDEF, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _FRR_PATH_PCEP_MEMORY_H_ -#define _FRR_PATH_PCEP_MEMORY_H_ - -#include "pathd/path_memory.h" - -DECLARE_MTYPE(PCEP); -DECLARE_MTYPE(PCEPLIB_INFRA); -DECLARE_MTYPE(PCEPLIB_MESSAGES); - -#endif /* _FRR_PATH_PCEP_MEMORY_H_ */ diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c index 986aa3c456..a2c1e7cd4c 100644 --- a/pathd/path_pcep_pcc.c +++ b/pathd/path_pcep_pcc.c @@ -41,7 +41,6 @@ #include "pathd/pathd.h" #include "pathd/path_zebra.h" #include "pathd/path_errors.h" -#include "pathd/path_pcep_memory.h" #include "pathd/path_pcep.h" #include "pathd/path_pcep_controller.h" #include "pathd/path_pcep_lib.h" diff --git a/pathd/pathd.c b/pathd/pathd.c index 4893da880a..ae82186315 100644 --- a/pathd/pathd.c +++ b/pathd/pathd.c @@ -24,12 +24,13 @@ #include "network.h" #include "pathd/pathd.h" -#include "pathd/path_memory.h" #include "pathd/path_zebra.h" #include "pathd/path_debug.h" #define HOOK_DELAY 3 +DEFINE_MGROUP(PATHD, "pathd"); + DEFINE_MTYPE_STATIC(PATHD, PATH_SEGMENT_LIST, "Segment List"); DEFINE_MTYPE_STATIC(PATHD, PATH_SR_POLICY, "SR Policy"); DEFINE_MTYPE_STATIC(PATHD, PATH_SR_CANDIDATE, "SR Policy candidate path"); diff --git a/pathd/pathd.conf.sample b/pathd/pathd.conf.sample deleted file mode 100644 index 9fe7d2d6e3..0000000000 --- a/pathd/pathd.conf.sample +++ /dev/null @@ -1,41 +0,0 @@ -! Default pathd configuration sample -! -password frr -log stdout - -segment-routing - traffic-eng - segment-list test1 - index 10 mpls label 123 - index 20 mpls label 456 - ! - segment-list test2 - index 10 mpls label 321 - index 20 mpls label 654 - ! - policy color 1 endpoint 1.1.1.1 - name one - binding-sid 100 - candidate-path preference 100 name test1 explicit segment-list test1 - candidate-path preference 200 name test2 explicit segment-list test2 - ! - policy color 2 endpoint 2.2.2.2 - name two - binding-sid 101 - candidate-path preference 100 name def explicit segment-list test2 - candidate-path preference 200 name dyn dynamic - bandwidth 12345 - metric bound abc 16 required - metric te 10 - ! - ! - pcep - pcc-peer PCE1 - address ip 127.0.0.1 - sr-draft07 - ! - pcc - peer PCE1 - ! - ! -! diff --git a/pathd/pathd.h b/pathd/pathd.h index e3d26a0ac5..9c4d256cef 100644 --- a/pathd/pathd.h +++ b/pathd/pathd.h @@ -19,11 +19,14 @@ #ifndef _FRR_PATHD_H_ #define _FRR_PATHD_H_ +#include "lib/memory.h" #include "lib/mpls.h" #include "lib/ipaddr.h" #include "lib/srte.h" #include "lib/hook.h" +DECLARE_MGROUP(PATHD); + enum srte_protocol_origin { SRTE_ORIGIN_UNDEFINED = 0, SRTE_ORIGIN_PCEP = 1, diff --git a/pathd/subdir.am b/pathd/subdir.am index 452d824669..38df326489 100644 --- a/pathd/subdir.am +++ b/pathd/subdir.am @@ -5,7 +5,6 @@ if PATHD noinst_LIBRARIES += pathd/libpath.a sbin_PROGRAMS += pathd/pathd -dist_examples_DATA += pathd/pathd.conf.sample vtysh_scan += $(top_srcdir)/pathd/path_cli.c vtysh_daemons += pathd # TODO add man page @@ -22,8 +21,6 @@ pathd_libpath_a_SOURCES = \ pathd/path_cli.c \ pathd/path_debug.c \ pathd/path_errors.c \ - pathd/path_main.c \ - pathd/path_memory.c \ pathd/path_nb.c \ pathd/path_nb_config.c \ pathd/path_nb_state.c \ @@ -39,21 +36,21 @@ clippy_scan += \ noinst_HEADERS += \ pathd/path_debug.h \ pathd/path_errors.h \ - pathd/path_memory.h \ pathd/path_nb.h \ pathd/path_pcep.h \ pathd/path_pcep_cli.h \ pathd/path_pcep_controller.h \ pathd/path_pcep_debug.h \ pathd/path_pcep_lib.h \ - pathd/path_pcep_memory.h \ pathd/path_pcep_config.h \ pathd/path_pcep_pcc.h \ pathd/path_zebra.h \ pathd/pathd.h \ # end -pathd_pathd_SOURCES = pathd/path_main.c +pathd_pathd_SOURCES = \ + pathd/path_main.c \ + # end nodist_pathd_pathd_SOURCES = \ yang/frr-pathd.yang.c \ # end @@ -65,7 +62,6 @@ pathd_pathd_pcep_la_SOURCES = \ pathd/path_pcep_controller.c \ pathd/path_pcep_debug.c \ pathd/path_pcep_lib.c \ - pathd/path_pcep_memory.c \ pathd/path_pcep_config.c \ pathd/path_pcep_pcc.c \ # end diff --git a/pbrd/pbrd.conf.sample b/pbrd/pbrd.conf.sample deleted file mode 100644 index c9e7dce01f..0000000000 --- a/pbrd/pbrd.conf.sample +++ /dev/null @@ -1,19 +0,0 @@ -! Sample pbrd configuration file -! -! A quick example of what a pbr configuration might look like -! -! -log stdout -! -! nexthop-group TEST -! nexthop 4.5.6.7 -! nexthop 5.6.7.8 -! ! -! pbr-map BLUE seq 100 -! match dst-ip 9.9.9.0/24 -! match src-ip 10.10.10.0/24 -! set nexthop-group TEST -! ! -! int swp1 -! pbr-policy BLUE -! diff --git a/pbrd/subdir.am b/pbrd/subdir.am index 7ad071af3b..bbe3f2ab71 100644 --- a/pbrd/subdir.am +++ b/pbrd/subdir.am @@ -5,7 +5,6 @@ if PBRD noinst_LIBRARIES += pbrd/libpbr.a sbin_PROGRAMS += pbrd/pbrd -dist_examples_DATA += pbrd/pbrd.conf.sample vtysh_scan += \ pbrd/pbr_vty.c \ pbrd/pbr_debug.c \ diff --git a/pceplib/.gitignore b/pceplib/.gitignore index 5861f25a41..a82fe5a867 100644 --- a/pceplib/.gitignore +++ b/pceplib/.gitignore @@ -1,10 +1,22 @@ pcep_pcc test/pcep_msg_tests +test/pcep_msg_tests.log +test/pcep_msg_tests.trs test/pcep_pcc_api_tests +test/pcep_pcc_api_tests.log +test/pcep_pcc_api_tests.trs test/pcep_session_logic_tests +test/pcep_session_logic_tests.log +test/pcep_session_logic_tests.trs test/pcep_socket_comm_tests +test/pcep_socket_comm_tests.log +test/pcep_socket_comm_tests.trs test/pcep_timers_tests +test/pcep_timers_tests.log +test/pcep_timers_tests.trs test/pcep_utils_tests +test/pcep_utils_tests.log +test/pcep_utils_tests.trs test/valgrind.pcep_msg_tests.log test/valgrind.pcep_pcc_api_tests.log test/valgrind.pcep_session_logic_tests.log diff --git a/pceplib/pcep.h b/pceplib/pcep.h index 278ab9d5dc..b5d02c7e62 100644 --- a/pceplib/pcep.h +++ b/pceplib/pcep.h @@ -28,12 +28,10 @@ #endif #if defined(linux) || defined(GNU_LINUX) -//#include <netinet/in.h> + #define ipv6_u __in6_u #else -// bsd family -#define TCP_MD5SIG_MAXKEYLEN 80 -//#include <netinet/in.h> +/* bsd family */ #define ipv6_u __u6_addr #ifdef __FreeBSD__ #include <sys/endian.h> @@ -45,4 +43,12 @@ #include <sys/socket.h> #include <netinet/in.h> #include <pthread.h> + +/* Cross-compilation seems to have trouble finding this */ +#if defined(TCP_MD5SIG_MAXKEYLEN) +#define PCEP_MD5SIG_MAXKEYLEN TCP_MD5SIG_MAXKEYLEN +#else +#define PCEP_MD5SIG_MAXKEYLEN 80 +#endif + #endif diff --git a/pceplib/pcep_msg_tlvs_encoding.c b/pceplib/pcep_msg_tlvs_encoding.c index 967f138143..37f3353f76 100644 --- a/pceplib/pcep_msg_tlvs_encoding.c +++ b/pceplib/pcep_msg_tlvs_encoding.c @@ -25,6 +25,15 @@ * Encoding and decoding for PCEP Object TLVs. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __FreeBSD__ +#include <sys/endian.h> +#else +#include <endian.h> +#endif /* __FreeBSD__ */ #include <stdlib.h> #include <string.h> diff --git a/pceplib/pcep_pcc.c b/pceplib/pcep_pcc.c index 1a702a8b63..d263f64f39 100644 --- a/pceplib/pcep_pcc.c +++ b/pceplib/pcep_pcc.c @@ -53,7 +53,7 @@ struct cmd_line_args { char dest_ip_str[MAX_DST_IP_STR]; short src_tcp_port; short dest_tcp_port; - char tcp_md5_str[TCP_MD5SIG_MAXKEYLEN]; /* RFC 2385 */ + char tcp_md5_str[PCEP_MD5SIG_MAXKEYLEN]; /* RFC 2385 */ bool is_ipv6; bool eventpoll; /* poll for pcep_event's, or use callback (default) */ }; diff --git a/pceplib/pcep_session_logic.h b/pceplib/pcep_session_logic.h index a082ec66da..9cdec5250f 100644 --- a/pceplib/pcep_session_logic.h +++ b/pceplib/pcep_session_logic.h @@ -122,7 +122,7 @@ typedef struct pcep_configuration_ { struct pcep_versioning *pcep_msg_versioning; - char tcp_authentication_str[TCP_MD5SIG_MAXKEYLEN]; + char tcp_authentication_str[PCEP_MD5SIG_MAXKEYLEN]; bool is_tcp_auth_md5; /* true: RFC 2385, false: RFC 5925 */ } pcep_configuration; diff --git a/pceplib/pcep_socket_comm.h b/pceplib/pcep_socket_comm.h index 797ffda860..89d2492914 100644 --- a/pceplib/pcep_socket_comm.h +++ b/pceplib/pcep_socket_comm.h @@ -91,9 +91,9 @@ typedef struct pcep_socket_comm_session_ { int received_bytes; bool close_after_write; void *external_socket_data; /* used for external socket infra */ - char tcp_authentication_str[TCP_MD5SIG_MAXKEYLEN - + 1]; /* should be used with is_tcp_auth_md5 - flag */ + /* should be used with is_tcp_auth_md5 flag */ + char tcp_authentication_str[PCEP_MD5SIG_MAXKEYLEN + 1]; + bool is_tcp_auth_md5; /* flag to distinguish between rfc 2385 (md5) and rfc 5925 (tcp-ao) */ diff --git a/pceplib/pcep_timers.c b/pceplib/pcep_timers.c index e9d9d4b21d..d0a2349d05 100644 --- a/pceplib/pcep_timers.c +++ b/pceplib/pcep_timers.c @@ -457,10 +457,17 @@ void pceplib_external_timer_expire_handler(void *data) } pcep_timer *timer = (pcep_timer *)data; + pthread_mutex_lock(&timers_context_->timer_list_lock); ordered_list_node *timer_node = ordered_list_find2(timers_context_->timer_list, timer, timer_list_node_timer_ptr_compare); + + /* Remove timer from list */ + if (timer_node) + ordered_list_remove_node2(timers_context_->timer_list, + timer_node); + pthread_mutex_unlock(&timers_context_->timer_list_lock); /* Cannot continue if the timer does not exist */ @@ -474,9 +481,5 @@ void pceplib_external_timer_expire_handler(void *data) timers_context_->expire_handler(timer->data, timer->timer_id); - pthread_mutex_lock(&timers_context_->timer_list_lock); - ordered_list_remove_node2(timers_context_->timer_list, timer_node); - pthread_mutex_unlock(&timers_context_->timer_list_lock); - pceplib_free(PCEPLIB_INFRA, timer); } diff --git a/pceplib/test/pcep_msg_tlvs_test.c b/pceplib/test/pcep_msg_tlvs_test.c index 6b650f6823..57e1d16e91 100644 --- a/pceplib/test/pcep_msg_tlvs_test.c +++ b/pceplib/test/pcep_msg_tlvs_test.c @@ -21,6 +21,15 @@ */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __FreeBSD__ +#include <sys/endian.h> +#else +#include <endian.h> +#endif /* __FreeBSD__ */ #include <stdlib.h> #include <CUnit/CUnit.h> diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index ae5b7940e9..4bbe7d35f0 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1094,6 +1094,8 @@ static void pim_show_interfaces_single(struct pim_instance *pim, json_object_int_add(json_row, "helloPeriod", pim_ifp->pim_hello_period); + json_object_int_add(json_row, "holdTime", + PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); json_object_string_add(json_row, "helloTimer", hello_timer); json_object_string_add(json_row, "helloStatStart", @@ -1243,6 +1245,8 @@ static void pim_show_interfaces_single(struct pim_instance *pim, vty_out(vty, "------\n"); vty_out(vty, "Period : %d\n", pim_ifp->pim_hello_period); + vty_out(vty, "HoldTime : %d\n", + PIM_IF_DEFAULT_HOLDTIME(pim_ifp)); vty_out(vty, "Timer : %s\n", hello_timer); vty_out(vty, "StatStart : %s\n", stat_uptime); vty_out(vty, "Receive : %d\n", @@ -8987,7 +8991,7 @@ DEFUN (interface_ip_pim_hello, DEFUN (interface_no_ip_pim_hello, interface_no_ip_pim_hello_cmd, - "no ip pim hello [(1-180) (1-180)]", + "no ip pim hello [(1-180) [(1-180)]]", NO_STR IP_STR PIM_STR diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index a7d7551cbd..475e393cf0 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -1830,6 +1830,7 @@ int lib_interface_pim_hello_interval_modify(struct nb_cb_modify_args *args) pim_ifp = ifp->info; pim_ifp->pim_hello_period = yang_dnode_get_uint8(args->dnode, NULL); + pim_ifp->pim_default_holdtime = -1; break; } @@ -2394,13 +2395,6 @@ int lib_interface_pim_address_family_mroute_oif_modify( struct ipaddr group_addr; const struct lyd_node *if_dnode; - iif = nb_running_get_entry(args->dnode, NULL, true); - pim_iifp = iif->info; - pim = pim_iifp->pim; - - oifname = yang_dnode_get_string(args->dnode, NULL); - oif = if_lookup_by_name(oifname, pim->vrf_id); - switch (args->event) { case NB_EV_VALIDATE: if_dnode = yang_dnode_get_parent(args->dnode, "interface"); @@ -2411,6 +2405,17 @@ int lib_interface_pim_address_family_mroute_oif_modify( } #ifdef PIM_ENFORCE_LOOPFREE_MFC + iif = nb_running_get_entry(args->dnode, NULL, false); + if (!iif) { + return NB_OK; + } + + pim_iifp = iif->info; + pim = pim_iifp->pim; + + oifname = yang_dnode_get_string(args->dnode, NULL); + oif = if_lookup_by_name(oifname, pim->vrf_id); + if (oif && (iif->ifindex == oif->ifindex)) { strlcpy(args->errmsg, "% IIF same as OIF and loopfree enforcement is enabled; rejecting", @@ -2423,6 +2428,12 @@ int lib_interface_pim_address_family_mroute_oif_modify( case NB_EV_ABORT: break; case NB_EV_APPLY: + iif = nb_running_get_entry(args->dnode, NULL, true); + pim_iifp = iif->info; + pim = pim_iifp->pim; + + oifname = yang_dnode_get_string(args->dnode, NULL); + oif = if_lookup_by_name(oifname, pim->vrf_id); if (!oif) { snprintf(args->errmsg, args->errmsg_len, "No such interface name %s", diff --git a/pimd/pimd.conf.sample b/pimd/pimd.conf.sample deleted file mode 100644 index de1da6b838..0000000000 --- a/pimd/pimd.conf.sample +++ /dev/null @@ -1,40 +0,0 @@ -! -! pimd sample configuration file -! -hostname quagga-pimd-router -password zebra -!enable password zebra -! -!log file pimd.log -log stdout -! -line vty - exec-timeout 60 -! -!debug igmp -!debug pim -!debug pim zebra -! -ip multicast-routing -! -! ! You may want to enable ssmpingd for troubleshooting -! ! See http://www.venaas.no/multicast/ssmping/ -! ! -! ip ssmpingd 1.1.1.1 -! ip ssmpingd 2.2.2.2 -! -! ! HINTS: -! ! - Enable "ip pim ssm" on the interface directly attached to the -! ! multicast source host (if this is the first-hop router) -! ! - Enable "ip pim ssm" on pim-routers-facing interfaces -! ! - Enable "ip igmp" on IGMPv3-hosts-facing interfaces -! ! - In order to inject IGMPv3 local membership information in the -! ! PIM protocol state, enable both "ip pim ssm" and "ip igmp" on -! ! the same interface; otherwise PIM won't advertise -! ! IGMPv3-learned membership to other PIM routers -! -interface eth0 - ip pim ssm - ip igmp - -! -x- diff --git a/pimd/subdir.am b/pimd/subdir.am index 717f4782f8..9910642ffa 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -7,7 +7,6 @@ noinst_LIBRARIES += pimd/libpim.a sbin_PROGRAMS += pimd/pimd bin_PROGRAMS += pimd/mtracebis noinst_PROGRAMS += pimd/test_igmpv3_join -dist_examples_DATA += pimd/pimd.conf.sample vtysh_scan += pimd/pim_cmd.c vtysh_daemons += pimd man8 += $(MANBUILD)/frr-pimd.8 diff --git a/python/clippy/elf.py b/python/clippy/elf.py index 4ed334f0c4..02cb2e38b3 100644 --- a/python/clippy/elf.py +++ b/python/clippy/elf.py @@ -162,7 +162,10 @@ class ELFDissectData(object): for field in parent._efields[self.elfclass]: if field[0] == fieldname: break - offset += struct.calcsize(field[1]) + spec = field[1] + if spec == 'P': + spec = 'I' if self.elfclass == 32 else 'Q' + offset += struct.calcsize(spec) else: raise AttributeError('%r not found in %r.fields' % (fieldname, parent)) diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index b6d7ab2416..47c6ad41af 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -456,6 +456,7 @@ ln -s %{_sbindir}/frrinit.sh %{buildroot}%{_initddir}/frr %endif install %{zeb_src}/tools/etc/frr/daemons %{buildroot}%{_sysconfdir}/frr +install %{zeb_src}/tools/etc/frr/frr.conf %{buildroot}%{_sysconfdir}/frr install -m644 %{zeb_rh_src}/frr.pam %{buildroot}%{_sysconfdir}/pam.d/frr install -m644 %{zeb_rh_src}/frr.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/frr install -d -m750 %{buildroot}%{rundir} @@ -641,7 +642,7 @@ fi %files -%doc */*.sample* COPYING +%doc COPYING %doc doc/mpls %doc README.md /usr/share/yang/*.yang @@ -654,9 +655,6 @@ fi %dir %attr(750,root,root) %{_localstatedir}/log/frr %dir %attr(750,root,root) %{rundir} %endif -%if 0%{?vty_group:1} - %attr(750,%{frr_user},%{vty_group}) %{configdir}/vtysh.conf.sample -%endif %{_infodir}/frr.info.gz %{_mandir}/man*/* %{_sbindir}/zebra diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c index 37bce7484c..b922d66912 100644 --- a/ripd/rip_snmp.c +++ b/ripd/rip_snmp.c @@ -271,7 +271,7 @@ static struct interface *rip2IfLookup(struct variable *v, oid name[], if (ifp == NULL) return NULL; - oid_copy_addr(name + v->namelen, addr, sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, addr); *length = v->namelen + sizeof(struct in_addr); @@ -320,8 +320,8 @@ static struct rip_peer *rip2PeerLookup(struct variable *v, oid name[], || (peer->domain > (int)name[v->namelen + sizeof(struct in_addr)])) { - oid_copy_addr(name + v->namelen, &peer->addr, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, + &peer->addr); name[v->namelen + sizeof(struct in_addr)] = peer->domain; *length = @@ -334,8 +334,7 @@ static struct rip_peer *rip2PeerLookup(struct variable *v, oid name[], if (!peer) return NULL; - oid_copy_addr(name + v->namelen, &peer->addr, - sizeof(struct in_addr)); + oid_copy_in_addr(name + v->namelen, &peer->addr); name[v->namelen + sizeof(struct in_addr)] = peer->domain; *length = sizeof(struct in_addr) + v->namelen + 1; diff --git a/ripd/ripd.conf.sample b/ripd/ripd.conf.sample deleted file mode 100644 index e11bf0bb23..0000000000 --- a/ripd/ripd.conf.sample +++ /dev/null @@ -1,22 +0,0 @@ -! -*- rip -*- -! -! RIPd sample configuration file -! -hostname ripd -password zebra -! -! debug rip events -! debug rip packet -! -router rip -! network 11.0.0.0/8 -! network eth0 -! route 10.0.0.0/8 -! distribute-list private-only in eth0 -! -!access-list private-only permit 10.0.0.0/8 -!access-list private-only deny any -! -!log file ripd.log -! -log stdout diff --git a/ripd/subdir.am b/ripd/subdir.am index 09d5590329..99979bff0d 100644 --- a/ripd/subdir.am +++ b/ripd/subdir.am @@ -5,7 +5,6 @@ if RIPD noinst_LIBRARIES += ripd/librip.a sbin_PROGRAMS += ripd/ripd -dist_examples_DATA += ripd/ripd.conf.sample vtysh_scan += \ ripd/rip_cli.c \ ripd/rip_debug.c \ diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c index 479fcaeb54..554b1d68f6 100644 --- a/ripngd/ripng_peer.c +++ b/ripngd/ripng_peer.c @@ -163,8 +163,8 @@ void ripng_peer_display(struct vty *vty, struct ripng *ripng) char timebuf[RIPNG_UPTIME_LEN]; for (ALL_LIST_ELEMENTS(ripng->peer_list, node, nnode, peer)) { - vty_out(vty, " %s \n%14s %10d %10d %10d %s\n", - inet6_ntoa(peer->addr), " ", peer->recv_badpackets, + vty_out(vty, " %pI6 \n%14s %10d %10d %10d %s\n", + &peer->addr, " ", peer->recv_badpackets, peer->recv_badroutes, ZEBRA_RIPNG_DISTANCE_DEFAULT, ripng_peer_uptime(peer, timebuf, RIPNG_UPTIME_LEN)); } diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 749feaca6c..0e83140149 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -191,7 +191,7 @@ int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to, if (IS_RIPNG_DEBUG_SEND) { if (to) - zlog_debug("send to %s", inet6_ntoa(to->sin6_addr)); + zlog_debug("send to %pI6", &to->sin6_addr); zlog_debug(" send interface %s", ifp->name); zlog_debug(" send packet size %d", bufsize); } @@ -237,8 +237,8 @@ int ripng_send_packet(caddr_t buf, int bufsize, struct sockaddr_in6 *to, if (ret < 0) { if (to) flog_err_sys(EC_LIB_SOCKET, - "RIPng send fail on %s to %s: %s", - ifp->name, inet6_ntoa(to->sin6_addr), + "RIPng send fail on %s to %pI6: %s", + ifp->name, &to->sin6_addr, safe_strerror(errno)); else flog_err_sys(EC_LIB_SOCKET, "RIPng send fail on %s: %s", @@ -338,11 +338,11 @@ void ripng_packet_dump(struct ripng_packet *packet, int size, for (lim = (caddr_t)packet + size; (caddr_t)rte < lim; rte++) { if (rte->metric == RIPNG_METRIC_NEXTHOP) - zlog_debug(" nexthop %s/%d", inet6_ntoa(rte->addr), + zlog_debug(" nexthop %pI6/%d", &rte->addr, rte->prefixlen); else - zlog_debug(" %s/%d metric %d tag %" ROUTE_TAG_PRI, - inet6_ntoa(rte->addr), rte->prefixlen, + zlog_debug(" %pI6/%d metric %d tag %" ROUTE_TAG_PRI, + &rte->addr, rte->prefixlen, rte->metric, (route_tag_t)ntohs(rte->tag)); } } @@ -353,9 +353,9 @@ static void ripng_nexthop_rte(struct rte *rte, struct sockaddr_in6 *from, { /* Logging before checking RTE. */ if (IS_RIPNG_DEBUG_RECV) - zlog_debug("RIPng nexthop RTE address %s tag %" ROUTE_TAG_PRI + zlog_debug("RIPng nexthop RTE address %pI6 tag %" ROUTE_TAG_PRI " prefixlen %d", - inet6_ntoa(rte->addr), (route_tag_t)ntohs(rte->tag), + &rte->addr, (route_tag_t)ntohs(rte->tag), rte->prefixlen); /* RFC2080 2.1.1 Next Hop: @@ -364,14 +364,13 @@ static void ripng_nexthop_rte(struct rte *rte, struct sockaddr_in6 *from, if (ntohs(rte->tag) != 0) zlog_warn( "RIPng nexthop RTE with non zero tag value %" ROUTE_TAG_PRI - " from %s", - (route_tag_t)ntohs(rte->tag), - inet6_ntoa(from->sin6_addr)); + " from %pI6", + (route_tag_t)ntohs(rte->tag), &from->sin6_addr); if (rte->prefixlen != 0) zlog_warn( - "RIPng nexthop RTE with non zero prefixlen value %d from %s", - rte->prefixlen, inet6_ntoa(from->sin6_addr)); + "RIPng nexthop RTE with non zero prefixlen value %d from %pI6", + rte->prefixlen, &from->sin6_addr); /* Specifying a value of 0:0:0:0:0:0:0:0 in the prefix field of a next hop RTE indicates that the next hop address should be the @@ -396,8 +395,8 @@ static void ripng_nexthop_rte(struct rte *rte, struct sockaddr_in6 *from, information is ignored, a possibly sub-optimal, but absolutely valid, route may be taken. If the received next hop address is not a link-local address, it should be treated as 0:0:0:0:0:0:0:0. */ - zlog_warn("RIPng nexthop RTE with non link-local address %s from %pI6", - inet6_ntoa(rte->addr), &from->sin6_addr); + zlog_warn("RIPng nexthop RTE with non link-local address %pI6 from %pI6", + &rte->addr, &from->sin6_addr); nexthop->flag = RIPNG_NEXTHOP_UNSPEC; memset(&nexthop->address, 0, sizeof(struct in6_addr)); @@ -750,8 +749,8 @@ static void ripng_route_process(struct rte *rte, struct sockaddr_in6 *from, if (ret == RMAP_DENYMATCH) { if (IS_RIPNG_DEBUG_PACKET) zlog_debug( - "RIPng %s/%d is filtered by route-map in", - inet6_ntoa(p.prefix), p.prefixlen); + "RIPng %pFX is filtered by route-map in", + &p); return; } @@ -993,8 +992,8 @@ void ripng_redistribute_add(struct ripng *ripng, int type, int sub_type, p, ifindex2ifname(ifindex, ripng->vrf->vrf_id)); else zlog_debug( - "Redistribute new prefix %pFX with nexthop %s on the interface %s", - p, inet6_ntoa(*nexthop), + "Redistribute new prefix %pFX with nexthop %pI6 on the interface %s", + p, nexthop, ifindex2ifname(ifindex, ripng->vrf->vrf_id)); } @@ -1106,8 +1105,8 @@ static void ripng_response_process(struct ripng_packet *packet, int size, /* RFC2080 2.4.2 Response Messages: The Response must be ignored if it is not from the RIPng port. */ if (ntohs(from->sin6_port) != RIPNG_PORT_DEFAULT) { - zlog_warn("RIPng packet comes from non RIPng port %d from %s", - ntohs(from->sin6_port), inet6_ntoa(from->sin6_addr)); + zlog_warn("RIPng packet comes from non RIPng port %d from %pI6", + ntohs(from->sin6_port), &from->sin6_addr); ripng_peer_bad_packet(ripng, from); return; } @@ -1116,8 +1115,8 @@ static void ripng_response_process(struct ripng_packet *packet, int size, whether the datagram is from a valid neighbor; the source of the datagram must be a link-local address. */ if (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) { - zlog_warn("RIPng packet comes from non link local address %s", - inet6_ntoa(from->sin6_addr)); + zlog_warn("RIPng packet comes from non link local address %pI6", + &from->sin6_addr); ripng_peer_bad_packet(ripng, from); return; } @@ -1129,8 +1128,8 @@ static void ripng_response_process(struct ripng_packet *packet, int size, and such datagrams must be ignored. */ if (ripng_lladdr_check(ifp, &from->sin6_addr)) { zlog_warn( - "RIPng packet comes from my own link local address %s", - inet6_ntoa(from->sin6_addr)); + "RIPng packet comes from my own link local address %pI6", + &from->sin6_addr); ripng_peer_bad_packet(ripng, from); return; } @@ -1141,8 +1140,8 @@ static void ripng_response_process(struct ripng_packet *packet, int size, packets) must be examined to ensure that the hop count is 255. */ if (hoplimit >= 0 && hoplimit != 255) { zlog_warn( - "RIPng packet comes with non 255 hop count %d from %s", - hoplimit, inet6_ntoa(from->sin6_addr)); + "RIPng packet comes with non 255 hop count %d from %pI6", + hoplimit, &from->sin6_addr); ripng_peer_bad_packet(ripng, from); return; } @@ -1173,25 +1172,22 @@ static void ripng_response_process(struct ripng_packet *packet, int size, should never be present in an RTE. */ if (IN6_IS_ADDR_MULTICAST(&rte->addr)) { zlog_warn( - "Destination prefix is a multicast address %s/%d [%d]", - inet6_ntoa(rte->addr), rte->prefixlen, - rte->metric); + "Destination prefix is a multicast address %pI6/%d [%d]", + &rte->addr, rte->prefixlen, rte->metric); ripng_peer_bad_route(ripng, from); continue; } if (IN6_IS_ADDR_LINKLOCAL(&rte->addr)) { zlog_warn( - "Destination prefix is a link-local address %s/%d [%d]", - inet6_ntoa(rte->addr), rte->prefixlen, - rte->metric); + "Destination prefix is a link-local address %pI6/%d [%d]", + &rte->addr, rte->prefixlen, rte->metric); ripng_peer_bad_route(ripng, from); continue; } if (IN6_IS_ADDR_LOOPBACK(&rte->addr)) { zlog_warn( - "Destination prefix is a loopback address %s/%d [%d]", - inet6_ntoa(rte->addr), rte->prefixlen, - rte->metric); + "Destination prefix is a loopback address %pI6/%d [%d]", + &rte->addr, rte->prefixlen, rte->metric); ripng_peer_bad_route(ripng, from); continue; } @@ -1199,17 +1195,17 @@ static void ripng_response_process(struct ripng_packet *packet, int size, /* - is the prefix length valid (i.e., between 0 and 128, inclusive) */ if (rte->prefixlen > 128) { - zlog_warn("Invalid prefix length %s/%d from %s%%%s", - inet6_ntoa(rte->addr), rte->prefixlen, - inet6_ntoa(from->sin6_addr), ifp->name); + zlog_warn("Invalid prefix length %pI6/%d from %pI6%%%s", + &rte->addr, rte->prefixlen, + &from->sin6_addr, ifp->name); ripng_peer_bad_route(ripng, from); continue; } /* - is the metric valid (i.e., between 1 and 16, inclusive) */ if (!(rte->metric >= 1 && rte->metric <= 16)) { - zlog_warn("Invalid metric %d from %s%%%s", rte->metric, - inet6_ntoa(from->sin6_addr), ifp->name); + zlog_warn("Invalid metric %d from %pI6%%%s", + rte->metric, &from->sin6_addr, ifp->name); ripng_peer_bad_route(ripng, from); continue; } @@ -1342,8 +1338,8 @@ static int ripng_read(struct thread *thread) /* Check RTE boundary. RTE size (Packet length - RIPng header size (4)) must be multiple size of one RTE size (20). */ if (((len - 4) % 20) != 0) { - zlog_warn("RIPng invalid packet size %d from %s (VRF %s)", len, - inet6_ntoa(from.sin6_addr), ripng->vrf_name); + zlog_warn("RIPng invalid packet size %d from %pI6 (VRF %s)", + len, &from.sin6_addr, ripng->vrf_name); ripng_peer_bad_packet(ripng, &from); return 0; } @@ -1354,8 +1350,8 @@ static int ripng_read(struct thread *thread) /* RIPng packet received. */ if (IS_RIPNG_DEBUG_EVENT) zlog_debug( - "RIPng packet received from %s port %d on %s (VRF %s)", - inet6_ntoa(from.sin6_addr), ntohs(from.sin6_port), + "RIPng packet received from %pI6 port %d on %s (VRF %s)", + &from.sin6_addr, ntohs(from.sin6_port), ifp ? ifp->name : "unknown", ripng->vrf_name); /* Logging before packet checking. */ @@ -1581,8 +1577,8 @@ void ripng_output_process(struct interface *ifp, struct sockaddr_in6 *to, if (IS_RIPNG_DEBUG_EVENT) { if (to) - zlog_debug("RIPng update routes to neighbor %s", - inet6_ntoa(to->sin6_addr)); + zlog_debug("RIPng update routes to neighbor %pI6", + &to->sin6_addr); else zlog_debug("RIPng update routes on interface %s", ifp->name); @@ -2089,8 +2085,8 @@ DEFUN (show_ipv6_ripng, #endif /* DEBUG */ vty_out(vty, "\n"); vty_out(vty, "%*s", 18, " "); - len = vty_out(vty, "%s", - inet6_ntoa(rinfo->nexthop)); + len = vty_out(vty, "%pI6", + &rinfo->nexthop); len = 28 - len; if (len > 0) diff --git a/ripngd/ripngd.conf.sample b/ripngd/ripngd.conf.sample deleted file mode 100644 index 28f08c399a..0000000000 --- a/ripngd/ripngd.conf.sample +++ /dev/null @@ -1,20 +0,0 @@ -! -*- rip -*- -! -! RIPngd sample configuration file -! -hostname ripngd -password zebra -! -! debug ripng events -! debug ripng packet -! -! -router ripng -! network sit1 -! route 3ffe:506::0/32 -! distribute-list local-only out sit1 -! -!ipv6 access-list local-only permit 3ffe:506::0/32 -!ipv6 access-list local-only deny any -! -log stdout diff --git a/ripngd/subdir.am b/ripngd/subdir.am index 8d370f1b5d..9d8d27d4cc 100644 --- a/ripngd/subdir.am +++ b/ripngd/subdir.am @@ -50,5 +50,3 @@ ripngd_ripngd_SOURCES = \ nodist_ripngd_ripngd_SOURCES = \ yang/frr-ripngd.yang.c \ # end - -dist_examples_DATA += ripngd/ripngd.conf.sample diff --git a/sharpd/sharp_globals.h b/sharpd/sharp_globals.h index ecb7053fd6..0b3776cd90 100644 --- a/sharpd/sharp_globals.h +++ b/sharpd/sharp_globals.h @@ -55,6 +55,9 @@ struct sharp_global { /* The list of nexthops that we are watching and data about them */ struct list *nhs; + + /* Traffic Engineering Database */ + struct ls_ted *ted; }; extern struct sharp_global sg; diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c index a1216247c0..e93db34ffa 100644 --- a/sharpd/sharp_main.c +++ b/sharpd/sharp_main.c @@ -43,6 +43,7 @@ #include "libfrr.h" #include "routemap.h" #include "nexthop_group.h" +#include "link_state.h" #include "sharp_zebra.h" #include "sharp_vty.h" @@ -138,6 +139,7 @@ static void sharp_global_init(void) { memset(&sg, 0, sizeof(sg)); sg.nhs = list_new(); + sg.ted = NULL; } static void sharp_start_configuration(void) diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 940415b067..002336616c 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -29,6 +29,7 @@ #include "vrf.h" #include "zclient.h" #include "nexthop_group.h" +#include "link_state.h" #include "sharpd/sharp_globals.h" #include "sharpd/sharp_zebra.h" @@ -700,6 +701,142 @@ DEFPY (neigh_discover, return CMD_SUCCESS; } +DEFPY (import_te, + import_te_cmd, + "sharp import-te", + SHARP_STR + "Import Traffic Engineering\n") +{ + sg.ted = ls_ted_new(1, "Sharp", 0); + sharp_zebra_register_te(); + + return CMD_SUCCESS; +} + +DEFUN (show_sharp_ted, + show_sharp_ted_cmd, + "show sharp ted [<vertex [A.B.C.D]|edge [A.B.C.D]|subnet [A.B.C.D/M]>] [verbose|json]", + SHOW_STR + SHARP_STR + "Traffic Engineering Database\n" + "MPLS-TE Vertex\n" + "MPLS-TE router ID (as an IP address)\n" + "MPLS-TE Edge\n" + "MPLS-TE Edge ID (as an IP address)\n" + "MPLS-TE Subnet\n" + "MPLS-TE Subnet ID (as an IP prefix)\n" + "Verbose output\n" + JSON_STR) +{ + int idx = 0; + struct in_addr ip_addr; + struct prefix pref; + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + uint64_t key; + bool verbose = false; + bool uj = use_json(argc, argv); + json_object *json = NULL; + + if (sg.ted == NULL) { + vty_out(vty, "MPLS-TE import is not enabled\n"); + return CMD_WARNING; + } + + if (uj) + json = json_object_new_object(); + + if (argv[argc - 1]->arg && strmatch(argv[argc - 1]->text, "verbose")) + verbose = true; + + if (argv_find(argv, argc, "vertex", &idx)) { + /* Show Vertex */ + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx + 1]->arg, &ip_addr)) { + vty_out(vty, + "Specified Router ID %s is invalid\n", + argv[idx + 1]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Vertex from the Link State Database */ + key = ((uint64_t)ip_addr.s_addr) & 0xffffffff; + vertex = ls_find_vertex_by_key(sg.ted, key); + if (!vertex) { + vty_out(vty, "No vertex found for ID %pI4\n", + &ip_addr); + return CMD_WARNING; + } + } else + vertex = NULL; + + if (vertex) + ls_show_vertex(vertex, vty, json, verbose); + else + ls_show_vertices(sg.ted, vty, json, verbose); + + } else if (argv_find(argv, argc, "edge", &idx)) { + /* Show Edge */ + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &ip_addr)) { + vty_out(vty, + "Specified Edge ID %s is invalid\n", + argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Edge from the Link State Database */ + key = ((uint64_t)ip_addr.s_addr) & 0xffffffff; + edge = ls_find_edge_by_key(sg.ted, key); + if (!edge) { + vty_out(vty, "No edge found for ID %pI4\n", + &ip_addr); + return CMD_WARNING; + } + } else + edge = NULL; + + if (edge) + ls_show_edge(edge, vty, json, verbose); + else + ls_show_edges(sg.ted, vty, json, verbose); + + } else if (argv_find(argv, argc, "subnet", &idx)) { + /* Show Subnet */ + if (argv_find(argv, argc, "A.B.C.D/M", &idx)) { + if (!str2prefix(argv[idx]->arg, &pref)) { + vty_out(vty, "Invalid prefix format %s\n", + argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Subnet from the Link State Database */ + subnet = ls_find_subnet(sg.ted, pref); + if (!subnet) { + vty_out(vty, "No subnet found for ID %pFX\n", + &pref); + return CMD_WARNING; + } + } else + subnet = NULL; + + if (subnet) + ls_show_subnet(subnet, vty, json, verbose); + else + ls_show_subnets(sg.ted, vty, json, verbose); + + } else { + /* Show the complete TED */ + ls_show_ted(sg.ted, vty, json, verbose); + } + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; +} + void sharp_vty_init(void) { install_element(ENABLE_NODE, &install_routes_data_dump_cmd); @@ -718,8 +855,10 @@ void sharp_vty_init(void) install_element(ENABLE_NODE, &send_opaque_unicast_cmd); install_element(ENABLE_NODE, &send_opaque_reg_cmd); install_element(ENABLE_NODE, &neigh_discover_cmd); + install_element(ENABLE_NODE, &import_te_cmd); install_element(ENABLE_NODE, &show_debugging_sharpd_cmd); + install_element(ENABLE_NODE, &show_sharp_ted_cmd); return; } diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 73bbaf0bc0..0f2c634049 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -30,6 +30,7 @@ #include "zclient.h" #include "nexthop.h" #include "nexthop_group.h" +#include "link_state.h" #include "sharp_globals.h" #include "sharp_nht.h" @@ -769,11 +770,15 @@ int sharp_zclient_delete(uint32_t session_id) return 0; } +static const char *const type2txt[] = { "Generic", "Vertex", "Edge", "Subnet" }; +static const char *const status2txt[] = { "Unknown", "New", "Update", + "Delete", "Sync", "Orphan"}; /* Handler for opaque messages */ static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS) { struct stream *s; struct zapi_opaque_msg info; + struct ls_element *lse; s = zclient->ibuf; @@ -783,6 +788,18 @@ static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS) zlog_debug("%s: [%u] received opaque type %u", __func__, zclient->session_id, info.type); + if (info.type == LINK_STATE_UPDATE) { + lse = ls_stream2ted(sg.ted, s, false); + if (lse) + zlog_debug(" |- Got %s %s from Link State Database", + status2txt[lse->status], + type2txt[lse->type]); + else + zlog_debug( + "%s: Error to convert Stream into Link State", + __func__); + } + return 0; } @@ -853,6 +870,16 @@ void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance, } +/* Link State registration */ +void sharp_zebra_register_te(void) +{ + /* First register to received Link State Update messages */ + zclient_register_opaque(zclient, LINK_STATE_UPDATE); + + /* Then, request initial TED with SYNC message */ + ls_request_sync(zclient); +} + void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p) { zclient_send_neigh_discovery_req(zclient, ifp, p); diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index e7247f5373..ffddb9e780 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -60,4 +60,7 @@ void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance, extern void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p); +/* Register Link State Opaque messages */ +extern void sharp_zebra_register_te(void); + #endif diff --git a/sharpd/sharpd.conf.sample b/sharpd/sharpd.conf.sample deleted file mode 100644 index d1cc19a51f..0000000000 --- a/sharpd/sharpd.conf.sample +++ /dev/null @@ -1,6 +0,0 @@ -! Default sharpd configuration sample -! -! There are no `default` configuration commands for sharpd -! all commands are at the view or enable level. -! -log stdout diff --git a/sharpd/subdir.am b/sharpd/subdir.am index d161eb6327..acf4fe5d00 100644 --- a/sharpd/subdir.am +++ b/sharpd/subdir.am @@ -5,7 +5,6 @@ if SHARPD noinst_LIBRARIES += sharpd/libsharp.a sbin_PROGRAMS += sharpd/sharpd -dist_examples_DATA += sharpd/sharpd.conf.sample vtysh_scan += sharpd/sharp_vty.c vtysh_daemons += sharpd man8 += $(MANBUILD)/frr-sharpd.8 diff --git a/staticd/static_memory.c b/staticd/static_memory.c deleted file mode 100644 index 9d8d7643e2..0000000000 --- a/staticd/static_memory.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * static memory code. - * Copyright (C) 2018 Cumulus Networks, Inc. - * Donald Sharp - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include <zebra.h> - -#include <memory.h> - -#include "staticd/static_memory.h" - -DEFINE_MGROUP(STATIC, "staticd"); - -DEFINE_MTYPE(STATIC, STATIC_NEXTHOP, "Static Nexthop"); diff --git a/staticd/static_routes.c b/staticd/static_routes.c index 9f7e19660d..cdafc4a76a 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -31,12 +31,71 @@ #include "static_vrf.h" #include "static_routes.h" -#include "static_memory.h" #include "static_zebra.h" #include "static_debug.h" -DEFINE_MTYPE(STATIC, STATIC_ROUTE, "Static Route Info"); -DEFINE_MTYPE(STATIC, STATIC_PATH, "Static Path"); +DEFINE_MGROUP(STATIC, "staticd"); + +DEFINE_MTYPE_STATIC(STATIC, STATIC_ROUTE, "Static Route Info"); +DEFINE_MTYPE_STATIC(STATIC, STATIC_PATH, "Static Path"); +DEFINE_MTYPE_STATIC(STATIC, STATIC_NEXTHOP, "Static Nexthop"); + +void zebra_stable_node_cleanup(struct route_table *table, + struct route_node *node) +{ + struct static_nexthop *nh; + struct static_path *pn; + struct static_route_info *si; + struct route_table *src_table; + struct route_node *src_node; + struct static_path *src_pn; + struct static_route_info *src_si; + + si = node->info; + + if (si) { + frr_each_safe(static_path_list, &si->path_list, pn) { + frr_each_safe(static_nexthop_list, &pn->nexthop_list, + nh) { + static_nexthop_list_del(&pn->nexthop_list, nh); + XFREE(MTYPE_STATIC_NEXTHOP, nh); + } + static_path_list_del(&si->path_list, pn); + XFREE(MTYPE_STATIC_PATH, pn); + } + + /* clean up for dst table */ + src_table = srcdest_srcnode_table(node); + if (src_table) { + /* This means the route_node is part of the top + * hierarchy and refers to a destination prefix. + */ + for (src_node = route_top(src_table); src_node; + src_node = route_next(src_node)) { + src_si = src_node->info; + + frr_each_safe(static_path_list, + &src_si->path_list, src_pn) { + frr_each_safe(static_nexthop_list, + &src_pn->nexthop_list, + nh) { + static_nexthop_list_del( + &src_pn->nexthop_list, + nh); + XFREE(MTYPE_STATIC_NEXTHOP, nh); + } + static_path_list_del(&src_si->path_list, + src_pn); + XFREE(MTYPE_STATIC_PATH, src_pn); + } + + XFREE(MTYPE_STATIC_ROUTE, src_node->info); + } + } + + XFREE(MTYPE_STATIC_ROUTE, node->info); + } +} /* Install static path into rib. */ void static_install_path(struct route_node *rn, struct static_path *pn, @@ -80,9 +139,6 @@ struct route_node *static_add_route(afi_t afi, safi_t safi, struct prefix *p, rn->info = si; - /* Mark as having FRR configuration */ - vrf_set_user_cfged(svrf->vrf); - return rn; } @@ -101,9 +157,6 @@ static void static_del_src_route(struct route_node *rn, safi_t safi, XFREE(MTYPE_STATIC_ROUTE, rn->info); route_unlock_node(rn); - /* If no other FRR config for this VRF, mark accordingly. */ - if (!static_vrf_has_config(svrf)) - vrf_reset_user_cfged(svrf->vrf); } void static_del_route(struct route_node *rn, safi_t safi, @@ -133,9 +186,6 @@ void static_del_route(struct route_node *rn, safi_t safi, } XFREE(MTYPE_STATIC_ROUTE, rn->info); route_unlock_node(rn); - /* If no other FRR config for this VRF, mark accordingly. */ - if (!static_vrf_has_config(svrf)) - vrf_reset_user_cfged(svrf->vrf); } bool static_add_nexthop_validate(const char *nh_vrf_name, static_types type, diff --git a/staticd/static_routes.h b/staticd/static_routes.h index 0fbf0674d7..f64a40329d 100644 --- a/staticd/static_routes.h +++ b/staticd/static_routes.h @@ -22,6 +22,9 @@ #include "lib/mpls.h" #include "table.h" +#include "memory.h" + +DECLARE_MGROUP(STATIC); /* Static route label information */ struct static_nh_label { @@ -198,6 +201,9 @@ extern bool static_add_nexthop_validate(const char *nh_vrf_name, extern struct stable_info *static_get_stable_info(struct route_node *rn); extern void static_route_info_init(struct static_route_info *si); +extern void zebra_stable_node_cleanup(struct route_table *table, + struct route_node *node); + /* * Max string return via API static_get_nh_str in size_t */ diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c index 2133093bb3..96e5d37d68 100644 --- a/staticd/static_vrf.c +++ b/staticd/static_vrf.c @@ -24,7 +24,6 @@ #include "table.h" #include "srcdest_table.h" -#include "static_memory.h" #include "static_vrf.h" #include "static_routes.h" #include "static_zebra.h" @@ -32,63 +31,6 @@ DEFINE_MTYPE_STATIC(STATIC, STATIC_RTABLE_INFO, "Static Route Table Info"); -static void zebra_stable_node_cleanup(struct route_table *table, - struct route_node *node) -{ - struct static_nexthop *nh; - struct static_path *pn; - struct static_route_info *si; - struct route_table *src_table; - struct route_node *src_node; - struct static_path *src_pn; - struct static_route_info *src_si; - - si = node->info; - - if (si) { - frr_each_safe(static_path_list, &si->path_list, pn) { - frr_each_safe(static_nexthop_list, &pn->nexthop_list, - nh) { - static_nexthop_list_del(&pn->nexthop_list, nh); - XFREE(MTYPE_STATIC_NEXTHOP, nh); - } - static_path_list_del(&si->path_list, pn); - XFREE(MTYPE_STATIC_PATH, pn); - } - - /* clean up for dst table */ - src_table = srcdest_srcnode_table(node); - if (src_table) { - /* This means the route_node is part of the top - * hierarchy and refers to a destination prefix. - */ - for (src_node = route_top(src_table); src_node; - src_node = route_next(src_node)) { - src_si = src_node->info; - - frr_each_safe(static_path_list, - &src_si->path_list, src_pn) { - frr_each_safe(static_nexthop_list, - &src_pn->nexthop_list, - nh) { - static_nexthop_list_del( - &src_pn->nexthop_list, - nh); - XFREE(MTYPE_STATIC_NEXTHOP, nh); - } - static_path_list_del(&src_si->path_list, - src_pn); - XFREE(MTYPE_STATIC_PATH, src_pn); - } - - XFREE(MTYPE_STATIC_ROUTE, src_node->info); - } - } - - XFREE(MTYPE_STATIC_ROUTE, node->info); - } -} - static struct static_vrf *static_vrf_alloc(void) { struct route_table *table; @@ -228,27 +170,6 @@ static int static_vrf_config_write(struct vty *vty) return 0; } -int static_vrf_has_config(struct static_vrf *svrf) -{ - struct route_table *table; - safi_t safi; - afi_t afi; - - /* - * NOTE: This is a don't care for the default VRF, but we go through - * the motions to keep things consistent. - */ - FOREACH_AFI_SAFI (afi, safi) { - table = svrf->stable[afi][safi]; - if (!table) - continue; - if (route_table_count(table)) - return 1; - } - - return 0; -} - void static_vrf_init(void) { vrf_init(static_vrf_new, static_vrf_enable, diff --git a/staticd/static_vrf.h b/staticd/static_vrf.h index 81296f2864..3977899054 100644 --- a/staticd/static_vrf.h +++ b/staticd/static_vrf.h @@ -37,8 +37,6 @@ struct stable_info { struct static_vrf *static_vrf_lookup_by_name(const char *vrf_name); struct static_vrf *static_vrf_lookup_by_id(vrf_id_t vrf_id); -int static_vrf_has_config(struct static_vrf *svrf); - void static_vrf_init(void); struct route_table *static_vrf_static_table(afi_t afi, safi_t safi, diff --git a/staticd/static_vty.c b/staticd/static_vty.c index dd03f83778..33a2728cd0 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -33,7 +33,6 @@ #include "northbound_cli.h" #include "static_vrf.h" -#include "static_memory.h" #include "static_vty.h" #include "static_routes.h" #include "static_debug.h" diff --git a/staticd/staticd.conf.sample b/staticd/staticd.conf.sample deleted file mode 100644 index 3b64eb9c90..0000000000 --- a/staticd/staticd.conf.sample +++ /dev/null @@ -1,5 +0,0 @@ -! Default staticd configuration sample -! -log stdout -! -! ip route 4.5.6.7/32 10.10.10.10 diff --git a/staticd/subdir.am b/staticd/subdir.am index 9c630e8f5f..62969a0a2a 100644 --- a/staticd/subdir.am +++ b/staticd/subdir.am @@ -5,7 +5,6 @@ if STATICD noinst_LIBRARIES += staticd/libstatic.a sbin_PROGRAMS += staticd/staticd -dist_examples_DATA += staticd/staticd.conf.sample vtysh_scan += staticd/static_vty.c vtysh_daemons += staticd man8 += $(MANBUILD)/frr-staticd.8 @@ -13,7 +12,6 @@ endif staticd_libstatic_a_SOURCES = \ staticd/static_debug.c \ - staticd/static_memory.c \ staticd/static_nht.c \ staticd/static_routes.c \ staticd/static_zebra.c \ @@ -25,7 +23,6 @@ staticd_libstatic_a_SOURCES = \ noinst_HEADERS += \ staticd/static_debug.h \ - staticd/static_memory.h \ staticd/static_nht.h \ staticd/static_zebra.h \ staticd/static_routes.h \ diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c index 24de3fa88d..21b3a916b8 100644 --- a/tests/lib/test_printfrr.c +++ b/tests/lib/test_printfrr.c @@ -59,21 +59,88 @@ static void printcmp(const char *fmt, ...) errors++; } -static void printchk(const char *ref, const char *fmt, ...) PRINTFRR(2, 3); -static void printchk(const char *ref, const char *fmt, ...) +static int printchk(const char *ref, const char *fmt, ...) PRINTFRR(2, 3); +static int printchk(const char *ref, const char *fmt, ...) { va_list ap; char bufrr[256]; + bool truncfail = false; + size_t i; + size_t expectlen; + memset(bufrr, 0xcc, sizeof(bufrr)); va_start(ap, fmt); - vsnprintfrr(bufrr, sizeof(bufrr), fmt, ap); + expectlen = vsnprintfrr(NULL, 0, fmt, ap); + va_end(ap); + + va_start(ap, fmt); + vsnprintfrr(bufrr, 7, fmt, ap); va_end(ap); - printf("fmt: \"%s\"\nref: \"%s\"\nfrr: \"%s\"\n%s\n\n", + if (strnlen(bufrr, 7) == 7) + truncfail = true; + if (strnlen(bufrr, 7) < 7 && strncmp(ref, bufrr, 6) != 0) + truncfail = true; + for (i = 7; i < sizeof(bufrr); i++) + if (bufrr[i] != (char)0xcc) { + truncfail = true; + break; + } + + if (truncfail) { + printf("truncation test FAILED:\n" + "fmt: \"%s\"\nref: \"%s\"\nfrr[:7]: \"%s\"\n%s\n\n", + fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok"); + errors++; + } + + struct fmt_outpos outpos[16]; + struct fbuf fb = { + .buf = bufrr, + .pos = bufrr, + .len = sizeof(bufrr) - 1, + .outpos = outpos, + .outpos_n = array_size(outpos), + }; + + va_start(ap, fmt); + vbprintfrr(&fb, fmt, ap); + fb.pos[0] = '\0'; + va_end(ap); + + printf("fmt: \"%s\"\nref: \"%s\"\nfrr: \"%s\"\n%s\n", fmt, ref, bufrr, strcmp(ref, bufrr) ? "ERROR" : "ok"); if (strcmp(ref, bufrr)) errors++; + if (strlen(bufrr) != expectlen) { + printf("return value <> length mismatch\n"); + errors++; + } + + for (size_t i = 0; i < fb.outpos_i; i++) + printf("\t[%zu: %u..%u] = \"%.*s\"\n", i, + outpos[i].off_start, + outpos[i].off_end, + (int)(outpos[i].off_end - outpos[i].off_start), + bufrr + outpos[i].off_start); + printf("\n"); + return 0; +} + +static void test_va(const char *ref, const char *fmt, ...) PRINTFRR(2, 3); +static void test_va(const char *ref, const char *fmt, ...) +{ + struct va_format vaf; + va_list ap; + + va_start(ap, fmt); + vaf.fmt = fmt; + vaf.va = ≈ + + printchk(ref, "VA [%pVA] %s", &vaf, "--"); + + va_end(ap); } int main(int argc, char **argv) @@ -112,9 +179,12 @@ int main(int argc, char **argv) inet_aton("192.168.1.2", &ip); printchk("192.168.1.2", "%pI4", &ip); printchk(" 192.168.1.2", "%20pI4", &ip); + printchk("192.168.1.2 ", "%-20pI4", &ip); printcmp("%p", &ip); + test_va("VA [192.168.1.2 1234] --", "%pI4 %u", &ip, 1234); + snprintfrr(buf, sizeof(buf), "test%s", "#1"); csnprintfrr(buf, sizeof(buf), "test%s", "#2"); assert(strcmp(buf, "test#1test#2") == 0); @@ -146,5 +216,42 @@ int main(int argc, char **argv) sg.src.s_addr = INADDR_ANY; printchk("(*,224.1.2.3)", "%pSG4", &sg); + uint8_t randhex[] = { 0x12, 0x34, 0x00, 0xca, 0xfe, 0x00, 0xaa, 0x55 }; + + FMT_NSTD(printchk("12 34 00 ca fe 00 aa 55", "%.8pHX", randhex)); + FMT_NSTD(printchk("12 34 00 ca fe 00 aa 55", "%.*pHX", + (int)sizeof(randhex), randhex)); + FMT_NSTD(printchk("12 34 00 ca", "%.4pHX", randhex)); + + printchk("12 34 00 ca fe 00 aa 55", "%8pHX", randhex); + printchk("12 34 00 ca fe 00 aa 55", "%*pHX", + (int)sizeof(randhex), randhex); + printchk("12 34 00 ca", "%4pHX", randhex); + + printchk("", "%pHX", randhex); + + printchk("12:34:00:ca:fe:00:aa:55", "%8pHXc", randhex); + printchk("123400cafe00aa55", "%8pHXn", randhex); + + printchk("/test/pa\\ th/\\~spe\\ncial\\x01/file.name", "%pSE", + "/test/pa th/~spe\ncial\x01/file.name"); + printchk("/test/pa\\ th/\\~spe\\n", "%17pSE", + "/test/pa th/~spe\ncial\x01/file.name"); + + char nulltest[] = { 'n', 'u', 0, 'l', 'l' }; + + printchk("nu\\x00ll", "%5pSE", nulltest); + printchk("nu\\x00ll", "%*pSE", 5, nulltest); + + printchk("bl\\\"ah\\x01te[st\\nab]c", "%pSQ", + "bl\"ah\x01te[st\nab]c"); + printchk("\"bl\\\"ah\\x01te[st\\nab]c\"", "%pSQq", + "bl\"ah\x01te[st\nab]c"); + printchk("\"bl\\\"ah\\x01te[st\\x0aab\\]c\"", "%pSQqs", + "bl\"ah\x01te[st\nab]c"); + printchk("\"\"", "%pSQqn", ""); + printchk("\"\"", "%pSQqn", (char *)NULL); + printchk("(null)", "%pSQq", (char *)NULL); + return !!errors; } diff --git a/tests/lib/test_table.c b/tests/lib/test_table.c index 290657bd56..9b6539e3bc 100644 --- a/tests/lib/test_table.c +++ b/tests/lib/test_table.c @@ -20,7 +20,7 @@ */ #include <zebra.h> - +#include "printfrr.h" #include "prefix.h" #include "table.h" @@ -113,7 +113,7 @@ static void print_subtree(struct route_node *rn, const char *legend, printf(" "); } - printf("%s: %pFX", legend, &rn->p); + printfrr("%s: %pFX", legend, &rn->p); if (!rn->info) { printf(" (internal)"); } diff --git a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post4.1.ref b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post4.1.ref index 6cc23a465c..b38701a53d 100644 --- a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post4.1.ref +++ b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post4.1.ref @@ -3,6 +3,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 192.168.0.0 0.0.0.0 0 32768 i diff --git a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post5.0.ref b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post5.0.ref index 2f348a7b77..82b64c0d98 100644 --- a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post5.0.ref +++ b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post5.0.ref @@ -3,6 +3,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 192.168.0.0/24 0.0.0.0 0 32768 i diff --git a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post6.1.ref b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post6.1.ref index d36d045397..fd333b3084 100644 --- a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post6.1.ref +++ b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv4-post6.1.ref @@ -4,6 +4,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> 192.168.0.0/24 0.0.0.0 0 32768 i diff --git a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6-post4.1.ref b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6-post4.1.ref index 8bb5da72be..20034b7408 100644 --- a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6-post4.1.ref +++ b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6-post4.1.ref @@ -3,6 +3,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> fc00::/64 :: 0 32768 i diff --git a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6_post6.1.ref b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6_post6.1.ref index de91b247d8..5b5f8596cf 100644 --- a/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6_post6.1.ref +++ b/tests/topotests/all-protocol-startup/r1/show_bgp_ipv6_post6.1.ref @@ -4,6 +4,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path *> fc00::/64 :: 0 32768 i diff --git a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py index c858571254..c10e32ad0a 100644 --- a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py +++ b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py @@ -346,9 +346,9 @@ def test_converge_protocols(): print("Show that v4 routes are right\n") v4_routesFile = "%s/r%s/ipv4_routes.ref" % (thisDir, i) - expected = net["r%s" % i].cmd( - "sort {} 2> /dev/null".format(v4_routesFile) - ).rstrip() + expected = ( + net["r%s" % i].cmd("sort {} 2> /dev/null".format(v4_routesFile)).rstrip() + ) expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) actual = ( @@ -379,9 +379,9 @@ def test_converge_protocols(): print("Show that v6 routes are right\n") v6_routesFile = "%s/r%s/ipv6_routes.ref" % (thisDir, i) - expected = net["r%s" % i].cmd( - "sort {} 2> /dev/null".format(v6_routesFile) - ).rstrip() + expected = ( + net["r%s" % i].cmd("sort {} 2> /dev/null".format(v6_routesFile)).rstrip() + ) expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1) actual = ( diff --git a/tests/topotests/bfd-profiles-topo1/r1/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r1/bfd-peers-initial.json index bab24c4fa0..86a7e5139c 100644 --- a/tests/topotests/bfd-profiles-topo1/r1/bfd-peers-initial.json +++ b/tests/topotests/bfd-profiles-topo1/r1/bfd-peers-initial.json @@ -6,14 +6,14 @@ "interface": "r1-eth1", "multihop": false, "peer": "172.16.100.2", - "receive-interval": 300, + "receive-interval": 800, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", "remote-id": "*", "remote-receive-interval": 300, "remote-transmit-interval": 300, "status": "up", - "transmit-interval": 300, + "transmit-interval": 800, "uptime": "*", "vrf": "default" }, diff --git a/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf b/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf index 4798d17c40..fcea5d48fc 100644 --- a/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf +++ b/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf @@ -2,7 +2,7 @@ interface r1-eth1 ip ospf area 0 ip ospf hello-interval 2 ip ospf dead-interval 10 - ip ospf bfd + ip ospf bfd profile slowtx ! router ospf ospf router-id 10.254.254.1 diff --git a/tests/topotests/bfd-profiles-topo1/r6/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r6/bfd-peers-initial.json index 4e6fa869ba..ec973eb365 100644 --- a/tests/topotests/bfd-profiles-topo1/r6/bfd-peers-initial.json +++ b/tests/topotests/bfd-profiles-topo1/r6/bfd-peers-initial.json @@ -10,8 +10,8 @@ "remote-detect-multiplier": 3, "remote-diagnostic": "ok", "remote-id": "*", - "remote-receive-interval": 300, - "remote-transmit-interval": 300, + "remote-receive-interval": 800, + "remote-transmit-interval": 800, "status": "up", "transmit-interval": 300, "uptime": "*", diff --git a/tests/topotests/bgp-default-ipv4-ipv6-unicast/__init__.py b/tests/topotests/bgp-default-ipv4-ipv6-unicast/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp-default-ipv4-ipv6-unicast/__init__.py diff --git a/tests/topotests/bgp-default-ipv4-ipv6-unicast/r1/bgpd.conf b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r1/bgpd.conf new file mode 100644 index 0000000000..bf39152ea8 --- /dev/null +++ b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r1/bgpd.conf @@ -0,0 +1,3 @@ +! +router bgp 65001 +! diff --git a/tests/topotests/bgp-default-ipv4-ipv6-unicast/r1/zebra.conf b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r1/zebra.conf new file mode 100644 index 0000000000..697765168d --- /dev/null +++ b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 192.168.255.1/24 +! +ip forwarding +! + diff --git a/tests/topotests/bgp-default-ipv4-ipv6-unicast/r2/bgpd.conf b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r2/bgpd.conf new file mode 100644 index 0000000000..abbd1b86fa --- /dev/null +++ b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r2/bgpd.conf @@ -0,0 +1,5 @@ +! +router bgp 65001 + no bgp default ipv4-unicast + bgp default ipv6-unicast +! diff --git a/tests/topotests/bgp-default-ipv4-ipv6-unicast/r2/zebra.conf b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r2/zebra.conf new file mode 100644 index 0000000000..606c17bec9 --- /dev/null +++ b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r2/zebra.conf @@ -0,0 +1,6 @@ +! +interface r2-eth0 + ip address 192.168.255.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp-default-ipv4-ipv6-unicast/r3/bgpd.conf b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r3/bgpd.conf new file mode 100644 index 0000000000..a405c047ca --- /dev/null +++ b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r3/bgpd.conf @@ -0,0 +1,4 @@ +! +router bgp 65001 + bgp default ipv6-unicast +! diff --git a/tests/topotests/bgp-default-ipv4-ipv6-unicast/r3/zebra.conf b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r3/zebra.conf new file mode 100644 index 0000000000..e9fdfb70c5 --- /dev/null +++ b/tests/topotests/bgp-default-ipv4-ipv6-unicast/r3/zebra.conf @@ -0,0 +1,6 @@ +! +interface r3-eth0 + ip address 192.168.255.3/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp-default-ipv4-ipv6-unicast/test_bgp-default-ipv4-ipv6-unicast.py b/tests/topotests/bgp-default-ipv4-ipv6-unicast/test_bgp-default-ipv4-ipv6-unicast.py new file mode 100644 index 0000000000..c1dbf0ebec --- /dev/null +++ b/tests/topotests/bgp-default-ipv4-ipv6-unicast/test_bgp-default-ipv4-ipv6-unicast.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +Test if `bgp default ipv4-unicast` and `bgp default ipv6-unicast` +commands work as expected. + +STEP 1: 'Check if neighbor 192.168.255.254 is enabled for ipv4 address-family only' +STEP 2: 'Check if neighbor 192.168.255.254 is enabled for ipv6 address-family only' +STEP 3: 'Check if neighbor 192.168.255.254 is enabled for ipv4 and ipv6 address-families' +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo +from lib.common_config import step + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_default_ipv4_ipv6_unicast(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Check if neighbor 192.168.255.254 is enabled for ipv4 address-family only") + + def _bgp_neighbor_ipv4_af_only(): + tgen.gears["r1"].vtysh_cmd( + "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external" + ) + + output = json.loads(tgen.gears["r1"].vtysh_cmd("show bgp summary json")) + + if "ipv4Unicast" in output and "ipv6Unicast" not in output: + return True + return False + + assert _bgp_neighbor_ipv4_af_only() == True + + step("Check if neighbor 192.168.255.254 is enabled for ipv6 address-family only") + + def _bgp_neighbor_ipv6_af_only(): + tgen.gears["r2"].vtysh_cmd( + "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external" + ) + + output = json.loads(tgen.gears["r2"].vtysh_cmd("show bgp summary json")) + + if "ipv4Unicast" not in output and "ipv6Unicast" in output: + return True + return False + + assert _bgp_neighbor_ipv6_af_only() == True + + step( + "Check if neighbor 192.168.255.254 is enabled for ipv4 and ipv6 address-families" + ) + + def _bgp_neighbor_ipv4_and_ipv6_af(): + tgen.gears["r3"].vtysh_cmd( + "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external" + ) + + output = json.loads(tgen.gears["r3"].vtysh_cmd("show bgp summary json")) + + if "ipv4Unicast" in output and "ipv6Unicast" in output: + return True + return False + + assert _bgp_neighbor_ipv4_and_ipv6_af() == True + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/__init__.py b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/__init__.py diff --git a/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r1/bgpd.conf b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r1/bgpd.conf new file mode 100644 index 0000000000..c320bb5d11 --- /dev/null +++ b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r1/bgpd.conf @@ -0,0 +1,7 @@ +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.101 remote-as external + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r1/zebra.conf b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r1/zebra.conf new file mode 100644 index 0000000000..1782edc2a5 --- /dev/null +++ b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r1/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.1.1/32 +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r2/bgpd.conf b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r2/bgpd.conf new file mode 100644 index 0000000000..cb712e9a8d --- /dev/null +++ b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r2/bgpd.conf @@ -0,0 +1,3 @@ +router bgp 65103 + no bgp ebgp-requires-policy + neighbor 192.168.1.101 remote-as external diff --git a/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r2/zebra.conf b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r2/zebra.conf new file mode 100644 index 0000000000..968171e7b9 --- /dev/null +++ b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.103/24 +! diff --git a/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r3/bgpd.conf b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r3/bgpd.conf new file mode 100644 index 0000000000..a6e3260d15 --- /dev/null +++ b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r3/bgpd.conf @@ -0,0 +1,5 @@ +router bgp 65000 + bgp router-id 192.168.1.101 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.103 remote-as external diff --git a/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r3/zebra.conf b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r3/zebra.conf new file mode 100644 index 0000000000..ddcf862132 --- /dev/null +++ b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/r3/zebra.conf @@ -0,0 +1,4 @@ +! +int r3-eth0 + ip address 192.168.1.101/24 +! diff --git a/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py new file mode 100644 index 0000000000..19c4c5f87d --- /dev/null +++ b/tests/topotests/bgp-ebgp-common-subnet-nexthop-unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python + +# Copyright (c) 2021 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +https://tools.ietf.org/html/rfc4271 + +Check if NEXT_HOP attribute is not changed if peer X shares a +common subnet with this address. + +- Otherwise, if the route being announced was learned from an + external peer, the speaker can use an IP address of any + adjacent router (known from the received NEXT_HOP attribute) + that the speaker itself uses for local route calculation in + the NEXT_HOP attribute, provided that peer X shares a common + subnet with this address. This is a second form of "third + party" NEXT_HOP attribute. +""" + +import os +import sys +import json +import time +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class TemplateTopo(Topo): + def build(self, *_args, **_opts): + tgen = get_topogen(self) + + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(TemplateTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_ebgp_common_subnet_nh_unchanged(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show ip bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "192.168.1.1": {"state": "Established"}, + "192.168.1.103": {"state": "Established"}, + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, r3) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Failed bgp convergence in "{}"'.format(r3) + + def _bgp_nh_unchanged(router): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.1.1/32 json")) + expected = {"paths": [{"nexthops": [{"ip": "192.168.1.1"}]}]} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_nh_unchanged, r2) + success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + + assert result is None, 'Wrong next-hop in "{}"'.format(r2) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py b/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py index 400e7e9bf5..75506d1a51 100644 --- a/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py +++ b/tests/topotests/bgp-ecmp-topo1/test_bgp_ecmp_topo1.py @@ -45,6 +45,10 @@ from lib.topolog import logger # Required to instantiate the topology builder class. from mininet.topo import Topo + +pytestmark = [pytest.mark.bgpd] + + total_ebgp_peers = 20 ##################################################### diff --git a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py index dfe6a8074d..fffcbbd0ef 100644 --- a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py +++ b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py @@ -67,6 +67,10 @@ from lib.topolog import logger from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp from lib.topojson import build_topo_from_json, build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + # Reading the data from JSON File for topology and configuration creation jsonFile = "{}/ebgp_ecmp_topo2.json".format(CWD) diff --git a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py index 2bde52af1d..342a0a4b2f 100644 --- a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py +++ b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py @@ -67,6 +67,10 @@ from lib.topolog import logger from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp from lib.topojson import build_topo_from_json, build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + # Reading the data from JSON File for topology and configuration creation jsonFile = "{}/ibgp_ecmp_topo2.json".format(CWD) diff --git a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py index 2744920272..f389632b1e 100644 --- a/tests/topotests/bgp-evpn-mh/test_evpn_mh.py +++ b/tests/topotests/bgp-evpn-mh/test_evpn_mh.py @@ -35,7 +35,7 @@ import json import platform from functools import partial -pytestmark = [pytest.mark.pimd] +pytestmark = [pytest.mark.bgpd, pytest.mark.pimd] # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py index db4eab9d3d..b830e16b9a 100755 --- a/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py +++ b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py @@ -505,8 +505,10 @@ def test_r1_mplsvpn_VrfTable(): associated_int = r1_snmp.get( "mplsL3VpnVrfAssociatedInterfaces.{}".format(snmp_str_to_oid("VRF-a")) ) - assertmsg = "mplsL3VpnVrfAssociatedInterfaces incorrect should be 3 value {}".format( - associated_int + assertmsg = ( + "mplsL3VpnVrfAssociatedInterfaces incorrect should be 3 value {}".format( + associated_int + ) ) assert associated_int == "3", assertmsg diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py index 222478f12d..320e6d430c 100644 --- a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py +++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py @@ -42,13 +42,11 @@ sys.path.append(os.path.join(CWD, "../")) from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger +from lib.common_config import adjust_router_l3mdev # Required to instantiate the topology builder class. from mininet.topo import Topo -l3mdev_accept = 0 -krel = "" - class BGPEVPNTopo(Topo): "Test topology builder" @@ -73,8 +71,6 @@ class BGPEVPNTopo(Topo): def setup_module(mod): "Sets up the pytest environment" - global l3mdev_accept - global krel tgen = Topogen(BGPEVPNTopo, mod.__name__) tgen.start_topology() @@ -90,18 +86,13 @@ def setup_module(mod): ) return pytest.skip("Skipping BGP EVPN RT5 NETNS Test. Kernel not supported") - l3mdev_accept = 1 - logger.info("setting net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept)) - # create VRF vrf-101 on R1 and R2 # create loop101 cmds_vrflite = [ - "sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept), "ip link add {}-vrf-101 type vrf table 101", "ip ru add oif {}-vrf-101 table 101", "ip ru add iif {}-vrf-101 table 101", "ip link set dev {}-vrf-101 up", - "sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept), "ip link add loop101 type dummy", "ip link set dev loop101 master {}-vrf-101", "ip link set dev loop101 up", @@ -139,6 +130,7 @@ def setup_module(mod): logger.info("result: " + output) router = tgen.gears["r2"] + adjust_router_l3mdev(tgen, "r2") for cmd in cmds_vrflite: logger.info("cmd to r2: " + cmd.format("r2")) output = router.run(cmd.format("r2")) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py index a6338d0c70..b70626fcce 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1.py @@ -258,10 +258,12 @@ def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) for addr_type in ADDR_TYPES: - clear_bgp(tgen, addr_type, dut) + neighbor = topo["routers"][peer]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, dut, neighbor=neighbor) for addr_type in ADDR_TYPES: - clear_bgp(tgen, addr_type, peer) + neighbor = topo["routers"][dut]["links"]["r2-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, peer, neighbor=neighbor) result = verify_bgp_convergence_from_running_config(tgen) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) @@ -551,7 +553,7 @@ def test_BGP_GR_TC_46_p1(request): write_test_footer(tc_name) -def test_BGP_GR_TC_50_p1(request): +def BGP_GR_TC_50_p1(request): """ Test Objective : Transition from Peer-level helper to Global inherit helper Global Mode : None @@ -613,9 +615,6 @@ def test_BGP_GR_TC_50_p1(request): configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") - result = verify_bgp_convergence_from_running_config(tgen) - assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Verify on R2 that R1 advertises GR capabilities as a helper node") for addr_type in ADDR_TYPES: @@ -721,7 +720,12 @@ def test_BGP_GR_TC_50_p1(request): } } - configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) result = verify_bgp_convergence_from_running_config(tgen) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) @@ -979,7 +983,15 @@ def test_BGP_GR_TC_51_p1(request): } } - configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2") + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) + + result = verify_bgp_convergence_from_running_config(tgen) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Verify on R2 that R1 advertises GR capabilities as a helper node") @@ -1317,20 +1329,22 @@ def test_BGP_GR_TC_4_p0(request): result = verify_bgp_rib( tgen, addr_type, dut, input_topo, next_hop, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) logger.info("[Phase 5] : R2 is about to come up now ") @@ -1695,10 +1709,10 @@ def test_BGP_GR_TC_6_1_2_p1(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) for addr_type in ADDR_TYPES: - clear_bgp(tgen, addr_type, "r1") - clear_bgp(tgen, addr_type, "r2") + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) - result = verify_bgp_convergence_from_running_config(tgen, topo) + result = verify_bgp_convergence_from_running_config(tgen) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) # Verify GR stats @@ -1790,10 +1804,11 @@ def test_BGP_GR_TC_6_1_2_p1(request): result = verify_r_bit( tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r2: R-bit is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r2: R-bit is set to True\n Error: {}".format( tc_name, result - )) + ) logger.info("Restart BGPd on R2 ") kill_router_daemons(tgen, "r2", ["bgpd"]) @@ -1811,10 +1826,11 @@ def test_BGP_GR_TC_6_1_2_p1(request): result = verify_r_bit( tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r2: R-bit is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r2: R-bit is set to True\n Error: {}".format( tc_name, result - )) + ) write_test_footer(tc_name) @@ -2096,20 +2112,22 @@ def test_BGP_GR_TC_17_p1(request): result = verify_bgp_rib( tgen, addr_type, dut, input_topo, next_hop, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) logger.info("[Phase 5] : R2 is about to come up now ") @@ -2128,10 +2146,11 @@ def test_BGP_GR_TC_17_p1(request): result = verify_r_bit( tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: R-bit is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: R-bit is set to True\n Error: {}".format( tc_name, result - )) + ) # Verifying BGP RIB routes next_hop = next_hop_per_address_family( @@ -2457,20 +2476,22 @@ def test_BGP_GR_TC_20_p1(request): result = verify_bgp_rib( tgen, addr_type, dut, input_topo, next_hop, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) logger.info("[Phase 5] : R2 is about to come up now ") @@ -2651,10 +2672,10 @@ def test_BGP_GR_TC_31_1_p1(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) for addr_type in ADDR_TYPES: - clear_bgp(tgen, addr_type, "r1") - clear_bgp(tgen, addr_type, "r2") + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) - result = verify_bgp_convergence_from_running_config(tgen, topo) + result = verify_bgp_convergence_from_running_config(tgen) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) # Verify GR stats @@ -2743,10 +2764,10 @@ def test_BGP_GR_TC_31_1_p1(request): result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info("[Phase 4] : R1 is about to come up now ") start_router_daemons(tgen, "r1", ["bgpd"]) @@ -2932,10 +2953,10 @@ def test_BGP_GR_TC_31_2_p1(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) for addr_type in ADDR_TYPES: - clear_bgp(tgen, addr_type, "r1") - clear_bgp(tgen, addr_type, "r2") + neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, "r1", neighbor=neighbor) - result = verify_bgp_convergence_from_running_config(tgen, topo) + result = verify_bgp_convergence_from_running_config(tgen) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) # Verify GR stats @@ -3225,10 +3246,12 @@ def test_BGP_GR_TC_9_p1(request): result = verify_bgp_rib( tgen, addr_type, dut, input_topo, next_hop, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes @@ -3236,10 +3259,10 @@ def test_BGP_GR_TC_9_p1(request): result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) logger.info("[Phase 5] : R2 is about to come up now ") @@ -3269,10 +3292,11 @@ def test_BGP_GR_TC_9_p1(request): result = verify_f_bit( tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: F-bit is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: F-bit is set to True\n Error: {}".format( tc_name, result - )) + ) write_test_footer(tc_name) @@ -3404,10 +3428,12 @@ def test_BGP_GR_TC_17_p1(request): result = verify_bgp_rib( tgen, addr_type, dut, input_topo, next_hop, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes @@ -3415,10 +3441,10 @@ def test_BGP_GR_TC_17_p1(request): result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) logger.info("[Phase 5] : R2 is about to come up now ") @@ -3440,10 +3466,11 @@ def test_BGP_GR_TC_17_p1(request): result = verify_r_bit( tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: R-bit is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: R-bit is set to True\n Error: {}".format( tc_name, result - )) + ) # Verifying BGP RIB routes next_hop = next_hop_per_address_family( @@ -3663,10 +3690,12 @@ def test_BGP_GR_TC_43_p1(request): result = verify_bgp_rib( tgen, addr_type, dut, input_topo, next_hop, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) protocol = "bgp" result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False @@ -3971,10 +4000,12 @@ def test_BGP_GR_TC_44_p1(request): result = verify_bgp_rib( tgen, addr_type, dut, input_topo, next_hop, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) @@ -4999,10 +5030,10 @@ def test_BGP_GR_TC_48_p1(request): result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) dut = "r2" peer = "r1" @@ -5013,17 +5044,19 @@ def test_BGP_GR_TC_48_p1(request): result = verify_bgp_rib( tgen, addr_type, dut, input_topo, next_hop, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r2: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r2: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) step("Bring up BGP on R1 and remove Peer-level GR config from R1") @@ -5258,7 +5291,7 @@ def test_BGP_GR_TC_49_p1(request): write_test_footer(tc_name) -def test_BGP_GR_TC_52_p1(request): +def BGP_GR_TC_52_p1(request): """ Test Objective : Transition from Peer-level disbale to Global inherit helper Global Mode : None @@ -5382,17 +5415,19 @@ def test_BGP_GR_TC_52_p1(request): result = verify_bgp_rib( tgen, addr_type, dut, input_topo, next_hop, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) result = verify_rib( tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) step("Bring up BGP on R2 and remove Peer-level GR config from R1") diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py index 2c5dd92f54..9438b90ef8 100644 --- a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py +++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py @@ -251,12 +251,14 @@ def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) for addr_type in ADDR_TYPES: - clear_bgp(tgen, addr_type, dut) + neighbor = topo["routers"][peer]["links"][dut][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, dut, neighbor=neighbor) for addr_type in ADDR_TYPES: - clear_bgp(tgen, addr_type, peer) + neighbor = topo["routers"][dut]["links"][peer][addr_type].split("/")[0] + clear_bgp(tgen, addr_type, peer, neighbor=neighbor) - result = verify_bgp_convergence_from_running_config(tgen, topo) + result = verify_bgp_convergence_from_running_config(tgen) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) return True @@ -555,10 +557,11 @@ def test_BGP_GR_TC_3_p0(request): result = verify_eor( tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r2: EOR is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r2: EOR is set to True\n Error: {}".format( tc_name, result - )) + ) logger.info( "Waiting for selection deferral timer({} sec)..".format(GR_SELECT_DEFER_TIMER) @@ -701,10 +704,11 @@ def test_BGP_GR_TC_11_p0(request): result = verify_eor( tgen, topo, addr_type, input_dict, dut="r1", peer="r3", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: EOR is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: EOR is set to True\n Error: {}".format( tc_name, result - )) + ) logger.info( "Waiting for selection deferral timer({} sec).. ".format( @@ -731,10 +735,11 @@ def test_BGP_GR_TC_11_p0(request): result = verify_eor( tgen, topo, addr_type, input_dict, dut="r3", peer="r1", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r3: EOR is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r3: EOR is set to True\n Error: {}".format( tc_name, result - )) + ) write_test_footer(tc_name) @@ -930,7 +935,7 @@ def test_BGP_GR_10_p2(request): write_test_footer(tc_name) -def test_BGP_GR_16_p2(request): +def BGP_GR_16_p2(request): """ Test Objective : Verify BGP-GR feature when restarting node is a transit router for it's iBGP peers. @@ -1468,35 +1473,39 @@ def test_BGP_GR_18_p1(request): dut = "r6" input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r6: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes before shutting down BGPd daemon result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r6: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r6: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying BGP RIB routes dut = "r2" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes before shutting down BGPd daemon result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r6: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r6: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) write_test_footer(tc_name) @@ -1957,18 +1966,20 @@ def test_BGP_GR_chaos_29_p1(request): # Verifying BGP RIB routes before shutting down BGPd daemon input_dict = {key: topo["routers"][key] for key in ["r1"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes before shutting down BGPd daemon result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) logger.info("[Step 4] : Start BGPd daemon on R1..") @@ -2210,10 +2221,12 @@ def test_BGP_GR_chaos_33_p1(request): result = verify_rib( tgen, addr_type, dut, input_dict_2, next_hop_4, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) if addr_type == "ipv6": @@ -2225,10 +2238,12 @@ def test_BGP_GR_chaos_33_p1(request): result = verify_rib( tgen, addr_type, dut, input_dict_2, next_hop_6, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) logger.info("[Step 4] : Start BGPd daemon on R1 and R4..") @@ -2409,27 +2424,30 @@ def test_BGP_GR_chaos_34_2_p1(request): result = verify_f_bit( tgen, topo, addr_type, input_dict, "r3", "r1", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r3: F-bit is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r3: F-bit is set to True\n Error: {}".format( tc_name, result - )) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying BGP RIB routes after starting BGPd daemon input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) write_test_footer(tc_name) @@ -2566,10 +2584,11 @@ def test_BGP_GR_chaos_34_1_p1(request): result = verify_f_bit( tgen, topo, addr_type, input_dict_2, "r3", "r1", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r3: F-bit is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r3: F-bit is set to True\n Error: {}".format( tc_name, result - )) + ) logger.info(" Expected behavior: {}".format(result)) logger.info("[Step 3] : Kill BGPd daemon on R1..") @@ -2585,18 +2604,20 @@ def test_BGP_GR_chaos_34_1_p1(request): # Verifying BGP RIB routes input_dict = {key: topo["routers"][key] for key in ["r1"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) # Start BGPd daemon on R1 @@ -2770,27 +2791,30 @@ def test_BGP_GR_chaos_32_p1(request): result = verify_eor( tgen, topo, addr_type, input_dict_3, dut="r5", peer="r1", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r5: EOR is set to TRUE\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r5: EOR is set to TRUE\n Error: {}".format( tc_name, result - )) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying BGP RIB routes after starting BGPd daemon input_dict_1 = {key: topo["routers"][key] for key in ["r5"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) write_test_footer(tc_name) @@ -2896,10 +2920,11 @@ def test_BGP_GR_chaos_37_p1(request): result = verify_eor( tgen, topo, addr_type, input_dict, dut="r3", peer="r1", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r3: EOR is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r3: EOR is set to True\n Error: {}".format( tc_name, result - )) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying BGP RIB routes after starting BGPd daemon @@ -2962,10 +2987,11 @@ def test_BGP_GR_chaos_37_p1(request): result = verify_eor( tgen, topo, addr_type, input_dict_3, dut="r1", peer="r3", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: EOR is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: EOR is set to True\n Error: {}".format( tc_name, result - )) + ) write_test_footer(tc_name) @@ -3117,18 +3143,20 @@ def test_BGP_GR_chaos_30_p1(request): # Verifying BGP RIB routes before shutting down BGPd daemon input_dict = {key: topo["routers"][key] for key in ["r3"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes before shutting down BGPd daemon result = verify_rib(tgen, addr_type, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) write_test_footer(tc_name) @@ -3530,10 +3558,10 @@ def BGP_GR_TC_7_p1(request): dut = "r1" input_dict_1 = {key: topo["routers"][key] for key in ["r3"]} result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) write_test_footer(tc_name) @@ -3707,10 +3735,11 @@ def test_BGP_GR_TC_23_p1(request): result = verify_eor( tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: EOR is set to True\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: EOR is set to True\n Error: {}".format( tc_name, result - )) + ) # Verifying BGP RIB routes received from router R1 dut = "r1" @@ -3831,18 +3860,20 @@ def test_BGP_GR_20_p1(request): dut = "r3" input_dict_1 = {key: topo["routers"][key] for key in ["r1"]} result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: routes are still present in BGP RIB\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info(" Expected behavior: {}".format(result)) # Verifying RIB routes before shutting down BGPd daemon result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r3: routes are still present in ZEBRA\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result) + ) logger.info(" Expected behavior: {}".format(result)) # Start BGPd daemon on R1 diff --git a/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py b/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py index 47cc0eb39d..0c7e84a5a3 100755 --- a/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py +++ b/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py @@ -30,6 +30,9 @@ sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../") from lib.ltemplate import * +pytestmark = [pytest.mark.bgpd, pytest.mark.ldpd, pytest.mark.ospfd] + + def test_check_linux_vrf(): CliOnFail = None # For debugging, uncomment the next line diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py index 783e746418..cd845be296 100644 --- a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py +++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py @@ -46,6 +46,9 @@ from lib.topolog import logger from mininet.topo import Topo +pytestmark = [pytest.mark.bgpd] + + class BGPIPV6RTADVTopo(Topo): "Test topology builder" diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py index 1c3c51f68e..5d97537bd0 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -84,6 +84,7 @@ from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger from lib.ltemplate import ltemplateRtrCmd +from lib.common_config import adjust_router_l3mdev # Required to instantiate the topology builder class. from mininet.topo import Topo @@ -145,26 +146,12 @@ class ThisTestTopo(Topo): switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth1") -l3mdev_accept = 0 - - def ltemplatePreRouterStartHook(): - global l3mdev_accept cc = ltemplateRtrCmd() krel = platform.release() tgen = get_topogen() logger.info("pre router-start hook, kernel=" + krel) - if ( - topotest.version_cmp(krel, "4.15") >= 0 - and topotest.version_cmp(krel, "4.18") <= 0 - ): - l3mdev_accept = 1 - - if topotest.version_cmp(krel, "5.0") >= 0: - l3mdev_accept = 1 - - logger.info("setting net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept)) # check for mpls if tgen.hasmpls != True: logger.info("MPLS not available, skipping setup") @@ -187,10 +174,11 @@ def ltemplatePreRouterStartHook(): "ip ru add oif {0}-cust1 table 10", "ip ru add iif {0}-cust1 table 10", "ip link set dev {0}-cust1 up", - "sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept), ] for rtr in rtrs: - router = tgen.gears[rtr] + # adjust handling of VRF traffic + adjust_router_l3mdev(tgen, rtr) + for cmd in cmds: cc.doCmd(tgen, rtr, cmd.format(rtr)) cc.doCmd(tgen, rtr, "ip link set dev {0}-eth4 master {0}-cust1".format(rtr)) @@ -229,9 +217,11 @@ def ltemplatePreRouterStartHook(): "ip ru add oif {0}-cust2 table 20", "ip ru add iif {0}-cust2 table 20", "ip link set dev {0}-cust2 up", - "sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept), ] for rtr in rtrs: + # adjust handling of VRF traffic + adjust_router_l3mdev(tgen, rtr) + for cmd in cmds: cc.doCmd(tgen, rtr, cmd.format(rtr)) cc.doCmd(tgen, rtr, "ip link set dev {0}-eth0 master {0}-cust2".format(rtr)) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py index 7c154ecd15..650ba20b8c 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py @@ -1,6 +1,7 @@ from lib.lutil import luCommand -from customize import l3mdev_accept +from lib.common_config import kernel_requires_l3mdev_adjustment +l3mdev_accept = kernel_requires_l3mdev_adjustment() l3mdev_rtrs = ["r1", "r3", "r4", "ce4"] for rtr in l3mdev_rtrs: luCommand(rtr, "sysctl net.ipv4.tcp_l3mdev_accept", " = \d*", "none", "") diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py index 8e5ffe10be..84d9c48f35 100644 --- a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py +++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py @@ -598,10 +598,12 @@ def test_large_community_lists_with_rmap_apply_and_remove(request): result = verify_bgp_community( tgen, adt, dut, NETWORKS[adt], input_dict_4, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "largeCommunity is still present after deleting route-map \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) write_test_footer(tc_name) @@ -899,10 +901,10 @@ def test_large_community_lists_with_rmap_set_none(request): dut = "r6" for adt in ADDR_TYPES: result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "Community-list is still present \n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "Community-list is still present \n Error: {}".format(tc_name, result) + ) write_test_footer(tc_name) @@ -2238,10 +2240,10 @@ def test_large_community_lists_with_rmap_match_regex(request): result = verify_bgp_community( tgen, adt, dut, NETWORKS[adt], input_dict_7, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "largeCommunity is still present \n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "largeCommunity is still present \n Error: {}".format(tc_name, result) + ) write_test_footer(tc_name) diff --git a/tests/topotests/bgp_lu_topo1/test_bgp_lu.py b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py index d550c38a2f..d1745674f0 100644 --- a/tests/topotests/bgp_lu_topo1/test_bgp_lu.py +++ b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py @@ -45,6 +45,10 @@ from lib.topolog import logger # Required to instantiate the topology builder class. from mininet.topo import Topo + +pytestmark = [pytest.mark.bgpd] + + # Basic scenario for BGP-LU. Nodes are directly connected. # Node 3 is advertising many routes to 2, which advertises them # as BGP-LU to 1; this way we get routes with actual labels, as diff --git a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py index ac7ee44b25..5ecaee2ece 100644 --- a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py +++ b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py @@ -115,7 +115,7 @@ sys.path.append(os.path.join(CWD, "../lib/")) # Import topogen and topotest helpers from lib.topogen import Topogen, get_topogen from mininet.topo import Topo - +from lib.topotest import iproute2_is_vrf_capable from lib.common_config import ( step, verify_rib, @@ -215,6 +215,10 @@ def setup_module(mod): if result is not True: pytest.skip("Kernel requirements are not met") + # iproute2 needs to support VRFs for this suite to run. + if not iproute2_is_vrf_capable(): + pytest.skip("Installed iproute2 version does not support VRFs") + testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) logger.info("=" * 40) @@ -504,10 +508,10 @@ def test_ambiguous_overlapping_addresses_in_different_vrfs_p0(request): ) result = verify_rib(tgen, addr_type, dut, input_dict_1, tag=500, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "Routes are present with tag value 500 \n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "Routes are present with tag value 500 \n Error: {}".format(tc_name, result) + ) logger.info("Expected Behavior: {}".format(result)) step( @@ -1143,10 +1147,12 @@ def test_prefixes_leaking_p0(request): result = verify_rib( tgen, addr_type, dut, input_dict_1, metric=123, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "Routes are present with metric value 123 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info("Expected Behavior: {}".format(result)) result = verify_rib(tgen, addr_type, dut, input_dict_2, metric=123) @@ -1157,10 +1163,12 @@ def test_prefixes_leaking_p0(request): result = verify_rib( tgen, addr_type, dut, input_dict_2, metric=0, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "Routes are present with metric value 0 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info("Expected Behavior: {}".format(result)) write_test_footer(tc_name) diff --git a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py index c6e1792e84..c8d1330122 100644 --- a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py +++ b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py @@ -71,7 +71,7 @@ sys.path.append(os.path.join(CWD, "../lib/")) # Import topogen and topotest helpers from lib.topogen import Topogen, get_topogen from mininet.topo import Topo - +from lib.topotest import iproute2_is_vrf_capable from lib.common_config import ( step, verify_rib, @@ -102,6 +102,10 @@ from lib.topolog import logger from lib.bgp import clear_bgp, verify_bgp_rib, create_router_bgp, verify_bgp_convergence from lib.topojson import build_config_from_json, build_topo_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + # Reading the data from JSON File for topology creation jsonFile = "{}/bgp_multi_vrf_topo2.json".format(CWD) @@ -164,6 +168,10 @@ def setup_module(mod): if result is not True: pytest.skip("Kernel requirements are not met") + # iproute2 needs to support VRFs for this suite to run. + if not iproute2_is_vrf_capable(): + pytest.skip("Installed iproute2 version does not support VRFs") + testsuite_run_time = time.asctime(time.localtime(time.time())) logger.info("Testsuite start time: {}".format(testsuite_run_time)) logger.info("=" * 40) @@ -2214,16 +2222,20 @@ def test_restart_bgpd_daemon_p1(request): } result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "Routes are still present in VRF RED_A and RED_B \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "Routes are still present in VRF BLUE_A and BLUE_B \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("Bring up BGPd daemon on R1.") start_router_daemons(tgen, "r1", ["bgpd"]) diff --git a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_1-post4.1.ref b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_1-post4.1.ref index 9e30bf2ef0..6b20e1df5a 100644 --- a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_1-post4.1.ref +++ b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_1-post4.1.ref @@ -3,6 +3,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path * 10.0.1.0/24 172.16.1.5 0 65005 i diff --git a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_1-post6.1.ref b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_1-post6.1.ref index 2cf87487ab..5469eaa1cc 100644 --- a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_1-post6.1.ref +++ b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_1-post6.1.ref @@ -4,6 +4,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path * 10.0.1.0/24 172.16.1.5 0 65005 i diff --git a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_2-post4.1.ref b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_2-post4.1.ref index 39eb3134be..a64927c92d 100644 --- a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_2-post4.1.ref +++ b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_2-post4.1.ref @@ -3,6 +3,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path * 10.0.1.0/24 172.16.1.4 0 65004 i diff --git a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_2-post6.1.ref b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_2-post6.1.ref index 9d1b948b5c..8d4a843b84 100644 --- a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_2-post6.1.ref +++ b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_2-post6.1.ref @@ -4,6 +4,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path * 10.0.1.0/24 172.16.1.4 0 65004 i diff --git a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_3-post4.1.ref b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_3-post4.1.ref index fa53d79e88..a3b9ef0888 100644 --- a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_3-post4.1.ref +++ b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_3-post4.1.ref @@ -3,6 +3,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path * 10.0.1.0/24 172.16.1.8 0 65008 i diff --git a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_3-post6.1.ref b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_3-post6.1.ref index 8b66fa67ec..117e48847a 100644 --- a/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_3-post6.1.ref +++ b/tests/topotests/bgp_multiview_topo1/r1/show_ip_bgp_view_3-post6.1.ref @@ -4,6 +4,7 @@ Status codes: s suppressed, d damped, h history, * valid, > best, = multipath, i internal, r RIB-failure, S Stale, R Removed Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self Origin codes: i - IGP, e - EGP, ? - incomplete +RPKI validation codes: V valid, I invalid, N Not found Network Next Hop Metric LocPrf Weight Path * 10.0.1.0/24 172.16.1.8 0 65008 i diff --git a/tests/topotests/bgp_peer-group/test_bgp_peer-group.py b/tests/topotests/bgp_peer-group/test_bgp_peer-group.py index 7c7a8b87ed..21dc725793 100644 --- a/tests/topotests/bgp_peer-group/test_bgp_peer-group.py +++ b/tests/topotests/bgp_peer-group/test_bgp_peer-group.py @@ -39,6 +39,9 @@ from lib.topolog import logger from mininet.topo import Topo +pytestmark = [pytest.mark.bgpd] + + class TemplateTopo(Topo): def build(self, *_args, **_opts): tgen = get_topogen(self) diff --git a/tests/topotests/bgp_peer-type_multipath-relax/exabgp.env b/tests/topotests/bgp_peer-type_multipath-relax/exabgp.env new file mode 100644 index 0000000000..6c554f5fa8 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/exabgp.env @@ -0,0 +1,53 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer1/exa-receive.py b/tests/topotests/bgp_peer-type_multipath-relax/peer1/exa-receive.py new file mode 100755 index 0000000000..031ff455ca --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer1/exa-receive.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +""" +exa-receive.py: Save received routes form ExaBGP into file +""" + +from sys import stdin, argv +from datetime import datetime + +# 1st arg is peer number +peer = int(argv[1]) + +# When the parent dies we are seeing continual newlines, so we only access so many before stopping +counter = 0 + +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") + +while True: + try: + line = stdin.readline() + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") + routesavefile.write(timestamp + line) + routesavefile.flush() + + if line == "": + counter += 1 + if counter > 100: + break + continue + + counter = 0 + except KeyboardInterrupt: + pass + except IOError: + # most likely a signal during readline + pass + +routesavefile.close() diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer1/exa_readpipe.py b/tests/topotests/bgp_peer-type_multipath-relax/peer1/exa_readpipe.py new file mode 100644 index 0000000000..9e689a27e3 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer1/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer1/exabgp.cfg b/tests/topotests/bgp_peer-type_multipath-relax/peer1/exabgp.cfg new file mode 100644 index 0000000000..4a7dc48126 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer1/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 1"; + receive-routes; + encoder text; + } + + neighbor 10.0.1.1 { + router-id 10.0.1.2; + local-address 10.0.1.2; + local-as 64510; + peer-as 64510; + } + +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer2/exa-receive.py b/tests/topotests/bgp_peer-type_multipath-relax/peer2/exa-receive.py new file mode 100755 index 0000000000..031ff455ca --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer2/exa-receive.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +""" +exa-receive.py: Save received routes form ExaBGP into file +""" + +from sys import stdin, argv +from datetime import datetime + +# 1st arg is peer number +peer = int(argv[1]) + +# When the parent dies we are seeing continual newlines, so we only access so many before stopping +counter = 0 + +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") + +while True: + try: + line = stdin.readline() + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") + routesavefile.write(timestamp + line) + routesavefile.flush() + + if line == "": + counter += 1 + if counter > 100: + break + continue + + counter = 0 + except KeyboardInterrupt: + pass + except IOError: + # most likely a signal during readline + pass + +routesavefile.close() diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer2/exa_readpipe.py b/tests/topotests/bgp_peer-type_multipath-relax/peer2/exa_readpipe.py new file mode 100644 index 0000000000..9e689a27e3 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer2/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer2/exabgp.cfg b/tests/topotests/bgp_peer-type_multipath-relax/peer2/exabgp.cfg new file mode 100644 index 0000000000..b53b054550 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer2/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 2"; + receive-routes; + encoder text; + } + + neighbor 10.0.2.1 { + router-id 10.0.2.2; + local-address 10.0.2.2; + local-as 64511; + peer-as 64511; + } + +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer3/exa-receive.py b/tests/topotests/bgp_peer-type_multipath-relax/peer3/exa-receive.py new file mode 100755 index 0000000000..031ff455ca --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer3/exa-receive.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +""" +exa-receive.py: Save received routes form ExaBGP into file +""" + +from sys import stdin, argv +from datetime import datetime + +# 1st arg is peer number +peer = int(argv[1]) + +# When the parent dies we are seeing continual newlines, so we only access so many before stopping +counter = 0 + +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") + +while True: + try: + line = stdin.readline() + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") + routesavefile.write(timestamp + line) + routesavefile.flush() + + if line == "": + counter += 1 + if counter > 100: + break + continue + + counter = 0 + except KeyboardInterrupt: + pass + except IOError: + # most likely a signal during readline + pass + +routesavefile.close() diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer3/exa_readpipe.py b/tests/topotests/bgp_peer-type_multipath-relax/peer3/exa_readpipe.py new file mode 100644 index 0000000000..9e689a27e3 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer3/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer3/exabgp.cfg b/tests/topotests/bgp_peer-type_multipath-relax/peer3/exabgp.cfg new file mode 100644 index 0000000000..6a1cc2fb3f --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer3/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 3"; + receive-routes; + encoder text; + } + + neighbor 10.0.3.1 { + router-id 10.0.3.2; + local-address 10.0.3.2; + local-as 64502; + peer-as 64501; + } + +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer4/exa-receive.py b/tests/topotests/bgp_peer-type_multipath-relax/peer4/exa-receive.py new file mode 100755 index 0000000000..031ff455ca --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer4/exa-receive.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python + +""" +exa-receive.py: Save received routes form ExaBGP into file +""" + +from sys import stdin, argv +from datetime import datetime + +# 1st arg is peer number +peer = int(argv[1]) + +# When the parent dies we are seeing continual newlines, so we only access so many before stopping +counter = 0 + +routesavefile = open("/tmp/peer%s-received.log" % peer, "w") + +while True: + try: + line = stdin.readline() + timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ") + routesavefile.write(timestamp + line) + routesavefile.flush() + + if line == "": + counter += 1 + if counter > 100: + break + continue + + counter = 0 + except KeyboardInterrupt: + pass + except IOError: + # most likely a signal during readline + pass + +routesavefile.close() diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer4/exa_readpipe.py b/tests/topotests/bgp_peer-type_multipath-relax/peer4/exa_readpipe.py new file mode 100644 index 0000000000..9e689a27e3 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer4/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, "r") + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_peer-type_multipath-relax/peer4/exabgp.cfg b/tests/topotests/bgp_peer-type_multipath-relax/peer4/exabgp.cfg new file mode 100644 index 0000000000..2cc26cb80f --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/peer4/exabgp.cfg @@ -0,0 +1,21 @@ +group controller { + + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in"; + encoder text; + } + + process receive-routes { + run "/etc/exabgp/exa-receive.py 4"; + receive-routes; + encoder text; + } + + neighbor 10.0.4.1 { + router-id 10.0.4.2; + local-address 10.0.4.2; + local-as 64503; + peer-as 64501; + } + +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/bgpd.conf b/tests/topotests/bgp_peer-type_multipath-relax/r1/bgpd.conf new file mode 100644 index 0000000000..038f108aa8 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/bgpd.conf @@ -0,0 +1,16 @@ +! +router bgp 64510 + bgp router-id 10.0.1.1 + no bgp ebgp-requires-policy + bgp confederation identifier 64501 + bgp confederation peers 64511 + bgp bestpath as-path multipath-relax + bgp bestpath compare-routerid + bgp bestpath peer-type multipath-relax + neighbor 10.0.1.2 remote-as 64510 + neighbor 10.0.3.2 remote-as 64502 + neighbor 10.0.4.2 remote-as 64503 + neighbor 10.0.5.2 remote-as 64511 +! +line vty +! diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/multipath.json b/tests/topotests/bgp_peer-type_multipath-relax/r1/multipath.json new file mode 100644 index 0000000000..11dad786f2 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/multipath.json @@ -0,0 +1,50 @@ +{ + "routes": { "203.0.113.0/30": [ + { + "valid":true, + "multipath":true, + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Peer Type", + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "multipath":true, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.4/30": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"Confed Peer Type", + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "multipath":true, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.8/30": [ + { + "valid":true, + "multipath":true, + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Router ID", + "pathFrom":"external", + "peerId":"10.0.3.2" + } +] } } diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/not-multipath.json b/tests/topotests/bgp_peer-type_multipath-relax/r1/not-multipath.json new file mode 100644 index 0000000000..c621832157 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/not-multipath.json @@ -0,0 +1,50 @@ +{ + "routes": { "203.0.113.0/30": [ + { + "valid":true, + "multipath":null, + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Peer Type", + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "multipath":null, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.4/30": [ + { + "valid":true, + "bestpath":true, + "selectionReason":"Confed Peer Type", + "pathFrom":"external", + "peerId":"10.0.5.2" + }, + { + "valid":true, + "multipath":null, + "pathFrom":"internal", + "peerId":"10.0.1.2" + } +],"203.0.113.8/30": [ + { + "valid":true, + "multipath":true, + "pathFrom":"external", + "peerId":"10.0.4.2" + }, + { + "valid":true, + "bestpath":true, + "selectionReason":"Router ID", + "pathFrom":"external", + "peerId":"10.0.3.2" + } +] } } diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-eBGP-confed.json b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-eBGP-confed.json new file mode 100644 index 0000000000..22ec2c298b --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-eBGP-confed.json @@ -0,0 +1,33 @@ +{ + "203.0.113.0\/30":[ + { + "prefix":"203.0.113.0\/30", + "protocol":"bgp", + "installed":true, + "internalNextHopNum":4, + "internalNextHopActiveNum":4, + "nexthops":[ + { + "ip":"198.51.100.2", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + }, + { + "ip":"198.51.100.10", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-eBGP-iBGP.json b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-eBGP-iBGP.json new file mode 100644 index 0000000000..facddcda46 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-eBGP-iBGP.json @@ -0,0 +1,33 @@ +{ + "203.0.113.0\/30":[ + { + "prefix":"203.0.113.0\/30", + "protocol":"bgp", + "installed":true, + "internalNextHopNum":4, + "internalNextHopActiveNum":4, + "nexthops":[ + { + "ip":"198.51.100.1", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + }, + { + "ip":"198.51.100.10", + "active":true, + "recursive":true + }, + { + "fib":true, + "ip":"10.0.3.2", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-no-recursive.json b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-no-recursive.json new file mode 100644 index 0000000000..5399ceefcc --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-no-recursive.json @@ -0,0 +1,35 @@ +{ + "prefix":"203.0.113.0\/30", + "paths":[ + { + "valid":false, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true, + "selectionReason":"Confed Peer Type" + }, + "peer":{ + "peerId":"10.0.5.2", + "routerId":"10.0.5.2", + "type":"confed-external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.1.2", + "routerId":"10.0.1.2", + "type":"confed-internal" + } + } + ] +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-recursive.json b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-recursive.json new file mode 100644 index 0000000000..7da95aed1c --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1-recursive.json @@ -0,0 +1,36 @@ +{ + "prefix":"203.0.113.0\/30", + "paths":[ + { + "valid":true, + "multipath":true, + "bestpath":{ + "overall":true, + "selectionReason":"Peer Type" + }, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.5.2", + "routerId":"10.0.5.2", + "type":"confed-external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.1.2", + "routerId":"10.0.1.2", + "type":"confed-internal" + } + } + ] +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1.json b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1.json new file mode 100644 index 0000000000..a90669a474 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix1.json @@ -0,0 +1,33 @@ +{ + "prefix":"203.0.113.0\/30", + "paths":[ + { + "multipath":true, + "peer":{ + "peerId":"10.0.5.2", + "routerId":"10.0.5.2", + "type":"confed-external" + } + }, + { + "multipath":true, + "bestpath":{ + "overall":true, + "selectionReason":"Peer Type" + }, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "multipath":true, + "peer":{ + "peerId":"10.0.1.2", + "routerId":"10.0.1.2", + "type":"confed-internal" + } + } + ] +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix3-ip-route.json b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix3-ip-route.json new file mode 100644 index 0000000000..1bf38efcc5 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix3-ip-route.json @@ -0,0 +1,23 @@ +{ + "203.0.113.8\/30":[ + { + "prefix":"203.0.113.8\/30", + "protocol":"bgp", + "installed":true, + "internalNextHopNum":2, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "fib":true, + "ip":"10.0.3.2", + "active":true + }, + { + "fib":null, + "ip":"198.51.100.10", + "active":null + } + ] + } + ] +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix3-no-recursive.json b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix3-no-recursive.json new file mode 100644 index 0000000000..33d0f2d1ce --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix3-no-recursive.json @@ -0,0 +1,21 @@ +{ + "prefix":"203.0.113.8\/30", + "paths":[ + { + "valid":false, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "peer":{ + "peerId":"10.0.3.2", + "routerId":"10.0.3.2", + "type":"external" + } + } + ] +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix3-recursive.json b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix3-recursive.json new file mode 100644 index 0000000000..6ac2512a60 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/prefix3-recursive.json @@ -0,0 +1,23 @@ +{ + "prefix":"203.0.113.8\/30", + "paths":[ + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.4.2", + "routerId":"10.0.4.2", + "type":"external" + } + }, + { + "valid":true, + "multipath":true, + "peer":{ + "peerId":"10.0.3.2", + "routerId":"10.0.3.2", + "type":"external" + } + } + ] +} diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r1/zebra.conf b/tests/topotests/bgp_peer-type_multipath-relax/r1/zebra.conf new file mode 100644 index 0000000000..911aa1c39d --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r1/zebra.conf @@ -0,0 +1,27 @@ +! +hostname r1 +! +interface r1-eth0 + description ExaBGP iBGP peer1 + ip address 10.0.1.1/24 + no link-detect +! +interface r1-eth1 + description ExaBGP peer3 + ip address 10.0.3.1/24 + no link-detect +! +interface r1-eth2 + description ExaBGP peer4 + ip address 10.0.4.1/24 + no link-detect +! +interface r1-eth3 + description r2 confed peer + ip address 10.0.5.1/24 + no link-detect +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r2/bgpd.conf b/tests/topotests/bgp_peer-type_multipath-relax/r2/bgpd.conf new file mode 100644 index 0000000000..2362a19f26 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r2/bgpd.conf @@ -0,0 +1,19 @@ +! +!log file bgpd.log +! +router bgp 64511 + bgp confederation identifier 64501 + bgp confederation peers 64510 + bgp router-id 10.0.5.2 + no bgp ebgp-requires-policy + neighbor 10.0.2.2 remote-as 64511 + neighbor 10.0.5.1 remote-as 64510 + ! + address-family ipv4 unicast + neighbor 10.0.5.1 route-map dropall in + exit-address-family +! +route-map dropall deny 10 +! +line vty +! diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r2/staticd.conf b/tests/topotests/bgp_peer-type_multipath-relax/r2/staticd.conf new file mode 100644 index 0000000000..35ebe0dc66 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r2/staticd.conf @@ -0,0 +1,4 @@ +hostname r2 +! +ip route 198.51.100.0/24 10.0.2.2 +! diff --git a/tests/topotests/bgp_peer-type_multipath-relax/r2/zebra.conf b/tests/topotests/bgp_peer-type_multipath-relax/r2/zebra.conf new file mode 100644 index 0000000000..900e7d4fbc --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/r2/zebra.conf @@ -0,0 +1,19 @@ +! +! +hostname r2 +! +interface r2-eth0 + description ExaBGP peer + ip address 10.0.2.1/24 + no link-detect +! +interface r2-eth1 + description r1 confed peer + ip address 10.0.5.2/24 + no link-detect +! +ip forwarding +! +! +line vty +! diff --git a/tests/topotests/bgp_peer-type_multipath-relax/test_bgp_peer-type_multipath-relax.py b/tests/topotests/bgp_peer-type_multipath-relax/test_bgp_peer-type_multipath-relax.py new file mode 100755 index 0000000000..39a0beeb11 --- /dev/null +++ b/tests/topotests/bgp_peer-type_multipath-relax/test_bgp_peer-type_multipath-relax.py @@ -0,0 +1,386 @@ +#!/usr/bin/env python + +# +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 Arista Networks, Inc. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND Arista Networks DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bgp_peer-type_multipath-relax.py: + +Test the effects of the "bgp bestpath peer-type multipath-relax" command + +- enabling the command allows eBGP, iBGP, and confed routes to be multipath +- the choice of best path is not affected +- disabling the command removes iBGP/confed routes from multipath +- enabling the command does not forgive eBGP routes of the requirement + (when enabled) that next hops resolve over connected routes +- a mixed-type multipath next hop, when published to zebra, does not + require resolving next hops over connected routes +- with the command enabled, an all-eBGP multipath next hop still requires + resolving next hops over connected routes when published to zebra + +Topology used by the test: + + eBGP +------+ iBGP + peer1 ---- | r1 | ---- peer3 + | | +peer2 ---- r2 ---- | | ---- peer4 + iBGP confed +------+ eBGP + +r2 is present in this topology because ExaBGP does not currently support +confederations so we use FRR to advertise the required AS_CONFED_SEQUENCE. + +Routes are advertised from different peers to form interesting multipaths. + + peer1 peer2 peer3 peer4 multipath on r1 + +203.0.113.0/30 x x x all 3 +203.0.113.4/30 x x confed-iBGP +203.0.113.8/30 x x eBGP-only + +There is also a BGP-advertised route used only for recursively resolving +next hops. +""" + +import functools +import json +import os +import pytest +import sys + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from mininet.topo import Topo + + +class PeerTypeRelaxTopo(Topo): + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Set up routers + tgen.add_router("r1") # DUT + tgen.add_router("r2") + + # Set up peers + for peern in range(1, 5): + peer = tgen.add_exabgp_peer( + "peer{}".format(peern), + ip="10.0.{}.2/24".format(peern), + defaultRoute="via 10.0.{}.1".format(peern), + ) + if peern == 2: + tgen.add_link(tgen.gears["r2"], peer) + else: + tgen.add_link(tgen.gears["r1"], peer) + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(PeerTypeRelaxTopo, mod.__name__) + tgen.start_topology() + + # For all registered routers, load the zebra configuration file + for rname, router in tgen.routers().items(): + router.run("/bin/bash {}/setup_vrfs".format(CWD)) + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + # Start up exabgp peers + peers = tgen.exabgp_peers() + for peer in peers: + fifo_in = "/var/run/exabgp_{}.in".format(peer) + if os.path.exists(fifo_in): + os.remove(fifo_in) + os.mkfifo(fifo_in, 0o777) + logger.info("Starting ExaBGP on peer {}".format(peer)) + peer_dir = os.path.join(CWD, peer) + env_file = os.path.join(CWD, "exabgp.env") + peers[peer].start(peer_dir, env_file) + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_bgp_peer_type_multipath_relax(): + tgen = get_topogen() + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def exabgp_cmd(peer, cmd): + pipe = open("/run/exabgp_{}.in".format(peer), "w") + with pipe: + pipe.write(cmd) + pipe.close() + + # Prefixes used in the test + prefix1 = "203.0.113.0/30" + prefix2 = "203.0.113.4/30" + prefix3 = "203.0.113.8/30" + # Next hops used for iBGP/confed routes + resolved_nh1 = "198.51.100.1" + resolved_nh2 = "198.51.100.2" + # BGP route used for recursive resolution + bgp_resolving_prefix = "198.51.100.0/24" + # Next hop that will require non-connected recursive resolution + ebgp_resolved_nh = "198.51.100.10" + + # Send a non-connected route to resolve others + exabgp_cmd( + "peer3", "announce route {} next-hop self\n".format(bgp_resolving_prefix) + ) + router = tgen.gears["r1"] + + # It seems that if you write to the exabgp socket too quickly in + # succession, requests get lost. So verify prefix1 now instead of + # after all the prefixes are advertised. + logger.info("Create and verify mixed-type multipaths") + exabgp_cmd( + "peer1", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh1 + ), + ) + exabgp_cmd( + "peer2", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh2 + ), + ) + exabgp_cmd("peer4", "announce route {} next-hop self\n".format(prefix1)) + reffile = os.path.join(CWD, "r1/prefix1.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Mixed-type multipath not found" + assert res is None, assertMsg + + logger.info("Create and verify eBGP and iBGP+confed multipaths") + exabgp_cmd( + "peer1", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix2, resolved_nh1 + ), + ) + exabgp_cmd( + "peer2", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix2, resolved_nh2 + ), + ) + exabgp_cmd("peer3", "announce route {} next-hop self".format(prefix3)) + exabgp_cmd("peer4", "announce route {} next-hop self".format(prefix3)) + reffile = os.path.join(CWD, "r1/multipath.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Not all expected multipaths found" + assert res is None, assertMsg + + logger.info("Toggle peer-type multipath-relax and verify the changes") + router.vtysh_cmd( + "conf\n router bgp 64510\n no bgp bestpath peer-type multipath-relax\n" + ) + # This file verifies "multipath" is not set + reffile = os.path.join(CWD, "r1/not-multipath.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Disabling peer-type multipath-relax did not take effect" + assert res is None, assertMsg + + router.vtysh_cmd( + "conf\n router bgp 64510\n bgp bestpath peer-type multipath-relax\n" + ) + reffile = os.path.join(CWD, "r1/multipath.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Reenabling peer-type multipath-relax did not take effect" + assert res is None, assertMsg + + logger.info("Check recursive resolution of eBGP next hops is not affected") + # eBGP next hop resolution rejects recursively resolved next hops by + # default, even with peer-type multipath-relax + exabgp_cmd( + "peer4", "announce route {} next-hop {}\n".format(prefix3, ebgp_resolved_nh) + ) + reffile = os.path.join(CWD, "r1/prefix3-no-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix3), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3) + assert res is None, assertMsg + + exabgp_cmd( + "peer4", "announce route {} next-hop {}\n".format(prefix1, ebgp_resolved_nh) + ) + reffile = os.path.join(CWD, "r1/prefix1-no-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1) + assert res is None, assertMsg + + # When other config allows recursively resolved eBGP next hops, + # such next hops in all-eBGP multipaths should be valid + router.vtysh_cmd("conf\n router bgp 64510\n neighbor 10.0.4.2 ebgp-multihop\n") + reffile = os.path.join(CWD, "r1/prefix3-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix3), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3) + assert res is None, assertMsg + + reffile = os.path.join(CWD, "r1/prefix1-recursive.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip bgp {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1) + assert res is None, assertMsg + + logger.info("Check mixed-type multipath next hop recursive resolution in FIB") + # There are now two eBGP-learned routes with a recursively resolved next; + # hop; one is all-eBGP multipath, and the other is iBGP/eBGP/ + # confed-external. The peer-type multipath-relax feature only enables + # recursive resolution in FIB if any next hop is iBGP/confed-learned. The + # all-eBGP multipath will have only one valid next hop in the FIB. + reffile = os.path.join(CWD, "r1/prefix3-ip-route.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip route {} json".format(prefix3), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "FIB next hops mismatch for all-eBGP multipath" + assert res is None, assertMsg + + # check confed-external enables recursively resolved next hops by itself + exabgp_cmd( + "peer1", + "withdraw route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh1 + ), + ) + reffile = os.path.join(CWD, "r1/prefix1-eBGP-confed.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip route {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "FIB next hops mismatch for eBGP+confed-external multipath" + assert res is None, assertMsg + + # check iBGP by itself + exabgp_cmd( + "peer1", + "announce route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh1 + ), + ) + exabgp_cmd( + "peer2", + "withdraw route {} next-hop {} as-path [ 64499 ]\n".format( + prefix1, resolved_nh2 + ), + ) + reffile = os.path.join(CWD, "r1/prefix1-eBGP-iBGP.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip route {} json".format(prefix1), + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertMsg = "FIB next hops mismatch for eBGP+iBGP multipath" + assert res is None, assertMsg + + +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/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py index 4764ff8945..2a98cb341d 100644 --- a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py +++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py @@ -86,6 +86,10 @@ from lib.bgp import ( ) from lib.topojson import build_topo_from_json, build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + + # Reading the data from JSON File for topology and configuration creation jsonFile = "{}/bgp_recursive_route_ebgp_multi_hop.json".format(CWD) try: @@ -365,10 +369,11 @@ def test_recursive_routes_iBGP_peer_p1(request): protocol="bgp", expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "Routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( tc_name, result - )) + ) step("Reconfigure the same static route on R2 again") dut = "r2" @@ -486,10 +491,11 @@ def test_recursive_routes_iBGP_peer_p1(request): result = verify_rib( tgen, addr_type, "r2", input_dict_4, protocol="bgp", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "Routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( tc_name, result - )) + ) write_test_footer(tc_name) @@ -598,10 +604,11 @@ def test_next_hop_as_self_ip_p1(request): next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "Routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( tc_name, result - )) + ) step("Shut interface on R2 that has IP from the subnet as BGP next-hop") intf_r2_r4 = topo["routers"]["r2"]["links"]["r4"]["interface"] @@ -676,10 +683,11 @@ def test_next_hop_as_self_ip_p1(request): next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0], expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "Routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( tc_name, result - )) + ) write_test_footer(tc_name) @@ -1622,10 +1630,11 @@ def test_BGP_peering_bw_loopback_and_physical_p1(request): step("Verify that once eBGP multi-hop is removed, BGP session goes down") result = verify_bgp_convergence_from_running_config(tgen, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "BGP is converged \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( tc_name, result - )) + ) step("Add ebgp-multihop command on R3 again") for addr_type in ADDR_TYPES: @@ -1663,10 +1672,11 @@ def test_BGP_peering_bw_loopback_and_physical_p1(request): step("Verify that BGP session goes down, when update-source is removed") result = verify_bgp_convergence_from_running_config(tgen, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "BGP is converged \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( tc_name, result - )) + ) step("Add update-source command on R1 again") for addr_type in ADDR_TYPES: @@ -1715,18 +1725,20 @@ def test_BGP_peering_bw_loopback_and_physical_p1(request): next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0], expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "Routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( tc_name, result - )) + ) sleep(3) step("Verify that BGP session goes down, when static route is removed") result = verify_bgp_convergence_from_running_config(tgen, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "BGP is converged \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( tc_name, result - )) + ) step("Add static route on R3 again") for addr_type in ADDR_TYPES: @@ -1768,10 +1780,11 @@ def test_BGP_peering_bw_loopback_and_physical_p1(request): sleep(3) step("Verify that BGP neighborship between R1 and R3 goes down") result = verify_bgp_convergence_from_running_config(tgen, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "BGP is converged \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( tc_name, result - )) + ) intf_r1_r3 = topo["routers"]["r1"]["links"]["r3"]["interface"] shutdown_bringup_interface(tgen, "r1", intf_r1_r3, True) @@ -2087,10 +2100,11 @@ def test_BGP_active_standby_preemption_and_ecmp_p1(request): ], expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "Routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( tc_name, result - )) + ) step("Reconfigure multipath-relax command on R4") result = create_router_bgp(tgen, topo, maxpath_relax) @@ -2147,10 +2161,11 @@ def test_BGP_active_standby_preemption_and_ecmp_p1(request): ], expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "Routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format( tc_name, result - )) + ) step("Re-configure maximum-path 2 command on R4") input_dict_8 = { @@ -2338,10 +2353,11 @@ def test_password_authentication_for_eBGP_and_iBGP_peers_p1(request): "configured but not peer routers" ) result = verify_bgp_convergence(tgen, topo, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "BGP is converged \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( tc_name, result - )) + ) step("configure same password on R2 and R3") for routerN in ["r2", "r3"]: @@ -2368,10 +2384,11 @@ def test_password_authentication_for_eBGP_and_iBGP_peers_p1(request): "strings are in CAPs on R2 and R3" ) result = verify_bgp_convergence(tgen, topo, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "BGP is converged \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( tc_name, result - )) + ) step("Configure same password on R2 and R3 without CAPs") for routerN in ["r2", "r3"]: @@ -2395,10 +2412,11 @@ def test_password_authentication_for_eBGP_and_iBGP_peers_p1(request): step("Verify if password is removed from R1, both sessions go down again") result = verify_bgp_convergence(tgen, topo, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "BGP is converged \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format( tc_name, result - )) + ) step("Configure alphanumeric password on R1 and peer routers R2,R3") for bgp_neighbor in ["r2", "r3"]: diff --git a/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py b/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py index 88935ae4d1..dffe24f3a0 100644 --- a/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py +++ b/tests/topotests/bgp_sender-as-path-loop-detection/test_bgp_sender-as-path-loop-detection.py @@ -44,6 +44,9 @@ from lib.topolog import logger from mininet.topo import Topo +pytestmark = [pytest.mark.bgpd] + + class TemplateTopo(Topo): def build(self, *_args, **_opts): tgen = get_topogen(self) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py index b99f1a7418..291a6e7c3a 100644 --- a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py @@ -498,6 +498,7 @@ def disable_route_map_to_prefer_global_next_hop(tgen, topo): # ##################################################### + def test_dynamic_imported_routes_advertised_to_iBGP_peer_p0(request): """ TC5_FUNC_5: @@ -762,9 +763,7 @@ def test_dynamic_imported_routes_advertised_to_iBGP_peer_p0(request): for addr_type in ADDR_TYPES: - step( - "On router R1 delete static routes in vrf ISR to LOOPBACK_1" - ) + step("On router R1 delete static routes in vrf ISR to LOOPBACK_1") input_routes_r1 = { "r1": { @@ -772,7 +771,7 @@ def test_dynamic_imported_routes_advertised_to_iBGP_peer_p0(request): { "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], "next_hop": (intf_r2_r1[addr_type]).split("/")[0], - "delete": True + "delete": True, } ] } diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py index 97d98415db..92ee8513e1 100644 --- a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py +++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py @@ -42,6 +42,7 @@ sys.path.append(os.path.join(CWD, "../")) from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger +from lib.common_config import adjust_router_l3mdev # Required to instantiate the topology builder class. from mininet.topo import Topo @@ -71,22 +72,6 @@ def setup_module(mod): router_list = tgen.routers() logger.info("Testing with VRF Lite support") - krel = platform.release() - - # May need to adjust handling of vrf traffic depending on kernel version - l3mdev_accept = 0 - if ( - topotest.version_cmp(krel, "4.15") >= 0 - and topotest.version_cmp(krel, "4.18") <= 0 - ): - l3mdev_accept = 1 - - if topotest.version_cmp(krel, "5.0") >= 0: - l3mdev_accept = 1 - - logger.info( - "krel '{0}' setting net.ipv4.tcp_l3mdev_accept={1}".format(krel, l3mdev_accept) - ) cmds = [ "ip link add {0}-cust1 type vrf table 1001", @@ -99,15 +84,8 @@ def setup_module(mod): for cmd in cmds: output = tgen.net[rname].cmd(cmd.format(rname)) - output = tgen.net[rname].cmd("sysctl -n net.ipv4.tcp_l3mdev_accept") - logger.info( - "router {0}: existing tcp_l3mdev_accept was {1}".format(rname, output) - ) - - if l3mdev_accept: - output = tgen.net[rname].cmd( - "sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept) - ) + # adjust handling of vrf traffic + adjust_router_l3mdev(tgen, rname) for rname, router in router_list.items(): router.load_config( diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 04e9961f10..cf64956bfd 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -2,12 +2,14 @@ Topotest conftest.py file. """ +import os +import pdb +import pytest + from lib.topogen import get_topogen, diagnose_env from lib.topotest import json_cmp_result +from lib.topotest import g_extra_config as topotest_extra_config from lib.topolog import logger -import pytest - -topology_only = False def pytest_addoption(parser): @@ -16,20 +18,72 @@ def pytest_addoption(parser): only run the setup_module() to setup the topology without running any tests. """ parser.addoption( + "--gdb-breakpoints", + metavar="SYMBOL[,SYMBOL...]", + help="Comma-separated list of functions to set gdb breakpoints on", + ) + + parser.addoption( + "--gdb-daemons", + metavar="DAEMON[,DAEMON...]", + help="Comma-separated list of daemons to spawn gdb on, or 'all'", + ) + + parser.addoption( + "--gdb-routers", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to spawn gdb on, or 'all'", + ) + + parser.addoption( + "--mininet-on-error", + action="store_true", + help="Mininet cli on test failure", + ) + + parser.addoption( + "--pause-after", + action="store_true", + help="Pause after each test", + ) + + parser.addoption( + "--shell", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to spawn shell on, or 'all'", + ) + + parser.addoption( + "--shell-on-error", + action="store_true", + help="Spawn shell on all routers on test failure", + ) + + parser.addoption( "--topology-only", action="store_true", help="Only set up this topology, don't run tests", ) + parser.addoption( + "--vtysh", + metavar="ROUTER[,ROUTER...]", + help="Comma-separated list of routers to spawn vtysh on, or 'all'", + ) + + parser.addoption( + "--vtysh-on-error", + action="store_true", + help="Spawn vtysh on all routers on test failure", + ) + def pytest_runtest_call(): """ This function must be run after setup_module(), it does standarized post setup routines. It is only being used for the 'topology-only' option. """ - global topology_only - - if topology_only: + if topotest_extra_config["topology_only"]: tgen = get_topogen() if tgen is not None: # Allow user to play with the setup. @@ -42,6 +96,8 @@ def pytest_assertrepr_compare(op, left, right): """ Show proper assertion error message for json_cmp results. """ + del op + json_result = left if not isinstance(json_result, json_cmp_result): json_result = right @@ -52,43 +108,104 @@ def pytest_assertrepr_compare(op, left, right): def pytest_configure(config): - "Assert that the environment is correctly configured." - - global topology_only + """ + Assert that the environment is correctly configured, and get extra config. + """ if not diagnose_env(): - pytest.exit("enviroment has errors, please read the logs") + pytest.exit("environment has errors, please read the logs") + + gdb_routers = config.getoption("--gdb-routers") + gdb_routers = gdb_routers.split(",") if gdb_routers else [] + topotest_extra_config["gdb_routers"] = gdb_routers + + gdb_daemons = config.getoption("--gdb-daemons") + gdb_daemons = gdb_daemons.split(",") if gdb_daemons else [] + topotest_extra_config["gdb_daemons"] = gdb_daemons + + gdb_breakpoints = config.getoption("--gdb-breakpoints") + gdb_breakpoints = gdb_breakpoints.split(",") if gdb_breakpoints else [] + topotest_extra_config["gdb_breakpoints"] = gdb_breakpoints - if config.getoption("--topology-only"): - topology_only = True + mincli_on_error = config.getoption("--mininet-on-error") + topotest_extra_config["mininet_on_error"] = mincli_on_error + + shell = config.getoption("--shell") + topotest_extra_config["shell"] = shell.split(",") if shell else [] + + pause_after = config.getoption("--pause-after") + + shell_on_error = config.getoption("--shell-on-error") + topotest_extra_config["shell_on_error"] = shell_on_error + + vtysh = config.getoption("--vtysh") + topotest_extra_config["vtysh"] = vtysh.split(",") if vtysh else [] + + vtysh_on_error = config.getoption("--vtysh-on-error") + topotest_extra_config["vtysh_on_error"] = vtysh_on_error + + topotest_extra_config["pause_after"] = pause_after or shell or vtysh + + topotest_extra_config["topology_only"] = config.getoption("--topology-only") def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" - # Nothing happened - if call.excinfo is None: - return - parent = item.parent - modname = parent.module.__name__ + # Nothing happened + if call.when == "call": + pause = topotest_extra_config["pause_after"] + else: + pause = False - # Treat skips as non errors - if call.excinfo.typename != "AssertionError": - logger.info( - 'assert skipped at "{}/{}": {}'.format( - modname, item.name, call.excinfo.value + if call.excinfo is None: + error = False + else: + parent = item.parent + modname = parent.module.__name__ + + # Treat skips as non errors, don't pause after + if call.excinfo.typename != "AssertionError": + pause = False + error = False + logger.info( + 'assert skipped at "{}/{}": {}'.format( + modname, item.name, call.excinfo.value + ) + ) + else: + error = True + # Handle assert failures + parent._previousfailed = item # pylint: disable=W0212 + logger.error( + 'assert failed at "{}/{}": {}'.format( + modname, item.name, call.excinfo.value + ) ) - ) - return - - # Handle assert failures - parent._previousfailed = item - logger.error( - 'assert failed at "{}/{}": {}'.format(modname, item.name, call.excinfo.value) - ) - # (topogen) Set topology error to avoid advancing in the test. - tgen = get_topogen() - if tgen is not None: - # This will cause topogen to report error on `routers_have_failure`. - tgen.set_error("{}/{}".format(modname, item.name)) + # (topogen) Set topology error to avoid advancing in the test. + tgen = get_topogen() + if tgen is not None: + # This will cause topogen to report error on `routers_have_failure`. + tgen.set_error("{}/{}".format(modname, item.name)) + + if error and topotest_extra_config["shell_on_error"]: + for router in tgen.routers(): + pause = True + tgen.net[router].runInWindow(os.getenv("SHELL", "bash")) + + if error and topotest_extra_config["vtysh_on_error"]: + for router in tgen.routers(): + pause = True + tgen.net[router].runInWindow("vtysh") + + if error and topotest_extra_config["mininet_on_error"]: + tgen.mininet_cli() + + if pause: + try: + user = raw_input('Testing paused, "pdb" to debug, "Enter" to continue: ') + except NameError: + user = input('Testing paused, "pdb" to debug, "Enter" to continue: ') + if user.strip() == "pdb": + pdb.set_trace() diff --git a/tests/topotests/isis-snmp/test_isis_snmp.py b/tests/topotests/isis-snmp/test_isis_snmp.py index 1bcd0eefc6..07f3335e23 100755 --- a/tests/topotests/isis-snmp/test_isis_snmp.py +++ b/tests/topotests/isis-snmp/test_isis_snmp.py @@ -124,7 +124,6 @@ class TemplateTopo(Topo): switch.add_link(tgen.gears["r3"]) - def setup_module(mod): "Sets up the pytest environment" @@ -148,20 +147,24 @@ def setup_module(mod): # Don't start the following in the CE nodes if router.name[0] == "r": router.load_config( - TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)), + TopoRouter.RD_ISIS, + os.path.join(CWD, "{}/isisd.conf".format(rname)), "-M snmp", ) router.load_config( - TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)), + TopoRouter.RD_LDP, + os.path.join(CWD, "{}/ldpd.conf".format(rname)), ) router.load_config( - TopoRouter.RD_SNMP, os.path.join(CWD, "{}/snmpd.conf".format(rname)), + TopoRouter.RD_SNMP, + os.path.join(CWD, "{}/snmpd.conf".format(rname)), "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap", ) # After loading the configurations, this function loads configured daemons. tgen.start_router() + def teardown_module(mod): "Teardown the pytest environment" tgen = get_topogen() @@ -169,6 +172,7 @@ def teardown_module(mod): # This function tears down the whole topology. tgen.stop_topology() + def router_compare_json_output(rname, command, reference): "Compare router JSON output" @@ -184,6 +188,7 @@ def router_compare_json_output(rname, command, reference): assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) assert diff is None, assertmsg + def generate_oid(numoids, index1, index2): if numoids == 1: oid = "{}".format(index1) @@ -200,7 +205,9 @@ def test_isis_convergence(): router_compare_json_output( rname, "show yang operational-data /frr-interface:lib isisd", - "show_yang_interface_isis_adjacencies.ref") + "show_yang_interface_isis_adjacencies.ref", + ) + def test_r1_scalar_snmp(): "Wait for protocol convergence" @@ -213,26 +220,26 @@ def test_r1_scalar_snmp(): r1 = tgen.net.get("r1") r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") - assert r1_snmp.test_oid('isisSysVersion', "one(1)") - assert r1_snmp.test_oid('isisSysLevelType', "level1and2(3)") - assert r1_snmp.test_oid('isisSysID',"00 00 00 00 00 01") - assert r1_snmp.test_oid('isisSysMaxPathSplits',"32") - assert r1_snmp.test_oid('isisSysMaxLSPGenInt',"900 seconds") - assert r1_snmp.test_oid('isisSysAdminState',"on(1)") - assert r1_snmp.test_oid('isisSysMaxAge',"1200 seconds") - assert r1_snmp.test_oid('isisSysProtSupported',"07 5 6 7") + assert r1_snmp.test_oid("isisSysVersion", "one(1)") + assert r1_snmp.test_oid("isisSysLevelType", "level1and2(3)") + assert r1_snmp.test_oid("isisSysID", "00 00 00 00 00 01") + assert r1_snmp.test_oid("isisSysMaxPathSplits", "32") + assert r1_snmp.test_oid("isisSysMaxLSPGenInt", "900 seconds") + assert r1_snmp.test_oid("isisSysAdminState", "on(1)") + assert r1_snmp.test_oid("isisSysMaxAge", "1200 seconds") + assert r1_snmp.test_oid("isisSysProtSupported", "07 5 6 7") r2 = tgen.net.get("r2") r2_snmp = SnmpTester(r2, "2.2.2.2", "public", "2c") - - assert r2_snmp.test_oid('isisSysVersion', "one(1)") - assert r2_snmp.test_oid('isisSysLevelType', "level1and2(3)") - assert r2_snmp.test_oid('isisSysID',"00 00 00 00 00 02") - assert r2_snmp.test_oid('isisSysMaxPathSplits',"32") - assert r2_snmp.test_oid('isisSysMaxLSPGenInt',"900 seconds") - assert r2_snmp.test_oid('isisSysAdminState',"on(1)") - assert r2_snmp.test_oid('isisSysMaxAge',"1200 seconds") - assert r2_snmp.test_oid('isisSysProtSupported',"07 5 6 7") + + assert r2_snmp.test_oid("isisSysVersion", "one(1)") + assert r2_snmp.test_oid("isisSysLevelType", "level1and2(3)") + assert r2_snmp.test_oid("isisSysID", "00 00 00 00 00 02") + assert r2_snmp.test_oid("isisSysMaxPathSplits", "32") + assert r2_snmp.test_oid("isisSysMaxLSPGenInt", "900 seconds") + assert r2_snmp.test_oid("isisSysAdminState", "on(1)") + assert r2_snmp.test_oid("isisSysMaxAge", "1200 seconds") + assert r2_snmp.test_oid("isisSysProtSupported", "07 5 6 7") circtable_test = { @@ -245,7 +252,8 @@ circtable_test = { "isisCircMeshGroupEnabled": ["inactive(1)", "inactive(1)", "inactive(1)"], "isisCircSmallHellos": ["false(2)", "false(2)", "false(2)"], "isisCirc3WayEnabled": ["false(2)", "false(2)", "false(2)"], - } +} + def test_r1_isisCircTable(): tgen = get_topogen() @@ -256,9 +264,9 @@ def test_r1_isisCircTable(): r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") oids = [] - oids.append(generate_oid(1,1,0)) - oids.append(generate_oid(1,2,0)) - oids.append(generate_oid(1,3,0)) + oids.append(generate_oid(1, 1, 0)) + oids.append(generate_oid(1, 2, 0)) + oids.append(generate_oid(1, 3, 0)) # check items for item in circtable_test.keys(): @@ -267,14 +275,26 @@ def test_r1_isisCircTable(): ) assert r1_snmp.test_oid_walk(item, circtable_test[item], oids), assertmsg + circleveltable_test = { "isisCircLevelMetric": ["10", "10", "10", "10"], "isisCircLevelWideMetric": ["10", "10", "0", "0"], "isisCircLevelISPriority": ["64", "64", "64", "64"], "isisCircLevelHelloMultiplier": ["10", "10", "10", "10"], - "isisCircLevelHelloTimer": ["3000 milliseconds", "3000 milliseconds", "3000 milliseconds", "3000 milliseconds"], - "isisCircLevelMinLSPRetransInt": ["1 seconds", "1 seconds", "0 seconds", "0 seconds"], - } + "isisCircLevelHelloTimer": [ + "3000 milliseconds", + "3000 milliseconds", + "3000 milliseconds", + "3000 milliseconds", + ], + "isisCircLevelMinLSPRetransInt": [ + "1 seconds", + "1 seconds", + "0 seconds", + "0 seconds", + ], +} + def test_r1_isislevelCircTable(): tgen = get_topogen() @@ -285,10 +305,10 @@ def test_r1_isislevelCircTable(): r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") oids = [] - oids.append(generate_oid(2,1,"area")) - oids.append(generate_oid(2,2,"area")) - oids.append(generate_oid(2,3,"area")) - oids.append(generate_oid(2,3,"domain")) + oids.append(generate_oid(2, 1, "area")) + oids.append(generate_oid(2, 2, "area")) + oids.append(generate_oid(2, 3, "area")) + oids.append(generate_oid(2, 3, "domain")) # check items for item in circleveltable_test.keys(): @@ -316,6 +336,7 @@ adjtable_down_test = { "isisISAdjNeighPriority": ["64"], } + def test_r1_isisAdjTable(): "check ISIS Adjacency Table" tgen = get_topogen() @@ -324,11 +345,11 @@ def test_r1_isisAdjTable(): r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") oids = [] - oids.append(generate_oid(2,1,1)) - oids.append(generate_oid(2,2,1)) + oids.append(generate_oid(2, 1, 1)) + oids.append(generate_oid(2, 2, 1)) oids_down = [] - oids_down.append(generate_oid(2,1,1)) + oids_down.append(generate_oid(2, 1, 1)) # check items for item in adjtable_test.keys(): @@ -337,7 +358,6 @@ def test_r1_isisAdjTable(): ) assert r1_snmp.test_oid_walk(item, adjtable_test[item], oids), assertmsg - # shutdown interface and one adjacency should be removed "check ISIS adjacency is removed when interface is shutdown" r1_cmd.vtysh_cmd("conf t\ninterface r1-eth1\nshutdown") @@ -347,7 +367,9 @@ def test_r1_isisAdjTable(): assertmsg = "{} should be {} oids {} full dict {}:".format( item, adjtable_down_test[item], oids_down, r1_snmp.walk(item) ) - assert r1_snmp.test_oid_walk(item, adjtable_down_test[item], oids_down), assertmsg + assert r1_snmp.test_oid_walk( + item, adjtable_down_test[item], oids_down + ), assertmsg # no shutdown interface and adjacency should be restored r1_cmd.vtysh_cmd("conf t\ninterface r1-eth1\nno shutdown") diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_topology.json b/tests/topotests/isis-topo1-vrf/r1/r1_topology.json index 8e3cdc7bd6..1a6fe6d5c6 100644 --- a/tests/topotests/isis-topo1-vrf/r1/r1_topology.json +++ b/tests/topotests/isis-topo1-vrf/r1/r1_topology.json @@ -5,73 +5,73 @@ { "vertex": "r1" } - ], + ], "ipv6": [ { "vertex": "r1" } ] - }, + }, "level-2": { "ipv4": [ { "vertex": "r1" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP", + "metric": "0", + "parent": "r1(4)", + "type": "IP internal", "vertex": "10.0.20.0/24" - }, + }, { - "interface": "r1-eth0", - "metric": "10", - "next-hop": "r3", - "parent": "r1(4)", - "type": "TE-IS", + "interface": "r1-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r1(4)", + "type": "TE-IS", "vertex": "r3" - }, + }, { - "interface": "r3", - "metric": "TE", - "next-hop": "20", - "parent": "r1-eth0", - "type": "IP", + "interface": "r1-eth0", + "metric": "20", + "next-hop": "r3", + "parent": "r3(4)", + "type": "IP TE", "vertex": "10.0.20.0/24" - }, + }, { - "interface": "r3", - "metric": "TE", - "next-hop": "20", - "parent": "r1-eth0", - "type": "IP", + "interface": "r1-eth0", + "metric": "20", + "next-hop": "r3", + "parent": "r3(4)", + "type": "IP TE", "vertex": "10.0.10.0/24" } - ], + ], "ipv6": [ { "vertex": "r1" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP6", + "metric": "0", + "parent": "r1(4)", + "type": "IP6 internal", "vertex": "2001:db8:1:1::/64" - }, + }, { - "interface": "r1-eth0", - "metric": "10", - "next-hop": "r3", - "parent": "r1(4)", - "type": "TE-IS", + "interface": "r1-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r1(4)", + "type": "TE-IS", "vertex": "r3" - }, + }, { - "interface": "r3", - "metric": "internal", - "next-hop": "20", - "parent": "r1-eth0", - "type": "IP6", + "interface": "r1-eth0", + "metric": "20", + "next-hop": "r3", + "parent": "r3(4)", + "type": "IP6 internal", "vertex": "2001:db8:2:1::/64" } ] diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_topology.json b/tests/topotests/isis-topo1-vrf/r2/r2_topology.json index 72022a8167..a77f7977f9 100644 --- a/tests/topotests/isis-topo1-vrf/r2/r2_topology.json +++ b/tests/topotests/isis-topo1-vrf/r2/r2_topology.json @@ -5,76 +5,76 @@ { "vertex": "r2" } - ], + ], "ipv6": [ { "vertex": "r2" } ] - }, + }, "level-2": { "ipv4": [ { "vertex": "r2" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP", + "metric": "0", + "parent": "r2(4)", + "type": "IP internal", "vertex": "10.0.21.0/24" - }, + }, { - "interface": "r2-eth0", - "metric": "10", - "next-hop": "r4", - "parent": "r2(4)", - "type": "TE-IS", + "interface": "r2-eth0", + "metric": "10", + "next-hop": "r4", + "parent": "r2(4)", + "type": "TE-IS", "vertex": "r4" - }, + }, { - "interface": "r4", - "metric": "TE", - "next-hop": "20", - "parent": "r2-eth0", - "type": "IP", + "interface": "r2-eth0", + "metric": "20", + "next-hop": "r4", + "parent": "r4(4)", + "type": "IP TE", "vertex": "10.0.21.0/24" - }, + }, { - "interface": "r4", - "metric": "TE", - "next-hop": "20", - "parent": "r2-eth0", - "type": "IP", + "interface": "r2-eth0", + "metric": "20", + "next-hop": "r4", + "parent": "r4(4)", + "type": "IP TE", "vertex": "10.0.11.0/24" } - ], + ], "ipv6": [ { "vertex": "r2" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP6", + "metric": "0", + "parent": "r2(4)", + "type": "IP6 internal", "vertex": "2001:db8:1:2::/64" - }, + }, { - "interface": "r2-eth0", - "metric": "10", - "next-hop": "r4", - "parent": "r2(4)", - "type": "TE-IS", + "interface": "r2-eth0", + "metric": "10", + "next-hop": "r4", + "parent": "r2(4)", + "type": "TE-IS", "vertex": "r4" - }, + }, { - "interface": "r4", - "metric": "internal", - "next-hop": "20", - "parent": "r2-eth0", - "type": "IP6", + "interface": "r2-eth0", + "metric": "20", + "next-hop": "r4", + "parent": "r4(4)", + "type": "IP6 internal", "vertex": "2001:db8:2:2::/64" } ] } } -}
\ No newline at end of file +} diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_topology.json b/tests/topotests/isis-topo1-vrf/r3/r3_topology.json index 62b895766e..1e5d331965 100644 --- a/tests/topotests/isis-topo1-vrf/r3/r3_topology.json +++ b/tests/topotests/isis-topo1-vrf/r3/r3_topology.json @@ -4,126 +4,126 @@ "ipv4": [ { "vertex": "r3" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP", + "metric": "0", + "parent": "r3(4)", + "type": "IP internal", "vertex": "10.0.10.0/24" - }, + }, { - "interface": "r3-eth1", - "metric": "10", - "next-hop": "r5", - "parent": "r3(4)", - "type": "TE-IS", + "interface": "r3-eth1", + "metric": "10", + "next-hop": "r5", + "parent": "r3(4)", + "type": "TE-IS", "vertex": "r5" - }, + }, { - "interface": "r5", - "metric": "TE", - "next-hop": "20", - "parent": "r3-eth1", - "type": "IP", + "interface": "r3-eth1", + "metric": "20", + "next-hop": "r5", + "parent": "r5(4)", + "type": "IP TE", "vertex": "10.0.10.0/24" - }, + }, { - "interface": "r5", - "metric": "TE", - "next-hop": "20", - "parent": "r3-eth1", - "type": "IP", + "interface": "r3-eth1", + "metric": "20", + "next-hop": "r5", + "parent": "r5(4)", + "type": "IP TE", "vertex": "10.0.11.0/24" - }, + }, { - "interface": "r5", - "metric": "TE", - "next-hop": "30", - "parent": "r3-eth1", - "type": "IP", + "interface": "r3-eth1", + "metric": "30", + "next-hop": "r5", + "parent": "r4(4)", + "type": "IP TE", "vertex": "10.0.21.0/24" } - ], + ], "ipv6": [ { "vertex": "r3" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP6", + "metric": "0", + "parent": "r3(4)", + "type": "IP6 internal", "vertex": "2001:db8:2:1::/64" - }, + }, { - "interface": "r3-eth1", - "metric": "10", - "next-hop": "r5", - "parent": "r3(4)", - "type": "TE-IS", + "interface": "r3-eth1", + "metric": "10", + "next-hop": "r5", + "parent": "r3(4)", + "type": "TE-IS", "vertex": "r5" - }, + }, { - "interface": "r5", - "metric": "internal", - "next-hop": "20", - "parent": "r3-eth1", - "type": "IP6", + "interface": "r3-eth1", + "metric": "20", + "next-hop": "r5", + "parent": "r5(4)", + "type": "IP6 internal", "vertex": "2001:db8:2:2::/64" - }, + }, { - "interface": "r5", - "metric": "internal", - "next-hop": "30", - "parent": "r3-eth1", - "type": "IP6", + "interface": "r3-eth1", + "metric": "30", + "next-hop": "r5", + "parent": "r4(4)", + "type": "IP6 internal", "vertex": "2001:db8:1:2::/64" } ] - }, + }, "level-2": { "ipv4": [ { "vertex": "r3" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP", + "metric": "0", + "parent": "r3(4)", + "type": "IP internal", "vertex": "10.0.20.0/24" - }, + }, { - "interface": "r3-eth0", - "metric": "10", - "next-hop": "r3", - "parent": "r3(4)", - "type": "TE-IS", + "interface": "r3-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r3(4)", + "type": "TE-IS", "vertex": "r3" - }, + }, { - "interface": "r3", - "metric": "TE", - "next-hop": "20", - "parent": "r3-eth0", - "type": "IP", + "interface": "r3-eth0", + "metric": "20", + "next-hop": "r3", + "parent": "r3(4)", + "type": "IP TE", "vertex": "10.0.20.0/24" } - ], + ], "ipv6": [ { "vertex": "r3" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP6", + "metric": "0", + "parent": "r3(4)", + "type": "IP6 internal", "vertex": "2001:db8:1:1::/64" - }, + }, { - "interface": "r3-eth0", - "metric": "10", - "next-hop": "r3", - "parent": "r3(4)", - "type": "TE-IS", + "interface": "r3-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r3(4)", + "type": "TE-IS", "vertex": "r3" } ] diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_topology.json b/tests/topotests/isis-topo1-vrf/r4/r4_topology.json index 0d69550cad..34f5ac9ca4 100644 --- a/tests/topotests/isis-topo1-vrf/r4/r4_topology.json +++ b/tests/topotests/isis-topo1-vrf/r4/r4_topology.json @@ -4,126 +4,126 @@ "ipv4": [ { "vertex": "r4" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP", + "metric": "0", + "parent": "r4(4)", + "type": "IP internal", "vertex": "10.0.11.0/24" - }, + }, { - "interface": "r4-eth1", - "metric": "10", - "next-hop": "r5", - "parent": "r4(4)", - "type": "TE-IS", + "interface": "r4-eth1", + "metric": "10", + "next-hop": "r5", + "parent": "r4(4)", + "type": "TE-IS", "vertex": "r5" - }, + }, { - "interface": "r5", - "metric": "TE", - "next-hop": "20", - "parent": "r4-eth1", - "type": "IP", + "interface": "r4-eth1", + "metric": "20", + "next-hop": "r5", + "parent": "r5(4)", + "type": "IP TE", "vertex": "10.0.10.0/24" - }, + }, { - "interface": "r5", - "metric": "TE", - "next-hop": "20", - "parent": "r4-eth1", - "type": "IP", + "interface": "r4-eth1", + "metric": "20", + "next-hop": "r5", + "parent": "r5(4)", + "type": "IP TE", "vertex": "10.0.11.0/24" - }, + }, { - "interface": "r5", - "metric": "TE", - "next-hop": "30", - "parent": "r4-eth1", - "type": "IP", + "interface": "r4-eth1", + "metric": "30", + "next-hop": "r5", + "parent": "r3(4)", + "type": "IP TE", "vertex": "10.0.20.0/24" } - ], + ], "ipv6": [ { "vertex": "r4" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP6", + "metric": "0", + "parent": "r4(4)", + "type": "IP6 internal", "vertex": "2001:db8:2:2::/64" - }, + }, { - "interface": "r4-eth1", - "metric": "10", - "next-hop": "r5", - "parent": "r4(4)", - "type": "TE-IS", + "interface": "r4-eth1", + "metric": "10", + "next-hop": "r5", + "parent": "r4(4)", + "type": "TE-IS", "vertex": "r5" - }, + }, { - "interface": "r5", - "metric": "internal", - "next-hop": "20", - "parent": "r4-eth1", - "type": "IP6", + "interface": "r4-eth1", + "metric": "20", + "next-hop": "r5", + "parent": "r5(4)", + "type": "IP6 internal", "vertex": "2001:db8:2:1::/64" - }, + }, { - "interface": "r5", - "metric": "internal", - "next-hop": "30", - "parent": "r4-eth1", - "type": "IP6", + "interface": "r4-eth1", + "metric": "30", + "next-hop": "r5", + "parent": "r3(4)", + "type": "IP6 internal", "vertex": "2001:db8:1:1::/64" } ] - }, + }, "level-2": { "ipv4": [ { "vertex": "r4" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP", + "metric": "0", + "parent": "r4(4)", + "type": "IP internal", "vertex": "10.0.21.0/24" - }, + }, { - "interface": "r4-eth0", - "metric": "10", - "next-hop": "r2", - "parent": "r4(4)", - "type": "TE-IS", + "interface": "r4-eth0", + "metric": "10", + "next-hop": "r2", + "parent": "r4(4)", + "type": "TE-IS", "vertex": "r2" - }, + }, { - "interface": "r2", - "metric": "TE", - "next-hop": "20", - "parent": "r4-eth0", - "type": "IP", + "interface": "r4-eth0", + "metric": "20", + "next-hop": "r2", + "parent": "r2(4)", + "type": "IP TE", "vertex": "10.0.21.0/24" } - ], + ], "ipv6": [ { "vertex": "r4" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP6", + "metric": "0", + "parent": "r4(4)", + "type": "IP6 internal", "vertex": "2001:db8:1:2::/64" - }, + }, { - "interface": "r4-eth0", - "metric": "10", - "next-hop": "r2", - "parent": "r4(4)", - "type": "TE-IS", + "interface": "r4-eth0", + "metric": "10", + "next-hop": "r2", + "parent": "r4(4)", + "type": "TE-IS", "vertex": "r2" } ] diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_topology.json b/tests/topotests/isis-topo1-vrf/r5/r5_topology.json index b4ed6a069d..ace56536e9 100644 --- a/tests/topotests/isis-topo1-vrf/r5/r5_topology.json +++ b/tests/topotests/isis-topo1-vrf/r5/r5_topology.json @@ -4,121 +4,121 @@ "ipv4": [ { "vertex": "r5" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP", + "metric": "0", + "parent": "r5(4)", + "type": "IP internal", "vertex": "10.0.10.0/24" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP", + "metric": "0", + "parent": "r5(4)", + "type": "IP internal", "vertex": "10.0.11.0/24" - }, + }, { - "interface": "r5-eth0", - "metric": "10", - "next-hop": "r3", - "parent": "r5(4)", - "type": "TE-IS", + "interface": "r5-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r5(4)", + "type": "TE-IS", "vertex": "r3" - }, + }, { - "interface": "r5-eth1", - "metric": "10", - "next-hop": "r4", - "parent": "r5(4)", - "type": "TE-IS", + "interface": "r5-eth1", + "metric": "10", + "next-hop": "r4", + "parent": "r5(4)", + "type": "TE-IS", "vertex": "r4" - }, + }, { - "interface": "r3", - "metric": "TE", - "next-hop": "20", - "parent": "r5-eth0", - "type": "IP", + "interface": "r5-eth0", + "metric": "20", + "next-hop": "r3", + "parent": "r3(4)", + "type": "IP TE", "vertex": "10.0.20.0/24" - }, + }, { - "interface": "r3", - "metric": "TE", - "next-hop": "20", - "parent": "r5-eth0", - "type": "IP", + "interface": "r5-eth0", + "metric": "20", + "next-hop": "r3", + "parent": "r3(4)", + "type": "IP TE", "vertex": "10.0.10.0/24" - }, + }, { - "interface": "r4", - "metric": "TE", - "next-hop": "20", - "parent": "r5-eth1", - "type": "IP", + "interface": "r5-eth1", + "metric": "20", + "next-hop": "r4", + "parent": "r4(4)", + "type": "IP TE", "vertex": "10.0.21.0/24" - }, + }, { - "interface": "r4", - "metric": "TE", - "next-hop": "20", - "parent": "r5-eth1", - "type": "IP", + "interface": "r5-eth1", + "metric": "20", + "next-hop": "r4", + "parent": "r4(4)", + "type": "IP TE", "vertex": "10.0.11.0/24" } - ], + ], "ipv6": [ { "vertex": "r5" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP6", + "metric": "0", + "parent": "r5(4)", + "type": "IP6 internal", "vertex": "2001:db8:2:1::/64" - }, + }, { - "metric": "internal", - "parent": "0", - "type": "IP6", + "metric": "0", + "parent": "r5(4)", + "type": "IP6 internal", "vertex": "2001:db8:2:2::/64" - }, + }, { - "interface": "r5-eth0", - "metric": "10", - "next-hop": "r3", - "parent": "r5(4)", - "type": "TE-IS", + "interface": "r5-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r5(4)", + "type": "TE-IS", "vertex": "r3" - }, + }, { - "interface": "r5-eth1", - "metric": "10", - "next-hop": "r4", - "parent": "r5(4)", - "type": "TE-IS", + "interface": "r5-eth1", + "metric": "10", + "next-hop": "r4", + "parent": "r5(4)", + "type": "TE-IS", "vertex": "r4" - }, + }, { - "interface": "r3", - "metric": "internal", - "next-hop": "20", - "parent": "r5-eth0", - "type": "IP6", + "interface": "r5-eth0", + "metric": "20", + "next-hop": "r3", + "parent": "r3(4)", + "type": "IP6 internal", "vertex": "2001:db8:1:1::/64" - }, + }, { - "interface": "r4", - "metric": "internal", - "next-hop": "20", - "parent": "r5-eth1", - "type": "IP6", + "interface": "r5-eth1", + "metric": "20", + "next-hop": "r4", + "parent": "r4(4)", + "type": "IP6 internal", "vertex": "2001:db8:1:2::/64" } ] - }, + }, "level-2": { - "ipv4": [], + "ipv4": [], "ipv6": [] } } -}
\ No newline at end of file +} diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py index 6ab78c385e..b7fe0c2ddb 100644 --- a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py +++ b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py @@ -40,11 +40,30 @@ sys.path.append(os.path.join(CWD, "../")) from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger +from lib.topotest import iproute2_is_vrf_capable +from lib.common_config import ( + required_linux_kernel_version, + adjust_router_l3mdev, +) from mininet.topo import Topo pytestmark = [pytest.mark.isisd] +VERTEX_TYPE_LIST = [ + "pseudo_IS", + "pseudo_TE-IS", + "IS", + "TE-IS", + "ES", + "IP internal", + "IP external", + "IP TE", + "IP6 internal", + "IP6 external", + "UNKNOWN", +] + class ISISTopo1(Topo): "Simple two layer ISIS vrf topology" @@ -91,22 +110,6 @@ def setup_module(mod): tgen.start_topology() logger.info("Testing with VRF Lite support") - krel = platform.release() - - # May need to adjust handling of vrf traffic depending on kernel version - l3mdev_accept = 0 - if ( - topotest.version_cmp(krel, "4.15") >= 0 - and topotest.version_cmp(krel, "4.18") <= 0 - ): - l3mdev_accept = 1 - - if topotest.version_cmp(krel, "5.0") >= 0: - l3mdev_accept = 1 - - logger.info( - "krel '{0}' setting net.ipv4.tcp_l3mdev_accept={1}".format(krel, l3mdev_accept) - ) cmds = [ "ip link add {0}-cust1 type vrf table 1001", @@ -120,15 +123,9 @@ def setup_module(mod): # create VRF rx-cust1 and link rx-eth0 to rx-cust1 for cmd in cmds: output = tgen.net[rname].cmd(cmd.format(rname)) - output = tgen.net[rname].cmd("sysctl -n net.ipv4.tcp_l3mdev_accept") - logger.info( - "router {0}: existing tcp_l3mdev_accept was {1}".format(rname, output) - ) - if l3mdev_accept: - output = tgen.net[rname].cmd( - "sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept) - ) + # adjust handling of vrf traffic + adjust_router_l3mdev(tgen, rname) for rname, router in tgen.routers().items(): router.load_config( @@ -193,18 +190,21 @@ def test_isis_route_installation(): def test_isis_linux_route_installation(): - - dist = platform.dist() - - if dist[1] == "16.04": - pytest.skip("Kernel not supported for vrf") - "Check whether all expected routes are present and installed in the OS" tgen = get_topogen() # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + # iproute2 needs to support VRFs for this suite to run. + if not iproute2_is_vrf_capable(): + pytest.skip("Installed iproute2 version does not support VRFs") + logger.info("Checking routers for installed ISIS vrf routes in OS") # Check for routes in `ip route show vrf {}-cust1` for rname, router in tgen.routers().items(): @@ -236,18 +236,21 @@ def test_isis_route6_installation(): def test_isis_linux_route6_installation(): - - dist = platform.dist() - - if dist[1] == "16.04": - pytest.skip("Kernel not supported for vrf") - "Check whether all expected routes are present and installed in the OS" tgen = get_topogen() # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.15") + if result is not True: + pytest.skip("Kernel requirements are not met") + + # iproute2 needs to support VRFs for this suite to run. + if not iproute2_is_vrf_capable(): + pytest.skip("Installed iproute2 version does not support VRFs") + logger.info("Checking routers for installed ISIS vrf IPv6 routes in OS") # Check for routes in `ip -6 route show vrf {}-cust1` for rname, router in tgen.routers().items(): @@ -308,6 +311,7 @@ def parse_topology(lines, level): areas = {} area = None ipv = None + vertex_type_regex = "|".join(VERTEX_TYPE_LIST) for line in lines: area_match = re.match(r"Area (.+):", line) @@ -327,44 +331,57 @@ def parse_topology(lines, level): ipv = "ipv4" continue - item_match = re.match(r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", line) - if item_match is not None: + item_match = re.match( + r"([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+)", line + ) + if ( + item_match is not None + and item_match.group(1) == "Vertex" + and item_match.group(2) == "Type" + and item_match.group(3) == "Metric" + and item_match.group(4) == "Next-Hop" + and item_match.group(5) == "Interface" + and item_match.group(6) == "Parent" + ): # Skip header - if ( - item_match.group(1) == "Vertex" - and item_match.group(2) == "Type" - and item_match.group(3) == "Metric" - and item_match.group(4) == "Next-Hop" - and item_match.group(5) == "Interface" - and item_match.group(6) == "Parent" - ): - continue + continue + item_match = re.match( + r"([^\s]+) ({}) ([0]|([1-9][0-9]*)) ([^\s]+) ([^\s]+) ([^\s]+)".format( + vertex_type_regex + ), + line, + ) + if item_match is not None: areas[area][level][ipv].append( { "vertex": item_match.group(1), "type": item_match.group(2), "metric": item_match.group(3), - "next-hop": item_match.group(4), - "interface": item_match.group(5), - "parent": item_match.group(6), + "next-hop": item_match.group(5), + "interface": item_match.group(6), + "parent": item_match.group(7), } ) continue - item_match = re.match(r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", line) + item_match = re.match( + r"([^\s]+) ({}) ([0]|([1-9][0-9]*)) ([^\s]+)".format(vertex_type_regex), + line, + ) + if item_match is not None: areas[area][level][ipv].append( { "vertex": item_match.group(1), "type": item_match.group(2), "metric": item_match.group(3), - "parent": item_match.group(4), + "parent": item_match.group(5), } ) continue - item_match = re.match(r"([^ ]+)", line) + item_match = re.match(r"([^\s]+)", line) if item_match is not None: areas[area][level][ipv].append({"vertex": item_match.group(1)}) continue diff --git a/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py b/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py index 4144f9b261..f47d906157 100644 --- a/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py +++ b/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py @@ -141,15 +141,16 @@ def setup_module(mod): TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) ) router.load_config( - TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname)), - "-M snmp" + TopoRouter.RD_LDP, + os.path.join(CWD, "{}/ldpd.conf".format(rname)), + "-M snmp", ) router.load_config( - TopoRouter.RD_SNMP, os.path.join(CWD, "{}/snmpd.conf".format(rname)), - "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap" + TopoRouter.RD_SNMP, + os.path.join(CWD, "{}/snmpd.conf".format(rname)), + "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap", ) - tgen.start_router() @@ -199,7 +200,7 @@ def test_rib(): # Skip if previous fatal error condition is raised # TODO: disabling this check to avoid 'snmpd not running' errors - #if tgen.routers_have_failure(): + # if tgen.routers_have_failure(): # pytest.skip(tgen.errors) for rname in ["r1", "r2", "r3"]: @@ -212,7 +213,7 @@ def test_ldp_adjacencies(): # Skip if previous fatal error condition is raised # TODO: disabling this check to avoid 'snmpd not running' errors - #if tgen.routers_have_failure(): + # if tgen.routers_have_failure(): # pytest.skip(tgen.errors) for rname in ["r1", "r2", "r3"]: @@ -226,7 +227,7 @@ def test_ldp_neighbors(): tgen = get_topogen() # Skip if previous fatal error condition is raised - #if tgen.routers_have_failure(): + # if tgen.routers_have_failure(): # pytest.skip(tgen.errors) for rname in ["r1", "r2", "r3"]: @@ -242,8 +243,8 @@ def test_r1_ldp_lsr_objects(): r1 = tgen.net.get("r1") r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") - assert r1_snmp.test_oid('mplsLdpLsrId', "01 01 01 01") - assert r1_snmp.test_oid('mplsLdpLsrLoopDetectionCapable', 'none(1)') + assert r1_snmp.test_oid("mplsLdpLsrId", "01 01 01 01") + assert r1_snmp.test_oid("mplsLdpLsrLoopDetectionCapable", "none(1)") def test_r1_ldp_entity_table(): @@ -253,52 +254,31 @@ def test_r1_ldp_entity_table(): r1 = tgen.net.get("r1") r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityLdpId', ['1.1.1.1:0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityIndex', ['1']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityProtocolVersion', ['1']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityAdminStatus', ['enable(1)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityOperStatus', ['enabled(2)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityTcpPort', ['646']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityUdpDscPort', ['646']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityMaxPduLength', ['4096 octets']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityKeepAliveHoldTimer', ['180 seconds']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityHelloHoldTimer', ['0 seconds']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityInitSessionThreshold', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityLabelDistMethod', ['downstreamUnsolicited(2)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityLabelRetentionMode', ['liberal(2)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityPathVectorLimit', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityHopCountLimit', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityTransportAddrKind', ['loopback(2)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityTargetPeer', ['true(1)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityTargetPeerAddrType', ['ipv4(1)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityTargetPeerAddr', ['01 01 01 01']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityLabelType', ['generic(1)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityDiscontinuityTime', ['(0) 0:00:00.00']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStorageType', ['nonVolatile(3)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityRowStatus', ['createAndGo(4)']) + assert r1_snmp.test_oid_walk("mplsLdpEntityLdpId", ["1.1.1.1:0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityIndex", ["1"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityProtocolVersion", ["1"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityAdminStatus", ["enable(1)"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityOperStatus", ["enabled(2)"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityTcpPort", ["646"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityUdpDscPort", ["646"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityMaxPduLength", ["4096 octets"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityKeepAliveHoldTimer", ["180 seconds"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityHelloHoldTimer", ["0 seconds"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityInitSessionThreshold", ["0"]) + assert r1_snmp.test_oid_walk( + "mplsLdpEntityLabelDistMethod", ["downstreamUnsolicited(2)"] + ) + assert r1_snmp.test_oid_walk("mplsLdpEntityLabelRetentionMode", ["liberal(2)"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityPathVectorLimit", ["0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityHopCountLimit", ["0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityTransportAddrKind", ["loopback(2)"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityTargetPeer", ["true(1)"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityTargetPeerAddrType", ["ipv4(1)"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityTargetPeerAddr", ["01 01 01 01"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityLabelType", ["generic(1)"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityDiscontinuityTime", ["(0) 0:00:00.00"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityStorageType", ["nonVolatile(3)"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityRowStatus", ["createAndGo(4)"]) def test_r1_ldp_entity_stats_table(): @@ -308,32 +288,23 @@ def test_r1_ldp_entity_stats_table(): r1 = tgen.net.get("r1") r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsSessionAttempts", ["0"]) assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsSessionAttempts', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsSessionRejectedNoHelloErrors', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsSessionRejectedAdErrors', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsSessionRejectedMaxPduErrors', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsSessionRejectedLRErrors', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsBadLdpIdentifierErrors', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsBadPduLengthErrors', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsBadMessageLengthErrors', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsBadTlvLengthErrors', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsMalformedTlvValueErrors', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsKeepAliveTimerExpErrors', ['0']) + "mplsLdpEntityStatsSessionRejectedNoHelloErrors", ["0"] + ) + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsSessionRejectedAdErrors", ["0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsSessionRejectedMaxPduErrors", ["0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsSessionRejectedLRErrors", ["0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsBadLdpIdentifierErrors", ["0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsBadPduLengthErrors", ["0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsBadMessageLengthErrors", ["0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsBadTlvLengthErrors", ["0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsMalformedTlvValueErrors", ["0"]) + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsKeepAliveTimerExpErrors", ["0"]) assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsShutdownReceivedNotifications', ['0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpEntityStatsShutdownSentNotifications', ['0']) + "mplsLdpEntityStatsShutdownReceivedNotifications", ["0"] + ) + assert r1_snmp.test_oid_walk("mplsLdpEntityStatsShutdownSentNotifications", ["0"]) def test_r1_ldp_peer_table(): @@ -343,17 +314,16 @@ def test_r1_ldp_peer_table(): r1 = tgen.net.get("r1") r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") + assert r1_snmp.test_oid_walk("mplsLdpPeerLdpId", ["2.2.2.2:0", "3.3.3.3:0"]) assert r1_snmp.test_oid_walk( - 'mplsLdpPeerLdpId', ['2.2.2.2:0', '3.3.3.3:0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpPeerLabelDistMethod', - ['downstreamUnsolicited(2)', 'downstreamUnsolicited(2)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpPeerPathVectorLimit', ['0', '0']) + "mplsLdpPeerLabelDistMethod", + ["downstreamUnsolicited(2)", "downstreamUnsolicited(2)"], + ) + assert r1_snmp.test_oid_walk("mplsLdpPeerPathVectorLimit", ["0", "0"]) + assert r1_snmp.test_oid_walk("mplsLdpPeerTransportAddrType", ["ipv4(1)", "ipv4(1)"]) assert r1_snmp.test_oid_walk( - 'mplsLdpPeerTransportAddrType', ['ipv4(1)', 'ipv4(1)']) - assert r1_snmp.test_oid_walk( - 'mplsLdpPeerTransportAddr', ['02 02 02 02', '03 03 03 03']) + "mplsLdpPeerTransportAddr", ["02 02 02 02", "03 03 03 03"] + ) def test_r1_ldp_session_table(): @@ -363,18 +333,20 @@ def test_r1_ldp_session_table(): r1 = tgen.net.get("r1") r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") - assert r1_snmp.test_oid_walk('mplsLdpSessionState', - ['operational(5)', 'operational(5)']) - assert r1_snmp.test_oid_walk('mplsLdpSessionRole', - ['passive(3)', 'passive(3)']) - assert r1_snmp.test_oid_walk('mplsLdpSessionProtocolVersion', - ['1', '1']) - assert r1_snmp.test_oid_walk('mplsLdpSessionKeepAliveTime', - ['180 seconds', '180 seconds']) - assert r1_snmp.test_oid_walk('mplsLdpSessionMaxPduLength', - ['4096 octets', '4096 octets']) - assert r1_snmp.test_oid_walk('mplsLdpSessionDiscontinuityTime', - ['(0) 0:00:00.00', '(0) 0:00:00.00']) + assert r1_snmp.test_oid_walk( + "mplsLdpSessionState", ["operational(5)", "operational(5)"] + ) + assert r1_snmp.test_oid_walk("mplsLdpSessionRole", ["passive(3)", "passive(3)"]) + assert r1_snmp.test_oid_walk("mplsLdpSessionProtocolVersion", ["1", "1"]) + assert r1_snmp.test_oid_walk( + "mplsLdpSessionKeepAliveTime", ["180 seconds", "180 seconds"] + ) + assert r1_snmp.test_oid_walk( + "mplsLdpSessionMaxPduLength", ["4096 octets", "4096 octets"] + ) + assert r1_snmp.test_oid_walk( + "mplsLdpSessionDiscontinuityTime", ["(0) 0:00:00.00", "(0) 0:00:00.00"] + ) def test_r1_ldp_session_stats_table(): @@ -384,10 +356,8 @@ def test_r1_ldp_session_stats_table(): r1 = tgen.net.get("r1") r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") - assert r1_snmp.test_oid_walk( - 'mplsLdpSessionStatsUnknownMesTypeErrors', ['0', '0']) - assert r1_snmp.test_oid_walk( - 'mplsLdpSessionStatsUnknownTlvErrors', ['0', '0']) + assert r1_snmp.test_oid_walk("mplsLdpSessionStatsUnknownMesTypeErrors", ["0", "0"]) + assert r1_snmp.test_oid_walk("mplsLdpSessionStatsUnknownTlvErrors", ["0", "0"]) def test_r1_ldp_hello_adjacency_table(): @@ -397,12 +367,11 @@ def test_r1_ldp_hello_adjacency_table(): r1 = tgen.net.get("r1") r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") - assert r1_snmp.test_oid_walk('mplsLdpHelloAdjacencyIndex', - ['1', '2', '1']) - assert r1_snmp.test_oid_walk('mplsLdpHelloAdjacencyHoldTime', - ['15', '45', '15']) - assert r1_snmp.test_oid_walk('mplsLdpHelloAdjacencyType', - ['link(1)', 'targeted(2)', 'link(1)']) + assert r1_snmp.test_oid_walk("mplsLdpHelloAdjacencyIndex", ["1", "2", "1"]) + assert r1_snmp.test_oid_walk("mplsLdpHelloAdjacencyHoldTime", ["15", "45", "15"]) + assert r1_snmp.test_oid_walk( + "mplsLdpHelloAdjacencyType", ["link(1)", "targeted(2)", "link(1)"] + ) # Memory leak test template diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index 867831e114..d2212d1807 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -43,7 +43,7 @@ from lib.common_config import ( run_frr_cmd, FRRCFG_FILE, retry, - get_ipv6_linklocal_address + get_ipv6_linklocal_address, ) LOGDIR = "/tmp/topotests/" @@ -1582,7 +1582,7 @@ def verify_bgp_convergence_from_running_config(tgen, dut=None): return True -def clear_bgp(tgen, addr_type, router, vrf=None): +def clear_bgp(tgen, addr_type, router, vrf=None, neighbor=None): """ This API is to clear bgp neighborship by running clear ip bgp */clear bgp ipv6 * command, @@ -1593,6 +1593,7 @@ def clear_bgp(tgen, addr_type, router, vrf=None): * `addr_type`: ip type ipv4/ipv6 * `router`: device under test * `vrf`: vrf name + * `neighbor`: Neighbor for which bgp needs to be cleared Usage ----- @@ -1616,12 +1617,16 @@ def clear_bgp(tgen, addr_type, router, vrf=None): if vrf: for _vrf in vrf: run_frr_cmd(rnode, "clear ip bgp vrf {} *".format(_vrf)) + elif neighbor: + run_frr_cmd(rnode, "clear bgp ipv4 {}".format(neighbor)) else: run_frr_cmd(rnode, "clear ip bgp *") elif addr_type == "ipv6": if vrf: for _vrf in vrf: run_frr_cmd(rnode, "clear bgp vrf {} ipv6 *".format(_vrf)) + elif neighbor: + run_frr_cmd(rnode, "clear bgp ipv6 {}".format(neighbor)) else: run_frr_cmd(rnode, "clear bgp ipv6 *") else: diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index a4c98924b6..ead593d2ca 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -72,6 +72,59 @@ config.read(PYTESTINI_PATH) config_section = "topogen" +# Debug logs for daemons +DEBUG_LOGS = { + "pimd": [ + "debug msdp events", + "debug msdp packets", + "debug igmp events", + "debug igmp trace", + "debug mroute", + "debug mroute detail", + "debug pim events", + "debug pim packets", + "debug pim trace", + "debug pim zebra", + "debug pim bsm", + "debug pim packets joins", + "debug pim packets register", + "debug pim nht", + ], + "bgpd": [ + "debug bgp neighbor-events", + "debug bgp updates", + "debug bgp zebra", + "debug bgp nht", + "debug bgp neighbor-events", + "debug bgp graceful-restart", + "debug bgp update-groups", + "debug bgp vpn leak-from-vrf", + "debug bgp vpn leak-to-vrf", + "debug bgp zebr", + "debug bgp updates", + "debug bgp nht", + "debug bgp neighbor-events", + "debug vrf", + ], + "zebra": [ + "debug zebra events", + "debug zebra rib", + "debug zebra vxlan", + "debug zebra nht", + ], + "ospf": [ + "debug ospf event", + "debug ospf ism", + "debug ospf lsa", + "debug ospf nsm", + "debug ospf nssa", + "debug ospf packet all", + "debug ospf sr", + "debug ospf te", + "debug ospf zebra", + ], +} + if config.has_option("topogen", "verbosity"): loglevel = config.get("topogen", "verbosity") loglevel = loglevel.upper() @@ -249,6 +302,7 @@ def create_common_configuration( config_map = OrderedDict( { "general_config": "! FRR General Config\n", + "debug_log_config": "! Debug log Config\n", "interface_config": "! Interfaces Config\n", "static_route": "! Static Route Config\n", "prefix_list": "! Prefix List Config\n", @@ -1052,6 +1106,89 @@ def tcpdump_capture_stop(tgen, router): return True +def create_debug_log_config(tgen, input_dict, build=False): + """ + Enable/disable debug logs for any protocol with defined debug + options and logs would be saved to created log file + + Parameters + ---------- + * `tgen` : Topogen object + * `input_dict` : details to enable debug logs for protocols + * `build` : Only for initial setup phase this is set as True. + + + Usage: + ------ + input_dict = { + "r2": { + "debug":{ + "log_file" : "debug.log", + "enable": ["pimd", "zebra"], + "disable": { + "bgpd":[ + 'debug bgp neighbor-events', + 'debug bgp updates', + 'debug bgp zebra', + ] + } + } + } + } + + result = create_debug_log_config(tgen, input_dict) + + Returns + ------- + True or False + """ + + result = False + try: + for router in input_dict.keys(): + debug_config = [] + if "debug" in input_dict[router]: + debug_dict = input_dict[router]["debug"] + + disable_logs = debug_dict.setdefault("disable", None) + enable_logs = debug_dict.setdefault("enable", None) + log_file = debug_dict.setdefault("log_file", None) + + if log_file: + _log_file = os.path.join(LOGDIR, tgen.modname, log_file) + debug_config.append("log file {} \n".format(_log_file)) + + if type(enable_logs) is list: + for daemon in enable_logs: + for debug_log in DEBUG_LOGS[daemon]: + debug_config.append("{}".format(debug_log)) + elif type(enable_logs) is dict: + for daemon, debug_logs in enable_logs.items(): + for debug_log in debug_logs: + debug_config.append("{}".format(debug_log)) + + if type(disable_logs) is list: + for daemon in disable_logs: + for debug_log in DEBUG_LOGS[daemon]: + debug_config.append("no {}".format(debug_log)) + elif type(disable_logs) is dict: + for daemon, debug_logs in disable_logs.items(): + for debug_log in debug_logs: + debug_config.append("no {}".format(debug_log)) + + result = create_common_configuration( + tgen, router, debug_config, "debug_log_config", build=build + ) + except InvalidCLIError: + # Traceback + errormsg = traceback.format_exc() + logger.error(errormsg) + return errormsg + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return result + + ############################################# # Common APIs, will be used by all protocols ############################################# @@ -3728,7 +3865,7 @@ def get_ipv6_linklocal_address(topo, node, intf): """ tgen = get_topogen() ext_nh = tgen.net[node].get_ipv6_linklocal() - req_nh = topo[node]['links'][intf]['interface'] + req_nh = topo[node]["links"][intf]["interface"] llip = None for llips in ext_nh: if llips[0] == req_nh: @@ -3736,8 +3873,9 @@ def get_ipv6_linklocal_address(topo, node, intf): logger.info("Link local ip found = %s", llip) return llip - errormsg = "Failed: Link local ip not found on router {}, "\ - "interface {}".format(node, intf) + errormsg = "Failed: Link local ip not found on router {}, " "interface {}".format( + node, intf + ) return errormsg @@ -4372,3 +4510,51 @@ def verify_ip_nht(tgen, input_dict): logger.debug("Exiting lib API: verify_ip_nht()") return False + + +def kernel_requires_l3mdev_adjustment(): + """ + Checks if the L3 master device needs to be adjusted to handle VRF traffic + based on kernel version. + + Returns + ------- + 1 or 0 + """ + + if version_cmp(platform.release(), "4.15") >= 0: + return 1 + return 0 + + +def adjust_router_l3mdev(tgen, router): + """ + Adjusts a routers L3 master device to handle VRF traffic depending on kernel + version. + + Parameters + ---------- + * `tgen` : tgen object + * `router` : router id to be configured. + + Returns + ------- + True + """ + + l3mdev_accept = kernel_requires_l3mdev_adjustment() + + logger.info( + "router {0}: setting net.ipv4.tcp_l3mdev_accept={1}".format( + router, l3mdev_accept + ) + ) + + output = tgen.net[router].cmd("sysctl -n net.ipv4.tcp_l3mdev_accept") + logger.info("router {0}: existing tcp_l3mdev_accept was {1}".format(router, output)) + + tgen.net[router].cmd( + "sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept) + ) + + return True diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 9f642411b5..04a12d0eec 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -344,9 +344,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config= for lnk in input_dict[router]["links"].keys(): if "ospf" not in input_dict[router]["links"][lnk]: logger.debug( - "Router %s: ospf config is not present in" - "input_dict", - router + "Router %s: ospf config is not present in" "input_dict", router ) continue ospf_data = input_dict[router]["links"][lnk]["ospf"] diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 5cc1a6981d..2a46115850 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -50,6 +50,9 @@ from mininet.node import Node, OVSSwitch, Host from mininet.log import setLogLevel, info from mininet.cli import CLI from mininet.link import Intf +from mininet.term import makeTerm + +g_extra_config = {} def gdb_core(obj, daemon, corefiles): @@ -516,6 +519,43 @@ def normalize_text(text): return text +def is_linux(): + """ + Parses unix name output to check if running on GNU/Linux. + + Returns True if running on Linux, returns False otherwise. + """ + + if os.uname()[0] == "Linux": + return True + return False + + +def iproute2_is_vrf_capable(): + """ + Checks if the iproute2 version installed on the system is capable of + handling VRFs by interpreting the output of the 'ip' utility found in PATH. + + Returns True if capability can be detected, returns False otherwise. + """ + + if is_linux(): + try: + subp = subprocess.Popen( + ["ip", "route", "show", "vrf"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + ) + iproute2_err = subp.communicate()[1].splitlines()[0].split()[0] + + if iproute2_err != "Error:": + return True + except Exception: + pass + return False + + def module_present_linux(module, load): """ Returns whether `module` is present. @@ -1303,6 +1343,28 @@ class Router(Node): logger.info("No daemon {} known".format(daemon)) # print "Daemons after:", self.daemons + # Run a command in a new window (gnome-terminal, screen, tmux, xterm) + def runInWindow(self, cmd, title=None): + topo_terminal = os.getenv("FRR_TOPO_TERMINAL") + if topo_terminal or ("TMUX" not in os.environ and "STY" not in os.environ): + term = topo_terminal if topo_terminal else "xterm" + makeTerm(self, title=title if title else cmd, term=term, cmd=cmd) + else: + nscmd = "sudo nsenter -m -n -t {} {}".format(self.pid, cmd) + if "TMUX" in os.environ: + self.cmd("tmux select-layout main-horizontal") + wcmd = "tmux split-window -h" + cmd = "{} {}".format(wcmd, nscmd) + elif "STY" in os.environ: + if os.path.exists( + "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"]) + ): + wcmd = "screen" + else: + wcmd = "sudo -u {} screen".format(os.environ["SUDO_USER"]) + cmd = "{} {}".format(wcmd, nscmd) + self.cmd(cmd) + def startRouter(self, tgen=None): # Disable integrated-vtysh-config self.cmd( @@ -1355,6 +1417,14 @@ class Router(Node): return "LDP/MPLS Tests need mpls kernel modules" self.cmd("echo 100000 > /proc/sys/net/mpls/platform_labels") + shell_routers = g_extra_config["shell"] + if "all" in shell_routers or self.name in shell_routers: + self.runInWindow(os.getenv("SHELL", "bash")) + + vtysh_routers = g_extra_config["vtysh"] + if "all" in vtysh_routers or self.name in vtysh_routers: + self.runInWindow("vtysh") + if self.daemons["eigrpd"] == 1: eigrpd_path = os.path.join(self.daemondir, "eigrpd") if not os.path.isfile(eigrpd_path): @@ -1381,6 +1451,10 @@ class Router(Node): def startRouterDaemons(self, daemons=None): "Starts all FRR daemons for this router." + gdb_breakpoints = g_extra_config["gdb_breakpoints"] + gdb_daemons = g_extra_config["gdb_daemons"] + gdb_routers = g_extra_config["gdb_routers"] + bundle_data = "" if os.path.exists("/etc/frr/support_bundle_commands.conf"): @@ -1410,7 +1484,7 @@ class Router(Node): # If `daemons` was specified then some upper API called us with # specific daemons, otherwise just use our own configuration. daemons_list = [] - if daemons != None: + if daemons is not None: daemons_list = daemons else: # Append all daemons configured. @@ -1418,47 +1492,64 @@ class Router(Node): if self.daemons[daemon] == 1: daemons_list.append(daemon) - # Start Zebra first - if "zebra" in daemons_list: - zebra_path = os.path.join(self.daemondir, "zebra") - zebra_option = self.daemons_options["zebra"] - self.cmd( - "ASAN_OPTIONS=log_path=zebra.asan {0} {1} --log file:zebra.log --log-level debug -s 90000000 -d > zebra.out 2> zebra.err".format( - zebra_path, zebra_option + def start_daemon(daemon, extra_opts=None): + daemon_opts = self.daemons_options.get(daemon, "") + rediropt = " > {0}.out 2> {0}.err".format(daemon) + if daemon == "snmpd": + binary = "/usr/sbin/snmpd" + cmdenv = "" + cmdopt = "{} -C -c /etc/frr/snmpd.conf -p ".format( + daemon_opts + ) + "/var/run/{}/snmpd.pid -x /etc/frr/agentx".format(self.routertype) + else: + binary = os.path.join(self.daemondir, daemon) + cmdenv = "ASAN_OPTIONS=log_path={0}.asan".format(daemon) + cmdopt = "{} --log file:{}.log --log-level debug".format( + daemon_opts, daemon ) - ) - logger.debug("{}: {} zebra started".format(self, self.routertype)) + if extra_opts: + cmdopt += " " + extra_opts - # Remove `zebra` so we don't attempt to start it again. + if ( + (gdb_routers or gdb_daemons) + and ( + not gdb_routers or self.name in gdb_routers or "all" in gdb_routers + ) + and (not gdb_daemons or daemon in gdb_daemons or "all" in gdb_daemons) + ): + if daemon == "snmpd": + cmdopt += " -f " + + cmdopt += rediropt + gdbcmd = "sudo -E gdb " + binary + if gdb_breakpoints: + gdbcmd += " -ex 'set breakpoint pending on'" + for bp in gdb_breakpoints: + gdbcmd += " -ex 'b {}'".format(bp) + gdbcmd += " -ex 'run {}'".format(cmdopt) + + self.runInWindow(gdbcmd, daemon) + else: + if daemon != "snmpd": + cmdopt += " -d " + cmdopt += rediropt + self.cmd(" ".join([cmdenv, binary, cmdopt])) + logger.info("{}: {} {} started".format(self, self.routertype, daemon)) + + # Start Zebra first + if "zebra" in daemons_list: + start_daemon("zebra", "-s 90000000") while "zebra" in daemons_list: daemons_list.remove("zebra") # Start staticd next if required if "staticd" in daemons_list: - staticd_path = os.path.join(self.daemondir, "staticd") - staticd_option = self.daemons_options["staticd"] - self.cmd( - "ASAN_OPTIONS=log_path=staticd.asan {0} {1} --log file:staticd.log --log-level debug -d > staticd.out 2> staticd.err".format( - staticd_path, staticd_option - ) - ) - logger.debug("{}: {} staticd started".format(self, self.routertype)) - - # Remove `staticd` so we don't attempt to start it again. + start_daemon("staticd") while "staticd" in daemons_list: daemons_list.remove("staticd") if "snmpd" in daemons_list: - snmpd_path = "/usr/sbin/snmpd" - snmpd_option = self.daemons_options["snmpd"] - self.cmd( - "{0} {1} -C -c /etc/frr/snmpd.conf -p /var/run/{2}/snmpd.pid -x /etc/frr/agentx > snmpd.out 2> snmpd.err".format( - snmpd_path, snmpd_option, self.routertype - ) - ) - logger.info("{}: {} snmpd started".format(self, self.routertype)) - - # Remove `snmpd` so we don't attempt to start it again. + start_daemon("snmpd") while "snmpd" in daemons_list: daemons_list.remove("snmpd") @@ -1470,17 +1561,9 @@ class Router(Node): # Now start all the other daemons for daemon in daemons_list: - # Skip disabled daemons and zebra if self.daemons[daemon] == 0: continue - - daemon_path = os.path.join(self.daemondir, daemon) - self.cmd( - "ASAN_OPTIONS=log_path={2}.asan {0} {1} --log file:{2}.log --log-level debug -d > {2}.out 2> {2}.err".format( - daemon_path, self.daemons_options.get(daemon, ""), daemon - ) - ) - logger.debug("{}: {} {} started".format(self, self.routertype, daemon)) + start_daemon(daemon) # Check if daemons are running. rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype) diff --git a/tests/topotests/multicast-pim-bsm-topo1/test_mcast_pim_bsmp_01.py b/tests/topotests/multicast-pim-bsm-topo1/test_mcast_pim_bsmp_01.py index 6b7180978e..cd398a5111 100644 --- a/tests/topotests/multicast-pim-bsm-topo1/test_mcast_pim_bsmp_01.py +++ b/tests/topotests/multicast-pim-bsm-topo1/test_mcast_pim_bsmp_01.py @@ -648,7 +648,11 @@ def test_BSR_CRP_with_blackhole_address_p1(request): input_dict = { "i1": {"static_routes": [{"network": BSR1_ADDR, "next_hop": next_hop_rp}]}, "l1": {"static_routes": [{"network": BSR1_ADDR, "next_hop": next_hop_lhr}]}, - "f1": {"static_routes": [{"network": CRP, "next_hop": next_hop_fhr, "delete": True}]}, + "f1": { + "static_routes": [ + {"network": CRP, "next_hop": next_hop_fhr, "delete": True} + ] + }, } result = create_static_routes(tgen, input_dict) @@ -692,10 +696,11 @@ def test_BSR_CRP_with_blackhole_address_p1(request): step("Verify if b1 chosen as BSR in l1") result = verify_pim_bsr(tgen, topo, "l1", BSR_IP_1, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "b1 is not chosen as BSR in l1 \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "b1 is not chosen as BSR in l1 \n Error: {}".format( tc_name, result - )) + ) state_after = verify_pim_interface_traffic(tgen, state_dict) assert isinstance( @@ -841,10 +846,12 @@ def test_new_router_fwd_p0(request): # Verify bsr state in l1 step("Verify no BSR in l1 as i1 would not forward the no-forward bsm") result = verify_pim_bsr(tgen, topo, "l1", bsr_ip, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "BSR data is present after no-forward bsm also \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) # unconfigure unicast bsm on f1-i1-eth2 step("unconfigure unicast bsm on f1-i1-eth2, will forward with only mcast") @@ -966,10 +973,11 @@ def test_int_bsm_config_p1(request): result = verify_ip_mroutes( tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "Mroutes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "Mroutes are still present \n Error: {}".format( tc_name, result - )) + ) # unconfigure bsm processing on f1 on f1-i1-eth2 step("unconfigure bsm processing on f1 in f1-i1-eth2, will drop bsm") @@ -989,20 +997,21 @@ def test_int_bsm_config_p1(request): # Verify bsr state in i1 step("Verify if b1 is not chosen as BSR in i1") result = verify_pim_bsr(tgen, topo, "i1", bsr_ip, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "b1 is chosen as BSR in i1 \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "b1 is chosen as BSR in i1 \n Error: {}".format( tc_name, result - )) + ) # check if mroute still not installed because of rp not available step("check if mroute still not installed because of rp not available") result = verify_ip_mroutes( tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "mroute installed but rp not available \n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "mroute installed but rp not available \n Error: {}".format(tc_name, result) + ) # configure bsm processing on i1 on f1-i1-eth2 step("configure bsm processing on f1 in f1-i1-eth2, will accept bsm") @@ -1464,10 +1473,11 @@ def test_BSM_timeout_p0(request): tgen, topo, "f1", group, rp_source="BSR", expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "bsr has not aged out in f1 \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "bsr has not aged out in f1 \n Error: {}".format( tc_name, result - )) + ) # Verify RP mapping removed after hold timer expires group = "225.1.1.1/32" @@ -1491,20 +1501,23 @@ def test_BSM_timeout_p0(request): result = verify_join_state_and_timer( tgen, dut, iif, src_addr, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "join state is up and join timer is running in l1 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) # Verify ip mroute is not installed step("Verify mroute not installed in l1") result = verify_ip_mroutes( tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "mroute installed in l1 \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "mroute installed in l1 \n Error: {}".format( tc_name, result - )) + ) step("clear BSM database before moving to next case") clear_bsrp_data(tgen, topo) @@ -1657,10 +1670,11 @@ def test_iif_join_state_p0(request): result = verify_ip_mroutes( tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "mroute installed in l1 \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "mroute installed in l1 \n Error: {}".format( tc_name, result - )) + ) # Add back route for RP to make it reachable step("Add back route for RP to make it reachable") diff --git a/tests/topotests/multicast-pim-bsm-topo2/test_mcast_pim_bsmp_02.py b/tests/topotests/multicast-pim-bsm-topo2/test_mcast_pim_bsmp_02.py index 5fc5e52518..199746d5f6 100644 --- a/tests/topotests/multicast-pim-bsm-topo2/test_mcast_pim_bsmp_02.py +++ b/tests/topotests/multicast-pim-bsm-topo2/test_mcast_pim_bsmp_02.py @@ -104,6 +104,10 @@ from lib.pim import ( from lib.topolog import logger from lib.topojson import build_topo_from_json, build_config_from_json + +pytestmark = [pytest.mark.pimd] + + # Reading the data from JSON File for topology creation jsonFile = "{}/mcast_pim_bsmp_02.json".format(CWD) try: @@ -454,10 +458,11 @@ def test_starg_mroute_p0(request): result = verify_ip_mroutes( tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, wait=20, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "mroute installed in l1 \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "mroute installed in l1 \n Error: {}".format( tc_name, result - )) + ) # Send BSM again to configure rp step("Add back RP by sending BSM from b1") @@ -807,10 +812,11 @@ def test_BSR_election_p0(request): # Verify bsr state in FHR step("Verify if b2 is not chosen as bsr in f1") result = verify_pim_bsr(tgen, topo, "f1", bsr_ip2, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "b2 is chosen as bsr in f1 \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "b2 is chosen as bsr in f1 \n Error: {}".format( tc_name, result - )) + ) # Verify if b1 is still chosen as bsr step("Verify if b1 is still chosen as bsr in f1") diff --git a/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py index 1c22654541..33f476de44 100755 --- a/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py +++ b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo3.py @@ -3487,11 +3487,11 @@ def test_prune_sent_to_LHR_and_FHR_when_PIMnbr_down_p2(request): IGMP_JOIN_RANGE_1, expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "upstream is still present after shut the link from " - "FHR to RP from RP node \n Error: {}".format( - tc_name, result - )) + "FHR to RP from RP node \n Error: {}".format(tc_name, result) + ) step(" No shut the link from FHR to RP from RP node") @@ -3638,11 +3638,11 @@ def test_prune_sent_to_LHR_and_FHR_when_PIMnbr_down_p2(request): IGMP_JOIN_RANGE_1, expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "upstream is still present after shut the link from " - "FHR to RP from FHR node \n Error: {}".format( - tc_name, result - )) + "FHR to RP from FHR node \n Error: {}".format(tc_name, result) + ) step(" No shut the link from FHR to RP from FHR node") diff --git a/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py index 68b7849c2b..1081b764ac 100755 --- a/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py +++ b/tests/topotests/multicast-pim-sm-topo3/test_multicast_pim_sm_topo4.py @@ -490,10 +490,12 @@ def test_mroute_when_RP_reachable_default_route_p2(request): data["oil"], expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "mroutes(S,G) are present after delete of static routes on c1 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) result = verify_upstream_iif( tgen, @@ -503,10 +505,12 @@ def test_mroute_when_RP_reachable_default_route_p2(request): IGMP_JOIN_RANGE_1, expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "upstream is present after delete of static routes on c1 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) for data in input_dict_starg: result = verify_ip_mroutes( @@ -518,10 +522,12 @@ def test_mroute_when_RP_reachable_default_route_p2(request): data["oil"], expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "mroutes(*,G) are present after delete of static routes on c1 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) result = verify_upstream_iif( tgen, @@ -531,10 +537,12 @@ def test_mroute_when_RP_reachable_default_route_p2(request): IGMP_JOIN_RANGE_1, expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "upstream is present after delete of static routes on c1 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("Configure default routes on c2") @@ -557,10 +565,12 @@ def test_mroute_when_RP_reachable_default_route_p2(request): result = verify_pim_rp_info( tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "RP info is unknown after removing static route from c2 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("Verify (s,g) populated after adding default route ") @@ -787,10 +797,11 @@ def test_mroute_with_RP_default_route_all_nodes_p2(request): data["oil"], expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "mroutes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format( tc_name, result - )) + ) result = verify_upstream_iif( tgen, @@ -800,10 +811,11 @@ def test_mroute_with_RP_default_route_all_nodes_p2(request): IGMP_JOIN_RANGE_1, expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "upstream is still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "upstream is still present \n Error: {}".format( tc_name, result - )) + ) step("Configure default routes on all the nodes") @@ -840,10 +852,12 @@ def test_mroute_with_RP_default_route_all_nodes_p2(request): result = verify_pim_rp_info( tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "RP info is unknown after removing static route from c2 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("Verify (s,g) populated after adding default route ") diff --git a/tests/topotests/multicast-pim-static-rp-topo1/test_multicast_pim_static_rp.py b/tests/topotests/multicast-pim-static-rp-topo1/test_multicast_pim_static_rp.py index 1317ec67b4..e90230eb3b 100755 --- a/tests/topotests/multicast-pim-static-rp-topo1/test_multicast_pim_static_rp.py +++ b/tests/topotests/multicast-pim-static-rp-topo1/test_multicast_pim_static_rp.py @@ -423,10 +423,12 @@ def test_add_delete_static_RP_p0(request): dut = "r1" interface = "r1-r0-eth0" result = verify_igmp_groups(tgen, dut, interface, GROUP_ADDRESS, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: igmp group present without any IGMP join \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r1: Verify show ip pim interface traffic without any IGMP join") state_dict = {"r1": {"r1-r2-eth1": ["pruneTx"]}} @@ -492,26 +494,29 @@ def test_add_delete_static_RP_p0(request): result = verify_pim_rp_info( tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: RP info present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: RP info present \n Error: {}".format( tc_name, result - )) + ) step("r1: Verify upstream IIF interface") result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: upstream IIF interface present \n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: upstream IIF interface present \n Error: {}".format(tc_name, result) + ) step("r1: Verify upstream join state and join timer") result = verify_join_state_and_timer( tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: upstream join state is up and join timer is running \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r1: Verify PIM state") result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False) @@ -521,10 +526,11 @@ def test_add_delete_static_RP_p0(request): step("r1: Verify ip mroutes") result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: mroutes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: mroutes are still present \n Error: {}".format( tc_name, result - )) + ) step("r1: Verify show ip pim interface traffic without any IGMP join") state_after = verify_pim_interface_traffic(tgen, state_dict) @@ -681,10 +687,12 @@ def test_SPT_RPT_path_same_p1(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S, G) upstream join state is up and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r2-eth1" @@ -811,17 +819,19 @@ def test_not_reachable_static_RP_p0(request): "using show ip pim state" ) result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "OIL is not same and IIF is not cleared on R1 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r1: upstream IIF should be unknown , verify using show ip pim" "upstream") result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: upstream IIF is not unknown \n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: upstream IIF is not unknown \n Error: {}".format(tc_name, result) + ) step( "r1: join state should not be joined and join timer should stop," @@ -830,10 +840,12 @@ def test_not_reachable_static_RP_p0(request): result = verify_join_state_and_timer( tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: join state is joined and timer is not stopped \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step( "r1: (*,G) prune is sent towards the RP interface, verify using" @@ -850,10 +862,12 @@ def test_not_reachable_static_RP_p0(request): step("r1: (*, G) cleared from mroute table using show ip mroute") result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: (*, G) are not cleared from mroute table \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) logger.info("Expected behavior: {}".format(result)) # Uncomment next line for debugging @@ -920,10 +934,11 @@ def test_add_RP_after_join_received_p1(request): result = verify_pim_rp_info( tgen, topo, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: rp-info is present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: rp-info is present \n Error: {}".format( tc_name, result - )) + ) step("joinTx value before join sent") state_dict = {"r1": {"r1-r2-eth1": ["joinTx"]}} @@ -944,34 +959,38 @@ def test_add_RP_after_join_received_p1(request): step("r1: Verify upstream IIF interface") result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: upstream IFF interface is present \n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: upstream IFF interface is present \n Error: {}".format(tc_name, result) + ) step("r1: Verify upstream join state and join timer") result = verify_join_state_and_timer( tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: upstream join state is joined and timer is running \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r1: Verify PIM state") result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: PIM state is up\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: PIM state is up\n Error: {}".format( tc_name, result - )) + ) step("r1: Verify ip mroutes") result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: mroutes are still present\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: mroutes are still present\n Error: {}".format( tc_name, result - )) + ) step("r1: Configure static RP") input_dict = { @@ -1095,33 +1114,37 @@ def test_reachable_static_RP_after_join_p0(request): step("r1 : Verify upstream IIF interface") iif = "r1-r2-eth1" result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: upstream IIF interface is present\n Error: {}".format( - tc_name, result - )) + assert result is not True, ( + "Testcase {} : Failed \n " + "r1: upstream IIF interface is present\n Error: {}".format(tc_name, result) + ) step("r1 : Verify upstream join state and join timer") result = verify_join_state_and_timer( tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: upstream join state is joined and timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r1 : Verify PIM state") result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: PIM state is up\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: PIM state is up\n Error: {}".format( tc_name, result - )) + ) step("r1 : Verify ip mroutes") result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: mroutes are still present\n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: mroutes are still present\n Error: {}".format( tc_name, result - )) + ) step("r1: Make RP reachable") intf = "r1-r2-eth1" @@ -1362,10 +1385,12 @@ def test_send_join_on_higher_preffered_rp_p1(request): result = verify_pim_rp_info( tgen, topo, dut, GROUP_RANGE, oif, rp_address_2, SOURCE, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: rp-info is present for group 225.1.1.1 \n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step( "r1 : Verify RPF interface updated in mroute when higher preferred" @@ -1619,11 +1644,11 @@ def test_RP_configured_as_LHR_1_p1(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S, G) upstream join state is joined and join" - " timer is running \n Error: {}".format( - tc_name, result - )) + " timer is running \n Error: {}".format(tc_name, result) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -1828,10 +1853,12 @@ def test_RP_configured_as_LHR_2_p1(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -2036,10 +2063,12 @@ def test_RP_configured_as_FHR_1_p1(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -2245,10 +2274,12 @@ def test_RP_configured_as_FHR_2_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -2372,10 +2403,12 @@ def test_SPT_RPT_path_different_p1(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -2394,10 +2427,12 @@ def test_SPT_RPT_path_different_p1(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r2: Verify (S, G) ip mroutes") oif = "none" @@ -2623,10 +2658,12 @@ def test_restart_pimd_process_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -2791,10 +2828,12 @@ def test_multiple_groups_same_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -2813,10 +2852,12 @@ def test_multiple_groups_same_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r2: Verify (S, G) ip mroutes") oif = "none" @@ -2932,10 +2973,12 @@ def test_multiple_groups_same_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r2: Verify (S, G) ip mroutes") oif = "none" @@ -2952,10 +2995,12 @@ def test_multiple_groups_same_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -3132,10 +3177,12 @@ def test_multiple_groups_different_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r2: Verify (S, G) ip mroutes") oif = "none" @@ -3154,10 +3201,12 @@ def test_multiple_groups_different_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -3224,10 +3273,12 @@ def test_multiple_groups_different_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r4: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r4: Verify (S, G) ip mroutes") oif = "none" @@ -3399,10 +3450,12 @@ def test_multiple_groups_different_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r2: Verify (S, G) ip mroutes") oif = "none" @@ -3421,10 +3474,12 @@ def test_multiple_groups_different_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -3491,10 +3546,12 @@ def test_multiple_groups_different_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r4: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r4: Verify (S, G) ip mroutes") oif = "none" @@ -3513,10 +3570,12 @@ def test_multiple_groups_different_RP_address_p2(request): result = verify_join_state_and_timer( tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (S, G) ip mroutes") oif = "r3-r1-eth0" @@ -3643,30 +3702,36 @@ def test_shutdown_primary_path_p1(request): iif = "r1-r3-eth2" oif = "r1-r0-eth0" result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: (*,G) mroutes are not cleared after shut of R1 to R3 link\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r2: Verify (*, G) ip mroutes") dut = "r2" iif = "lo" oif = "r2-r3-eth1" result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: (*,G) mroutes are not cleared after shut of R1 to R3 link\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: Verify (*, G) ip mroutes") dut = "r3" iif = "r3-r2-eth1" oif = "r3-r1-eth0" result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r3: (*,G) mroutes are not cleared after shut of R1 to R3 link\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r3: No shutdown the link from R1 to R3 from R3 node") dut = "r3" @@ -3826,20 +3891,24 @@ def test_delete_RP_shut_noshut_upstream_interface_p1(request): iif = "r1-r2-eth1" oif = "r1-r0-eth0" result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: (*,G) mroutes are not cleared after shut of R1 to R0 link\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r2: Verify (*, G) ip mroutes cleared") dut = "r2" iif = "lo" oif = "r2-r1-eth0" result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: (*,G) mroutes are not cleared after shut of R1 to R0 link\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) write_test_footer(tc_name) @@ -3949,20 +4018,24 @@ def test_delete_RP_shut_noshut_RP_interface_p1(request): iif = "r1-r2-eth1" oif = "r1-r0-eth0" result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: (*,G) mroutes are not cleared after shut of R1 to R2 and R3 link\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) step("r2: Verify (*, G) ip mroutes cleared") dut = "r2" iif = "lo" oif = "r2-r1-eth0" result = verify_ip_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r2: (*,G) mroutes are not cleared after shut of R1 to R2 and R3 link\n Error: {}".format( - tc_name, result - )) + tc_name, result + ) + ) write_test_footer(tc_name) diff --git a/tests/topotests/nhrp-topo/r1/nhrp4_cache.json b/tests/topotests/nhrp-topo/r1/nhrp4_cache.json new file mode 100644 index 0000000000..6426a939be --- /dev/null +++ b/tests/topotests/nhrp-topo/r1/nhrp4_cache.json @@ -0,0 +1,29 @@ +{ + "attr":{ + "entriesCount":2 + }, + "table":[ + { + "interface":"r1-gre0", + "type":"nhs", + "protocol":"10.255.255.2", + "nbma":"10.2.1.2", + "claimed_nbma":"10.2.1.2", + "used":false, + "timeout":true, + "auth":false, + "identity":"" + }, + { + "interface":"r1-gre0", + "type":"local", + "protocol":"10.255.255.1", + "nbma":"10.1.1.1", + "claimed_nbma":"10.1.1.1", + "used":false, + "timeout":false, + "auth":false, + "identity":"-" + } + ] +} diff --git a/tests/topotests/nhrp-topo/r1/nhrp_route4.json b/tests/topotests/nhrp-topo/r1/nhrp_route4.json new file mode 100644 index 0000000000..68b5a6ece2 --- /dev/null +++ b/tests/topotests/nhrp-topo/r1/nhrp_route4.json @@ -0,0 +1,25 @@ +{ + "10.255.255.2\/32":[ + { + "prefix":"10.255.255.2\/32", + "protocol":"nhrp", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":10, + "metric":0, + "installed":true, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r1-gre0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/nhrp-topo/r1/nhrpd.conf b/tests/topotests/nhrp-topo/r1/nhrpd.conf new file mode 100644 index 0000000000..04114bdbe6 --- /dev/null +++ b/tests/topotests/nhrp-topo/r1/nhrpd.conf @@ -0,0 +1,10 @@ +log stdout debugging +debug nhrp all +interface r1-gre0 + ip nhrp holdtime 500 + ip nhrp shortcut + ip nhrp network-id 42 + ip nhrp nhs dynamic nbma 10.2.1.2 + ip nhrp registration no-unique + tunnel source r1-eth0 +exit diff --git a/tests/topotests/nhrp-topo/r1/zebra.conf b/tests/topotests/nhrp-topo/r1/zebra.conf new file mode 100644 index 0000000000..b45670fcb2 --- /dev/null +++ b/tests/topotests/nhrp-topo/r1/zebra.conf @@ -0,0 +1,12 @@ +interface r1-eth0 + ip address 10.1.1.1/24 +! +ip route 10.2.1.0/24 10.1.1.3 +interface r1-gre0 + ip address 10.255.255.1/32 + no link-detect + ipv6 nd suppress-ra +exit +interface r1-eth1 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/nhrp-topo/r2/nhrp4_cache.json b/tests/topotests/nhrp-topo/r2/nhrp4_cache.json new file mode 100644 index 0000000000..34558e0c28 --- /dev/null +++ b/tests/topotests/nhrp-topo/r2/nhrp4_cache.json @@ -0,0 +1,29 @@ +{ + "attr":{ + "entriesCount":2 + }, + "table":[ + { + "interface":"r2-gre0", + "type":"local", + "protocol":"10.255.255.2", + "nbma":"10.2.1.2", + "claimed_nbma":"10.2.1.2", + "used":false, + "timeout":false, + "auth":false, + "identity":"-" + }, + { + "interface":"r2-gre0", + "type":"dynamic", + "protocol":"10.255.255.1", + "nbma":"10.1.1.1", + "claimed_nbma":"10.1.1.1", + "used":false, + "timeout":true, + "auth":false, + "identity":"" + } + ] +} diff --git a/tests/topotests/nhrp-topo/r2/nhrp_route4.json b/tests/topotests/nhrp-topo/r2/nhrp_route4.json new file mode 100644 index 0000000000..7393cba893 --- /dev/null +++ b/tests/topotests/nhrp-topo/r2/nhrp_route4.json @@ -0,0 +1,25 @@ +{ + "10.255.255.1\/32":[ + { + "prefix":"10.255.255.1\/32", + "protocol":"nhrp", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":10, + "metric":0, + "installed":true, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "fib":true, + "directlyConnected":true, + "interfaceName":"r2-gre0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/nhrp-topo/r2/nhrpd.conf b/tests/topotests/nhrp-topo/r2/nhrpd.conf new file mode 100644 index 0000000000..e4f6fb7445 --- /dev/null +++ b/tests/topotests/nhrp-topo/r2/nhrpd.conf @@ -0,0 +1,10 @@ +debug nhrp all +log stdout debugging +nhrp nflog-group 1 +interface r2-gre0 + ip nhrp holdtime 500 + ip nhrp redirect + ip nhrp network-id 42 + ip nhrp registration no-unique + tunnel source r2-eth0 +exit diff --git a/tests/topotests/nhrp-topo/r2/zebra.conf b/tests/topotests/nhrp-topo/r2/zebra.conf new file mode 100644 index 0000000000..9f40d4d72e --- /dev/null +++ b/tests/topotests/nhrp-topo/r2/zebra.conf @@ -0,0 +1,12 @@ +interface r2-eth0 + ip address 10.2.1.2/24 +! +ip route 10.1.1.0/24 10.2.1.3 +interface r2-gre0 + ip address 10.255.255.2/32 + no link-detect + ipv6 nd suppress-ra +! +interface r2-eth1 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/nhrp-topo/r3/zebra.conf b/tests/topotests/nhrp-topo/r3/zebra.conf new file mode 100644 index 0000000000..6d3d267978 --- /dev/null +++ b/tests/topotests/nhrp-topo/r3/zebra.conf @@ -0,0 +1,11 @@ +debug zebra kernel +debug zebra rib +debug zebra events +debug zebra packet +ip forwarding +interface r3-eth0 + ip address 10.1.1.3/24 +! +interface r3-eth1 + ip address 10.2.1.3/24 +exit diff --git a/tests/topotests/nhrp-topo/test_nhrp_topo.dot b/tests/topotests/nhrp-topo/test_nhrp_topo.dot new file mode 100644 index 0000000000..6b68fb398f --- /dev/null +++ b/tests/topotests/nhrp-topo/test_nhrp_topo.dot @@ -0,0 +1,73 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n10.0.3.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n2001:db8:4::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0"]; + r2 -- sw1 [label="eth0"]; + + r2 -- sw2 [label="eth1"]; + r3 -- sw2 [label="eth0"]; + + r2 -- sw3 [label="eth2"]; + r4 -- sw3 [label="eth0"]; +} diff --git a/tests/topotests/nhrp-topo/test_nhrp_topo.py b/tests/topotests/nhrp-topo/test_nhrp_topo.py new file mode 100644 index 0000000000..1687961f34 --- /dev/null +++ b/tests/topotests/nhrp-topo/test_nhrp_topo.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python + +# +# test_nhrp_topo.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_nhrp_topo.py: Test the FRR/Quagga NHRP daemon +""" + +import os +import sys +import json +from functools import partial +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 +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class NHRPTopo(Topo): + "Test topology builder" + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 3 routers. + for routern in range(1, 4): + tgen.add_router('r{}'.format(routern)) + + switch = tgen.add_switch('s1') + switch.add_link(tgen.gears['r1']) + switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch('s2') + switch.add_link(tgen.gears['r2']) + switch.add_link(tgen.gears['r3']) + switch = tgen.add_switch('s3') + switch.add_link(tgen.gears['r2']) + switch = tgen.add_switch('s4') + switch.add_link(tgen.gears['r1']) + + +def _populate_iface(): + tgen = get_topogen() + cmds_tot_hub = ['ip tunnel add {0}-gre0 mode gre ttl 64 key 42 dev {0}-eth0 local 10.2.1.{1} remote 0.0.0.0', + 'ip link set dev {0}-gre0 up', + 'echo 0 > /proc/sys/net/ipv4/ip_forward_use_pmtu', + 'echo 1 > /proc/sys/net/ipv6/conf/{0}-eth0/disable_ipv6', + 'echo 1 > /proc/sys/net/ipv6/conf/{0}-gre0/disable_ipv6'] + + cmds_tot = ['ip tunnel add {0}-gre0 mode gre ttl 64 key 42 dev {0}-eth0 local 10.1.1.{1} remote 0.0.0.0', + 'ip link set dev {0}-gre0 up', + 'echo 0 > /proc/sys/net/ipv4/ip_forward_use_pmtu', + 'echo 1 > /proc/sys/net/ipv6/conf/{0}-eth0/disable_ipv6', + 'echo 1 > /proc/sys/net/ipv6/conf/{0}-gre0/disable_ipv6'] + + for cmd in cmds_tot_hub: + input = cmd.format('r2', '2') + logger.info('input: '+cmd) + output = tgen.net['r2'].cmd(cmd.format('r2', '2')) + logger.info('output: '+output); + + for cmd in cmds_tot: + input = cmd.format('r1', '1') + logger.info('input: '+cmd) + output = tgen.net['r1'].cmd(cmd.format('r1', '1')) + logger.info('output: '+output); + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(NHRPTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, '{}/zebra.conf'.format(rname)), + ) + if rname in ('r1', 'r2'): + router.load_config( + TopoRouter.RD_NHRP, + os.path.join(CWD, '{}/nhrpd.conf'.format(rname)) + ) + + # Initialize all routers. + logger.info('Launching BGP, NHRP') + for name in router_list: + router = tgen.gears[name] + router.start() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged before checking for the NHRP + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check IPv4 routing tables. + logger.info("Checking NHRP cache and IPv4 routes for convergence") + router_list = tgen.routers() + + for rname, router in router_list.iteritems(): + if rname == 'r3': + continue + + json_file = '{}/{}/nhrp4_cache.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip nhrp cache json', expected) + _, result = topotest.run_and_expect(test_func, None, count=40, + wait=0.5) + + output = router.vtysh_cmd('show ip nhrp cache') + logger.info(output) + + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + for rname, router in router_list.iteritems(): + if rname == 'r3': + continue + + json_file = '{}/{}/nhrp_route4.json'.format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info('skipping file {}'.format(json_file)) + continue + + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, + router, 'show ip route nhrp json', expected) + _, result = topotest.run_and_expect(test_func, None, count=40, + wait=0.5) + + output = router.vtysh_cmd('show ip route nhrp') + logger.info(output) + + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + for rname, router in router_list.iteritems(): + if rname == 'r3': + continue + logger.info('Dump neighbor information on {}-gre0'.format(rname)) + output = router.run('ip neigh show') + logger.info(output) + + +def test_nhrp_connection(): + "Assert that the NHRP peers can find themselves." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pingrouter = tgen.gears['r1'] + logger.info('Check Ping IPv4 from R1 to R2 = 10.255.255.2)') + output = pingrouter.run('ping 10.255.255.2 -f -c 1000') + logger.info(output) + if '1000 packets transmitted, 1000 received' not in output: + assertmsg = 'expected ping IPv4 from R1 to R2 should be ok' + assert 0, assertmsg + else: + logger.info('Check Ping IPv4 from R1 to R2 OK') + + +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/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py b/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py index 57b93c3fd5..b6e5e14830 100644 --- a/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py +++ b/tests/topotests/ospf-sr-topo1/test_ospf_sr_topo1.py @@ -86,6 +86,7 @@ from mininet.topo import Topo pytestmark = [pytest.mark.ospfd] + class TemplateTopo(Topo): "Test topology builder" @@ -385,9 +386,7 @@ def test_rib_ipv4_step5(): pytest.skip(tgen.errors) logger.info("Disabling SR on rt6") - tgen.net["rt6"].cmd( - 'vtysh -c "conf t" -c "router ospf" -c "no segment-routing on"' - ) + tgen.net["rt6"].cmd('vtysh -c "conf t" -c "router ospf" -c "no segment-routing on"') for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]: router_compare_json_output( @@ -652,7 +651,7 @@ def test_mpls_lib_step10(): # Expected changes: # -All commands should be rejected # -#def test_ospf_invalid_config_step11(): +# def test_ospf_invalid_config_step11(): # logger.info("Test (step 11): check if invalid configuration is rejected") # tgen = get_topogen() # diff --git a/tests/topotests/ospf-te-topo1/__init__.py b/tests/topotests/ospf-te-topo1/__init__.py new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/ospf-te-topo1/__init__.py diff --git a/tests/topotests/ospf-te-topo1/r1/ospfd.conf b/tests/topotests/ospf-te-topo1/r1/ospfd.conf new file mode 100644 index 0000000000..312dd2697e --- /dev/null +++ b/tests/topotests/ospf-te-topo1/r1/ospfd.conf @@ -0,0 +1,23 @@ +! +interface lo + ip ospf area 0.0.0.0 +! +interface r1-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface r1-eth1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 10.0.255.1 + capability opaque + mpls-te on + mpls-te router-address 10.0.255.1 +! + diff --git a/tests/topotests/ospf-te-topo1/r1/zebra.conf b/tests/topotests/ospf-te-topo1/r1/zebra.conf new file mode 100644 index 0000000000..7c5dc3ffe0 --- /dev/null +++ b/tests/topotests/ospf-te-topo1/r1/zebra.conf @@ -0,0 +1,21 @@ +! +interface lo + ip address 10.0.255.1/32 +! +interface r1-eth0 + ip address 10.0.0.1/24 + link-params + metric 20 + delay 10000 + ava-bw 1.25e+08 + enable + exit-link-params +! +interface r1-eth1 + ip address 10.0.1.1/24 + link-params + enable + exit-link-params +! +ip forwarding +! diff --git a/tests/topotests/ospf-te-topo1/r2/ospfd.conf b/tests/topotests/ospf-te-topo1/r2/ospfd.conf new file mode 100644 index 0000000000..e9c3f65bc2 --- /dev/null +++ b/tests/topotests/ospf-te-topo1/r2/ospfd.conf @@ -0,0 +1,34 @@ +! +interface lo + ip ospf area 0.0.0.0 +! +interface r2-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface r2-eth1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +interface r2-eth2 + ip ospf network point-to-point + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +interface r2-eth3 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +router ospf + ospf router-id 10.0.255.2 + capability opaque + mpls-te on + mpls-te router-address 10.0.255.2 +! diff --git a/tests/topotests/ospf-te-topo1/r2/zebra.conf b/tests/topotests/ospf-te-topo1/r2/zebra.conf new file mode 100644 index 0000000000..69e10191f3 --- /dev/null +++ b/tests/topotests/ospf-te-topo1/r2/zebra.conf @@ -0,0 +1,33 @@ +! +interface lo + ip address 10.0.255.2/32 +! +interface r2-eth0 + ip address 10.0.0.2/24 + link-params + enable + exit-link-params +! +interface r2-eth1 + ip address 10.0.1.2/24 + link-params + enable + exit-link-params +! +interface r2-eth2 + ip address 10.0.3.2/24 + link-params + enable + exit-link-params +! +interface r2-eth3 + ip address 10.0.4.2/24 + link-params + metric 30 + delay 25000 + use-bw 1.25e+8 + enable + exit-link-params +! +ip forwarding +! diff --git a/tests/topotests/ospf-te-topo1/r3/ospfd.conf b/tests/topotests/ospf-te-topo1/r3/ospfd.conf new file mode 100644 index 0000000000..caa5f1e1eb --- /dev/null +++ b/tests/topotests/ospf-te-topo1/r3/ospfd.conf @@ -0,0 +1,24 @@ +! +interface lo + ip ospf area 0.0.0.0 +! +interface r3-eth0 + ip ospf network point-to-point + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +interface r3-eth1 + ip ospf network point-to-point + ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +! +router ospf + ospf router-id 10.0.255.3 + capability opaque + mpls-te on + mpls-te router-address 10.0.255.3 + mpls-te inter-as as +! diff --git a/tests/topotests/ospf-te-topo1/r3/zebra.conf b/tests/topotests/ospf-te-topo1/r3/zebra.conf new file mode 100644 index 0000000000..4cf9077085 --- /dev/null +++ b/tests/topotests/ospf-te-topo1/r3/zebra.conf @@ -0,0 +1,22 @@ +! +interface lo + ip address 10.0.255.3/32 +! +interface r3-eth0 + ip address 10.0.3.1/24 + link-params + enable + admin-grp 0x20 + exit-link-params +! +interface r3-eth1 + ip address 10.0.5.1/24 + link-params + enable + metric 10 + delay 50000 + neighbor 10.0.255.5 as 65535 + exit-link-params +! +ip forwarding +! diff --git a/tests/topotests/ospf-te-topo1/r4/ospfd.conf b/tests/topotests/ospf-te-topo1/r4/ospfd.conf new file mode 100644 index 0000000000..e454673153 --- /dev/null +++ b/tests/topotests/ospf-te-topo1/r4/ospfd.conf @@ -0,0 +1,22 @@ +! +interface lo + ip ospf area 0.0.0.0 +! +interface r4-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 + ip ospf area 0.0.0.0 +! +! +router ospf + ospf router-id 10.0.255.4 + capability opaque + mpls-te on + mpls-te router-address 10.0.255.4 + segment-routing on + segment-routing local-block 5000 5999 + segment-routing global-block 10000 19999 + segment-routing node-msd 12 + segment-routing prefix 10.0.255.4/32 index 400 no-php-flag +! diff --git a/tests/topotests/ospf-te-topo1/r4/zebra.conf b/tests/topotests/ospf-te-topo1/r4/zebra.conf new file mode 100644 index 0000000000..18c003b230 --- /dev/null +++ b/tests/topotests/ospf-te-topo1/r4/zebra.conf @@ -0,0 +1,12 @@ +! +interface lo + ip address 10.0.255.4/32 +! +interface r4-eth0 + ip address 10.0.4.1/24 + link-params + enable + exit-link-params +! +ip forwarding +! diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step1.json b/tests/topotests/ospf-te-topo1/reference/ted_step1.json new file mode 100644 index 0000000000..9624292ccd --- /dev/null +++ b/tests/topotests/ospf-te-topo1/reference/ted_step1.json @@ -0,0 +1,577 @@ +{ + "ted":{ + "name":"OSPF", + "key":1, + "verticesCount":5, + "edgesCount":9, + "subnetsCount":14, + "vertices":[ + { + "vertex-id":167837441, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.1", + "vertex-type":"Standard" + }, + { + "vertex-id":167837442, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.2", + "vertex-type":"Standard" + }, + { + "vertex-id":167837443, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.3", + "vertex-type":"ASBR" + }, + { + "vertex-id":167837444, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.4", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":10000, + "srgb-lower":10000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":5000, + "msd":12 + } + }, + { + "vertex-id":167837445, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.5", + "vertex-type":"Remote ASBR", + "asn":65535 + } + ], + "edges":[ + { + "edge-id":167772161, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":20, + "local-address":"10.0.0.1", + "remote-address":"10.0.0.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":10000, + "available-bandwidth":125000000.0 + } + }, + { + "edge-id":167772162, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.0.2", + "remote-address":"10.0.0.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772417, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.1.1", + "remote-address":"10.0.1.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772418, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.1.2", + "remote-address":"10.0.1.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772929, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "local-vertex-id":167837443, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "admin-group":32, + "local-address":"10.0.3.1", + "remote-address":"10.0.3.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772930, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837443, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.3.2", + "remote-address":"10.0.3.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167773185, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "local-vertex-id":167837444, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.4.1", + "remote-address":"10.0.4.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167773186, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837444, + "metric":10, + "edge-attributes":{ + "te-metric":30, + "local-address":"10.0.4.2", + "remote-address":"10.0.4.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":25000, + "utilized-bandwidth":125000000.0 + } + }, + { + "edge-id":167773441, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "local-vertex-id":167837443, + "remote-vertex-id":167837445, + "metric":0, + "edge-attributes":{ + "te-metric":10, + "local-address":"10.0.5.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "remote-asn":65535, + "remote-as-address":"10.0.255.5", + "delay":50000 + } + } + ], + "subnets":[ + { + "subnet-id":"10.0.0.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.0.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.1.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.1.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.3.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":10 + }, + { + "subnet-id":"10.0.3.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.4.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":10 + }, + { + "subnet-id":"10.0.4.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.5.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":10 + }, + { + "subnet-id":"10.0.255.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":0 + }, + { + "subnet-id":"10.0.255.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":0 + }, + { + "subnet-id":"10.0.255.3\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":0 + }, + { + "subnet-id":"10.0.255.4\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":0, + "segment-routing":{ + "pref-sid":400, + "algo":0, + "flags":"0x40" + } + }, + { + "subnet-id":"10.0.255.5\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.5", + "vertex-id":167837445, + "metric":10 + } + ] + } +} diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step2.json b/tests/topotests/ospf-te-topo1/reference/ted_step2.json new file mode 100644 index 0000000000..623d1dc7e0 --- /dev/null +++ b/tests/topotests/ospf-te-topo1/reference/ted_step2.json @@ -0,0 +1,477 @@ +{ + "ted":{ + "name":"OSPF", + "key":1, + "verticesCount":5, + "edgesCount":7, + "subnetsCount":12, + "vertices":[ + { + "vertex-id":167837441, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.1", + "vertex-type":"Standard" + }, + { + "vertex-id":167837442, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.2", + "vertex-type":"Standard" + }, + { + "vertex-id":167837443, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.3", + "vertex-type":"ASBR" + }, + { + "vertex-id":167837444, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.4", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":10000, + "srgb-lower":10000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":5000, + "msd":12 + } + }, + { + "vertex-id":167837445, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.5", + "vertex-type":"Remote ASBR", + "asn":65535 + } + ], + "edges":[ + { + "edge-id":167772161, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":20, + "local-address":"10.0.0.1", + "remote-address":"10.0.0.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":10000, + "available-bandwidth":125000000.0 + } + }, + { + "edge-id":167772162, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.0.2", + "remote-address":"10.0.0.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772929, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "local-vertex-id":167837443, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "admin-group":32, + "local-address":"10.0.3.1", + "remote-address":"10.0.3.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772930, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837443, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.3.2", + "remote-address":"10.0.3.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167773185, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "local-vertex-id":167837444, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.4.1", + "remote-address":"10.0.4.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167773186, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837444, + "metric":10, + "edge-attributes":{ + "te-metric":30, + "local-address":"10.0.4.2", + "remote-address":"10.0.4.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":25000, + "utilized-bandwidth":125000000.0 + } + }, + { + "edge-id":167773441, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "local-vertex-id":167837443, + "remote-vertex-id":167837445, + "metric":0, + "edge-attributes":{ + "te-metric":10, + "local-address":"10.0.5.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "remote-asn":65535, + "remote-as-address":"10.0.255.5", + "delay":50000 + } + } + ], + "subnets":[ + { + "subnet-id":"10.0.0.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.0.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.3.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":10 + }, + { + "subnet-id":"10.0.3.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.4.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":10 + }, + { + "subnet-id":"10.0.4.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.5.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":10 + }, + { + "subnet-id":"10.0.255.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":0 + }, + { + "subnet-id":"10.0.255.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":0 + }, + { + "subnet-id":"10.0.255.3\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":0 + }, + { + "subnet-id":"10.0.255.4\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":0, + "segment-routing":{ + "pref-sid":400, + "algo":0, + "flags":"0x40" + } + }, + { + "subnet-id":"10.0.255.5\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.5", + "vertex-id":167837445, + "metric":10 + } + ] + } +} diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step3.json b/tests/topotests/ospf-te-topo1/reference/ted_step3.json new file mode 100644 index 0000000000..117011a43a --- /dev/null +++ b/tests/topotests/ospf-te-topo1/reference/ted_step3.json @@ -0,0 +1,409 @@ +{ + "ted":{ + "name":"OSPF", + "key":1, + "verticesCount":4, + "edgesCount":6, + "subnetsCount":10, + "vertices":[ + { + "vertex-id":167837441, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.1", + "vertex-type":"Standard" + }, + { + "vertex-id":167837442, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.2", + "vertex-type":"Standard" + }, + { + "vertex-id":167837443, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.3", + "vertex-type":"ASBR" + }, + { + "vertex-id":167837444, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.4", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":10000, + "srgb-lower":10000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":5000, + "msd":12 + } + } + ], + "edges":[ + { + "edge-id":167772161, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":20, + "local-address":"10.0.0.1", + "remote-address":"10.0.0.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":10000, + "available-bandwidth":125000000.0 + } + }, + { + "edge-id":167772162, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.0.2", + "remote-address":"10.0.0.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772929, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "local-vertex-id":167837443, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "admin-group":32, + "local-address":"10.0.3.1", + "remote-address":"10.0.3.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772930, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837443, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.3.2", + "remote-address":"10.0.3.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167773185, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "local-vertex-id":167837444, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.4.1", + "remote-address":"10.0.4.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167773186, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837444, + "metric":10, + "edge-attributes":{ + "te-metric":30, + "local-address":"10.0.4.2", + "remote-address":"10.0.4.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":25000, + "utilized-bandwidth":125000000.0 + } + } + ], + "subnets":[ + { + "subnet-id":"10.0.0.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.0.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.3.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":10 + }, + { + "subnet-id":"10.0.3.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.4.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":10 + }, + { + "subnet-id":"10.0.4.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.255.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":0 + }, + { + "subnet-id":"10.0.255.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":0 + }, + { + "subnet-id":"10.0.255.3\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":0 + }, + { + "subnet-id":"10.0.255.4\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":0, + "segment-routing":{ + "pref-sid":400, + "algo":0, + "flags":"0x40" + } + } + ] + } +} diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step4.json b/tests/topotests/ospf-te-topo1/reference/ted_step4.json new file mode 100644 index 0000000000..5c2dee1e4b --- /dev/null +++ b/tests/topotests/ospf-te-topo1/reference/ted_step4.json @@ -0,0 +1,490 @@ +{ + "ted":{ + "name":"OSPF", + "key":1, + "verticesCount":4, + "edgesCount":6, + "subnetsCount":10, + "vertices":[ + { + "vertex-id":167837441, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.1", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":4000, + "srgb-lower":20000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":15000 + } + }, + { + "vertex-id":167837442, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.2", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":8000, + "srgb-lower":16000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":2000, + "srlb-lower":5000, + "msd":16 + } + }, + { + "vertex-id":167837443, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.3", + "vertex-type":"ASBR" + }, + { + "vertex-id":167837444, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.4", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":10000, + "srgb-lower":10000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":5000, + "msd":12 + } + } + ], + "edges":[ + { + "edge-id":167772161, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":20, + "local-address":"10.0.0.1", + "remote-address":"10.0.0.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":10000, + "available-bandwidth":125000000.0 + }, + "segment-routing":[ + { + "adj-sid":15001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":15000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772162, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.0.2", + "remote-address":"10.0.0.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772929, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "local-vertex-id":167837443, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "admin-group":32, + "local-address":"10.0.3.1", + "remote-address":"10.0.3.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772930, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837443, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.3.2", + "remote-address":"10.0.3.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5003, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5002, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167773185, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "local-vertex-id":167837444, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.4.1", + "remote-address":"10.0.4.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167773186, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837444, + "metric":10, + "edge-attributes":{ + "te-metric":30, + "local-address":"10.0.4.2", + "remote-address":"10.0.4.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":25000, + "utilized-bandwidth":125000000.0 + }, + "segment-routing":[ + { + "adj-sid":5005, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5004, + "flags":"0xe0", + "weight":0 + } + ] + } + ], + "subnets":[ + { + "subnet-id":"10.0.0.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.0.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.3.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":10 + }, + { + "subnet-id":"10.0.3.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.4.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":10 + }, + { + "subnet-id":"10.0.4.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.255.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":0, + "segment-routing":{ + "pref-sid":10, + "algo":0, + "flags":"0x0" + } + }, + { + "subnet-id":"10.0.255.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":0, + "segment-routing":{ + "pref-sid":20, + "algo":0, + "flags":"0x50" + } + }, + { + "subnet-id":"10.0.255.3\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":0 + }, + { + "subnet-id":"10.0.255.4\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":0, + "segment-routing":{ + "pref-sid":400, + "algo":0, + "flags":"0x40" + } + } + ] + } +} diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step5.json b/tests/topotests/ospf-te-topo1/reference/ted_step5.json new file mode 100644 index 0000000000..47e747f3ca --- /dev/null +++ b/tests/topotests/ospf-te-topo1/reference/ted_step5.json @@ -0,0 +1,614 @@ +{ + "ted":{ + "name":"OSPF", + "key":1, + "verticesCount":4, + "edgesCount":8, + "subnetsCount":12, + "vertices":[ + { + "vertex-id":167837441, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.1", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":4000, + "srgb-lower":20000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":15000 + } + }, + { + "vertex-id":167837442, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.2", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":8000, + "srgb-lower":16000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":2000, + "srlb-lower":5000, + "msd":16 + } + }, + { + "vertex-id":167837443, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.3", + "vertex-type":"ASBR" + }, + { + "vertex-id":167837444, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.4", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":10000, + "srgb-lower":10000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":5000, + "msd":12 + } + } + ], + "edges":[ + { + "edge-id":167772161, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":20, + "local-address":"10.0.0.1", + "remote-address":"10.0.0.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":10000, + "available-bandwidth":125000000.0 + }, + "segment-routing":[ + { + "adj-sid":15001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":15000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772162, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.0.2", + "remote-address":"10.0.0.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772417, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.1.1", + "remote-address":"10.0.1.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":15003, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":15002, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772418, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.1.2", + "remote-address":"10.0.1.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5007, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5006, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772929, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "local-vertex-id":167837443, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "admin-group":32, + "local-address":"10.0.3.1", + "remote-address":"10.0.3.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772930, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837443, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.3.2", + "remote-address":"10.0.3.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5003, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5002, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167773185, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "local-vertex-id":167837444, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.4.1", + "remote-address":"10.0.4.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167773186, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837444, + "metric":10, + "edge-attributes":{ + "te-metric":30, + "local-address":"10.0.4.2", + "remote-address":"10.0.4.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":25000, + "utilized-bandwidth":125000000.0 + }, + "segment-routing":[ + { + "adj-sid":5005, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5004, + "flags":"0xe0", + "weight":0 + } + ] + } + ], + "subnets":[ + { + "subnet-id":"10.0.0.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.0.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.1.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.1.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.3.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":10 + }, + { + "subnet-id":"10.0.3.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.4.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":10 + }, + { + "subnet-id":"10.0.4.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.255.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":0, + "segment-routing":{ + "pref-sid":10, + "algo":0, + "flags":"0x0" + } + }, + { + "subnet-id":"10.0.255.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":0, + "segment-routing":{ + "pref-sid":20, + "algo":0, + "flags":"0x50" + } + }, + { + "subnet-id":"10.0.255.3\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":0 + }, + { + "subnet-id":"10.0.255.4\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":0, + "segment-routing":{ + "pref-sid":400, + "algo":0, + "flags":"0x40" + } + } + ] + } +} diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step6.json b/tests/topotests/ospf-te-topo1/reference/ted_step6.json new file mode 100644 index 0000000000..74bd83fbdb --- /dev/null +++ b/tests/topotests/ospf-te-topo1/reference/ted_step6.json @@ -0,0 +1,615 @@ +{ + "ted":{ + "name":"OSPF", + "key":1, + "verticesCount":4, + "edgesCount":8, + "subnetsCount":12, + "vertices":[ + { + "vertex-id":167837441, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.1", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":4000, + "srgb-lower":20000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":15000 + } + }, + { + "vertex-id":167837442, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.2", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":8000, + "srgb-lower":16000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":2000, + "srlb-lower":5000, + "msd":16 + } + }, + { + "vertex-id":167837443, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.3", + "vertex-type":"ASBR" + }, + { + "vertex-id":167837444, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.4", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":10000, + "srgb-lower":10000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":5000, + "msd":12 + } + } + ], + "edges":[ + { + "edge-id":167772161, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":20, + "local-address":"10.0.0.1", + "remote-address":"10.0.0.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":10000, + "available-bandwidth":125000000.0 + }, + "segment-routing":[ + { + "adj-sid":15001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":15000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772162, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.0.2", + "remote-address":"10.0.0.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772417, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.1.1", + "remote-address":"10.0.1.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":15003, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":15002, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772418, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.1.2", + "remote-address":"10.0.1.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5007, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5006, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772929, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "local-vertex-id":167837443, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "admin-group":32, + "local-address":"10.0.3.1", + "remote-address":"10.0.3.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772930, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837443, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.3.2", + "remote-address":"10.0.3.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5003, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5002, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167773185, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "local-vertex-id":167837444, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.4.1", + "remote-address":"10.0.4.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":20000, + "jitter":10000 + }, + "segment-routing":[ + { + "adj-sid":5001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167773186, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837444, + "metric":10, + "edge-attributes":{ + "te-metric":30, + "local-address":"10.0.4.2", + "remote-address":"10.0.4.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":25000 + }, + "segment-routing":[ + { + "adj-sid":5005, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5004, + "flags":"0xe0", + "weight":0 + } + ] + } + ], + "subnets":[ + { + "subnet-id":"10.0.0.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.0.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.1.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.1.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.3.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":10 + }, + { + "subnet-id":"10.0.3.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.4.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":10 + }, + { + "subnet-id":"10.0.4.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.255.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":0, + "segment-routing":{ + "pref-sid":10, + "algo":0, + "flags":"0x0" + } + }, + { + "subnet-id":"10.0.255.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":0, + "segment-routing":{ + "pref-sid":20, + "algo":0, + "flags":"0x50" + } + }, + { + "subnet-id":"10.0.255.3\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":0 + }, + { + "subnet-id":"10.0.255.4\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.4", + "vertex-id":167837444, + "metric":0, + "segment-routing":{ + "pref-sid":400, + "algo":0, + "flags":"0x40" + } + } + ] + } +} diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step7.json b/tests/topotests/ospf-te-topo1/reference/ted_step7.json new file mode 100644 index 0000000000..1cea9f0455 --- /dev/null +++ b/tests/topotests/ospf-te-topo1/reference/ted_step7.json @@ -0,0 +1,456 @@ +{ + "ted":{ + "name":"OSPF", + "key":1, + "verticesCount":3, + "edgesCount":6, + "subnetsCount":9, + "vertices":[ + { + "vertex-id":167837441, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.1", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":4000, + "srgb-lower":20000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":1000, + "srlb-lower":15000 + } + }, + { + "vertex-id":167837442, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.2", + "vertex-type":"Standard", + "segment-routing":{ + "srgb-size":8000, + "srgb-lower":16000, + "algorithms":[ + { + "0":"SPF" + } + ], + "srlb-size":2000, + "srlb-lower":5000, + "msd":16 + } + }, + { + "vertex-id":167837443, + "status":"Sync", + "origin":"OSPFv2", + "router-id":"10.0.255.3", + "vertex-type":"ASBR" + } + ], + "edges":[ + { + "edge-id":167772161, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":20, + "local-address":"10.0.0.1", + "remote-address":"10.0.0.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ], + "delay":10000, + "available-bandwidth":125000000.0 + }, + "segment-routing":[ + { + "adj-sid":15001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":15000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772162, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.0.2", + "remote-address":"10.0.0.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5001, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5000, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772417, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "local-vertex-id":167837441, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.1.1", + "remote-address":"10.0.1.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":15003, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":15002, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772418, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837441, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.1.2", + "remote-address":"10.0.1.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5007, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5006, + "flags":"0xe0", + "weight":0 + } + ] + }, + { + "edge-id":167772929, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "local-vertex-id":167837443, + "remote-vertex-id":167837442, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "admin-group":32, + "local-address":"10.0.3.1", + "remote-address":"10.0.3.2", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + } + }, + { + "edge-id":167772930, + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "local-vertex-id":167837442, + "remote-vertex-id":167837443, + "metric":10, + "edge-attributes":{ + "te-metric":0, + "local-address":"10.0.3.2", + "remote-address":"10.0.3.1", + "max-link-bandwidth":176258176.0, + "max-resv-link-bandwidth":176258176.0, + "unreserved-bandwidth":[ + { + "class-type-0":176258176.0 + }, + { + "class-type-1":176258176.0 + }, + { + "class-type-2":176258176.0 + }, + { + "class-type-3":176258176.0 + }, + { + "class-type-4":176258176.0 + }, + { + "class-type-5":176258176.0 + }, + { + "class-type-6":176258176.0 + }, + { + "class-type-7":176258176.0 + } + ] + }, + "segment-routing":[ + { + "adj-sid":5003, + "flags":"0x60", + "weight":0 + }, + { + "adj-sid":5002, + "flags":"0xe0", + "weight":0 + } + ] + } + ], + "subnets":[ + { + "subnet-id":"10.0.0.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.0.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.1.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":10 + }, + { + "subnet-id":"10.0.1.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.3.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":10 + }, + { + "subnet-id":"10.0.3.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":10 + }, + { + "subnet-id":"10.0.255.1\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.1", + "vertex-id":167837441, + "metric":0, + "segment-routing":{ + "pref-sid":10, + "algo":0, + "flags":"0x0" + } + }, + { + "subnet-id":"10.0.255.2\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.2", + "vertex-id":167837442, + "metric":0, + "segment-routing":{ + "pref-sid":20, + "algo":0, + "flags":"0x50" + } + }, + { + "subnet-id":"10.0.255.3\/32", + "status":"Sync", + "origin":"OSPFv2", + "advertised-router":"10.0.255.3", + "vertex-id":167837443, + "metric":0 + } + ] + } +} diff --git a/tests/topotests/ospf-te-topo1/test_ospf_te_topo1.py b/tests/topotests/ospf-te-topo1/test_ospf_te_topo1.py new file mode 100644 index 0000000000..32f9b3453e --- /dev/null +++ b/tests/topotests/ospf-te-topo1/test_ospf_te_topo1.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python + +# +# test_ospf_te_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by Orange +# Author: Olivier Dugeon <olivier.dugeon@orange.com> +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_ospf_te_topo1.py: Test the FRR OSPF with Traffic Engineering. + + +------------+ + | | + | R1 | + | 10.0.225.1 | + | | + +------------+ + r1-eth0| |r1-eth1 + | | + 10.0.0.0/24| |10.0.1.0/24 + | | + r2-eth0| |r2-eth1 + +------------+ +------------+ + | | | | + | R2 |r2-eth2 r3-eth0| R3 | + | 10.0.255.2 +------------------+ 10.0.255.3 | + | | 10.0.3.0/24 | | + +------------+ +------+-----+ + r2-eth3| r3-eth1| + | | + 10.0.4.0/24| 10.0.5.0/24| + | | + r4-eth0| V + +------------+ ASBR 10.0.255.5 + | | + | R4 | + | 10.0.255.4 | + | | + +------------+ + +""" + +import os +import sys +import json +from functools import partial + +# 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 +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# and Finally pytest +import pytest + +pytestmark = [pytest.mark.ospfd] + + +class OspfTeTopo(Topo): + "Test topology builder" + + def build(self): + "Build function" + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + # Interconect router 1 and 2 with 2 links + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 3 and 2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 4 and 2 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r2"]) + + # Interconnect router 3 with next AS + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + logger.info("\n\n---- Starting OSPF TE tests ----\n") + + tgen = Topogen(OspfTeTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(): + "Teardown the pytest environment" + + tgen = get_topogen() + tgen.stop_topology() + + logger.info("\n\n---- OSPF TE tests End ----\n") + + +def compare_ted_json_output(tgen, rname, fileref): + "Compare TED JSON output" + + logger.info('Comparing router "%s" TED output', rname) + + filename = "{}/reference/{}".format(CWD, fileref) + expected = json.loads(open(filename).read()) + command = "show ip ospf mpls-te database json" + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) + _, diff = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = '"{}" TED JSON output mismatches the expected result'.format(rname) + assert diff is None, assertmsg + + +def setup_testcase(msg): + "Setup test case" + + logger.info(msg) + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + return tgen + + +# Note that all routers must discover the same Network Topology, so the same TED. + + +def test_step1(): + "Step1: Check initial topology" + + tgen = setup_testcase("Step1: test initial OSPF TE Data Base") + + for rname in ["r1", "r2", "r3", "r4"]: + compare_ted_json_output(tgen, rname, "ted_step1.json") + + +def test_step2(): + "Step2: Shutdown interface between r1 and r2 and verify that \ + corresponding Edges are removed from the TED on all routers " + + tgen = setup_testcase("Step2: Shutdown interface between r1 & r2") + + tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth1" -c "shutdown"') + tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth1" -c "shutdown"') + + for rname in ["r1", "r2", "r3", "r4"]: + compare_ted_json_output(tgen, rname, "ted_step2.json") + + +def test_step3(): + "Step3: Disable Inter-AS on r3 and verify that corresponding Edge and \ + remote ASBR are removed from the TED on all routers" + + tgen = setup_testcase("Step3: Disable Inter-AS on r3") + + tgen.net["r3"].cmd('vtysh -c "conf t" -c "router ospf" -c "no mpls-te inter-as"') + for rname in ["r1", "r2", "r3", "r4"]: + compare_ted_json_output(tgen, rname, "ted_step3.json") + + +def test_step4(): + "Step4: Enable Segment Routing on r1 and r2 and verify that corresponding \ + Edges are updated with Adjacency SID and Subnets with Prefix SID in the \ + TED on all routers" + + tgen = setup_testcase("Step4: Enable Segment Routing on r1 & r2") + + tgen.net["r1"].cmd('vtysh -c "conf t" -c "router ospf" -c "segment-routing on"') + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "segment-routing global-block 20000 23999"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 10.0.255.1/32 index 10"' + ) + tgen.net["r2"].cmd('vtysh -c "conf t" -c "router ospf" -c "segment-routing on"') + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "segment-routing node-msd 16"' + ) + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "segment-routing local-block 5000 6999"' + ) + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 10.0.255.2/32 index 20 explicit-null"' + ) + + for rname in ["r1", "r2", "r3", "r4"]: + compare_ted_json_output(tgen, rname, "ted_step4.json") + + +def test_step5(): + "Step5: Re-enable interface between r1 & r2 and verify that corresponding \ + Edges are added in the TED on all routers" + + tgen = setup_testcase("Step5: Re-enable interface between r1 & r2") + + tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth1" -c "no shutdown"') + tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth1" -c "no shutdown"') + + for rname in ["r1", "r2", "r3", "r4"]: + compare_ted_json_output(tgen, rname, "ted_step5.json") + + +def test_step6(): + "Step6: Set delay and jitter for interface r4-eth0 on r4, remove use-bw \ + for interface r2-eth3 on r2 and verify that corresponding Edges are \ + updated in the TED on all routers" + + tgen = setup_testcase("Step6: Modify link parameters on r2 & r4") + + tgen.net["r2"].cmd( + 'vtysh -c "conf t" -c "interface r2-eth3" -c "link-params" -c "no use-bw"' + ) + tgen.net["r4"].cmd( + 'vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay 20000"' + ) + tgen.net["r4"].cmd( + 'vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay-variation 10000"' + ) + + for rname in ["r1", "r2", "r3", "r4"]: + compare_ted_json_output(tgen, rname, "ted_step6.json") + + +def test_step7(): + "Step7: Disable OSPF on r4 and verify that corresponding Vertex, Edges and \ + Subnets are removed from the TED on all remaining routers" + + tgen = setup_testcase("Step7: Disable OSPF on r4") + + tgen.net["r4"].cmd('vtysh -c "conf t" -c "no router ospf"') + + for rname in ["r1", "r2", "r3"]: + compare_ted_json_output(tgen, rname, "ted_step7.json") + + +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/tests/topotests/ospf-topo1/r1/ospf6d.conf-pre-v4 b/tests/topotests/ospf-topo1/r1/ospf6d.conf-pre-v4 deleted file mode 100644 index 6a40f859db..0000000000 --- a/tests/topotests/ospf-topo1/r1/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,9 +0,0 @@ -! -router ospf6 - router-id 10.0.255.1 - redistribute kernel - redistribute connected - redistribute static - interface r1-eth0 area 0.0.0.0 - interface r1-eth1 area 0.0.0.0 -! diff --git a/tests/topotests/ospf-topo1/r2/ospf6d.conf-pre-v4 b/tests/topotests/ospf-topo1/r2/ospf6d.conf-pre-v4 deleted file mode 100644 index 7448b25327..0000000000 --- a/tests/topotests/ospf-topo1/r2/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,9 +0,0 @@ -! -router ospf6 - router-id 10.0.255.2 - redistribute kernel - redistribute connected - redistribute static - interface r2-eth0 area 0.0.0.0 - interface r2-eth1 area 0.0.0.0 -! diff --git a/tests/topotests/ospf-topo1/r3/ospf6d.conf-pre-v4 b/tests/topotests/ospf-topo1/r3/ospf6d.conf-pre-v4 deleted file mode 100644 index e853e0e2b2..0000000000 --- a/tests/topotests/ospf-topo1/r3/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,10 +0,0 @@ -! -router ospf6 - router-id 10.0.255.3 - redistribute kernel - redistribute connected - redistribute static - interface r3-eth0 area 0.0.0.0 - interface r3-eth1 area 0.0.0.0 - interface r3-eth2 area 0.0.0.1 -! diff --git a/tests/topotests/ospf-topo1/r4/ospf6d.conf-pre-v4 b/tests/topotests/ospf-topo1/r4/ospf6d.conf-pre-v4 deleted file mode 100644 index dcc07a4fdc..0000000000 --- a/tests/topotests/ospf-topo1/r4/ospf6d.conf-pre-v4 +++ /dev/null @@ -1,9 +0,0 @@ -! -router ospf6 - router-id 10.0.255.4 - redistribute kernel - redistribute connected - redistribute static - interface r4-eth0 area 0.0.0.1 - interface r4-eth1 area 0.0.0.1 -! diff --git a/tests/topotests/ospf-topo1/test_ospf_topo1.py b/tests/topotests/ospf-topo1/test_ospf_topo1.py index 5bb6c2c818..42634ce906 100644 --- a/tests/topotests/ospf-topo1/test_ospf_topo1.py +++ b/tests/topotests/ospf-topo1/test_ospf_topo1.py @@ -93,8 +93,6 @@ def setup_module(mod): tgen.start_topology() ospf6_config = "ospf6d.conf" - if tgen.gears["r1"].has_version("<", "4.0"): - ospf6_config = "ospf6d.conf-pre-v4" router_list = tgen.routers() for rname, router in router_list.items(): @@ -118,6 +116,88 @@ def teardown_module(mod): tgen.stop_topology() +def test_wait_protocol_convergence(): + "Wait for OSPFv2/OSPFv3 to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_ospfv2_neighbor_full(router, neighbor): + "Wait until OSPFv2 convergence." + logger.info("waiting OSPFv2 router '{}'".format(router)) + + def run_command_and_expect(): + """ + Function that runs command and expect the following outcomes: + * Full/DR + * Full/DROther + * Full/Backup + """ + result = tgen.gears[router].vtysh_cmd( + "show ip ospf neighbor json", isjson=True + ) + if ( + topotest.json_cmp( + result, {"neighbors": {neighbor: [{"state": "Full/DR"}]}} + ) + is None + ): + return None + + if ( + topotest.json_cmp( + result, {"neighbors": {neighbor: [{"state": "Full/DROther"}]}} + ) + is None + ): + return None + + return topotest.json_cmp( + result, {"neighbors": {neighbor: [{"state": "Full/Backup"}]}} + ) + + _, result = topotest.run_and_expect( + run_command_and_expect, None, count=130, wait=1 + ) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + def expect_ospfv3_neighbor_full(router, neighbor): + "Wait until OSPFv3 convergence." + logger.info("waiting OSPFv3 router '{}'".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ipv6 ospf6 neighbor json", + {"neighbors": [{"neighborId": neighbor, "state": "Full"}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + # Wait for OSPFv2 convergence + expect_ospfv2_neighbor_full("r1", "10.0.255.2") + expect_ospfv2_neighbor_full("r1", "10.0.255.3") + expect_ospfv2_neighbor_full("r2", "10.0.255.1") + expect_ospfv2_neighbor_full("r2", "10.0.255.3") + expect_ospfv2_neighbor_full("r3", "10.0.255.1") + expect_ospfv2_neighbor_full("r3", "10.0.255.2") + expect_ospfv2_neighbor_full("r3", "10.0.255.4") + expect_ospfv2_neighbor_full("r4", "10.0.255.3") + + # Wait for OSPFv3 convergence + expect_ospfv3_neighbor_full("r1", "10.0.255.2") + expect_ospfv3_neighbor_full("r1", "10.0.255.3") + expect_ospfv3_neighbor_full("r2", "10.0.255.1") + expect_ospfv3_neighbor_full("r2", "10.0.255.3") + expect_ospfv3_neighbor_full("r3", "10.0.255.1") + expect_ospfv3_neighbor_full("r3", "10.0.255.2") + expect_ospfv3_neighbor_full("r3", "10.0.255.4") + expect_ospfv3_neighbor_full("r4", "10.0.255.3") + + def compare_show_ipv6_ospf6(rname, expected): """ Calls 'show ipv6 ospf6 route' for router `rname` and compare the obtained diff --git a/tests/topotests/ospf6-topo1/r1/ospf6d.conf b/tests/topotests/ospf6-topo1/r1/ospf6d.conf index ab2c0c647e..9f7e058931 100644 --- a/tests/topotests/ospf6-topo1/r1/ospf6d.conf +++ b/tests/topotests/ospf6-topo1/r1/ospf6d.conf @@ -11,9 +11,13 @@ debug ospf6 flooding ! interface r1-stubnet ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! interface r1-sw5 ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! router ospf6 ospf6 router-id 10.0.0.1 diff --git a/tests/topotests/ospf6-topo1/r2/ospf6d.conf b/tests/topotests/ospf6-topo1/r2/ospf6d.conf index 075e815ed8..26ebc2c0ea 100644 --- a/tests/topotests/ospf6-topo1/r2/ospf6d.conf +++ b/tests/topotests/ospf6-topo1/r2/ospf6d.conf @@ -11,9 +11,13 @@ debug ospf6 flooding ! interface r2-stubnet ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! interface r2-sw5 ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! router ospf6 ospf6 router-id 10.0.0.2 diff --git a/tests/topotests/ospf6-topo1/r3/ospf6d.conf b/tests/topotests/ospf6-topo1/r3/ospf6d.conf index e9a07a7e28..e902496530 100644 --- a/tests/topotests/ospf6-topo1/r3/ospf6d.conf +++ b/tests/topotests/ospf6-topo1/r3/ospf6d.conf @@ -11,12 +11,18 @@ debug ospf6 flooding ! interface r3-stubnet ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! interface r3-sw5 ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! interface r3-sw6 ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! router ospf6 ospf6 router-id 10.0.0.3 diff --git a/tests/topotests/ospf6-topo1/r4/ospf6d.conf b/tests/topotests/ospf6-topo1/r4/ospf6d.conf index fa66645f5a..5607a789de 100644 --- a/tests/topotests/ospf6-topo1/r4/ospf6d.conf +++ b/tests/topotests/ospf6-topo1/r4/ospf6d.conf @@ -11,9 +11,13 @@ debug ospf6 flooding ! interface r4-stubnet ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! interface r4-sw6 ipv6 ospf6 network broadcast + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! router ospf6 ospf6 router-id 10.0.0.4 diff --git a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py index 6ae886b76e..f8c3476e18 100644 --- a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py @@ -185,70 +185,38 @@ def teardown_module(mod): tgen.stop_topology() -def test_ospf6_converged(): - +def test_wait_protocol_convergence(): + "Wait for OSPFv3 to converge" tgen = get_topogen() - - # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) - # For debugging, uncomment the next line - # tgen.mininet_cli() - - # Wait for OSPF6 to converge (All Neighbors in either Full or TwoWay State) - logger.info("Waiting for OSPF6 convergence") - - # Set up for regex - pat1 = re.compile("^[0-9]") - pat2 = re.compile("Full") - - timeout = 60 - while timeout > 0: - logger.info("Timeout in %s: " % timeout), - sys.stdout.flush() - - # Look for any node not yet converged - for router, rnode in tgen.routers().items(): - resStr = rnode.vtysh_cmd("show ipv6 ospf neigh") - - isConverged = False + logger.info("waiting for protocols to converge") - for line in resStr.splitlines(): - res1 = pat1.match(line) - if res1: - isConverged = True - res2 = pat2.search(line) - - if res2 == None: - isConverged = False - break - - if isConverged == False: - logger.info("Waiting for {}".format(router)) - sys.stdout.flush() - break - - if isConverged: - logger.info("Done") - break - else: - sleep(5) - timeout -= 5 + def expect_neighbor_full(router, neighbor): + "Wait until OSPFv3 convergence." + logger.info("waiting OSPFv3 router '{}'".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ipv6 ospf6 neighbor json", + {"neighbors": [{"neighborId": neighbor, "state": "Full"}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg - if timeout == 0: - # Bail out with error if a router fails to converge - ospfStatus = rnode.vtysh_cmd("show ipv6 ospf neigh") - assert False, "OSPFv6 did not converge:\n{}".format(ospfStatus) + expect_neighbor_full("r1", "10.0.0.2") + expect_neighbor_full("r1", "10.0.0.3") - logger.info("OSPFv3 converged.") + expect_neighbor_full("r2", "10.0.0.1") + expect_neighbor_full("r2", "10.0.0.3") - # For debugging, uncomment the next line - # tgen.mininet_cli() + expect_neighbor_full("r3", "10.0.0.1") + expect_neighbor_full("r3", "10.0.0.2") + expect_neighbor_full("r3", "10.0.0.4") - # Make sure that all daemons are still running - if tgen.routers_have_failure(): - assert tgen.errors == "", tgen.errors + expect_neighbor_full("r4", "10.0.0.3") def compare_show_ipv6(rname, expected): diff --git a/tests/topotests/ospf6-topo2/r1/ospf6d.conf b/tests/topotests/ospf6-topo2/r1/ospf6d.conf new file mode 100644 index 0000000000..c403fcd8dc --- /dev/null +++ b/tests/topotests/ospf6-topo2/r1/ospf6d.conf @@ -0,0 +1,9 @@ +interface r1-eth0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +router ospf6 + ospf6 router-id 10.254.254.1 + area 0.0.0.1 stub + interface r1-eth0 area 0.0.0.1 +! diff --git a/tests/topotests/ospf6-topo2/r1/zebra.conf b/tests/topotests/ospf6-topo2/r1/zebra.conf new file mode 100644 index 0000000000..7fee2da8ba --- /dev/null +++ b/tests/topotests/ospf6-topo2/r1/zebra.conf @@ -0,0 +1,5 @@ +ipv6 forwarding +! +interface r1-eth0 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/ospf6-topo2/r2/ospf6d.conf b/tests/topotests/ospf6-topo2/r2/ospf6d.conf new file mode 100644 index 0000000000..d4bb0e2a41 --- /dev/null +++ b/tests/topotests/ospf6-topo2/r2/ospf6d.conf @@ -0,0 +1,17 @@ +interface r2-eth0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +interface r2-eth1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +router ospf6 + ospf6 router-id 10.254.254.2 + redistribute connected + redistribute static + default-information originate always metric 123 + area 0.0.0.1 stub + interface r2-eth0 area 0.0.0.1 + interface r2-eth1 area 0.0.0.0 +! diff --git a/tests/topotests/ospf6-topo2/r2/zebra.conf b/tests/topotests/ospf6-topo2/r2/zebra.conf new file mode 100644 index 0000000000..891945a4e7 --- /dev/null +++ b/tests/topotests/ospf6-topo2/r2/zebra.conf @@ -0,0 +1,8 @@ +ipv6 forwarding +! +interface r2-eth0 + ipv6 address 2001:db8:1::1/64 +! +interface r2-eth1 + ipv6 address 2001:db8:2::2/64 +! diff --git a/tests/topotests/ospf6-topo2/r3/ospf6d.conf b/tests/topotests/ospf6-topo2/r3/ospf6d.conf new file mode 100644 index 0000000000..aaef00d5bb --- /dev/null +++ b/tests/topotests/ospf6-topo2/r3/ospf6d.conf @@ -0,0 +1,10 @@ +interface r3-eth0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +router ospf6 + ospf6 router-id 10.254.254.3 + redistribute connected + redistribute static + interface r3-eth0 area 0.0.0.0 +! diff --git a/tests/topotests/ospf6-topo2/r3/zebra.conf b/tests/topotests/ospf6-topo2/r3/zebra.conf new file mode 100644 index 0000000000..dea2fe4778 --- /dev/null +++ b/tests/topotests/ospf6-topo2/r3/zebra.conf @@ -0,0 +1,8 @@ +ipv6 forwarding +! +interface r3-eth0 + ipv6 address 2001:db8:2::1/64 +! +ipv6 route fc00:1::/64 fc00:100::1234 +ipv6 route fc00:2::/64 fc00:100::1234 +ipv6 route fc00:3::/64 fc00:100::1234 diff --git a/tests/topotests/ospf6-topo2/test_ospf6_topo2.dot b/tests/topotests/ospf6-topo2/test_ospf6_topo2.dot new file mode 100644 index 0000000000..ba7a36f2b5 --- /dev/null +++ b/tests/topotests/ospf6-topo2/test_ospf6_topo2.dot @@ -0,0 +1,71 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="ospf6-topo2"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n2001:db8:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n2001:db8:3::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + subgraph cluster0 { + label="area 0.0.0.1"; + r1 -- sw1 [label="eth0\n.2"]; + } + + subgraph cluster1 { + label="area 0.0.0.0"; + r2 -- sw1 [label="eth0\n.1"]; + r2 -- sw2 [label="eth1\n.2"]; + r3 -- sw2 [label="eth0\n.1"]; + r3 -- sw3 [label="eth1\n.2"]; + } +} diff --git a/tests/topotests/ospf6-topo2/test_ospf6_topo2.png b/tests/topotests/ospf6-topo2/test_ospf6_topo2.png Binary files differnew file mode 100644 index 0000000000..ee1de60736 --- /dev/null +++ b/tests/topotests/ospf6-topo2/test_ospf6_topo2.png diff --git a/tests/topotests/ospf6-topo2/test_ospf6_topo2.py b/tests/topotests/ospf6-topo2/test_ospf6_topo2.py new file mode 100644 index 0000000000..efc8565bb3 --- /dev/null +++ b/tests/topotests/ospf6-topo2/test_ospf6_topo2.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python + +# +# test_ospf6_topo2.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_ospf6_topo2.py: Test the FRR OSPFv3 daemon. +""" + +import os +import sys +from functools import partial +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 +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +pytestmark = [pytest.mark.ospf6d] + + +class OSPFv3Topo2(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 3 routers + for routern in range(1, 4): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(OSPFv3Topo2, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + daemon_file = "{}/{}/ospf6d.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_OSPF6, daemon_file) + + # Initialize all routers. + tgen.start_router() + + +def test_wait_protocol_convergence(): + "Wait for OSPFv3 to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_neighbor_full(router, neighbor): + "Wait until OSPFv3 convergence." + logger.info("waiting OSPFv3 router '{}'".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ipv6 ospf6 neighbor json", + {"neighbors": [{"neighborId": neighbor, "state": "Full"}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + expect_neighbor_full("r1", "10.254.254.2") + expect_neighbor_full("r2", "10.254.254.1") + expect_neighbor_full("r2", "10.254.254.3") + expect_neighbor_full("r3", "10.254.254.2") + + +def test_ospf6_default_route(): + "Wait for OSPFv3 default route in stub area." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for default route") + + def expect_route(router, route, metric): + "Test OSPF6 route existence." + logger.info("waiting OSPFv3 router '{}' routes".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ipv6 route json", + {route: [{"metric": metric}]}, + ) + _, result = topotest.run_and_expect(test_func, None, count=4, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + def expect_lsa(router, area, prefix, metric): + "Test OSPF6 LSA existence." + logger.info("waiting OSPFv3 router '{}' LSA".format(router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show ipv6 ospf6 database inter-prefix detail json", + { + "areaScopedLinkStateDb": [ + { + "areaId": area, + "lsa": [ + { + "prefix": prefix, + "metric": metric, + } + ], + } + ] + }, + ) + _, result = topotest.run_and_expect(test_func, None, count=4, wait=1) + assertmsg = '"{}" convergence failure'.format(router) + assert result is None, assertmsg + + metric = 123 + expect_lsa("r1", "0.0.0.1", "::/0", metric) + expect_route("r1", "::/0", metric + 10) + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +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/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py index cebe55b39c..c117fc6a72 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py @@ -249,17 +249,20 @@ def test_ospf_chaos_tc31_p1(request): dut = "r1" protocol = "ospf" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, - expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) step("Bring up OSPFd daemon on R0.") start_router_daemons(tgen, "r0", ["ospfd"]) @@ -482,17 +485,20 @@ def test_ospf_chaos_tc34_p1(request): dut = "r1" protocol = "ospf" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) - result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, - expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) step("Bring up staticd daemon on R0.") start_router_daemons(tgen, "r0", ["staticd"]) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py index adf82a5e85..1aabc06db0 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py @@ -259,19 +259,21 @@ def test_ospf_ecmp_tc16_p0(request): shutdown_bringup_interface(tgen, dut, intf, False) result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) protocol = "ospf" result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) for intfr in range(1, 7): intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"] @@ -326,10 +328,11 @@ def test_ospf_ecmp_tc16_p0(request): result = verify_ospf_rib( tgen, dut, input_dict, next_hop=nh, attempts=5, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) protocol = "ospf" result = verify_rib( @@ -342,10 +345,11 @@ def test_ospf_ecmp_tc16_p0(request): attempts=5, expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) step("Re configure the static route in R0.") dut = "r0" @@ -432,10 +436,11 @@ def test_ospf_ecmp_tc17_p0(request): result = verify_ospf_rib( tgen, dut, input_dict, next_hop=nh, attempts=5, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) protocol = "ospf" result = verify_rib( @@ -448,10 +453,11 @@ def test_ospf_ecmp_tc17_p0(request): attempts=5, expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) step("Reconfigure the static route in R0.Change ECMP value to 2.") dut = "r0" diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py index c5230d6614..e6dc18a434 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py @@ -307,10 +307,11 @@ def test_ospf_lan_ecmp_tc18_p0(request): result = verify_ospf_rib( tgen, dut, input_dict, next_hop=nh, attempts=5, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) protocol = "ospf" result = verify_rib( @@ -323,10 +324,11 @@ def test_ospf_lan_ecmp_tc18_p0(request): attempts=5, expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) write_test_footer(tc_name) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py index 2fbb27f4fc..d9b90a132a 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py @@ -397,10 +397,11 @@ def test_ospf_lan_tc1_p0(request): shutdown_bringup_interface(tgen, dut, intf, False) result = verify_ospf_neighbor(tgen, topo, dut, lan=True, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r0: OSPF neighbors-hip is up \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r0: OSPF neighbors-hip is up \n Error: {}".format( tc_name, result - )) + ) step("No Shut interface on R0") dut = "r0" diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py index b99ce6cfb8..7864d0307a 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py @@ -332,18 +332,20 @@ def test_ospf_routemaps_functionality_tc19_p0(request): } } result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are present in fib \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are present in fib \n Error: {}".format( tc_name, result - )) + ) step("Delete and reconfigure prefix list.") # Create ip prefix list @@ -383,18 +385,20 @@ def test_ospf_routemaps_functionality_tc19_p0(request): } } result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) pfx_list = { "r0": { @@ -438,18 +442,20 @@ def test_ospf_routemaps_functionality_tc19_p0(request): } } result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) write_test_footer(tc_name) @@ -496,18 +502,20 @@ def test_ospf_routemaps_functionality_tc20_p0(request): dut = "r1" protocol = "ospf" result = verify_ospf_rib(tgen, dut, input_dict, attempts=2, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, attempts=2, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) step( "configure the route map with the same name that is used " @@ -523,18 +531,20 @@ def test_ospf_routemaps_functionality_tc20_p0(request): dut = "r1" protocol = "ospf" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) # Create route map routemaps = {"r0": {"route_maps": {"rmap_ipv4": [{"action": "deny"}]}}} @@ -545,18 +555,20 @@ def test_ospf_routemaps_functionality_tc20_p0(request): dut = "r1" protocol = "ospf" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) step("Delete the route map.") # Create route map @@ -573,18 +585,20 @@ def test_ospf_routemaps_functionality_tc20_p0(request): dut = "r1" protocol = "ospf" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert result is not True, ("Testcase {} : Failed \n " - "r1: OSPF routes are present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( tc_name, result - )) + ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert result is not True, ("Testcase {} : Failed \n " - "r1: routes are still present \n Error: {}".format( + assert ( + result is not True + ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( tc_name, result - )) + ) write_test_footer(tc_name) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py index fb6b28ce5b..1432a82b12 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py @@ -247,11 +247,11 @@ def test_ospf_redistribution_tc5_p0(request): if result is not True: break - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: OSPF routes are present after deleting ip address of newly " - "configured interface of R0 \n Error: {}".format( - tc_name, result - )) + "configured interface of R0 \n Error: {}".format(tc_name, result) + ) protocol = "ospf" result = verify_rib( @@ -264,11 +264,11 @@ def test_ospf_redistribution_tc5_p0(request): attempts=5, expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: OSPF routes are present in fib after deleting ip address of newly " - "configured interface of R0 \n Error: {}".format( - tc_name, result - )) + "configured interface of R0 \n Error: {}".format(tc_name, result) + ) step("Add back the deleted ip address on newly configured interface of R0") topo1 = { @@ -370,11 +370,11 @@ def test_ospf_redistribution_tc6_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False) if result is not True: break - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: OSPF routes are present after deleting ip address of newly " - "configured loopback of R0 \n Error: {}".format( - tc_name, result - )) + "configured loopback of R0 \n Error: {}".format(tc_name, result) + ) protocol = "ospf" result = verify_rib( @@ -386,11 +386,11 @@ def test_ospf_redistribution_tc6_p0(request): next_hop=nh, expected=False, ) - assert result is not True, ("Testcase {} : Failed \n " + assert result is not True, ( + "Testcase {} : Failed \n " "r1: OSPF routes are present in fib after deleting ip address of newly " - "configured loopback of R0 \n Error: {}".format( - tc_name, result - )) + "configured loopback of R0 \n Error: {}".format(tc_name, result) + ) step("Add back the deleted ip address on newly configured interface of R0") topo1 = { diff --git a/tests/topotests/ospf_suppress_fa/__init__.py b/tests/topotests/ospf_suppress_fa/__init__.py new file mode 100755 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/__init__.py diff --git a/tests/topotests/ospf_suppress_fa/r1/ospfd.conf b/tests/topotests/ospf_suppress_fa/r1/ospfd.conf new file mode 100644 index 0000000000..c02be35b14 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r1/ospfd.conf @@ -0,0 +1,9 @@ +! +interface r1-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 10.0.12.0/24 area 0 +! diff --git a/tests/topotests/ospf_suppress_fa/r1/zebra.conf b/tests/topotests/ospf_suppress_fa/r1/zebra.conf new file mode 100644 index 0000000000..c1e31fb474 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r1/zebra.conf @@ -0,0 +1,4 @@ +! +interface r1-eth0 + ip address 10.0.12.1/24 +! diff --git a/tests/topotests/ospf_suppress_fa/r2/ospfd.conf b/tests/topotests/ospf_suppress_fa/r2/ospfd.conf new file mode 100644 index 0000000000..ebc7d252fd --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r2/ospfd.conf @@ -0,0 +1,16 @@ +! +interface r2-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +interface r2-eth1 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + network 10.0.12.0/24 area 0 + network 10.0.23.0/24 area 1 + area 1 nssa +! diff --git a/tests/topotests/ospf_suppress_fa/r2/zebra.conf b/tests/topotests/ospf_suppress_fa/r2/zebra.conf new file mode 100644 index 0000000000..9f1a26349e --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r2/zebra.conf @@ -0,0 +1,7 @@ +! +interface r2-eth0 + ip address 10.0.12.2/24 +! +interface r2-eth1 + ip address 10.0.23.2/24 +! diff --git a/tests/topotests/ospf_suppress_fa/r3/ospfd.conf b/tests/topotests/ospf_suppress_fa/r3/ospfd.conf new file mode 100644 index 0000000000..08be11a7b7 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r3/ospfd.conf @@ -0,0 +1,11 @@ +! +interface r3-eth0 + ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +router ospf + redistribute static + network 10.0.23.0/24 area 1 + area 1 nssa +! diff --git a/tests/topotests/ospf_suppress_fa/r3/zebra.conf b/tests/topotests/ospf_suppress_fa/r3/zebra.conf new file mode 100644 index 0000000000..f76cbf74d2 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/r3/zebra.conf @@ -0,0 +1,8 @@ +! +ip route 3.3.1.1/32 Null0 +ip route 3.3.2.2/32 Null0 +ip route 3.3.3.3/32 Null0 +! +interface r3-eth0 + ip address 10.0.23.3/24 +! diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot new file mode 100644 index 0000000000..1036658f1a --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot @@ -0,0 +1,66 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph ospf_topo1 { + label="ospf suppress-fa"; + + # Routers + r1 [ + label="r1\nrtr-id 10.0.12.1", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + r2 [ + label="r2 (ABR)\nrtr-id 10.0.23.2", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + r3 [ + label="r3 (ASBR)\nrtr-id 10.0.23.3", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + + # Switches + s1 [ + label="s1\n10.0.12.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + label="s2\n10.0.23.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + subgraph cluster0 { + label="area 0" + r1 -- s1 [label="eth1\n.1"]; + r2 -- s1 [label="eth1\n.2"]; + } + + subgraph cluster1 { + label="area 1\nNSSA" + r2 -- s2 [label="eth2\n.2"]; + r3 -- s2 [label="eth1\n.3"]; + } + + { rank=same; r1; r2; r3; } +} diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg Binary files differnew file mode 100644 index 0000000000..2907d799f5 --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py new file mode 100644 index 0000000000..76e50beb5c --- /dev/null +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python + +# +# test_ospf_suppres_fa.py +# Carles Kishimoto +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_ospf_suppres_fa.py: Test OSPF suppress-fa feature +- Topology: r1 --- R2 (ABR) --- R3 (redistribute static) + +test_ospf_set_suppress_fa() + 1) R1: Get a dict[LSA_ID] = fwd_addr for all type 5 LSA + 2) R2: Configure: area 1 nssa suppress-fa + 3) R1: Get a dict[LSA_ID] and compare fwd_address with 0.0.0.0 + +test_ospf_unset_suppress_fa() + 4) R2: Configure: no area 1 nssa suppress-fa + 5) R1: Get a dict[LSA_ID] = fwd_addr and compare it with the dict obtained in 1) +""" + +import os +import sys +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 +from lib.topogen import Topogen, TopoRouter, get_topogen + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class NetworkTopo(Topo): + "OSPF topology builder" + + def build(self, *_args, **_opts): + "Build function" + + tgen = get_topogen(self) + + # Create routers + for router in range(1, 4): + tgen.add_router("r{}".format(router)) + + # R1-R2 backbone area + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # R2-R3 NSSA area + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(NetworkTopo, mod.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registred routers, load the zebra and ospf configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + + tgen = get_topogen() + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + topotest.sleep(10, "Waiting for OSPF convergence") + + +def ospf_configure_suppress_fa(router_name, area): + "Configure OSPF suppress-fa in router_name" + + tgen = get_topogen() + router = tgen.gears[router_name] + router.vtysh_cmd( + "conf t\nrouter ospf\narea {} nssa suppress-fa\nexit\n".format(area) + ) + + +def ospf_unconfigure_suppress_fa(router_name, area): + "Remove OSPF suppress-fa in router_name" + + tgen = get_topogen() + router = tgen.gears[router_name] + router.vtysh_cmd( + "conf t\nrouter ospf\nno area {} nssa suppress-fa\nexit\n".format(area) + ) + + +def ospf_get_lsa_type5(router_name): + "Return a dict with link state id as key and forwarding addresses as value" + + result = dict() + tgen = get_topogen() + router = tgen.gears[router_name] + cmd = "show ip ospf database external\n" + output = topotest.normalize_text(router.vtysh_cmd(cmd)) + for line in output.splitlines(): + re0 = re.match(r"\s+Link State ID: (\S+) \(External Network Number\)", line) + if re0: + lsa = re0.group(1) + re1 = re.match(r"\s+Forward Address: (\S+)", line) + if re1: + result[lsa] = re1.group(1) + return result + + +@pytest.fixture(scope="module", name="original") +def test_ospf_set_suppress_fa(): + "Test OSPF area [x] nssa suppress-fa" + + # Get current forwarding address for each LSA type-5 in r1 + initial = ospf_get_lsa_type5("r1") + + # Configure suppres-fa in r2 area 1 + ospf_configure_suppress_fa("r2", "1") + topotest.sleep(10, "Waiting for OSPF convergence") + + # Check forwarding address on r1 for all statics is 0.0.0.0 + assertmsg = "Forwarding address is not 0.0.0.0 after enabling OSPF suppress-fa" + suppress = ospf_get_lsa_type5("r1") + for prefix in suppress: + assert suppress[prefix] == "0.0.0.0", assertmsg + + # Return the original forwarding addresses so we can compare them + # in the test_ospf_unset_supress_fa + return initial + + +def test_ospf_unset_supress_fa(original): + "Test OSPF no area [x] nssa suppress-fa" + + # Remove suppress-fa in r2 area 1 + ospf_unconfigure_suppress_fa("r2", "1") + topotest.sleep(10, "Waiting for OSPF convergence") + + # Check forwarding address is the original value on r1 for all statics + assertmsg = "Forwarding address is not correct after removing OSPF suppress-fa" + restore = ospf_get_lsa_type5("r1") + for prefix in restore: + assert restore[prefix] == original[prefix], assertmsg + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py index dc4e29ebde..5d4950a70e 100644 --- a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py +++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py @@ -949,10 +949,11 @@ def static_routes_rmap_pfxlist_p0_tc7_ebgp(request): result4 = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert result4 is not True, ("Testcase {} : Failed \n" - "routes are still present \n Error: {}".format( + assert ( + result4 is not True + ), "Testcase {} : Failed \n" "routes are still present \n Error: {}".format( tc_name, result4 - )) + ) step("vm4 should be present in FRR1") dut = "r1" diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py index 14db729195..09c437c3c4 100644 --- a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py +++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py @@ -947,10 +947,11 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ibgp(request): result4 = verify_rib( tgen, addr_type, dut, input_dict, protocol=protocol, expected=False ) - assert result4 is not True, ("Testcase {} : Failed \n" - "routes are still present \n Error: {}".format( + assert ( + result4 is not True + ), "Testcase {} : Failed \n" "routes are still present \n Error: {}".format( tc_name, result4 - )) + ) step("vm4 should be present in FRR1") dut = "r1" diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons index f6d512be72..b1526888ed 100644 --- a/tools/etc/frr/daemons +++ b/tools/etc/frr/daemons @@ -30,6 +30,7 @@ pbrd=no bfdd=no fabricd=no vrrpd=no +pathd=no # # If this option is set the /etc/init.d/frr script automatically loads @@ -55,6 +56,7 @@ staticd_options="-A 127.0.0.1" bfdd_options=" -A 127.0.0.1" fabricd_options="-A 127.0.0.1" vrrpd_options=" -A 127.0.0.1" +pathd_options=" -A 127.0.0.1" # configuration profile # diff --git a/tools/etc/frr/frr.conf b/tools/etc/frr/frr.conf index 61f7a65620..5ee6d15c91 100644 --- a/tools/etc/frr/frr.conf +++ b/tools/etc/frr/frr.conf @@ -1,3 +1,10 @@ -# default to using syslog. /etc/rsyslog.d/45-frr.conf places the log -# in /var/log/frr/frr.log +# default to using syslog. /etc/rsyslog.d/45-frr.conf places the log in +# /var/log/frr/frr.log +# +# Note: +# FRR's configuration shell, vtysh, dynamically edits the live, in-memory +# configuration while FRR is running. When instructed, vtysh will persist the +# live configuration to this file, overwriting its contents. If you want to +# avoid this, you can edit this file manually before starting FRR, or instruct +# vtysh to write configuration to a different file. log syslog informational diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf index 0bc6547994..f902d3dd21 100644 --- a/tools/etc/frr/support_bundle_commands.conf +++ b/tools/etc/frr/support_bundle_commands.conf @@ -84,6 +84,25 @@ CMD_LIST_END # CMD_LIST_END # PIM Support Bundle Command List -# PROC_NAME:pim -# CMD_LIST_START -# CMD_LIST_END +PROC_NAME:pim +CMD_LIST_START +show ip multicast +show ip pim interface +show ip pim interface traffic +show ip pim nexthop +show ip pim neighbor +show ip pim bsr +show ip pim bsrp-info +show ip pim bsm-database +show ip pim rp-info +show ip igmp groups +show ip igmp interface +show ip igmp join +show ip igmp sources +show ip pim upstream +show ip mroute +show ip pim join +show ip pim state +show ip pim statistics +show ip pim rpf +CMD_LIST_END diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 1461e0f296..f4b832691e 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -108,7 +108,7 @@ class Vtysh(object): stdout, stderr = proc.communicate() if proc.wait() != 0: if stdouts is not None: - stdouts.append(stdout.decode('UTF-8')) + stdouts.append(stdout.decode("UTF-8")) raise VtyshException( 'vtysh returned status %d for command "%s"' % (proc.returncode, command) ) @@ -279,6 +279,35 @@ class Config(object): if ":" in line: line = get_normalized_mac_ip_line(line) + """ + vrf static routes can be added in two ways. The old way is: + + "ip route x.x.x.x/x y.y.y.y vrf <vrfname>" + + but it's rendered in the configuration as the new way:: + + vrf <vrf-name> + ip route x.x.x.x/x y.y.y.y + exit-vrf + + this difference causes frr-reload to not consider them a + match and delete vrf static routes incorrectly. + fix the old way to match new "show running" output so a + proper match is found. + """ + if ( + line.startswith("ip route ") or line.startswith("ipv6 route ") + ) and " vrf " in line: + newline = line.split(" ") + vrf_index = newline.index("vrf") + vrf_ctx = newline[vrf_index] + " " + newline[vrf_index + 1] + del newline[vrf_index : vrf_index + 2] + newline = " ".join(newline) + self.lines.append(vrf_ctx) + self.lines.append(newline) + self.lines.append("exit-vrf") + line = "end" + self.lines.append(line) self.load_contexts() @@ -460,6 +489,23 @@ class Config(object): ): key[0] = re.sub(r"\s+null0(\s*$)", " Null0", key[0]) + """ + Similar to above, but when the static is in a vrf, it turns into a + blackhole nexthop for both null0 and Null0. Fix it accordingly + """ + if lines and key[0].startswith("vrf "): + newlines = [] + for line in lines: + if line.startswith("ip route ") or line.startswith("ipv6 route "): + if "null0" in line: + line = re.sub(r"\s+null0(\s*$)", " blackhole", line) + elif "Null0" in line: + line = re.sub(r"\s+Null0(\s*$)", " blackhole", line) + newlines.append(line) + else: + newlines.append(line) + lines = newlines + if lines: if tuple(key) not in self.contexts: ctx = Context(tuple(key), lines) @@ -582,11 +628,13 @@ end if line.startswith("!") or line.startswith("#"): continue - if (len(ctx_keys) == 2 - and ctx_keys[0].startswith('bfd') - and ctx_keys[1].startswith('profile ') - and line == 'end'): - log.debug('LINE %-50s: popping from sub context, %-50s', line, ctx_keys) + if ( + len(ctx_keys) == 2 + and ctx_keys[0].startswith("bfd") + and ctx_keys[1].startswith("profile ") + and line == "end" + ): + log.debug("LINE %-50s: popping from sub context, %-50s", line, ctx_keys) if main_ctx_key: self.save_contexts(ctx_keys, current_context_lines) @@ -907,9 +955,9 @@ end ctx_keys.append(line) elif ( - line.startswith('profile ') + line.startswith("profile ") and len(ctx_keys) == 1 - and ctx_keys[0].startswith('bfd') + and ctx_keys[0].startswith("bfd") ): # Save old context first @@ -918,7 +966,7 @@ end main_ctx_key = copy.deepcopy(ctx_keys) log.debug( "LINE %-50s: entering BFD profile sub-context, append to ctx_keys", - line + line, ) ctx_keys.append(line) @@ -1067,6 +1115,16 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): for (ctx_keys, line) in lines_to_del: deleted = False + # If there is a change in the segment routing block ranges, do it + # in-place, to avoid requesting spurious label chunks which might fail + if line and "segment-routing global-block" in line: + for (add_key, add_line) in lines_to_add: + if ctx_keys[0] == add_key[0] and add_line and "segment-routing global-block" in add_line: + lines_to_del_to_del.append((ctx_keys, line)) + break + continue + + if ctx_keys[0].startswith("router bgp") and line: if line.startswith("neighbor "): diff --git a/tools/frr.service.in b/tools/frr.service.in index 836ce06be7..f67bc41b09 100644 --- a/tools/frr.service.in +++ b/tools/frr.service.in @@ -4,7 +4,7 @@ Documentation=https://frrouting.readthedocs.io/en/latest/setup.html Wants=network.target After=network-pre.target systemd-sysctl.service Before=network.target -OnFailure=heartbeat-failed@%n.service +OnFailure=heartbeat-failed@%n [Service] Nice=-5 diff --git a/tools/frr@.service.in b/tools/frr@.service.in index 1e5d252325..7b94a11f5b 100644 --- a/tools/frr@.service.in +++ b/tools/frr@.service.in @@ -4,7 +4,7 @@ Documentation=https://frrouting.readthedocs.io/en/latest/setup.html Wants=network.target After=network-pre.target systemd-sysctl.service Before=network.target -OnFailure=heartbeat-failed@%n.service +OnFailure=heartbeat-failed@%n [Service] Nice=-5 diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index 3dbc6a1b43..475e56cf72 100644 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -35,7 +35,7 @@ FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter # - keep zebra first # - watchfrr does NOT belong in this list -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd" +DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd" RELOAD_SCRIPT="$D_PATH/frr-reload.py" # @@ -149,6 +149,10 @@ daemon_prep() { daemon_start() { local dmninst daemon inst args instopt wrap bin + + all=false + [ "$1" = "--all" ] && { all=true; shift; } + daemon_inst "$1" ulimit -n $MAX_FDS > /dev/null 2> /dev/null @@ -165,7 +169,11 @@ daemon_start() { if eval "$all_wrap $wrap $bin $nsopt -d $frr_global_options $instopt $args"; then log_success_msg "Started $dmninst" - vtysh_b "$daemon" + if $all; then + debug "Skipping startup of vtysh until all have started" + else + vtysh_b "$daemon" + fi else log_failure_msg "Failed to start $dmninst!" fi @@ -237,8 +245,9 @@ print_status() { all_start() { daemon_list daemons for dmninst in $daemons; do - daemon_start "$dmninst" + daemon_start --all "$dmninst" done + vtysh_b } all_stop() { diff --git a/tools/gcc-plugins/frr-format.c b/tools/gcc-plugins/frr-format.c index 6d91d2cdcd..e9f397f225 100644 --- a/tools/gcc-plugins/frr-format.c +++ b/tools/gcc-plugins/frr-format.c @@ -2343,7 +2343,7 @@ check_argument_type (const format_char_info *fci, /* note printf extension type checks are *additional* - %p must always * be pointer compatible, %d always int compatible. */ - if (!kef) + if (first_wanted_type->kind != CF_KIND_FORMAT || !kef) return true; const struct kernel_ext_fmt *kef_now; @@ -4241,6 +4241,11 @@ handle_finish_parse (void *event_data, void *data) continue; } node = TREE_TYPE (node); + + if (etab->t_unsigned) + node = c_common_unsigned_type (node); + else if (etab->t_signed) + node = c_common_signed_type (node); } etab->type = node; @@ -4357,9 +4362,17 @@ handle_pragma_printfrr_ext (cpp_reader *dummy) ttype = pragma_lex (&token, &loc); /* qualifiers */ - if (ttype == CPP_NAME && !strcmp (IDENTIFIER_POINTER (token), "const")) + while (ttype == CPP_NAME) { - etab->t_const = true; + if (!strcmp (IDENTIFIER_POINTER (token), "const")) + etab->t_const = true; + else if (!strcmp (IDENTIFIER_POINTER (token), "signed")) + etab->t_signed = true; + else if (!strcmp (IDENTIFIER_POINTER (token), "unsigned")) + etab->t_unsigned = true; + else + break; + ttype = pragma_lex (&token, &loc); } diff --git a/tools/gcc-plugins/frr-format.h b/tools/gcc-plugins/frr-format.h index 87d2049ed4..599dbc56f9 100644 --- a/tools/gcc-plugins/frr-format.h +++ b/tools/gcc-plugins/frr-format.h @@ -113,6 +113,8 @@ struct kernel_ext_fmt tree_code type_code; int ptrlevel; bool t_const; + bool t_unsigned; + bool t_signed; bool warned; const char *type_str; diff --git a/vrrpd/subdir.am b/vrrpd/subdir.am index 96ae530894..dfa9b261c3 100644 --- a/vrrpd/subdir.am +++ b/vrrpd/subdir.am @@ -5,7 +5,6 @@ if VRRPD noinst_LIBRARIES += vrrpd/libvrrp.a sbin_PROGRAMS += vrrpd/vrrpd -# dist_examples_DATA += staticd/staticd.conf.sample vtysh_scan += vrrpd/vrrp_vty.c vtysh_daemons += vrrpd man8 += $(MANBUILD)/frr-vrrpd.8 diff --git a/vtysh/subdir.am b/vtysh/subdir.am index 86861b0390..5f7d854948 100644 --- a/vtysh/subdir.am +++ b/vtysh/subdir.am @@ -4,7 +4,6 @@ if VTYSH bin_PROGRAMS += vtysh/vtysh -dist_examples_DATA += vtysh/vtysh.conf.sample man1 += $(MANBUILD)/vtysh.1 endif diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 06d224ec24..75fee1d297 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -2237,7 +2237,7 @@ DEFUNSH(VTYSH_BFDD, bfd_peer_enter, bfd_peer_enter_cmd, } DEFUNSH(VTYSH_BFDD, bfd_profile_enter, bfd_profile_enter_cmd, - "profile WORD", + "profile BFDPROF", BFD_PROFILE_STR BFD_PROFILE_NAME_STR) { @@ -2851,7 +2851,7 @@ DEFUN (vtysh_show_error_code, } /* Northbound. */ -DEFUN (show_config_running, +DEFUN_HIDDEN (show_config_running, show_config_running_cmd, "show configuration running\ [<json|xml> [translate WORD]]\ @@ -2932,10 +2932,15 @@ DEFUN (vtysh_show_history, /* Memory */ DEFUN (vtysh_show_memory, vtysh_show_memory_cmd, - "show memory", + "show memory [" DAEMONS_LIST "]", SHOW_STR - "Memory statistics\n") + "Memory statistics\n" + DAEMONS_STR) { + if (argc == 3) + return show_one_daemon(vty, argv, argc - 1, + argv[argc - 1]->text); + return show_per_daemon(vty, argv, argc, "Memory statistics for %s:\n"); } @@ -3719,19 +3724,19 @@ DEFUN (no_vtysh_output_file, DEFUN(find, find_cmd, - "find REGEX", + "find REGEX...", "Find CLI command matching a regular expression\n" "Search pattern (POSIX regex)\n") { - char *pattern = argv[1]->arg; const struct cmd_node *node; const struct cmd_element *cli; vector clis; - regex_t exp = {}; - + char *pattern = argv_concat(argv, argc, 1); int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED); + XFREE(MTYPE_TMP, pattern); + if (cr != 0) { switch (cr) { case REG_BADBR: diff --git a/vtysh/vtysh.conf.sample b/vtysh/vtysh.conf.sample deleted file mode 100644 index 4e0a2beb44..0000000000 --- a/vtysh/vtysh.conf.sample +++ /dev/null @@ -1,7 +0,0 @@ -! -! Sample configuration file for vtysh. -! -!service integrated-vtysh-config -!hostname quagga-router -!username root nopassword -! diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 498d3e5f67..3414c764ce 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -342,6 +342,9 @@ void vtysh_config_parse_line(void *arg, const char *line) config = config_get(OPENFABRIC_NODE, line); else if (strncmp(line, "route-map", strlen("route-map")) == 0) config = config_get(RMAP_NODE, line); + else if (strncmp(line, "no route-map", strlen("no route-map")) + == 0) + config = config_get(RMAP_NODE, line); else if (strncmp(line, "pbr-map", strlen("pbr-map")) == 0) config = config_get(PBRMAP_NODE, line); else if (strncmp(line, "access-list", strlen("access-list")) diff --git a/yang/frr-bgp-common.yang b/yang/frr-bgp-common.yang index 1840e3728c..1a19d52965 100644 --- a/yang/frr-bgp-common.yang +++ b/yang/frr-bgp-common.yang @@ -393,7 +393,7 @@ submodule frr-bgp-common { container global-neighbor-config { leaf dynamic-neighbors-limit { type uint32 { - range "1..5000"; + range "1..65535"; } description "Maximum number of BGP Dynamic Neighbors that can be created."; diff --git a/yang/frr-bgp-filter.yang b/yang/frr-bgp-filter.yang new file mode 100644 index 0000000000..199e70997f --- /dev/null +++ b/yang/frr-bgp-filter.yang @@ -0,0 +1,329 @@ +module frr-bgp-filter { + yang-version 1.1; + namespace "http://frrouting.org/yang/bgp-filter"; + prefix frr-bgp-filter; + + import frr-filter { + prefix frr-filter; + } + + import ietf-routing-types { + prefix rt-types; + } + + organization + "Free Range Routing"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> + FRR Development List: <mailto:dev@lists.frrouting.org>"; + description + "This module defines filter settings"; + + revision 2020-01-15 { + description + "Initial revision"; + } + + typedef list-sequence { + type uint32 { + range "1..4294967295"; + } + description + "List instance priority (low number means higher priority)"; + } + + typedef list-action { + type enumeration { + enum "deny" { + value 0; + description + "Deny an entry"; + } + enum "permit" { + value 1; + description + "Accept an entry"; + } + } + description + "Return action on match"; + } + + typedef bgp-list-name { + type string; + description + "List name"; + } + + typedef community-string { + type string { + pattern "(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0)|((6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0):(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))|(local-AS)|(no-advertise)|(no-export)|(internet)"; + } + description + "The BGP community string"; + } + + typedef large-community-string { + type string { + pattern "(429496729[0-5]|42949672[0-8][0-9]|4294967[01][0-9]{2}|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|[1-3][0-9]{9}|[1-9][0-9]{0,8}|0)|(429496729[0-5]|42949672[0-8][0-9]|4294967[01][0-9]{2}|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):(429496729[0-5]|42949672[0-8][0-9]|4294967[01][0-9]{2}|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|[1-3][0-9]{9}|[1-9][0-9]{0,8}|0)|(429496729[0-5]|42949672[0-8][0-9]|4294967[01][0-9]{2}|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):(429496729[0-5]|42949672[0-8][0-9]|4294967[01][0-9]{2}|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|[1-3][0-9]{9}|[1-9][0-9]{0,8}|0):(429496729[0-5]|42949672[0-8][0-9]|4294967[01][0-9]{2}|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|429[0-3][0-9]{6}|42[0-8][0-9]{7}|4[01][0-9]{8}|[1-3][0-9]{9}|[1-9][0-9]{0,8}|0)"; + } + description + "The BGP large-community string"; + } + + augment "/frr-filter:lib" { + list community-list { + key "name"; + description + "Community-list instance"; + leaf name { + type string; + } + + list entry { + key "sequence"; + description + "Community-list entry"; + leaf sequence { + type list-sequence; + } + + leaf action { + type list-action; + } + + leaf type { + type enumeration { + enum "community-list-standard" { + value 0; + description + "Standard community-list name/identifier"; + } + enum "community-list-extended" { + value 1; + description + "Expanded community-list name/identifier"; + } + } + mandatory true; + description + "Community-list instance name/identifier"; + } + + choice community-string { + description + "Community string"; + case standard { + when "./type = 'community-list-standard'"; + leaf-list standard-community-string { + type community-string; + description + "Community string"; + } + } + + case expanded { + when "./type = 'community-list-extended'"; + leaf expanded-community-string { + type string; + description + "Community string reg-ex"; + } + } + } + } + } + + list large-community-list { + key "name"; + description + "Large community-list instance"; + leaf name { + type string; + } + + list entry { + key "sequence"; + description + "Large community-list entry"; + leaf sequence { + type list-sequence; + } + + leaf action { + type list-action; + } + + leaf type { + type enumeration { + enum "large-community-list-standard-id" { + value 0; + description + "Standard large-community-list identifier"; + } + enum "large-community-list-extended-id" { + value 1; + description + "Expanded large-community-list identifier"; + } + enum "large-community-list-standard-name" { + value 2; + description + "Standard large-community-list name"; + } + enum "large-community-list-extended-name" { + value 3; + description + "Expanded large-community-list name"; + } + } + mandatory true; + description + "Large community-list instance name/identifier"; + } + + choice large-community-string { + description + "Large community string"; + case standard { + when "./type = 'large-community-list-standard-id' or " + + "./type = 'large-community-list-standard-name'"; + leaf-list standard-large-community-string { + type large-community-string; + description + "Large community string"; + } + } + + case expanded { + when "./type = 'large-community-list-extended-id' or " + + "./type = 'large-community-list-extended-name'"; + leaf expanded-large-community-string { + type string; + description + "Large community string reg-ex"; + } + } + } + } + } + + list extcommunity-list { + key "name"; + description + "Extcommunity-list instance"; + leaf name { + type string; + } + + list entry { + key "sequence"; + description + "Extcommunity-list entry"; + leaf sequence { + type list-sequence; + } + + leaf action { + type list-action; + } + + leaf type { + type enumeration { + enum "extcommunity-list-standard-id" { + value 0; + description + "Standard extcommunity-list identifier"; + } + enum "extcommunity-list-extended-id" { + value 1; + description + "Expanded extcommunity-list identifier"; + } + enum "extcommunity-list-standard-name" { + value 2; + description + "Standard extcommunity-list name"; + } + enum "extcommunity-list-extended-name" { + value 3; + description + "Expanded extcommunity-list name"; + } + } + mandatory true; + description + "Extcommunity-list instance name/identifier"; + } + + choice extcommunity-string { + description + "Extcommunity string"; + case standard { + when "./type = 'extcommunity-list-standard-id' or " + + "./type = 'extcommunity-list-standard-name'"; + choice standard-extcommunity-string { + description + "Value of the ext-community"; + case extcommunity-rt { + description + "Set BGP ext-community route-target attribute"; + leaf-list extcommunity-rt { + type rt-types:route-target; + } + } + + case extcommunity-soo { + description + "Set BGP ext-community site-of-origin attribute"; + leaf-list extcommunity-soo { + type rt-types:route-target; + } + } + } + } + + case expanded { + when "./type = 'extcommunity-list-extended-id' or " + + "./type = 'extcommunity-list-extended-name'"; + leaf expanded-extcommunity-string { + type string; + description + "Extcommunity string reg-ex"; + } + } + } + } + } + + list as-path-list { + key "name"; + description + "AS-path access-list instance"; + leaf name { + type string; + description + "AS-path access-list instance name/identifier"; + } + + list entry { + key "sequence"; + description + "AS-path access-list entry"; + leaf sequence { + type list-sequence; + } + + leaf action { + type list-action; + } + + leaf as-path { + type string; + description + "AS-path access-list string reg-ex"; + } + } + } + } +} diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang new file mode 100644 index 0000000000..96505b08a8 --- /dev/null +++ b/yang/frr-bgp-route-map.yang @@ -0,0 +1,820 @@ +module frr-bgp-route-map { + yang-version 1.1; + namespace "http://frrouting.org/yang/bgp-route-map"; + prefix frr-bgp-route-map; + + import ietf-inet-types { + prefix inet; + } + + import frr-route-map { + prefix frr-route-map; + } + + import frr-filter { + prefix filter; + } + + import frr-bgp-filter { + prefix bgp-filter; + } + + import ietf-routing-types { + prefix rt-types; + } + + organization + "Free Range Routing"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> + FRR Development List: <mailto:dev@lists.frrouting.org>"; + description + "This module defines bgp route map settings"; + + revision 2020-01-02 { + description + "Initial revision"; + } + + identity match-local-preference { + base frr-route-map:rmap-match-type; + description + "Match local-preference of routes"; + } + + identity match-script { + base frr-route-map:rmap-match-type; + description + "Match script of routes"; + } + + identity match-origin { + base frr-route-map:rmap-match-type; + description + "Match BGP route origin code"; + } + + identity rpki { + base frr-route-map:rmap-match-type; + description + "Control rpki specific settings"; + } + + identity probability { + base frr-route-map:rmap-match-type; + description + "Match portion of routes defined by percentage value"; + } + + identity source-vrf { + base frr-route-map:rmap-match-type; + description + "Match source vrf of routes"; + } + + identity peer { + base frr-route-map:rmap-match-type; + description + "Match peer address"; + } + + identity mac-address-list { + base frr-route-map:rmap-match-type; + description + "Match MAC address access-list"; + } + + identity ip-route-source { + base frr-route-map:rmap-match-type; + description + "Match advertising source address of route"; + } + + identity ip-route-source-prefix-list { + base frr-route-map:rmap-match-type; + description + "Match advertising source address of route"; + } + + identity evpn-route-type { + base frr-route-map:rmap-match-type; + description + "Match EVPN route type"; + } + + identity evpn-default-route { + base frr-route-map:rmap-match-type; + description + "Match EVPN default Type-5 route"; + } + + identity evpn-vni { + base frr-route-map:rmap-match-type; + description + "Match EVPN VNI"; + } + + identity evpn-rd { + base frr-route-map:rmap-match-type; + description + "Match EVPN route distinguisher"; + } + + identity match-community { + base frr-route-map:rmap-match-type; + description + "Match BGP community list"; + } + + identity match-large-community { + base frr-route-map:rmap-match-type; + description + "Match BGP large-community list"; + } + + identity match-extcommunity { + base frr-route-map:rmap-match-type; + description + "Match BGP extcommunity list"; + } + + identity as-path-list { + base frr-route-map:rmap-match-type; + description + "Match BGP AS path list"; + } + + identity ipv4-nexthop { + base frr-route-map:rmap-match-type; + description + "Match IPv4 next hop address"; + } + + identity ipv6-nexthop { + base frr-route-map:rmap-match-type; + description + "Match IPv6 next hop address"; + } + + identity distance { + base frr-route-map:rmap-set-type; + description + "Set BGP administrative distance to use"; + } + + identity set-extcommunity-rt { + base frr-route-map:rmap-set-type; + description + "Set BGP extended community attribute"; + } + + identity set-extcommunity-soo { + base frr-route-map:rmap-set-type; + description + "Set BGP extended community attribute"; + } + + identity set-extcommunity-lb { + base frr-route-map:rmap-set-type; + description + "Set BGP extended community attribute"; + } + + identity set-ipv4-nexthop { + base frr-route-map:rmap-set-type; + description + "Set the IPv4 next-hop to peer-address/unchanged"; + } + + identity ipv4-vpn-address { + base frr-route-map:rmap-set-type; + description + "Set IPv4 VPN next-hop address"; + } + + identity ipv6-nexthop-global { + base frr-route-map:rmap-set-type; + description + "Set IPv6 next-hop global address"; + } + + identity ipv6-prefer-global { + base frr-route-map:rmap-set-type; + description + "Set IPv6 next-hop to prefer global address"; + } + + identity ipv6-peer-address { + base frr-route-map:rmap-set-type; + description + "Set IPv6 next-hop peer address"; + } + + identity ipv6-vpn-address { + base frr-route-map:rmap-set-type; + description + "Set IPv6 VPN next-hop address"; + } + + identity label-index { + base frr-route-map:rmap-set-type; + description + "Set the label index to associate with the prefixs"; + } + + identity set-local-preference { + base frr-route-map:rmap-set-type; + description + "Set the BGP local preference path attribute"; + } + + identity set-origin { + base frr-route-map:rmap-set-type; + description + "Set BGP route origin code"; + } + + identity weight { + base frr-route-map:rmap-set-type; + description + "Set the BGP weight attribute"; + } + + identity originator-id { + base frr-route-map:rmap-set-type; + description + "Set the BGP originator ID attribute"; + } + + identity table { + base frr-route-map:rmap-set-type; + description + "Export route to non-main kernel table"; + } + + identity atomic-aggregate { + base frr-route-map:rmap-set-type; + description + "Set BGP atomic-aggregate attribute"; + } + + identity as-path-prepend { + base frr-route-map:rmap-set-type; + description + "Set the BGP AS-path attribute"; + } + + identity as-path-exclude { + base frr-route-map:rmap-set-type; + description + "Set the BGP AS-path attribute"; + } + + identity set-community { + base frr-route-map:rmap-set-type; + description + "Set the BGP community attribute"; + } + + identity set-large-community { + base frr-route-map:rmap-set-type; + description + "Set the BGP large-community attribute"; + } + + identity aggregator { + base frr-route-map:rmap-set-type; + description + "Set the BGP aggregator attribute"; + } + + identity comm-list-delete { + base frr-route-map:rmap-set-type; + description + "Set BGP community list (for deletion)"; + } + + identity large-comm-list-delete { + base frr-route-map:rmap-set-type; + description + "Set BGP large community list (for deletion)"; + } + + grouping extcommunity-non-transitive-types { + leaf two-octet-as-specific { + type boolean; + description + "Non-Transitive Two-Octet AS-Specific Extended Community"; + } + } + + typedef extcommunity-lb-type { + type enumeration { + enum "explicit-bandwidth" { + value 0; + description + "Bandwidth value in Mbps"; + } + enum "cumulative-bandwidth" { + value 1; + description + "Cumulative bandwidth of all multipaths (outbound-only)"; + } + enum "computed-bandwidth" { + value 2; + description + "Internally computed bandwidth based on number of multipaths (outbound-only)"; + } + } + description + "ext-community link bandwidth types."; + } + + augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:rmap-match-condition/frr-route-map:match-condition" { + case local-preference { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'match-local-preference')"; + leaf local-preference { + type uint32 { + range "0..4294967295"; + } + } + } + + case script { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'match-script')"; + leaf script { + type string; + } + } + + case origin { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'match-origin')"; + leaf origin { + type enumeration { + enum "egp" { + value 0; + description + "Remote EGP"; + } + enum "igp" { + value 1; + description + "Local IGP"; + } + enum "incomplete" { + value 2; + description + "Unknown heritage"; + } + } + } + } + + case rpki { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'rpki')"; + leaf rpki { + type enumeration { + enum "invalid" { + value 0; + description + "Invalid prefix"; + } + enum "notfound" { + value 1; + description + "Prefix not found"; + } + enum "valid" { + value 2; + description + "Valid prefix"; + } + } + } + } + + case probability { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'probability')"; + leaf probability { + type uint8 { + range "0..100"; + } + } + } + + case source-vrf { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'source-vrf')"; + leaf source-vrf { + type string; + } + } + + case peer { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'peer')"; + choice peer { + description + "Value of the peer"; + case peer-ipv4-address { + description + "IP address of peer"; + leaf peer-ipv4-address { + type inet:ipv4-address; + } + } + + case peer-interface { + description + "Interface name of peer"; + leaf peer-interface { + type string; + } + } + + case peer-ipv6-address { + description + "IPv6 address of peer"; + leaf peer-ipv6-address { + type inet:ipv6-address; + } + } + + case peer-local { + description + "Static or Redistributed routes"; + leaf peer-local { + type boolean; + } + } + } + } + + case access-list-name { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'mac-address-list') or " + + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'as-path-list') or " + + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'ip-route-source') or " + + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'ip-route-source-prefix-list')"; + description + "Access-list name"; + leaf list-name { + type filter:access-list-name; + } + } + + case evpn-default-route { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'evpn-default-route')"; + description + "Match default EVPN type-5 route"; + leaf evpn-default-route { + type empty; + } + } + + case evpn-vni { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'evpn-vni')"; + description + "Match eVPN VNI"; + leaf evpn-vni { + type uint32 { + range "1..16777215"; + } + } + } + + case evpn-route-type { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'evpn-route-type')"; + description + "Match eVPN route-type"; + leaf evpn-route-type { + type enumeration { + enum "macip" { + value 0; + description + "Mac-IP route"; + } + enum "multicast" { + value 1; + description + "IMET route"; + } + enum "prefix" { + value 2; + description + "Prefix route"; + } + } + } + } + + case evpn-rd { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'evpn-rd')"; + description + "Match eVPN route-distinguisher"; + leaf route-distinguisher { + type rt-types:route-distinguisher; + } + } + + case comm-list-name { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'match-community') or " + + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'match-large-community') or " + + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'match-extcommunity')"; + container comm-list { + leaf comm-list-name { + type bgp-filter:bgp-list-name; + } + + leaf comm-list-name-exact-match { + type boolean; + description + "Do exact matching of communities"; + } + } + } + + case ipv4-address { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'ipv4-nexthop')"; + leaf ipv4-address { + type inet:ipv4-address; + description + "IPv4 address"; + } + } + + case ipv6-address { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'ipv6-nexthop')"; + leaf ipv6-address { + type inet:ipv6-address; + description + "IPv6 address"; + } + } + } + + augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:rmap-set-action/frr-route-map:set-action" { + case distance { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'distance')"; + leaf distance { + type uint8 { + range "0..255"; + } + } + } + + case extcommunity-rt { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'set-extcommunity-rt')"; + description + "Value of the ext-community"; + leaf extcommunity-rt { + type string; + description + "Set BGP ext-community route-target attribute"; + } + } + + case extcommunity-soo { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'set-extcommunity-soo')"; + description + "Value of the ext-community"; + leaf extcommunity-soo { + type string; + description + "Set BGP ext-community site-of-origin attribute"; + } + } + + case extcommunity-lb { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'set-extcommunity-lb')"; + container extcommunity-lb { + description + "Value of the ext-community."; + leaf lb-type { + type frr-bgp-route-map:extcommunity-lb-type; + } + + leaf bandwidth { + when "../lb-type = 'explicit-bandwidth'"; + type uint16 { + range "1..25600"; + } + description + "Bandwidth value in Mbps"; + } + uses extcommunity-non-transitive-types; + } + } + + case ipv4-address { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'ipv4-vpn-address')"; + description + "Set the IPv4 address"; + leaf ipv4-address { + type inet:ipv4-address; + } + } + + case ipv4-nexthop { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'set-ipv4-nexthop')"; + leaf ipv4-nexthop { + type string; + } + } + + case ipv6-address { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'ipv6-nexthop-global') or " + + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'ipv6-vpn-address')"; + description + "Set the IPv6 address"; + leaf ipv6-address { + type inet:ipv6-address; + } + } + + case preference { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'ipv6-prefer-global') or " + + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'ipv6-peer-address')"; + leaf preference { + type boolean; + } + } + + case label-index { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'label-index')"; + leaf label-index { + type uint32 { + range "0..1048560"; + } + } + } + + case local-pref { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'set-local-preference')"; + leaf local-pref { + type string; + } + } + + case weight { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'weight')"; + leaf weight { + type uint32 { + range "0..4294967295"; + } + } + } + + case origin { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'set-origin')"; + leaf origin { + type enumeration { + enum "egp" { + value 0; + description + "Remote EGP"; + } + enum "igp" { + value 1; + description + "Local IGP"; + } + enum "incomplete" { + value 2; + description + "Unknown heritage"; + } + } + } + } + + case originator-id { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'originator-id')"; + leaf originator-id { + type inet:ipv4-address; + } + } + + case table { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'table')"; + leaf table { + type uint32 { + range "1..4294967295"; + } + } + } + + case atomic-aggregate { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'atomic-aggregate')"; + leaf atomic-aggregate { + type empty; + } + } + + case as-path-prepend { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'as-path-prepend')"; + choice as-path-prepend { + description + "Value of the BGP AS-path attribute"; + case prepend-as { + description + "Prepend the mentioned AS-path"; + leaf prepend-as-path { + type string; + } + } + + case last-as { + description + "Prepend the last ASN in the AS-path"; + leaf last-as { + type uint8 { + range "1..10"; + } + } + } + } + } + + case as-path-exclude { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'as-path-exclude')"; + leaf exclude-as-path { + type string; + description + "Exclude the mentioned AS-path"; + } + } + + case community { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'set-community')"; + choice community { + description + "Value of the BGP community attribute"; + case none { + description + "No community attribute"; + leaf community-none { + type boolean; + } + } + + case community-string { + description + "Community string"; + leaf community-string { + type string; + } + } + } + } + + case large-community { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'set-large-community')"; + choice large-community { + description + "Value of the BGP large-community attribute"; + case none { + description + "No large-community attribute"; + leaf large-community-none { + type boolean; + } + } + + case large-community-string { + description + "Large-Community string"; + leaf large-community-string { + type string; + } + } + } + } + + case aggregator { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'aggregator')"; + container aggregator { + leaf aggregator-asn { + type uint32 { + range "1..4294967295"; + } + description + "ASN of the aggregator"; + } + + leaf aggregator-address { + when "../aggregator-asn > 0 or " + + "../aggregator-asn <= 4294967295"; + type inet:ipv4-address; + description + "IPv4 address of the aggregator"; + } + } + } + + case comm-list-name { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'comm-list-delete') or " + + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'large-comm-list-delete')"; + leaf comm-list-name { + type bgp-filter:bgp-list-name; + } + } + } +} diff --git a/yang/frr-bgp.yang b/yang/frr-bgp.yang index 24998a470d..ae44447df7 100644 --- a/yang/frr-bgp.yang +++ b/yang/frr-bgp.yang @@ -1057,6 +1057,8 @@ module frr-bgp { uses structure-neighbor-route-server; uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-group-filter-config; } augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-flowspec" { @@ -1332,6 +1334,8 @@ module frr-bgp { uses structure-neighbor-route-server; uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-group-filter-config; } augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-flowspec" { diff --git a/yang/frr-ospf-route-map.yang b/yang/frr-ospf-route-map.yang new file mode 100644 index 0000000000..ad7ba5c1ba --- /dev/null +++ b/yang/frr-ospf-route-map.yang @@ -0,0 +1,52 @@ +module frr-ospf-route-map { + yang-version 1.1; + namespace "http://frrouting.org/yang/ospf-route-map"; + prefix frr-ospf-route-map; + + import ietf-inet-types { + prefix inet; + } + + import frr-route-map { + prefix frr-route-map; + } + + organization + "Free Range Routing"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> + FRR Development List: <mailto:dev@lists.frrouting.org>"; + description + "This module defines ospf route map settings"; + + revision 2020-01-02 { + description + "Initial revision"; + } + + identity metric-type { + base frr-route-map:rmap-set-type; + description + "Set the type of metric"; + } + + augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:rmap-set-action/frr-route-map:set-action" { + case metric-type { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'metric-type')"; + leaf metric-type { + type enumeration { + enum "type-1" { + value 0; + description + "OSPF6 external type 1 metric"; + } + enum "type-2" { + value 1; + description + "OSPF6 external type 2 metric"; + } + } + } + } + } +} diff --git a/yang/frr-ospf6-route-map.yang b/yang/frr-ospf6-route-map.yang new file mode 100644 index 0000000000..e5d4969d45 --- /dev/null +++ b/yang/frr-ospf6-route-map.yang @@ -0,0 +1,47 @@ +module frr-ospf6-route-map { + yang-version 1.1; + namespace "http://frrouting.org/yang/ospf6-route-map"; + prefix frr-ospf6-route-map; + + import ietf-inet-types { + prefix inet; + } + + import frr-route-map { + prefix frr-route-map; + } + + organization + "Free Range Routing"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> + FRR Development List: <mailto:dev@lists.frrouting.org>"; + description + "This module defines ospf6 route map settings"; + + revision 2020-01-02 { + description + "Initial revision"; + } + + identity forwarding-address { + base frr-route-map:rmap-set-type; + description + "Set the forwarding address"; + } + + identity metric-type { + base frr-route-map:rmap-set-type; + description + "Set the type of metric"; + } + + augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:rmap-set-action/frr-route-map:set-action" { + case ipv6-address { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'forwarding-address')"; + leaf ipv6-address { + type inet:ipv6-address; + } + } + } +} diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index f959ff8be5..2070649ec2 100644 --- a/yang/frr-pim.yang +++ b/yang/frr-pim.yang @@ -294,6 +294,9 @@ module frr-pim { type uint8 { range "1..180"; } + must ". > ./../hello-interval" { + error-message "HoldTime must be greater than Hello"; + } description "Hello holdtime"; } diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang index b22a96a740..c4eb78608b 100644 --- a/yang/frr-route-map.yang +++ b/yang/frr-route-map.yang @@ -53,21 +53,124 @@ module frr-route-map { "Initial revision"; } - /* - * Types. - */ + identity rmap-match-type { + description + "Base route-map match-condition"; + } + + identity interface { + base rmap-match-type; + description + "Match interface"; + } + + identity ipv4-address-list { + base rmap-match-type; + description + "Match an IPv4 access-list"; + } + + identity ipv4-prefix-list { + base rmap-match-type; + description + "Match an IPv4 prefix-list"; + } + + identity ipv4-next-hop-list { + base rmap-match-type; + description + "Match an IPv4 next-hop"; + } + + identity ipv4-next-hop-prefix-list { + base rmap-match-type; + description + "Match an IPv4 next-hop prefix list"; + } + + identity ipv4-next-hop-type { + base rmap-match-type; + description + "Match an IPv4 next-hop type"; + } + + identity ipv6-address-list { + base rmap-match-type; + description + "Match an IPv6 access-list"; + } + + identity ipv6-prefix-list { + base rmap-match-type; + description + "Match an IPv6 prefix-list"; + } + + identity ipv6-next-hop-type { + base rmap-match-type; + description + "Match an IPv6 next-hop type"; + } + + identity match-metric { + base rmap-match-type; + description + "Match a route metric"; + } + + identity match-tag { + base rmap-match-type; + description + "Match a route tag"; + } + + identity rmap-set-type { + description + "Base route-map set-action"; + } + + identity ipv4-next-hop { + base rmap-set-type; + description + "Set IPv4 address of the next hop"; + } + + identity ipv6-next-hop { + base rmap-set-type; + description + "Set IPv6 address of the next hop"; + } + + identity set-metric { + base rmap-set-type; + description + "Set prefix/route metric"; + } + + identity set-tag { + base rmap-set-type; + description + "Set tag"; + } + + identity set-sr-te-color { + base rmap-set-type; + description + "Set Color of the SR-TE"; + } + typedef route-map-sequence { type uint16 { range "1..65535"; } description - "Route map valid sequence numbers."; + "Route map valid sequence numbers"; } typedef route-map-name { type string; description - "Route map name format."; + "Route map name format"; } typedef route-map-ref { @@ -79,349 +182,299 @@ module frr-route-map { "Reference to a route-map."; } - /* - * Operational data. - */ + grouping rmap-match-condition { + container rmap-match-condition { + choice match-condition { + description + "Value to match (interpretation depends on condition type)"; + case interface { + when "derived-from-or-self(../condition, 'interface')"; + leaf interface { + type frr-interface:interface-ref; + } + } + + case list-name { + when "derived-from-or-self(../condition, 'ipv4-address-list') or " + + "derived-from-or-self(../condition, 'ipv4-prefix-list') or " + + "derived-from-or-self(../condition, 'ipv4-next-hop-list') or " + + "derived-from-or-self(../condition, 'ipv4-next-hop-prefix-list') or " + + "derived-from-or-self(../condition, 'ipv6-address-list') or " + + "derived-from-or-self(../condition, 'ipv6-prefix-list')"; + leaf list-name { + type filter:access-list-name; + } + } + + case ipv4-next-hop-type { + when "derived-from-or-self(../condition, 'ipv4-next-hop-type')"; + leaf ipv4-next-hop-type { + type enumeration { + enum "blackhole" { + value 0; + } + } + } + } + + case ipv6-next-hop-type { + when "derived-from-or-self(../condition, 'ipv6-next-hop-type')"; + leaf ipv6-next-hop-type { + type enumeration { + enum "blackhole" { + value 0; + } + } + } + } + + case match-metric { + when "derived-from-or-self(../condition, 'match-metric')"; + leaf metric { + type uint32 { + range "1..4294967295"; + } + } + } + + case match-tag { + when "derived-from-or-self(../condition, 'match-tag')"; + leaf tag { + type uint32 { + range "1..4294967295"; + } + } + } + } + } + } + + grouping rmap-set-action { + container rmap-set-action { + choice set-action { + description + "Value to set (interpretation depends on action-type)"; + case ipv4-address { + when "derived-from-or-self(../action, 'ipv4-next-hop')"; + leaf ipv4-address { + type inet:ipv4-address; + description + "IPv4 address"; + } + } + + case ipv6-address { + when "derived-from-or-self(../action, 'ipv6-next-hop')"; + leaf ipv6-address { + type inet:ipv6-address; + description + "IPv6 address"; + } + } + + case set-metric { + when "derived-from-or-self(../action, 'set-metric')"; + choice metric-value { + description + "Metric to set or use"; + case value { + leaf value { + type uint32 { + range "0..4294967295"; + } + description + "Use the following metric value"; + } + } + + case add-metric { + leaf add-metric { + description "Add value to metric."; + type uint32 { + range "0..4294967295"; + } + } + } + + case subtract-metric { + leaf subtract-metric { + description "Subtract value from metric."; + type uint32 { + range "0..4294967295"; + } + } + } + + case use-round-trip-time { + leaf use-round-trip-time { + type boolean; + description + "Use the round trip time as metric"; + } + } + + case add-round-trip-time { + leaf add-round-trip-time { + type boolean; + description + "Add round trip time to metric"; + } + } + + case subtract-round-trip-time { + leaf subtract-round-trip-time { + type boolean; + description + "Subtract round trip time to metric"; + } + } + } + } + + case set-tag { + when "derived-from-or-self(../action, 'set-tag')"; + leaf tag { + type uint32 { + range "0..4294967295"; + } + description + "Tag value"; + } + } + + case set-sr-te-color { + when "derived-from-or-self(../action, 'set-sr-te-color')"; + leaf policy { + type string; + description + "Color of the SR-TE Policies to match with"; + } + } + } + } + } + container lib { list route-map { key "name"; description - "Route map instance."; + "Route map instance"; leaf name { type route-map-name; description - "Route map instance name."; + "Route map instance name"; + } + leaf optimization-disabled { + type boolean; + default false; + description "Disables or enables the optimization"; } list entry { key "sequence"; description - "Route map entry."; + "Route map entry"; leaf sequence { - description - "Route map instance priority (low number means higher priority)."; type route-map-sequence; + description + "Route map instance priority (low number means higher priority)"; } leaf description { - description "Route map description."; type string; + description + "Route map description"; } leaf action { - description - "Route map actions: permit (executes action), deny (quits evaluation)."; - mandatory true; type enumeration { - enum permit { + enum "permit" { + value 0; description "Executes configured action and permits the prefix/route if the conditions matched. An alternative exit action can be configured to continue processing the route map list or jump to process another route map."; - value 0; } - enum deny { + enum "deny" { + value 1; description "If all conditions are met the prefix/route is denied and route map processing stops."; - value 1; } } + mandatory true; + description + "Route map actions: permit (executes action), deny (quits evaluation)"; } leaf call { + type route-map-name; description "Call another route map before calling `exit-policy`. If the called route map returns deny then this route map will also - return deny."; - type route-map-name; + return deny"; } leaf exit-policy { - description "What do to after route map successful match, set and call."; type enumeration { - enum permit-or-deny { - description "End route map evaluation and return."; + enum "permit-or-deny" { value 0; - } - enum next { description - "Proceed evaluating next route map entry per sequence."; - value 1; + "End route map evaluation and return"; } - enum goto { + enum "next" { + value 1; description - "Go to route map entry with the provided sequence number."; + "Proceed evaluating next route map entry per sequence"; + } + enum "goto" { value 2; + description + "Go to route map entry with the provided sequence number"; } } default "permit-or-deny"; + description + "What do to after route map successful match, set and call"; } leaf goto-value { when "../exit-policy = 'goto'"; - description - "Sequence number to jump (when using `goto` exit policy)."; - mandatory true; type route-map-sequence; + mandatory true; + description + "Sequence number to jump (when using `goto` exit policy)"; } list match-condition { key "condition"; description - "Route map match conditions."; + "Route map match conditions"; leaf condition { - description "Match condition."; - type enumeration { - enum interface { - description "Match interface."; - value 0; - } - enum ipv4-address-list { - description "Match an IPv4 access-list."; - value 1; - } - enum ipv4-prefix-list { - description "Match an IPv4 prefix-list."; - value 2; - } - enum ipv4-next-hop-list { - description "Match an IPv4 next-hop."; - value 3; - } - enum ipv4-next-hop-prefix-list { - description "Match an IPv4 next-hop prefix list."; - value 4; - } - enum ipv4-next-hop-type { - description "Match an IPv4 next-hop type."; - value 5; - } - enum ipv6-address-list { - description "Match an IPv6 access-list."; - value 6; - } - enum ipv6-prefix-list { - description "Match an IPv6 prefix-list."; - value 7; - } - enum ipv6-next-hop-type { - description "Match an IPv6 next-hop type."; - value 8; - } - enum metric { - description "Match a route metric."; - value 9; - } - enum tag { - description "Match a route tag."; - value 10; - } - /* zebra specific conditions. */ - enum ipv4-prefix-length { - description "Match IPv4 prefix length."; - value 100; - } - enum ipv6-prefix-length { - description "Match IPv6 prefix length."; - value 101; - } - enum ipv4-next-hop-prefix-length { - description "Match next-hop prefix length."; - value 102; - } - enum source-protocol { - description "Match source protocol."; - value 103; - } - enum source-instance { - description "Match source protocol instance."; - value 104; - } + type identityref { + base rmap-match-type; } - } - - choice condition-value { description - "Value to match (interpretation depends on condition type)."; - mandatory true; - case interface { - when "./condition = 'interface'"; - leaf interface { - type frr-interface:interface-ref; - } - } - - case list-name { - when "./condition = 'ipv4-address-list' or - ./condition = 'ipv4-prefix-list' or - ./condition = 'ipv4-next-hop-list' or - ./condition = 'ipv4-next-hop-prefix-list' or - ./condition = 'ipv6-address-list' or - ./condition = 'ipv6-prefix-list'"; - leaf list-name { - type filter:access-list-name; - } - } - - case ipv4-next-hop-type { - when "./condition = 'ipv4-next-hop-type'"; - leaf ipv4-next-hop-type { - type enumeration { - enum blackhole { - value 0; - } - } - } - } - - case ipv6-next-hop-type { - when "./condition = 'ipv6-next-hop-type'"; - leaf ipv6-next-hop-type { - type enumeration { - enum blackhole { - value 0; - } - } - } - } - - case metric { - when "./condition = 'metric'"; - leaf metric { - type uint32 { - range "1..4294967295"; - } - } - } - - case tag { - when "./condition = 'tag'"; - leaf tag { - type uint32 { - range "1..4294967295"; - } - } - } + "Match condition"; } + + uses rmap-match-condition; } list set-action { - description "Route map set actions."; - key "action"; - + description + "Route map set actions"; leaf action { - description "Action to do when the route map matches."; - type enumeration { - enum ipv4-next-hop { - description "Set IPv4 address of the next hop."; - value 0; - } - enum ipv6-next-hop { - description "Set IPv6 address of the next hop."; - value 1; - } - enum metric { - description "Set prefix/route metric."; - value 2; - } - enum tag { - description "Set tag."; - value 3; - } - /* zebra specific conditions. */ - enum source { - description "Set source address for route."; - value 100; - } + type identityref { + base rmap-set-type; } - } - - choice action-value { description - "Value to set (interpretation depends on action-type)."; - case ipv4-address { - when "./action = 'ipv4-next-hop'"; - leaf ipv4-address { - description "IPv4 address."; - type inet:ipv4-address; - } - } - - case ipv6-address { - when "./action = 'ipv6-next-hop'"; - leaf ipv6-address { - description "IPv6 address."; - type inet:ipv6-address; - } - } - - case metric { - when "./action = 'metric'"; - choice metric-value { - description "Metric to set or use."; - case value { - leaf value { - description "Use the following metric value."; - type uint32 { - range "0..4294967295"; - } - } - } - - case add-metric { - leaf add-metric { - description "Add value to metric."; - type uint32 { - range "0..4294967295"; - } - } - } - - case subtract-metric { - leaf subtract-metric { - description "Subtract value from metric."; - type uint32 { - range "0..4294967295"; - } - } - } - - case use-round-trip-time { - leaf use-round-trip-time { - description "Use the round trip time as metric."; - type boolean; - } - } - - case add-round-trip-time { - leaf add-round-trip-time { - description "Add round trip time to metric."; - type boolean; - } - } - - case subtract-round-trip-time { - leaf subtract-round-trip-time { - description "Subtract round trip time to metric."; - type boolean; - } - } - } - } - - case tag { - when "./action = 'tag'"; - leaf tag { - description "Tag value."; - type uint32 { - range "0..4294967295"; - } - } - } + "Action to do when the route map matches"; } + + uses rmap-set-action; } } } diff --git a/yang/frr-zebra-route-map.yang b/yang/frr-zebra-route-map.yang new file mode 100644 index 0000000000..91f4c87e33 --- /dev/null +++ b/yang/frr-zebra-route-map.yang @@ -0,0 +1,126 @@ +module frr-zebra-route-map { + yang-version 1.1; + namespace "http://frrouting.org/yang/zebra-route-map"; + prefix frr-zebra-route-map; + + import ietf-inet-types { + prefix inet; + } + + import frr-route-map { + prefix frr-route-map; + } + + import frr-route-types { + prefix frr-route-types; + } + + organization + "Free Range Routing"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> + FRR Development List: <mailto:dev@lists.frrouting.org>"; + description + "This module defines zebra route map settings"; + + revision 2020-01-02 { + description + "Initial revision"; + } + + identity ipv4-prefix-length { + base frr-route-map:rmap-match-type; + description + "Match IPv4 address prefix length"; + } + + identity ipv4-next-hop-prefix-length { + base frr-route-map:rmap-match-type; + description + "Match IPv4 next-hop address prefix length"; + } + + identity ipv6-prefix-length { + base frr-route-map:rmap-match-type; + description + "Match IPv6 address prefix length"; + } + + identity source-instance { + base frr-route-map:rmap-match-type; + description + "Match the protocol's instance number"; + } + + identity source-protocol { + base frr-route-map:rmap-match-type; + description + "Match protocol via which the route was learnt"; + } + + identity src-address { + base frr-route-map:rmap-set-type; + description + "Set IPv4/IPv6 source address for route"; + } + + augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:rmap-match-condition/frr-route-map:match-condition" { + case ipv4-prefix-length { + when "derived-from-or-self(../condition, 'ipv4-prefix-length') or " + + "derived-from-or-self(../condition, 'ipv4-next-hop-prefix-length')"; + leaf ipv4-prefix-length { + type uint8 { + range "0..32"; + } + } + } + + case ipv6-prefix-length { + when "derived-from-or-self(../condition, 'ipv6-prefix-length')"; + leaf ipv6-prefix-length { + type uint8 { + range "0..128"; + } + } + } + + case source-instance { + when "derived-from-or-self(../condition, 'source-instance')"; + leaf source-instance { + type uint8 { + range "0..255"; + } + } + } + + case source-protocol { + when "derived-from-or-self(../condition, 'source-protocol')"; + leaf source-protocol { + type frr-route-types:frr-route-types; + } + } + } + + augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:rmap-set-action/frr-route-map:set-action" { + case src-address { + when "derived-from-or-self(../action, 'src-address')"; + choice src-address { + description + "Value of the source address"; + case ipv4-src-address { + leaf ipv4-src-address { + type inet:ipv4-address; + mandatory true; + } + } + + case ipv6-src-address { + leaf ipv6-src-address { + type inet:ipv6-address; + mandatory true; + } + } + } + } + } +} diff --git a/yang/frr-zebra.yang b/yang/frr-zebra.yang index 782e0a4b74..5c2560837f 100644 --- a/yang/frr-zebra.yang +++ b/yang/frr-zebra.yang @@ -597,6 +597,7 @@ module frr-zebra { grouping ribs { container ribs { + config false; description "RIBs supported by FRR."; list rib { @@ -617,7 +618,6 @@ module frr-zebra { list route { key "prefix"; - config false; leaf prefix { type inet:ip-prefix; description @@ -2175,65 +2175,4 @@ module frr-zebra { } // End of operational / state container } - - // End interface model augmentation - - augment "/frr-route-map:lib" - + "/frr-route-map:route-map" - + "/frr-route-map:entry" - + "/frr-route-map:match-condition" - + "/frr-route-map:condition-value" { - case ipv4-prefix-length { - when "./condition = 'ipv4-prefix-length' or - ./condition = 'ipv4-next-hop-prefix-length'"; - leaf ipv4-prefix-length { - type uint8 { - range "0..32"; - } - } - } - case ipv6-prefix-length { - when "./condition = 'ipv6-prefix-length'"; - leaf ipv6-prefix-length { - type uint8 { - range "0..128"; - } - } - } - case source-protocol { - when "./condition = 'source-protocol'"; - leaf source-protocol { - type frr-route-types:frr-route-types; - } - } - case source-instance { - when "./condition = 'source-instance'"; - leaf source-instance { - type uint8 { - range "0..255"; - } - } - } - } - - augment "/frr-route-map:lib" - + "/frr-route-map:route-map" - + "/frr-route-map:entry" - + "/frr-route-map:set-action" - + "/frr-route-map:action-value" { - case source-v4 { - when "./action = 'source'"; - leaf source-v4 { - description "IPv4 address"; - type inet:ipv4-address; - } - } - case source-v6 { - when "./action = 'source'"; - leaf source-v6 { - description "IPv6 address"; - type inet:ipv6-address; - } - } - } } diff --git a/yang/subdir.am b/yang/subdir.am index 47fc508901..a2243fb8e4 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -25,6 +25,11 @@ dist_yangmodels_DATA += yang/frr-nexthop.yang dist_yangmodels_DATA += yang/frr-test-module.yang dist_yangmodels_DATA += yang/frr-interface.yang dist_yangmodels_DATA += yang/frr-route-map.yang +dist_yangmodels_DATA += yang/frr-zebra-route-map.yang +dist_yangmodels_DATA += yang/frr-ospf-route-map.yang +dist_yangmodels_DATA += yang/frr-ospf6-route-map.yang +dist_yangmodels_DATA += yang/frr-bgp-filter.yang +dist_yangmodels_DATA += yang/frr-bgp-route-map.yang dist_yangmodels_DATA += yang/frr-vrf.yang dist_yangmodels_DATA += yang/frr-route-types.yang dist_yangmodels_DATA += yang/frr-routing.yang @@ -86,3 +91,9 @@ endif if PATHD dist_yangmodels_DATA += yang/frr-pathd.yang endif + +CLEANFILES += \ + yang/*.c \ + yang/ietf/*.c \ + yang/confd/*.c \ + # diff --git a/zebra/connected.c b/zebra/connected.c index dd8fab5e4e..6f405ca1bb 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -29,7 +29,6 @@ #include "table.h" #include "log.h" #include "memory.h" -#include "zebra_memory.h" #include "vty.h" #include "zebra/debug.h" diff --git a/zebra/debug.c b/zebra/debug.c index 21fa765c63..05eed0d26e 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -94,8 +94,11 @@ DEFUN_NOSH (show_debugging_zebra, vty_out(vty, " Zebra detailed next-hop tracking debugging is on\n"); else if (IS_ZEBRA_DEBUG_NHT) vty_out(vty, " Zebra next-hop tracking debugging is on\n"); - if (IS_ZEBRA_DEBUG_MPLS) + if (IS_ZEBRA_DEBUG_MPLS_DETAIL) + vty_out(vty, " Zebra detailed MPLS debugging is on\n"); + else if (IS_ZEBRA_DEBUG_MPLS) vty_out(vty, " Zebra MPLS debugging is on\n"); + if (IS_ZEBRA_DEBUG_VXLAN) vty_out(vty, " Zebra VXLAN debugging is on\n"); if (IS_ZEBRA_DEBUG_PW) @@ -159,14 +162,19 @@ DEFUN (debug_zebra_nht, return CMD_SUCCESS; } -DEFUN (debug_zebra_mpls, +DEFPY (debug_zebra_mpls, debug_zebra_mpls_cmd, - "debug zebra mpls", + "debug zebra mpls [detailed$detail]", DEBUG_STR "Zebra configuration\n" - "Debug option set for zebra MPLS LSPs\n") + "Debug option set for zebra MPLS LSPs\n" + "Debug option for detailed info\n") { zebra_debug_mpls = ZEBRA_DEBUG_MPLS; + + if (detail) + zebra_debug_mpls |= ZEBRA_DEBUG_MPLS_DETAILED; + return CMD_SUCCESS; } @@ -422,11 +430,12 @@ DEFUN (no_debug_zebra_nht, DEFUN (no_debug_zebra_mpls, no_debug_zebra_mpls_cmd, - "no debug zebra mpls", + "no debug zebra mpls [detailed]", NO_STR DEBUG_STR "Zebra configuration\n" - "Debug option set for zebra MPLS LSPs\n") + "Debug option set for zebra MPLS LSPs\n" + "Debug option for zebra detailed info\n") { zebra_debug_mpls = 0; return CMD_SUCCESS; @@ -628,10 +637,14 @@ static int config_write_debug(struct vty *vty) write++; } - if (IS_ZEBRA_DEBUG_MPLS) { + if (IS_ZEBRA_DEBUG_MPLS_DETAIL) { + vty_out(vty, "debug zebra mpls detailed\n"); + write++; + } else if (IS_ZEBRA_DEBUG_MPLS) { vty_out(vty, "debug zebra mpls\n"); write++; } + if (IS_ZEBRA_DEBUG_VXLAN) { vty_out(vty, "debug zebra vxlan\n"); write++; diff --git a/zebra/debug.h b/zebra/debug.h index 86506846ad..dc44367d01 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -48,7 +48,8 @@ extern "C" { #define ZEBRA_DEBUG_NHT 0x01 #define ZEBRA_DEBUG_NHT_DETAILED 0x02 -#define ZEBRA_DEBUG_MPLS 0x01 +#define ZEBRA_DEBUG_MPLS 0x01 +#define ZEBRA_DEBUG_MPLS_DETAILED 0x02 #define ZEBRA_DEBUG_VXLAN 0x01 @@ -93,6 +94,8 @@ extern "C" { #define IS_ZEBRA_DEBUG_NHT_DETAILED (zebra_debug_nht & ZEBRA_DEBUG_NHT_DETAILED) #define IS_ZEBRA_DEBUG_MPLS (zebra_debug_mpls & ZEBRA_DEBUG_MPLS) +#define IS_ZEBRA_DEBUG_MPLS_DETAIL \ + (zebra_debug_mpls & ZEBRA_DEBUG_MPLS_DETAILED) #define IS_ZEBRA_DEBUG_VXLAN (zebra_debug_vxlan & ZEBRA_DEBUG_VXLAN) #define IS_ZEBRA_DEBUG_PW (zebra_debug_pw & ZEBRA_DEBUG_PW) diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c index 8bec256355..14d8ac442e 100644 --- a/zebra/if_ioctl.c +++ b/zebra/if_ioctl.c @@ -29,7 +29,6 @@ #include "ioctl.h" #include "connected.h" #include "memory.h" -#include "zebra_memory.h" #include "log.h" #include "vrf.h" #include "vty.h" diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 98bde4b3c0..6aaf9d94f3 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -44,7 +44,6 @@ #include "connected.h" #include "table.h" #include "memory.h" -#include "zebra_memory.h" #include "rib.h" #include "thread.h" #include "privs.h" @@ -1662,8 +1661,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) else if (IS_ZEBRA_IF_VXLAN(ifp)) zebra_l2_vxlanif_del(ifp); - if (!IS_ZEBRA_IF_VRF(ifp)) - if_delete_update(ifp); + if_delete_update(ifp); } return 0; diff --git a/zebra/if_sysctl.c b/zebra/if_sysctl.c index 695cef1995..38729c8d38 100644 --- a/zebra/if_sysctl.c +++ b/zebra/if_sysctl.c @@ -28,7 +28,6 @@ #include "prefix.h" #include "connected.h" #include "memory.h" -#include "zebra_memory.h" #include "ioctl.h" #include "log.h" #include "interface.h" diff --git a/zebra/interface.c b/zebra/interface.c index c679e090ca..4b708496a1 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -28,7 +28,6 @@ #include "prefix.h" #include "command.h" #include "memory.h" -#include "zebra_memory.h" #include "ioctl.h" #include "connected.h" #include "log.h" @@ -899,7 +898,8 @@ void if_nbr_mac_to_ipv4ll_neigh_update(struct interface *ifp, * Remove and re-add any existing neighbor entry for this address, * since Netlink doesn't currently offer update message types. */ - kernel_neigh_update(0, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id); + kernel_neigh_update(0, ifp->ifindex, (void *)&ipv4_ll.s_addr, mac, 6, + ns_id, AF_INET, true); /* Add new neighbor entry. * @@ -911,8 +911,8 @@ void if_nbr_mac_to_ipv4ll_neigh_update(struct interface *ifp, * they'll be useless to us. */ if (add) - kernel_neigh_update(add, ifp->ifindex, ipv4_ll.s_addr, mac, 6, - ns_id); + kernel_neigh_update(add, ifp->ifindex, (void *)&ipv4_ll.s_addr, + mac, 6, ns_id, AF_INET, true); memcpy(&zif->neigh_mac[0], &mac[0], 6); diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c index 52f485dd17..28db2ad87d 100644 --- a/zebra/irdp_interface.c +++ b/zebra/irdp_interface.c @@ -34,7 +34,6 @@ #include "prefix.h" #include "command.h" #include "memory.h" -#include "zebra_memory.h" #include "stream.h" #include "ioctl.h" #include "connected.h" diff --git a/zebra/irdp_main.c b/zebra/irdp_main.c index 6a943a2e2b..600fc3f2fc 100644 --- a/zebra/irdp_main.c +++ b/zebra/irdp_main.c @@ -42,7 +42,6 @@ #include "prefix.h" #include "command.h" #include "memory.h" -#include "zebra_memory.h" #include "stream.h" #include "ioctl.h" #include "connected.h" diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c index 6134df9c41..7d67c42a79 100644 --- a/zebra/irdp_packet.c +++ b/zebra/irdp_packet.c @@ -54,7 +54,6 @@ #include "zclient.h" #include "lib_errors.h" -#include "zebra_memory.h" #include "zebra/interface.h" #include "zebra/rtadv.h" #include "zebra/rib.h" diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 9e655ab266..adb61023c1 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -29,7 +29,6 @@ #include "connected.h" #include "table.h" #include "memory.h" -#include "zebra_memory.h" #include "rib.h" #include "thread.h" #include "privs.h" @@ -1336,6 +1335,9 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: + case DPLANE_OP_NEIGH_TABLE_UPDATE: return netlink_put_neigh_update_msg(bth, ctx); case DPLANE_OP_RULE_ADD: diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index adbdf54c1f..03884a9168 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -32,7 +32,6 @@ #include "sockunion.h" #include "connected.h" #include "memory.h" -#include "zebra_memory.h" #include "ioctl.h" #include "log.h" #include "table.h" diff --git a/zebra/main.c b/zebra/main.c index f2fde35f0e..3f75b222ba 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -26,7 +26,6 @@ #include "thread.h" #include "filter.h" #include "memory.h" -#include "zebra_memory.h" #include "prefix.h" #include "log.h" #include "plist.h" @@ -261,6 +260,7 @@ static const struct frr_yang_module_info *const zebra_yang_modules[] = { &frr_zebra_info, &frr_vrf_info, &frr_routing_info, + &frr_zebra_route_map_info, }; FRR_DAEMON_INFO( diff --git a/zebra/redistribute.c b/zebra/redistribute.c index ac60d09ecc..9e675011ee 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -41,7 +41,6 @@ #include "zebra/debug.h" #include "zebra/router-id.h" #include "zebra/zapi_msg.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_vxlan.h" #include "zebra/zebra_errors.h" diff --git a/zebra/rib.h b/zebra/rib.h index 564e27497d..e7676a1324 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -23,6 +23,7 @@ #define _ZEBRA_RIB_H #include "zebra.h" +#include "memory.h" #include "hook.h" #include "typesafe.h" #include "linklist.h" @@ -41,6 +42,10 @@ extern "C" { #endif +DECLARE_MGROUP(ZEBRA); + +DECLARE_MTYPE(RE); + enum rnh_type { RNH_NEXTHOP_TYPE, RNH_IMPORT_CHECK_TYPE }; PREDECL_LIST(rnh_list); diff --git a/zebra/router-id.c b/zebra/router-id.c index ac21978ee8..3b556c92b5 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -29,7 +29,6 @@ #include "stream.h" #include "command.h" #include "memory.h" -#include "zebra_memory.h" #include "ioctl.h" #include "connected.h" #include "network.h" diff --git a/zebra/rt.h b/zebra/rt.h index 48f1df2868..daaa926a7d 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -68,8 +68,11 @@ kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx); #endif /* !HAVE_NETLINK */ -extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, - int llalen, ns_id_t ns_id); +extern int kernel_neigh_update(int cmd, int ifindex, void *addr, char *lla, + int llalen, ns_id_t ns_id, uint8_t family, + bool permanent); +extern int kernel_neigh_register(vrf_id_t vrf_id, struct zserv *client, + bool reg); extern int kernel_interface_set_master(struct interface *master, struct interface *slave); @@ -78,6 +81,9 @@ extern int mpls_kernel_init(void); extern uint32_t kernel_get_speed(struct interface *ifp, int *error); extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute); +extern int kernel_configure_if_link(struct interface *ifp, + struct interface *link_ifp, ns_id_t ns_id); + /* * Southbound Initialization routines to get initial starting * state. diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index fdeef2c88c..d2ec7da57c 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -41,7 +41,6 @@ #include "connected.h" #include "table.h" #include "memory.h" -#include "zebra_memory.h" #include "rib.h" #include "thread.h" #include "privs.h" @@ -186,6 +185,10 @@ static uint16_t neigh_state_to_netlink(uint16_t dplane_state) state |= NUD_PROBE; if (dplane_state & DPLANE_NUD_INCOMPLETE) state |= NUD_INCOMPLETE; + if (dplane_state & DPLANE_NUD_PERMANENT) + state |= NUD_PERMANENT; + if (dplane_state & DPLANE_NUD_FAILED) + state |= NUD_FAILED; return state; } @@ -1538,10 +1541,10 @@ static void _netlink_mpls_debug(int cmd, uint32_t label, const char *routedesc) routedesc, nl_msg_type_to_str(cmd), label); } -static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, - int llalen, ns_id_t ns_id) +static int netlink_neigh_update(int cmd, int ifindex, void *addr, char *lla, + int llalen, ns_id_t ns_id, uint8_t family, + bool permanent, uint8_t protocol) { - uint8_t protocol = RTPROT_ZEBRA; struct { struct nlmsghdr n; struct ndmsg ndm; @@ -1557,15 +1560,24 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, req.n.nlmsg_type = cmd; // RTM_NEWNEIGH or RTM_DELNEIGH req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; - req.ndm.ndm_family = AF_INET; - req.ndm.ndm_state = NUD_PERMANENT; + req.ndm.ndm_family = family; req.ndm.ndm_ifindex = ifindex; req.ndm.ndm_type = RTN_UNICAST; + if (cmd == RTM_NEWNEIGH) { + if (!permanent) + req.ndm.ndm_state = NUD_REACHABLE; + else + req.ndm.ndm_state = NUD_PERMANENT; + } else + req.ndm.ndm_state = NUD_FAILED; nl_attr_put(&req.n, sizeof(req), NDA_PROTOCOL, &protocol, sizeof(protocol)); - nl_attr_put32(&req.n, sizeof(req), NDA_DST, addr); - nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, lla, llalen); + req.ndm.ndm_type = RTN_UNICAST; + nl_attr_put(&req.n, sizeof(req), NDA_DST, addr, + family2addrsize(family)); + if (lla) + nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, lla, llalen); return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0); @@ -2680,11 +2692,12 @@ int netlink_nexthop_read(struct zebra_ns *zns) } -int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, - int llalen, ns_id_t ns_id) +int kernel_neigh_update(int add, int ifindex, void *addr, char *lla, int llalen, + ns_id_t ns_id, uint8_t family, bool permanent) { return netlink_neigh_update(add ? RTM_NEWNEIGH : RTM_DELNEIGH, ifindex, - addr, lla, llalen, ns_id); + addr, lla, llalen, ns_id, family, permanent, + RTPROT_ZEBRA); } /** @@ -2695,7 +2708,9 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, * entry. * @ctx: Dataplane context * @cmd: Netlink command (RTM_NEWNEIGH or RTM_DELNEIGH) - * @mac: A neighbor cache link layer address + * @lla: A pointer to neighbor cache link layer address + * @llalen: Length of the pointer to neighbor cache link layer + * address * @ip: A neighbor cache n/w layer destination address * In the case of bridge FDB, this represnts the remote * VTEP IP. @@ -2707,18 +2722,18 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, * @state: NUD_* states * @data: data buffer pointer * @datalen: total amount of data buffer space + * @protocol: protocol information * * Return: 0 when the msg doesn't fit entirely in the buffer * otherwise the number of bytes written to buf. */ static ssize_t netlink_neigh_update_msg_encode( - const struct zebra_dplane_ctx *ctx, int cmd, const struct ethaddr *mac, - const struct ipaddr *ip, bool replace_obj, uint8_t family, uint8_t type, - uint8_t flags, uint16_t state, uint32_t nhg_id, bool nfy, + const struct zebra_dplane_ctx *ctx, int cmd, const void *lla, + int llalen, const struct ipaddr *ip, bool replace_obj, uint8_t family, + uint8_t type, uint8_t flags, uint16_t state, uint32_t nhg_id, bool nfy, uint8_t nfy_flags, bool ext, uint32_t ext_flags, void *data, - size_t datalen) + size_t datalen, uint8_t protocol) { - uint8_t protocol = RTPROT_ZEBRA; struct { struct nlmsghdr n; struct ndmsg ndm; @@ -2750,8 +2765,8 @@ static ssize_t netlink_neigh_update_msg_encode( sizeof(protocol))) return 0; - if (mac) { - if (!nl_attr_put(&req->n, datalen, NDA_LLADDR, mac, 6)) + if (lla) { + if (!nl_attr_put(&req->n, datalen, NDA_LLADDR, lla, llalen)) return 0; } @@ -2815,12 +2830,17 @@ netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd, void *buf, size_t buflen) { struct ethaddr dst_mac = {.octet = {0}}; + int proto = RTPROT_ZEBRA; + + if (dplane_ctx_get_type(ctx) != 0) + proto = zebra2proto(dplane_ctx_get_type(ctx)); return netlink_neigh_update_msg_encode( - ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false, - PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT), 0 /*nhg*/, - false /*nfy*/, 0 /*nfy_flags*/, false /*ext*/, 0 /*ext_flags*/, - buf, buflen); + ctx, cmd, (const void *)&dst_mac, ETH_ALEN, + dplane_ctx_neigh_get_ipaddr(ctx), false, PF_BRIDGE, 0, NTF_SELF, + (NUD_NOARP | NUD_PERMANENT), 0 /*nhg*/, false /*nfy*/, + 0 /*nfy_flags*/, false /*ext*/, 0 /*ext_flags*/, buf, buflen, + proto); } #ifndef NDA_RTA @@ -3186,6 +3206,10 @@ ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data, uint32_t update_flags; bool nfy = false; uint8_t nfy_flags = 0; + int proto = RTPROT_ZEBRA; + + if (dplane_ctx_get_type(ctx) != 0) + proto = zebra2proto(dplane_ctx_get_type(ctx)); cmd = dplane_ctx_get_op(ctx) == DPLANE_OP_MAC_INSTALL ? RTM_NEWNEIGH : RTM_DELNEIGH; @@ -3252,9 +3276,10 @@ ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data, } total = netlink_neigh_update_msg_encode( - ctx, cmd, dplane_ctx_mac_get_addr(ctx), &vtep_ip, true, - AF_BRIDGE, 0, flags, state, nhg_id, nfy, nfy_flags, - false /*ext*/, 0 /*ext_flags*/, data, datalen); + ctx, cmd, (const void *)dplane_ctx_mac_get_addr(ctx), ETH_ALEN, + &vtep_ip, true, AF_BRIDGE, 0, flags, state, nhg_id, nfy, + nfy_flags, false /*ext*/, 0 /*ext_flags*/, data, datalen, + proto); return total; } @@ -3308,6 +3333,8 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) bool local_inactive; uint32_t ext_flags = 0; bool dp_static = false; + int l2_len = 0; + int cmd; ndm = NLMSG_DATA(h); @@ -3349,6 +3376,43 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) if (h->nlmsg_type == RTM_NEWNEIGH && !(ndm->ndm_state & NUD_VALID)) netlink_handle_5549(ndm, zif, ifp, &ip, true); + /* we send link layer information to client: + * - nlmsg_type = RTM_DELNEIGH|NEWNEIGH|GETNEIGH + * - struct ipaddr ( for DEL and GET) + * - struct ethaddr mac; (for NEW) + */ + if (h->nlmsg_type == RTM_NEWNEIGH) + cmd = ZEBRA_NHRP_NEIGH_ADDED; + else if (h->nlmsg_type == RTM_GETNEIGH) + cmd = ZEBRA_NHRP_NEIGH_GET; + else if (h->nlmsg_type == RTM_DELNEIGH) + cmd = ZEBRA_NHRP_NEIGH_REMOVED; + else { + zlog_debug("%s(): unknown nlmsg type %u", __func__, + h->nlmsg_type); + return 0; + } + if (tb[NDA_LLADDR]) { + /* copy LLADDR information */ + l2_len = RTA_PAYLOAD(tb[NDA_LLADDR]); + memcpy(&mac, RTA_DATA(tb[NDA_LLADDR]), l2_len); + } + if (l2_len == IPV4_MAX_BYTELEN || l2_len == 0) { + union sockunion link_layer_ipv4; + + if (l2_len) { + sockunion_family(&link_layer_ipv4) = AF_INET; + memcpy((void *)sockunion_get_addr(&link_layer_ipv4), + &mac, l2_len); + } else + sockunion_family(&link_layer_ipv4) = AF_UNSPEC; + zsend_nhrp_neighbor_notify(cmd, ifp, &ip, ndm->ndm_state, + &link_layer_ipv4); + } + + if (h->nlmsg_type == RTM_GETNEIGH) + return 0; + /* The neighbor is present on an SVI. From this, we locate the * underlying * bridge because we're only interested in neighbors on a VxLAN bridge. @@ -3616,7 +3680,8 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id) int len; struct ndmsg *ndm; - if (!(h->nlmsg_type == RTM_NEWNEIGH || h->nlmsg_type == RTM_DELNEIGH)) + if (!(h->nlmsg_type == RTM_NEWNEIGH || h->nlmsg_type == RTM_DELNEIGH + || h->nlmsg_type == RTM_GETNEIGH)) return 0; /* Length validity. */ @@ -3657,19 +3722,42 @@ static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd, void *buf, size_t buflen) { const struct ipaddr *ip; - const struct ethaddr *mac; + const struct ethaddr *mac = NULL; + const struct ipaddr *link_ip = NULL; + const void *link_ptr = NULL; + char buf2[ETHER_ADDR_STRLEN]; + + int llalen; uint8_t flags; uint16_t state; uint8_t family; uint32_t update_flags; uint32_t ext_flags = 0; bool ext = false; + int proto = RTPROT_ZEBRA; + + if (dplane_ctx_get_type(ctx) != 0) + proto = zebra2proto(dplane_ctx_get_type(ctx)); ip = dplane_ctx_neigh_get_ipaddr(ctx); - mac = dplane_ctx_neigh_get_mac(ctx); - if (is_zero_mac(mac)) - mac = NULL; + if (dplane_ctx_get_op(ctx) == DPLANE_OP_NEIGH_IP_INSTALL + || dplane_ctx_get_op(ctx) == DPLANE_OP_NEIGH_IP_DELETE) { + link_ip = dplane_ctx_neigh_get_link_ip(ctx); + llalen = IPADDRSZ(link_ip); + link_ptr = (const void *)&(link_ip->ip.addr); + ipaddr2str(link_ip, buf2, sizeof(buf2)); + } else { + mac = dplane_ctx_neigh_get_mac(ctx); + llalen = ETH_ALEN; + link_ptr = (const void *)mac; + if (is_zero_mac(mac)) + mac = NULL; + if (mac) + prefix_mac2str(mac, buf2, sizeof(buf2)); + else + snprintf(buf2, sizeof(buf2), "null"); + } update_flags = dplane_ctx_neigh_get_update_flags(ctx); flags = neigh_flags_to_netlink(dplane_ctx_neigh_get_flags(ctx)); state = neigh_state_to_netlink(dplane_ctx_neigh_get_state(ctx)); @@ -3683,7 +3771,7 @@ static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, */ if (update_flags & DPLANE_NEIGH_WAS_STATIC) ext = true; - } else { + } else if (!(update_flags & DPLANE_NEIGH_NO_EXTENSION)) { ext = true; /* local neigh */ if (update_flags & DPLANE_NEIGH_SET_STATIC) @@ -3691,15 +3779,63 @@ static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, } if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "Tx %s family %s IF %s(%u) Neigh %pIA MAC %pEA flags 0x%x state 0x%x %sext_flags 0x%x", + "Tx %s family %s IF %s(%u) Neigh %pIA %s %s flags 0x%x state 0x%x %sext_flags 0x%x", nl_msg_type_to_str(cmd), nl_family_to_str(family), dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx), - ip, mac, flags, state, ext ? "ext " : "", ext_flags); + ip, link_ip ? "Link " : "MAC ", buf2, flags, state, + ext ? "ext " : "", ext_flags); return netlink_neigh_update_msg_encode( - ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state, - 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, ext, ext_flags, buf, - buflen); + ctx, cmd, link_ptr, llalen, ip, true, family, RTN_UNICAST, + flags, state, 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, ext, + ext_flags, buf, buflen, proto); +} + +static int netlink_neigh_table_update_ctx(const struct zebra_dplane_ctx *ctx, + void *data, size_t datalen) +{ + struct { + struct nlmsghdr n; + struct ndtmsg ndtm; + char buf[]; + } *req = data; + struct rtattr *nest; + uint8_t family; + ifindex_t idx; + uint32_t val; + + if (datalen < sizeof(*req)) + return 0; + memset(req, 0, sizeof(*req)); + family = dplane_ctx_neightable_get_family(ctx); + idx = dplane_ctx_get_ifindex(ctx); + + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg)); + req->n.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE; + req->n.nlmsg_type = RTM_SETNEIGHTBL; + req->ndtm.ndtm_family = family; + + nl_attr_put(&req->n, datalen, NDTA_NAME, + family == AF_INET ? "arp_cache" : "ndisc_cache", 10); + nest = nl_attr_nest(&req->n, datalen, NDTA_PARMS); + if (nest == NULL) + return 0; + if (!nl_attr_put(&req->n, datalen, NDTPA_IFINDEX, &idx, sizeof(idx))) + return 0; + val = dplane_ctx_neightable_get_app_probes(ctx); + if (!nl_attr_put(&req->n, datalen, NDTPA_APP_PROBES, &val, sizeof(val))) + return 0; + val = dplane_ctx_neightable_get_mcast_probes(ctx); + if (!nl_attr_put(&req->n, datalen, NDTPA_MCAST_PROBES, &val, + sizeof(val))) + return 0; + val = dplane_ctx_neightable_get_ucast_probes(ctx); + if (!nl_attr_put(&req->n, datalen, NDTPA_UCAST_PROBES, &val, + sizeof(val))) + return 0; + nl_attr_nest_end(&req->n, nest); + + return NLMSG_ALIGN(req->n.nlmsg_len); } static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, @@ -3711,9 +3847,11 @@ static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_NEIGH_IP_INSTALL: ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen); break; case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_NEIGH_IP_DELETE: ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH, buf, buflen); break; case DPLANE_OP_VTEP_ADD: @@ -3724,6 +3862,9 @@ static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf, buflen); break; + case DPLANE_OP_NEIGH_TABLE_UPDATE: + ret = netlink_neigh_table_update_ctx(ctx, buf, buflen); + break; default: ret = -1; } diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index a0f401c334..ada828d016 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -362,8 +362,14 @@ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) return ZEBRA_DPLANE_REQUEST_SUCCESS; } -int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, - int llalen, ns_id_t ns_id) +int kernel_neigh_register(vrf_id_t vrf_id, struct zserv *client, bool reg) +{ + /* TODO */ + return 0; +} + +int kernel_neigh_update(int add, int ifindex, void *addr, char *lla, int llalen, + ns_id_t ns_id, uint8_t family, bool permanent) { /* TODO */ return 0; @@ -388,6 +394,12 @@ enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx) return ZEBRA_DPLANE_REQUEST_SUCCESS; } +int kernel_configure_if_link(struct interface *ifp, struct interface *link_ifp, + ns_id_t ns_id) +{ + return 0; +} + extern int kernel_interface_set_master(struct interface *master, struct interface *slave) { diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 8a7a15e46d..8ffb3870fa 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -23,7 +23,6 @@ #include <zebra.h> #include "memory.h" -#include "zebra_memory.h" #include "sockopt.h" #include "thread.h" #include "if.h" diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c index 01a97db8b3..74c6825ba1 100644 --- a/zebra/rtread_sysctl.c +++ b/zebra/rtread_sysctl.c @@ -24,7 +24,6 @@ #if !defined(GNU_LINUX) #include "memory.h" -#include "zebra_memory.h" #include "log.h" #include "vrf.h" diff --git a/zebra/subdir.am b/zebra/subdir.am index 47b18b2c0c..80ea9ac7b8 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -4,7 +4,6 @@ if ZEBRA sbin_PROGRAMS += zebra/zebra -dist_examples_DATA += zebra/zebra.conf.sample vtysh_scan += \ zebra/debug.c \ zebra/interface.c \ @@ -88,7 +87,6 @@ zebra_zebra_SOURCES = \ zebra/zebra_evpn_neigh.c \ zebra/zebra_mlag.c \ zebra/zebra_mlag_vty.c \ - zebra/zebra_memory.c \ zebra/zebra_mpls.c \ zebra/zebra_mpls_netlink.c \ zebra/zebra_mpls_openbsd.c \ @@ -112,6 +110,8 @@ zebra_zebra_SOURCES = \ zebra/zebra_router.c \ zebra/zebra_rnh.c \ zebra/zebra_routemap.c \ + zebra/zebra_routemap_nb.c \ + zebra/zebra_routemap_nb_config.c \ zebra/zebra_srte.c \ zebra/zebra_vrf.c \ zebra/zebra_vty.c \ @@ -158,7 +158,6 @@ noinst_HEADERS += \ zebra/zebra_evpn_vxlan.h \ zebra/zebra_fpm_private.h \ zebra/zebra_l2.h \ - zebra/zebra_memory.h \ zebra/zebra_mlag.h \ zebra/zebra_mlag_vty.h \ zebra/zebra_mpls.h \ @@ -176,6 +175,7 @@ noinst_HEADERS += \ zebra/zebra_pw.h \ zebra/zebra_rnh.h \ zebra/zebra_routemap.h \ + zebra/zebra_routemap_nb.h \ zebra/zebra_router.h \ zebra/zebra_srte.h \ zebra/zebra_vrf.h \ @@ -219,6 +219,7 @@ endif nodist_zebra_zebra_SOURCES = \ yang/frr-zebra.yang.c \ + yang/frr-zebra-route-map.yang.c \ # end zebra_zebra_cumulus_mlag_la_SOURCES = zebra/zebra_mlag_private.c diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 9f5adfa409..e854d7ff3a 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -38,7 +38,6 @@ #include "zebra/zebra_router.h" #include "zebra/rib.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/router-id.h" @@ -62,6 +61,8 @@ #include "zebra/zebra_opaque.h" #include "zebra/zebra_srte.h" +DEFINE_MTYPE_STATIC(ZEBRA, OPAQUE, "Opaque Data"); + static int zapi_nhg_decode(struct stream *s, int cmd, struct zapi_nhg *api_nhg); /* Encoding helpers -------------------------------------------------------- */ @@ -973,6 +974,37 @@ void zsend_ipset_entry_notify_owner(const struct zebra_dplane_ctx *ctx, zserv_send_message(client, s); } +void zsend_nhrp_neighbor_notify(int cmd, struct interface *ifp, + struct ipaddr *ipaddr, int ndm_state, + union sockunion *link_layer_ipv4) +{ + struct stream *s; + struct listnode *node, *nnode; + struct zserv *client; + afi_t afi; + union sockunion ip; + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug("%s: Notifying Neighbor entry (%u)", + __PRETTY_FUNCTION__, cmd); + + sockunion_family(&ip) = ipaddr_family(ipaddr); + afi = family2afi(sockunion_family(&ip)); + memcpy((char *)sockunion_get_addr(&ip), &ipaddr->ip.addr, + family2addrsize(sockunion_family(&ip))); + + for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { + if (!vrf_bitmap_check(client->nhrp_neighinfo[afi], ifp->vrf_id)) + continue; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + zclient_neigh_ip_encode(s, cmd, &ip, link_layer_ipv4, ifp); + stream_putw_at(s, 0, stream_get_endp(s)); + zserv_send_message(client, s); + } +} + + /* Router-id is updated. Send ZEBRA_ROUTER_ID_UPDATE to client. */ int zsend_router_id_update(struct zserv *client, afi_t afi, struct prefix *p, vrf_id_t vrf_id) @@ -2076,6 +2108,11 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) } } +void zapi_opaque_free(struct opaque *opaque) +{ + XFREE(MTYPE_OPAQUE, opaque); +} + static void zread_route_del(ZAPI_HANDLER_ARGS) { struct stream *s; @@ -2271,6 +2308,7 @@ static void zread_vrf_unregister(ZAPI_HANDLER_ARGS) vrf_bitmap_unset(client->redist[afi][i], zvrf_id(zvrf)); vrf_bitmap_unset(client->redist_default[afi], zvrf_id(zvrf)); vrf_bitmap_unset(client->ridinfo[afi], zvrf_id(zvrf)); + vrf_bitmap_unset(client->nhrp_neighinfo[afi], zvrf_id(zvrf)); } } @@ -3161,6 +3199,97 @@ stream_failure: return; } + +static inline void zebra_neigh_register(ZAPI_HANDLER_ARGS) +{ + afi_t afi; + + STREAM_GETW(msg, afi); + if (afi <= AFI_UNSPEC || afi >= AFI_MAX) { + zlog_warn( + "Invalid AFI %u while registering for neighbors notifications", + afi); + goto stream_failure; + } + vrf_bitmap_set(client->nhrp_neighinfo[afi], zvrf_id(zvrf)); +stream_failure: + return; +} + +static inline void zebra_neigh_unregister(ZAPI_HANDLER_ARGS) +{ + afi_t afi; + + STREAM_GETW(msg, afi); + if (afi <= AFI_UNSPEC || afi >= AFI_MAX) { + zlog_warn( + "Invalid AFI %u while unregistering from neighbor notifications", + afi); + goto stream_failure; + } + vrf_bitmap_unset(client->nhrp_neighinfo[afi], zvrf_id(zvrf)); +stream_failure: + return; +} + +static inline void zebra_configure_arp(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + uint8_t fam; + ifindex_t idx; + struct interface *ifp; + + s = msg; + STREAM_GETC(s, fam); + if (fam != AF_INET && fam != AF_INET6) + return; + STREAM_GETL(s, idx); + ifp = if_lookup_by_index_per_ns(zvrf->zns, idx); + if (!ifp) + return; + dplane_neigh_table_update(ifp, fam, 1, 0, 0); +stream_failure: + return; +} + +static inline void zebra_neigh_ip_add(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_neigh_ip api = {}; + int ret; + const struct interface *ifp; + + s = msg; + ret = zclient_neigh_ip_decode(s, &api); + if (ret < 0) + return; + ifp = if_lookup_by_index(api.index, zvrf_id(zvrf)); + if (!ifp) + return; + dplane_neigh_ip_update(DPLANE_OP_NEIGH_IP_INSTALL, ifp, &api.ip_out, + &api.ip_in, api.ndm_state, client->proto); +} + + +static inline void zebra_neigh_ip_del(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_neigh_ip api = {}; + int ret; + struct interface *ifp; + + s = msg; + ret = zclient_neigh_ip_decode(s, &api); + if (ret < 0) + return; + ifp = if_lookup_by_index(api.index, zvrf_id(zvrf)); + if (!ifp) + return; + dplane_neigh_ip_update(DPLANE_OP_NEIGH_IP_DELETE, ifp, &api.ip_out, + &api.ip_in, api.ndm_state, client->proto); +} + + static inline void zread_iptable(ZAPI_HANDLER_ARGS) { struct zebra_pbr_iptable *zpi = @@ -3344,6 +3473,13 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_NHG_ADD] = zread_nhg_add, [ZEBRA_NHG_DEL] = zread_nhg_del, [ZEBRA_ROUTE_NOTIFY_REQUEST] = zread_route_notify_request, + [ZEBRA_EVPN_REMOTE_NH_ADD] = zebra_evpn_proc_remote_nh, + [ZEBRA_EVPN_REMOTE_NH_DEL] = zebra_evpn_proc_remote_nh, + [ZEBRA_NEIGH_IP_ADD] = zebra_neigh_ip_add, + [ZEBRA_NEIGH_IP_DEL] = zebra_neigh_ip_del, + [ZEBRA_NHRP_NEIGH_REGISTER] = zebra_neigh_register, + [ZEBRA_NHRP_NEIGH_UNREGISTER] = zebra_neigh_unregister, + [ZEBRA_CONFIGURE_ARP] = zebra_configure_arp, }; /* diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index 023b9f74a8..0beb3cc100 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -104,6 +104,9 @@ extern int zsend_label_manager_connect_response(struct zserv *client, extern int zsend_sr_policy_notify_status(uint32_t color, struct ipaddr *endpoint, char *name, int status); +extern void zsend_nhrp_neighbor_notify(int cmd, struct interface *ifp, + struct ipaddr *ipaddr, int ndm_state, + union sockunion *link_layer_ipv4); extern int zsend_client_close_notify(struct zserv *client, struct zserv *closed_client); @@ -111,6 +114,8 @@ extern int zsend_client_close_notify(struct zserv *client, int zsend_nhg_notify(uint16_t type, uint16_t instance, uint32_t session_id, uint32_t id, enum zapi_nhg_notify_owner note); +extern void zapi_opaque_free(struct opaque *opaque); + #ifdef __cplusplus } #endif diff --git a/zebra/zebra.conf.sample b/zebra/zebra.conf.sample deleted file mode 100644 index 03042eb083..0000000000 --- a/zebra/zebra.conf.sample +++ /dev/null @@ -1,23 +0,0 @@ -! -*- zebra -*- -! -! zebra sample configuration file -! -hostname Router -password zebra -enable password zebra -! -! Interface's description. -! -!interface lo -! description test of desc. -! -!interface sit0 -! multicast - -! -! Static default route sample. -! -!ip route 0.0.0.0/0 203.181.89.241 -! - -!log file zebra.log diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 6f3e5f0427..c8ee8f9051 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -28,7 +28,6 @@ #include "lib/memory.h" #include "lib/queue.h" #include "lib/zebra.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_router.h" #include "zebra/zebra_dplane.h" #include "zebra/zebra_vxlan_private.h" @@ -221,13 +220,26 @@ struct dplane_mac_info { */ struct dplane_neigh_info { struct ipaddr ip_addr; - struct ethaddr mac; + union { + struct ethaddr mac; + struct ipaddr ip_addr; + } link; uint32_t flags; uint16_t state; uint32_t update_flags; }; /* + * Neighbor Table + */ +struct dplane_neigh_table { + uint8_t family; + uint32_t app_probes; + uint32_t ucast_probes; + uint32_t mcast_probes; +}; + +/* * Policy based routing rule info for the dataplane */ struct dplane_ctx_rule { @@ -314,6 +326,7 @@ struct zebra_dplane_ctx { struct zebra_pbr_ipset_entry entry; struct zebra_pbr_ipset_info info; } ipset_entry; + struct dplane_neigh_table neightable; } u; /* Namespace info, used especially for netlink kernel communication */ @@ -453,6 +466,9 @@ static struct zebra_dplane_globals { _Atomic uint32_t dg_ipset_entry_in; _Atomic uint32_t dg_ipset_entry_errors; + _Atomic uint32_t dg_neightable_in; + _Atomic uint32_t dg_neightable_errors; + /* Dataplane pthread */ struct frr_pthread *dg_pthread; @@ -497,12 +513,11 @@ static enum zebra_dplane_result mac_update_common( vlanid_t vid, const struct ethaddr *mac, struct in_addr vtep_ip, bool sticky, uint32_t nhg_id, uint32_t update_flags); -static enum zebra_dplane_result neigh_update_internal( - enum dplane_op_e op, - const struct interface *ifp, - const struct ethaddr *mac, - const struct ipaddr *ip, - uint32_t flags, uint16_t state, uint32_t update_flags); +static enum zebra_dplane_result +neigh_update_internal(enum dplane_op_e op, const struct interface *ifp, + const void *link, int link_family, + const struct ipaddr *ip, uint32_t flags, uint16_t state, + uint32_t update_flags, int protocol); /* * Public APIs @@ -670,6 +685,8 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_RULE_UPDATE: case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_BR_PORT_UPDATE: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: case DPLANE_OP_NONE: case DPLANE_OP_IPSET_ADD: case DPLANE_OP_IPSET_DELETE: @@ -678,6 +695,8 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_IPSET_ENTRY_ADD: case DPLANE_OP_IPSET_ENTRY_DELETE: break; + case DPLANE_OP_NEIGH_TABLE_UPDATE: + break; case DPLANE_OP_IPTABLE_ADD: case DPLANE_OP_IPTABLE_DELETE: if (ctx->u.iptable.interface_name_list) { @@ -951,6 +970,15 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_IPSET_ENTRY_DELETE: ret = "IPSET_ENTRY_DELETE"; break; + case DPLANE_OP_NEIGH_IP_INSTALL: + ret = "NEIGH_IP_INSTALL"; + break; + case DPLANE_OP_NEIGH_IP_DELETE: + ret = "NEIGH_IP_DELETE"; + break; + case DPLANE_OP_NEIGH_TABLE_UPDATE: + ret = "NEIGH_TABLE_UPDATE"; + break; } return ret; @@ -1712,11 +1740,18 @@ const struct ipaddr *dplane_ctx_neigh_get_ipaddr( return &(ctx->u.neigh.ip_addr); } +const struct ipaddr * +dplane_ctx_neigh_get_link_ip(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return &(ctx->u.neigh.link.ip_addr); +} + const struct ethaddr *dplane_ctx_neigh_get_mac( const struct zebra_dplane_ctx *ctx) { DPLANE_CTX_VALID(ctx); - return &(ctx->u.neigh.mac); + return &(ctx->u.neigh.link.mac); } uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx) @@ -1979,6 +2014,37 @@ uint32_t dplane_intf_extra_get_status(const struct dplane_intf_extra *ptr) return ptr->status; } +uint8_t dplane_ctx_neightable_get_family(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.neightable.family; +} + +uint32_t +dplane_ctx_neightable_get_app_probes(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.neightable.app_probes; +} + +uint32_t +dplane_ctx_neightable_get_ucast_probes(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.neightable.ucast_probes; +} + +uint32_t +dplane_ctx_neightable_get_mcast_probes(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.neightable.mcast_probes; +} + /* * End of interface extra info accessors */ @@ -3437,6 +3503,41 @@ enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp, } /* + * API to configure link local with either MAC address or IP information + */ +enum zebra_dplane_result dplane_neigh_ip_update(enum dplane_op_e op, + const struct interface *ifp, + struct ipaddr *link_ip, + struct ipaddr *ip, + uint32_t ndm_state, int protocol) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + uint16_t state = 0; + uint32_t update_flags; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char buf1[PREFIX_STRLEN], buf2[PREFIX_STRLEN]; + + ipaddr2str(link_ip, buf1, sizeof(buf1)); + ipaddr2str(ip, buf2, sizeof(buf2)); + zlog_debug("init link ctx %s: ifp %s, ip %s link %s", + dplane_op2str(op), ifp->name, buf1, buf2); + } + if (ndm_state == ZEBRA_NEIGH_STATE_REACHABLE) + state = DPLANE_NUD_REACHABLE; + else if (ndm_state == ZEBRA_NEIGH_STATE_FAILED) + state = DPLANE_NUD_FAILED; + + update_flags = DPLANE_NEIGH_NO_EXTENSION; + + result = neigh_update_internal(op, ifp, (const void *)link_ip, + ipaddr_family(link_ip), ip, 0, state, + update_flags, protocol); + + return result; +} + +/* * Enqueue local mac add (or update). */ enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp, @@ -3585,9 +3686,9 @@ enum zebra_dplane_result dplane_rem_neigh_add(const struct interface *ifp, if (was_static) update_flags |= DPLANE_NEIGH_WAS_STATIC; - result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, - ifp, mac, ip, flags, DPLANE_NUD_NOARP, - update_flags); + result = neigh_update_internal( + DPLANE_OP_NEIGH_INSTALL, ifp, (const void *)mac, AF_ETHERNET, + ip, flags, DPLANE_NUD_NOARP, update_flags, 0); return result; } @@ -3619,9 +3720,9 @@ enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp, if (set_router) ntf |= DPLANE_NTF_ROUTER; - result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, - ifp, mac, ip, ntf, - state, update_flags); + result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, ifp, + (const void *)mac, AF_ETHERNET, ip, ntf, + state, update_flags, 0); return result; } @@ -3637,8 +3738,8 @@ enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp, update_flags |= DPLANE_NEIGH_REMOTE; - result = neigh_update_internal(DPLANE_OP_NEIGH_DELETE, - ifp, NULL, ip, 0, 0, update_flags); + result = neigh_update_internal(DPLANE_OP_NEIGH_DELETE, ifp, NULL, + AF_ETHERNET, ip, 0, 0, update_flags, 0); return result; } @@ -3661,8 +3762,8 @@ enum zebra_dplane_result dplane_vtep_add(const struct interface *ifp, SET_IPADDR_V4(&addr); addr.ipaddr_v4 = *ip; - result = neigh_update_internal(DPLANE_OP_VTEP_ADD, - ifp, &mac, &addr, 0, 0, 0); + result = neigh_update_internal(DPLANE_OP_VTEP_ADD, ifp, &mac, + AF_ETHERNET, &addr, 0, 0, 0, 0); return result; } @@ -3686,8 +3787,9 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, SET_IPADDR_V4(&addr); addr.ipaddr_v4 = *ip; - result = neigh_update_internal(DPLANE_OP_VTEP_DELETE, - ifp, &mac, &addr, 0, 0, 0); + result = neigh_update_internal(DPLANE_OP_VTEP_DELETE, ifp, + (const void *)&mac, AF_ETHERNET, &addr, + 0, 0, 0, 0); return result; } @@ -3697,8 +3799,65 @@ enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, { enum zebra_dplane_result result; - result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, ip, - DPLANE_NTF_USE, DPLANE_NUD_INCOMPLETE, 0); + result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, + AF_ETHERNET, ip, DPLANE_NTF_USE, + DPLANE_NUD_INCOMPLETE, 0, 0); + + return result; +} + +enum zebra_dplane_result dplane_neigh_table_update(const struct interface *ifp, + const uint8_t family, + const uint32_t app_probes, + const uint32_t ucast_probes, + const uint32_t mcast_probes) +{ + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret; + struct zebra_dplane_ctx *ctx = NULL; + struct zebra_ns *zns; + enum dplane_op_e op = DPLANE_OP_NEIGH_TABLE_UPDATE; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + zlog_debug("set neigh ctx %s: ifp %s, family %s", + dplane_op2str(op), ifp->name, family2str(family)); + } + + ctx = dplane_ctx_alloc(); + + ctx->zd_op = op; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + ctx->zd_vrf_id = ifp->vrf_id; + + zns = zebra_ns_lookup(ifp->vrf_id); + dplane_ctx_ns_init(ctx, zns, false); + + strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname)); + ctx->zd_ifindex = ifp->ifindex; + + /* Init the neighbor-specific data area */ + memset(&ctx->u.neightable, 0, sizeof(ctx->u.neightable)); + + ctx->u.neightable.family = family; + ctx->u.neightable.app_probes = app_probes; + ctx->u.neightable.ucast_probes = ucast_probes; + ctx->u.neightable.mcast_probes = mcast_probes; + + /* Enqueue for processing on the dplane pthread */ + ret = dplane_update_enqueue(ctx); + + /* Increment counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_neightable_in, 1, + memory_order_relaxed); + + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else { + /* Error counter */ + atomic_fetch_add_explicit(&zdplane_info.dg_neightable_errors, 1, + memory_order_relaxed); + dplane_ctx_free(&ctx); + } return result; } @@ -3707,27 +3866,43 @@ enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, * Common helper api for neighbor updates */ static enum zebra_dplane_result -neigh_update_internal(enum dplane_op_e op, - const struct interface *ifp, - const struct ethaddr *mac, - const struct ipaddr *ip, - uint32_t flags, uint16_t state, - uint32_t update_flags) +neigh_update_internal(enum dplane_op_e op, const struct interface *ifp, + const void *link, const int link_family, + const struct ipaddr *ip, uint32_t flags, uint16_t state, + uint32_t update_flags, int protocol) { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret; struct zebra_dplane_ctx *ctx = NULL; struct zebra_ns *zns; + const struct ethaddr *mac = NULL; + const struct ipaddr *link_ip = NULL; - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) - zlog_debug("init neigh ctx %s: ifp %s, mac %pEA, ip %pIA", - dplane_op2str(op), ifp->name, mac, ip); + if (link_family == AF_ETHERNET) + mac = (const struct ethaddr *)link; + else + link_ip = (const struct ipaddr *)link; + + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + char buf1[PREFIX_STRLEN]; + + buf1[0] = '\0'; + if (link_family == AF_ETHERNET) + prefix_mac2str(mac, buf1, sizeof(buf1)); + else + ipaddr2str(link_ip, buf1, sizeof(buf1)); + zlog_debug("init neigh ctx %s: ifp %s, %s %s, ip %pIA", + dplane_op2str(op), ifp->name, + link_family == AF_ETHERNET ? "mac " : "link ", + buf1, ip); + } ctx = dplane_ctx_alloc(); ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; ctx->zd_vrf_id = ifp->vrf_id; + dplane_ctx_set_type(ctx, protocol); zns = zebra_ns_lookup(ifp->vrf_id); dplane_ctx_ns_init(ctx, zns, false); @@ -3740,7 +3915,10 @@ neigh_update_internal(enum dplane_op_e op, ctx->u.neigh.ip_addr = *ip; if (mac) - ctx->u.neigh.mac = *mac; + ctx->u.neigh.link.mac = *mac; + else if (link_ip) + ctx->u.neigh.link.ip_addr = *link_ip; + ctx->u.neigh.flags = flags; ctx->u.neigh.state = state; ctx->u.neigh.update_flags = update_flags; @@ -4049,6 +4227,13 @@ int dplane_show_helper(struct vty *vty, bool detailed) memory_order_relaxed); vty_out(vty, "IPset entry updates: %" PRIu64 "\n", incoming); vty_out(vty, "IPset entry errors: %" PRIu64 "\n", errs); + + incoming = atomic_load_explicit(&zdplane_info.dg_neightable_in, + memory_order_relaxed); + errs = atomic_load_explicit(&zdplane_info.dg_neightable_errors, + memory_order_relaxed); + vty_out(vty, "Neighbor Table updates: %"PRIu64"\n", incoming); + vty_out(vty, "Neighbor Table errors: %"PRIu64"\n", errs); return CMD_SUCCESS; } @@ -4434,6 +4619,8 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf, sizeof(buf)); @@ -4486,6 +4673,13 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) dplane_op2str(dplane_ctx_get_op(ctx)), ipent.unique, ctx); } break; + + case DPLANE_OP_NEIGH_TABLE_UPDATE: + zlog_debug("Dplane neigh table op %s, ifp %s, family %s", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifname(ctx), + family2str(dplane_ctx_neightable_get_family(ctx))); + break; } } @@ -4569,6 +4763,8 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_NEIGH_DISCOVER: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors, 1, memory_order_relaxed); @@ -4605,6 +4801,13 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) memory_order_relaxed); break; + case DPLANE_OP_NEIGH_TABLE_UPDATE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit( + &zdplane_info.dg_neightable_errors, 1, + memory_order_relaxed); + break; + /* Ignore 'notifications' - no-op */ case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 4913ca251f..8d51d93cd4 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -165,6 +165,12 @@ enum dplane_op_e { DPLANE_OP_IPSET_DELETE, DPLANE_OP_IPSET_ENTRY_ADD, DPLANE_OP_IPSET_ENTRY_DELETE, + + /* LINK LAYER IP address update */ + DPLANE_OP_NEIGH_IP_INSTALL, + DPLANE_OP_NEIGH_IP_DELETE, + + DPLANE_OP_NEIGH_TABLE_UPDATE, }; /* @@ -184,6 +190,8 @@ enum dplane_op_e { #define DPLANE_NUD_NOARP 0x04 #define DPLANE_NUD_PROBE 0x08 #define DPLANE_NUD_INCOMPLETE 0x10 +#define DPLANE_NUD_PERMANENT 0x20 +#define DPLANE_NUD_FAILED 0x40 /* MAC update flags - dplane_mac_info.update_flags */ #define DPLANE_MAC_REMOTE (1 << 0) @@ -196,6 +204,7 @@ enum dplane_op_e { #define DPLANE_NEIGH_WAS_STATIC (1 << 1) #define DPLANE_NEIGH_SET_STATIC (1 << 2) #define DPLANE_NEIGH_SET_INACTIVE (1 << 3) +#define DPLANE_NEIGH_NO_EXTENSION (1 << 4) #define DPLANE_BR_PORT_NON_DF (1 << 0) @@ -458,6 +467,8 @@ const struct ipaddr *dplane_ctx_neigh_get_ipaddr( const struct zebra_dplane_ctx *ctx); const struct ethaddr *dplane_ctx_neigh_get_mac( const struct zebra_dplane_ctx *ctx); +const struct ipaddr * +dplane_ctx_neigh_get_link_ip(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx); uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_neigh_get_update_flags(const struct zebra_dplane_ctx *ctx); @@ -507,6 +518,15 @@ dplane_ctx_get_br_port_sph_filters(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_get_br_port_backup_nhg_id(const struct zebra_dplane_ctx *ctx); +/* Accessors for neighbor table information */ +uint8_t dplane_ctx_neightable_get_family(const struct zebra_dplane_ctx *ctx); +uint32_t +dplane_ctx_neightable_get_app_probes(const struct zebra_dplane_ctx *ctx); +uint32_t +dplane_ctx_neightable_get_mcast_probes(const struct zebra_dplane_ctx *ctx); +uint32_t +dplane_ctx_neightable_get_ucast_probes(const struct zebra_dplane_ctx *ctx); + /* Namespace info - esp. for netlink communication */ const struct zebra_dplane_info *dplane_ctx_get_ns( const struct zebra_dplane_ctx *ctx); @@ -585,6 +605,16 @@ enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp, const struct connected *ifc); /* + * Link layer operations for the dataplane. + */ +enum zebra_dplane_result dplane_neigh_ip_update(enum dplane_op_e op, + const struct interface *ifp, + struct ipaddr *link_ip, + struct ipaddr *ip, + uint32_t ndm_state, + int protocol); + +/* * Enqueue evpn mac operations for the dataplane. */ enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp, @@ -656,6 +686,15 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, const struct ipaddr *ip); +/* + * Enqueue a neighbor table parameter set + */ +enum zebra_dplane_result dplane_neigh_table_update(const struct interface *ifp, + const uint8_t family, + const uint32_t app_probes, + const uint32_t ucast_probes, + const uint32_t mcast_probes); + /* Forward ref of zebra_pbr_rule */ struct zebra_pbr_rule; diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 27a5a07e48..80e06d913d 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -44,7 +44,6 @@ #include "zebra/rt_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_l2.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index b36e8034b7..7bbe092d8c 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -34,7 +34,6 @@ #include "zebra/zserv.h" #include "zebra/debug.h" #include "zebra/zebra_router.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_evpn.h" diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 5a28ee10c6..cabba707a0 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -41,7 +41,6 @@ #include "zebra/if_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_l2.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" @@ -3868,6 +3867,47 @@ static void zebra_evpn_mh_startup_delay_timer_start(const char *rc) } } +/***************************************************************************** + * Nexthop management: nexthops associated with Type-2 routes that have + * an ES as destination are consolidated by BGP into a per-VRF nh->rmac + * mapping which is the installed as a remote neigh/fdb entry with a + * dummy (type-1) prefix referencing it. + * This handling is needed because Type-2 routes with ES as dest use NHG + * that are setup using EAD routes (i.e. such NHGs do not include the + * RMAC info). + ****************************************************************************/ +void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + vrf_id_t vrf_id; + struct ipaddr nh; + struct ethaddr rmac; + struct prefix_evpn dummy_prefix; + + s = msg; + vrf_id = stream_getl(s); + stream_get(&nh, s, sizeof(nh)); + + memset(&dummy_prefix, 0, sizeof(dummy_prefix)); + dummy_prefix.family = AF_EVPN; + dummy_prefix.prefixlen = (sizeof(struct evpn_addr) * 8); + dummy_prefix.prefix.route_type = 1; /* XXX - fixup to type-1 def */ + + if (hdr->command == ZEBRA_EVPN_REMOTE_NH_ADD) { + stream_get(&rmac, s, sizeof(rmac)); + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("evpn remote nh %d %pIA rmac %pEA add", + vrf_id, &nh, &rmac); + zebra_vxlan_evpn_vrf_route_add(vrf_id, &rmac, &nh, + (struct prefix *)&dummy_prefix); + } else { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("evpn remote nh %d %pIA del", vrf_id, &nh); + zebra_vxlan_evpn_vrf_route_del(vrf_id, &nh, + (struct prefix *)&dummy_prefix); + } +} + /*****************************************************************************/ void zebra_evpn_mh_config_write(struct vty *vty) { diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 2361a70bff..8861e80cee 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -382,5 +382,6 @@ extern void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif, extern void zebra_evpn_acc_bd_svi_mac_add(struct interface *vlan_if); extern void zebra_evpn_es_bypass_update(struct zebra_evpn_es *es, struct interface *ifp, bool bypass); +extern void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS); #endif /* _ZEBRA_EVPN_MH_H */ diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index 0e31617c4f..d1b93dbe8a 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -34,7 +34,6 @@ #include "zebra/debug.h" #include "zebra/zebra_router.h" #include "zebra/rt.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_evpn.h" diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 4882397dd3..5fe8934a82 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -37,7 +37,6 @@ #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_errors.h" -#include "zebra/zebra_memory.h" #include "fpm/fpm.h" #include "zebra_fpm_private.h" diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index 3583c5fbf4..c3fbff2723 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -38,7 +38,6 @@ #include "zebra/zserv.h" #include "zebra/debug.h" #include "zebra/interface.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_vrf.h" #include "zebra/rt_netlink.h" #include "zebra/interface.h" diff --git a/zebra/zebra_memory.c b/zebra/zebra_memory.c deleted file mode 100644 index 004da0129c..0000000000 --- a/zebra/zebra_memory.c +++ /dev/null @@ -1,33 +0,0 @@ -/* zebra memory type definitions - * - * Copyright (C) 2015 David Lamparter - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "zebra_memory.h" - -DEFINE_MGROUP(ZEBRA, "zebra"); -DEFINE_MTYPE(ZEBRA, RE, "Route Entry"); -DEFINE_MTYPE(ZEBRA, RIB_DEST, "RIB destination"); -DEFINE_MTYPE(ZEBRA, ZVLAN, "VLAN"); -DEFINE_MTYPE(ZEBRA, ZVLAN_BITMAP, "VLAN bitmap"); -DEFINE_MTYPE(ZEBRA, OPAQUE, "Opaque Data"); diff --git a/zebra/zebra_memory.h b/zebra/zebra_memory.h deleted file mode 100644 index 769a808cb7..0000000000 --- a/zebra/zebra_memory.h +++ /dev/null @@ -1,41 +0,0 @@ -/* zebra memory type declarations - * - * Copyright (C) 2015 David Lamparter - * - * This file is part of Quagga. - * - * Quagga is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * Quagga is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; see the file COPYING; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef _QUAGGA_ZEBRA_MEMORY_H -#define _QUAGGA_ZEBRA_MEMORY_H - -#include "memory.h" - -#ifdef __cplusplus -extern "C" { -#endif - -DECLARE_MGROUP(ZEBRA); -DECLARE_MTYPE(ZEBRA_NS); -DECLARE_MTYPE(RE); -DECLARE_MTYPE(RIB_DEST); -DECLARE_MTYPE(OPAQUE); - -#ifdef __cplusplus -} -#endif - -#endif /* _QUAGGA_ZEBRA_MEMORY_H */ diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index 2e65307ea3..3b0c75151b 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -29,7 +29,6 @@ #include "zebra/zebra_mlag.h" #include "zebra/zebra_mlag_vty.h" #include "zebra/zebra_router.h" -#include "zebra/zebra_memory.h" #include "zebra/zapi_msg.h" #include "zebra/debug.h" diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index a879513539..6d42957b24 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -44,7 +44,6 @@ #include "zebra/zebra_router.h" #include "zebra/redistribute.h" #include "zebra/debug.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_mpls.h" #include "zebra/zebra_srte.h" @@ -873,6 +872,22 @@ static void lsp_schedule(struct hash_bucket *bucket, void *ctxt) zebra_lsp_t *lsp; lsp = (zebra_lsp_t *)bucket->data; + + /* In the common flow, this is used when external events occur. For + * LSPs with backup nhlfes, we'll assume that the forwarding + * plane will use the backups to handle these events, until the + * owning protocol can react. + */ + if (ctxt == NULL) { + /* Skip LSPs with backups */ + if (nhlfe_list_first(&lsp->backup_nhlfe_list) != NULL) { + if (IS_ZEBRA_DEBUG_MPLS_DETAIL) + zlog_debug("%s: skip LSP in-label %u", + __func__, lsp->ile.in_label); + return; + } + } + (void)lsp_processq_add(lsp); } diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c index 1fc1faff67..90d4ee7ced 100644 --- a/zebra/zebra_nb.c +++ b/zebra/zebra_nb.c @@ -401,14 +401,24 @@ const struct frr_yang_module_info frr_zebra_info = { { .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib", .cbs = { - .create = lib_vrf_zebra_ribs_rib_create, - .destroy = lib_vrf_zebra_ribs_rib_destroy, .get_next = lib_vrf_zebra_ribs_rib_get_next, .get_keys = lib_vrf_zebra_ribs_rib_get_keys, .lookup_entry = lib_vrf_zebra_ribs_rib_lookup_entry, } }, { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/afi-safi-name", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_afi_safi_name_get_elem, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/table-id", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_table_id_get_elem, + } + }, + { .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route", .cbs = { .get_next = lib_vrf_zebra_ribs_rib_route_get_next, @@ -634,48 +644,6 @@ const struct frr_yang_module_info frr_zebra_info = { } }, { - .xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv4-prefix-length", - .cbs = { - .modify = lib_route_map_entry_match_condition_ipv4_prefix_length_modify, - .destroy = lib_route_map_entry_match_condition_ipv4_prefix_length_destroy, - } - }, - { - .xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv6-prefix-length", - .cbs = { - .modify = lib_route_map_entry_match_condition_ipv6_prefix_length_modify, - .destroy = lib_route_map_entry_match_condition_ipv6_prefix_length_destroy, - } - }, - { - .xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:source-protocol", - .cbs = { - .modify = lib_route_map_entry_match_condition_source_protocol_modify, - .destroy = lib_route_map_entry_match_condition_source_protocol_destroy, - } - }, - { - .xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:source-instance", - .cbs = { - .modify = lib_route_map_entry_match_condition_source_instance_modify, - .destroy = lib_route_map_entry_match_condition_source_instance_destroy, - } - }, - { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/frr-zebra:source-v4", - .cbs = { - .modify = lib_route_map_entry_set_action_source_v4_modify, - .destroy = lib_route_map_entry_set_action_source_v4_destroy, - } - }, - { - .xpath = "/frr-route-map:lib/route-map/entry/set-action/frr-zebra:source-v6", - .cbs = { - .modify = lib_route_map_entry_set_action_source_v6_modify, - .destroy = lib_route_map_entry_set_action_source_v6_destroy, - } - }, - { .xpath = NULL, }, } diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h index e68b819767..95907059a8 100644 --- a/zebra/zebra_nb.h +++ b/zebra/zebra_nb.h @@ -109,30 +109,6 @@ int lib_interface_zebra_shutdown_modify(struct nb_cb_modify_args *args); int lib_interface_zebra_shutdown_destroy(struct nb_cb_destroy_args *args); int lib_interface_zebra_bandwidth_modify(struct nb_cb_modify_args *args); int lib_interface_zebra_bandwidth_destroy(struct nb_cb_destroy_args *args); -int lib_route_map_entry_match_condition_ipv4_prefix_length_modify( - struct nb_cb_modify_args *args); -int lib_route_map_entry_match_condition_ipv4_prefix_length_destroy( - struct nb_cb_destroy_args *args); -int lib_route_map_entry_match_condition_ipv6_prefix_length_modify( - struct nb_cb_modify_args *args); -int lib_route_map_entry_match_condition_ipv6_prefix_length_destroy( - struct nb_cb_destroy_args *args); -int lib_route_map_entry_match_condition_source_protocol_modify( - struct nb_cb_modify_args *args); -int lib_route_map_entry_match_condition_source_protocol_destroy( - struct nb_cb_destroy_args *args); -int lib_route_map_entry_match_condition_source_instance_modify( - struct nb_cb_modify_args *args); -int lib_route_map_entry_match_condition_source_instance_destroy( - struct nb_cb_destroy_args *args); -int lib_route_map_entry_set_action_source_v4_modify( - struct nb_cb_modify_args *args); -int lib_route_map_entry_set_action_source_v4_destroy( - struct nb_cb_destroy_args *args); -int lib_route_map_entry_set_action_source_v6_modify( - struct nb_cb_modify_args *args); -int lib_route_map_entry_set_action_source_v6_destroy( - struct nb_cb_destroy_args *args); struct yang_data * lib_interface_zebra_state_up_count_get_elem(struct nb_cb_get_elem_args *args); struct yang_data * @@ -149,12 +125,14 @@ struct yang_data *lib_interface_zebra_state_remote_vtep_get_elem( struct nb_cb_get_elem_args *args); struct yang_data *lib_interface_zebra_state_mcast_group_get_elem( struct nb_cb_get_elem_args *args); -int lib_vrf_zebra_ribs_rib_create(struct nb_cb_create_args *args); -int lib_vrf_zebra_ribs_rib_destroy(struct nb_cb_destroy_args *args); const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args); int lib_vrf_zebra_ribs_rib_get_keys(struct nb_cb_get_keys_args *args); const void * lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_afi_safi_name_get_elem(struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_table_id_get_elem(struct nb_cb_get_elem_args *args); const void * lib_vrf_zebra_ribs_rib_route_get_next(struct nb_cb_get_next_args *args); int lib_vrf_zebra_ribs_rib_route_get_keys(struct nb_cb_get_keys_args *args); @@ -201,19 +179,6 @@ lib_vrf_zebra_ribs_rib_route_nexthop_group_frr_nexthops_nexthop_get_next( struct nb_cb_get_next_args *args); int lib_vrf_zebra_ribs_rib_route_nexthop_group_frr_nexthops_nexthop_get_keys( struct nb_cb_get_keys_args *args); -int lib_vrf_zebra_ribs_rib_create(struct nb_cb_create_args *args); -int lib_vrf_zebra_ribs_rib_destroy(struct nb_cb_destroy_args *args); -const void *lib_vrf_zebra_ribs_rib_get_next(struct nb_cb_get_next_args *args); -int lib_vrf_zebra_ribs_rib_get_keys(struct nb_cb_get_keys_args *args); -const void * -lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args); -const void * -lib_vrf_zebra_ribs_rib_route_get_next(struct nb_cb_get_next_args *args); -int lib_vrf_zebra_ribs_rib_route_get_keys(struct nb_cb_get_keys_args *args); -const void * -lib_vrf_zebra_ribs_rib_route_lookup_entry(struct nb_cb_lookup_entry_args *args); -struct yang_data * -lib_vrf_zebra_ribs_rib_route_prefix_get_elem(struct nb_cb_get_elem_args *args); const void *lib_vrf_zebra_ribs_rib_route_route_entry_get_next( struct nb_cb_get_next_args *args); int lib_vrf_zebra_ribs_rib_route_route_entry_get_keys( diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c index ea2e20ed3b..6296f6f445 100644 --- a/zebra/zebra_nb_config.c +++ b/zebra/zebra_nb_config.c @@ -841,7 +841,6 @@ int lib_interface_zebra_ip_addrs_create(struct nb_cb_create_args *args) struct interface *ifp; struct prefix prefix; - ifp = nb_running_get_entry(args->dnode, NULL, true); // addr_family = yang_dnode_get_enum(dnode, "./address-family"); yang_dnode_get_prefix(&prefix, args->dnode, "./ip-prefix"); apply_mask(&prefix); @@ -864,6 +863,7 @@ int lib_interface_zebra_ip_addrs_create(struct nb_cb_create_args *args) case NB_EV_ABORT: break; case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); if (prefix.family == AF_INET) if_ip_address_install(ifp, &prefix, NULL, NULL); else if (prefix.family == AF_INET6) @@ -881,12 +881,15 @@ int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args) struct prefix prefix; struct connected *ifc; - ifp = nb_running_get_entry(args->dnode, NULL, true); yang_dnode_get_prefix(&prefix, args->dnode, "./ip-prefix"); apply_mask(&prefix); switch (args->event) { case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (!ifp) + return NB_OK; + if (prefix.family == AF_INET) { /* Check current interface address. */ ifc = connected_check_ptp(ifp, &prefix, NULL); @@ -927,6 +930,7 @@ int lib_interface_zebra_ip_addrs_destroy(struct nb_cb_destroy_args *args) case NB_EV_ABORT: break; case NB_EV_APPLY: + ifp = nb_running_get_entry(args->dnode, NULL, true); if_ip_address_uinstall(ifp, &prefix); break; } @@ -1068,6 +1072,9 @@ int lib_interface_zebra_link_detect_destroy(struct nb_cb_destroy_args *args) */ int lib_interface_zebra_shutdown_modify(struct nb_cb_modify_args *args) { + if (args->event != NB_EV_APPLY) + return NB_OK; + struct interface *ifp; ifp = nb_running_get_entry(args->dnode, NULL, true); @@ -1079,6 +1086,9 @@ int lib_interface_zebra_shutdown_modify(struct nb_cb_modify_args *args) int lib_interface_zebra_shutdown_destroy(struct nb_cb_destroy_args *args) { + if (args->event != NB_EV_APPLY) + return NB_OK; + struct interface *ifp; ifp = nb_running_get_entry(args->dnode, NULL, true); @@ -1130,61 +1140,6 @@ int lib_interface_zebra_bandwidth_destroy(struct nb_cb_destroy_args *args) } /* - * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib - */ -int lib_vrf_zebra_ribs_rib_create(struct nb_cb_create_args *args) -{ - struct vrf *vrf; - afi_t afi; - safi_t safi; - struct zebra_vrf *zvrf; - struct zebra_router_table *zrt; - uint32_t table_id; - const char *afi_safi_name; - - vrf = nb_running_get_entry(args->dnode, NULL, false); - zvrf = vrf_info_lookup(vrf->vrf_id); - table_id = yang_dnode_get_uint32(args->dnode, "./table-id"); - if (!table_id) - table_id = zvrf->table_id; - - afi_safi_name = yang_dnode_get_string(args->dnode, "./afi-safi-name"); - yang_afi_safi_identity2value(afi_safi_name, &afi, &safi); - - zrt = zebra_router_find_zrt(zvrf, table_id, afi, safi); - - switch (args->event) { - case NB_EV_VALIDATE: - if (!zrt) { - snprintf(args->errmsg, args->errmsg_len, - "vrf %s table is not found.", vrf->name); - return NB_ERR_VALIDATION; - } - break; - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - - nb_running_set_entry(args->dnode, zrt); - - break; - } - - return NB_OK; -} - -int lib_vrf_zebra_ribs_rib_destroy(struct nb_cb_destroy_args *args) -{ - if (args->event != NB_EV_APPLY) - return NB_OK; - - nb_running_unset_entry(args->dnode); - - return NB_OK; -} - -/* * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id */ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) @@ -1263,9 +1218,6 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) return NB_ERR; } - /* Mark as having FRR configuration */ - vrf_set_user_cfged(vrf); - break; } @@ -1313,10 +1265,6 @@ int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args) return NB_ERR; } - /* If no other FRR config for this VRF, mark accordingly. */ - if (!zebra_vrf_has_config(zvrf)) - vrf_reset_user_cfged(vrf); - break; } @@ -1339,323 +1287,3 @@ int lib_vrf_zebra_prefix_only_modify(struct nb_cb_modify_args *args) return NB_OK; } - -/* - * XPath: - * /frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv4-prefix-length - */ -int lib_route_map_entry_match_condition_ipv4_prefix_length_modify( - struct nb_cb_modify_args *args) -{ - struct routemap_hook_context *rhc; - const char *length; - int condition, rv; - - if (args->event != NB_EV_APPLY) - return NB_OK; - - /* Add configuration. */ - rhc = nb_running_get_entry(args->dnode, NULL, true); - length = yang_dnode_get_string(args->dnode, NULL); - condition = - yang_dnode_get_enum(args->dnode, "../frr-route-map:condition"); - - /* Set destroy information. */ - switch (condition) { - case 100: /* ipv4-prefix-length */ - rhc->rhc_rule = "ip address prefix-len"; - break; - - case 102: /* ipv4-next-hop-prefix-length */ - rhc->rhc_rule = "ip next-hop prefix-len"; - break; - } - rhc->rhc_mhook = generic_match_delete; - rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; - - rv = generic_match_add(NULL, rhc->rhc_rmi, rhc->rhc_rule, length, - RMAP_EVENT_MATCH_ADDED); - if (rv != CMD_SUCCESS) { - rhc->rhc_mhook = NULL; - return NB_ERR_INCONSISTENCY; - } - - return NB_OK; -} - -int lib_route_map_entry_match_condition_ipv4_prefix_length_destroy( - struct nb_cb_destroy_args *args) -{ - return lib_route_map_entry_match_destroy(args); -} - -/* - * XPath: - * /frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv6-prefix-length - */ -int lib_route_map_entry_match_condition_ipv6_prefix_length_modify( - struct nb_cb_modify_args *args) -{ - struct routemap_hook_context *rhc; - const char *length; - int rv; - - if (args->event != NB_EV_APPLY) - return NB_OK; - - /* Add configuration. */ - rhc = nb_running_get_entry(args->dnode, NULL, true); - length = yang_dnode_get_string(args->dnode, NULL); - - /* Set destroy information. */ - rhc->rhc_mhook = generic_match_delete; - rhc->rhc_rule = "ipv6 address prefix-len"; - rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; - - rv = generic_match_add(NULL, rhc->rhc_rmi, "ipv6 address prefix-len", - length, RMAP_EVENT_MATCH_ADDED); - if (rv != CMD_SUCCESS) { - rhc->rhc_mhook = NULL; - return NB_ERR_INCONSISTENCY; - } - - return NB_OK; -} - -int lib_route_map_entry_match_condition_ipv6_prefix_length_destroy( - struct nb_cb_destroy_args *args) -{ - return lib_route_map_entry_match_destroy(args); -} - -/* - * XPath: - * /frr-route-map:lib/route-map/entry/match-condition/frr-zebra:source-protocol - */ -int lib_route_map_entry_match_condition_source_protocol_modify( - struct nb_cb_modify_args *args) -{ - struct routemap_hook_context *rhc; - const char *type; - int rv; - - switch (args->event) { - case NB_EV_VALIDATE: - type = yang_dnode_get_string(args->dnode, NULL); - if (proto_name2num(type) == -1) { - snprintf(args->errmsg, args->errmsg_len, - "invalid protocol: %s", type); - return NB_ERR_VALIDATION; - } - return NB_OK; - case NB_EV_PREPARE: - case NB_EV_ABORT: - return NB_OK; - case NB_EV_APPLY: - /* NOTHING */ - break; - } - - /* Add configuration. */ - rhc = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_string(args->dnode, NULL); - - /* Set destroy information. */ - rhc->rhc_mhook = generic_match_delete; - rhc->rhc_rule = "source-protocol"; - rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; - - rv = generic_match_add(NULL, rhc->rhc_rmi, "source-protocol", type, - RMAP_EVENT_MATCH_ADDED); - if (rv != CMD_SUCCESS) { - rhc->rhc_mhook = NULL; - return NB_ERR_INCONSISTENCY; - } - - return NB_OK; -} - -int lib_route_map_entry_match_condition_source_protocol_destroy( - struct nb_cb_destroy_args *args) -{ - return lib_route_map_entry_match_destroy(args); -} - -/* - * XPath: - * /frr-route-map:lib/route-map/entry/match-condition/frr-zebra:source-instance - */ -int lib_route_map_entry_match_condition_source_instance_modify( - struct nb_cb_modify_args *args) -{ - struct routemap_hook_context *rhc; - const char *type; - int rv; - - if (args->event != NB_EV_APPLY) - return NB_OK; - - /* Add configuration. */ - rhc = nb_running_get_entry(args->dnode, NULL, true); - type = yang_dnode_get_string(args->dnode, NULL); - - /* Set destroy information. */ - rhc->rhc_mhook = generic_match_delete; - rhc->rhc_rule = "source-instance"; - rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; - - rv = generic_match_add(NULL, rhc->rhc_rmi, "source-instance", type, - RMAP_EVENT_MATCH_ADDED); - if (rv != CMD_SUCCESS) { - rhc->rhc_mhook = NULL; - return NB_ERR_INCONSISTENCY; - } - - return NB_OK; -} - -int lib_route_map_entry_match_condition_source_instance_destroy( - struct nb_cb_destroy_args *args) -{ - return lib_route_map_entry_match_destroy(args); -} - -/* - * XPath: /frr-route-map:lib/route-map/entry/set-action/frr-zebra:source-v4 - */ -int lib_route_map_entry_set_action_source_v4_modify( - struct nb_cb_modify_args *args) -{ - struct routemap_hook_context *rhc; - struct interface *pif = NULL; - const char *source; - struct vrf *vrf; - struct prefix p; - int rv; - - switch (args->event) { - case NB_EV_VALIDATE: - memset(&p, 0, sizeof(p)); - yang_dnode_get_ipv4p(&p, args->dnode, NULL); - if (zebra_check_addr(&p) == 0) { - snprintf(args->errmsg, args->errmsg_len, - "invalid IPv4 address: %s", - yang_dnode_get_string(args->dnode, NULL)); - return NB_ERR_VALIDATION; - } - - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - pif = if_lookup_exact_address(&p.u.prefix4, AF_INET, - vrf->vrf_id); - if (pif != NULL) - break; - } - /* - * On startup the local address *may* not have come up - * yet. We need to allow startup configuration of - * set src or we are fudged. Log it for future fun - */ - if (pif == NULL) - zlog_warn("set src %pI4 is not a local address", - &p.u.prefix4); - return NB_OK; - case NB_EV_PREPARE: - case NB_EV_ABORT: - return NB_OK; - case NB_EV_APPLY: - /* NOTHING */ - break; - } - - /* Add configuration. */ - rhc = nb_running_get_entry(args->dnode, NULL, true); - source = yang_dnode_get_string(args->dnode, NULL); - - /* Set destroy information. */ - rhc->rhc_shook = generic_set_delete; - rhc->rhc_rule = "src"; - - rv = generic_set_add(NULL, rhc->rhc_rmi, "src", source); - if (rv != CMD_SUCCESS) { - rhc->rhc_shook = NULL; - return NB_ERR_INCONSISTENCY; - } - - return NB_OK; -} - -int lib_route_map_entry_set_action_source_v4_destroy( - struct nb_cb_destroy_args *args) -{ - return lib_route_map_entry_set_destroy(args); -} - -/* - * XPath: /frr-route-map:lib/route-map/entry/set-action/frr-zebra:source-v6 - */ -int lib_route_map_entry_set_action_source_v6_modify( - struct nb_cb_modify_args *args) -{ - struct routemap_hook_context *rhc; - struct interface *pif = NULL; - const char *source; - struct vrf *vrf; - struct prefix p; - int rv; - - switch (args->event) { - case NB_EV_VALIDATE: - memset(&p, 0, sizeof(p)); - yang_dnode_get_ipv6p(&p, args->dnode, NULL); - if (zebra_check_addr(&p) == 0) { - snprintf(args->errmsg, args->errmsg_len, - "invalid IPv6 address: %s", - yang_dnode_get_string(args->dnode, NULL)); - return NB_ERR_VALIDATION; - } - - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - pif = if_lookup_exact_address(&p.u.prefix6, AF_INET6, - vrf->vrf_id); - if (pif != NULL) - break; - } - /* - * On startup the local address *may* not have come up - * yet. We need to allow startup configuration of - * set src or we are fudged. Log it for future fun - */ - if (pif == NULL) - zlog_warn("set src %pI6 is not a local address", - &p.u.prefix6); - return NB_OK; - case NB_EV_PREPARE: - case NB_EV_ABORT: - return NB_OK; - case NB_EV_APPLY: - /* NOTHING */ - break; - } - - /* Add configuration. */ - rhc = nb_running_get_entry(args->dnode, NULL, true); - source = yang_dnode_get_string(args->dnode, NULL); - - /* Set destroy information. */ - rhc->rhc_shook = generic_set_delete; - rhc->rhc_rule = "src"; - - rv = generic_set_add(NULL, rhc->rhc_rmi, "src", source); - if (rv != CMD_SUCCESS) { - rhc->rhc_shook = NULL; - return NB_ERR_INCONSISTENCY; - } - - return NB_OK; -} - -int lib_route_map_entry_set_action_source_v6_destroy( - struct nb_cb_destroy_args *args) -{ - return lib_route_map_entry_set_destroy(args); -} diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c index 21c89f64ed..a9cb096aee 100644 --- a/zebra/zebra_nb_state.c +++ b/zebra/zebra_nb_state.c @@ -214,6 +214,29 @@ lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args) } /* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/afi-safi-name + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_afi_safi_name_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct zebra_router_table *zrt = args->list_entry; + + return yang_data_new_string(args->xpath, + yang_afi_safi_value2identity(zrt->afi, zrt->safi)); +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/table-id + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_table_id_get_elem(struct nb_cb_get_elem_args *args) +{ + const struct zebra_router_table *zrt = args->list_entry; + + return yang_data_new_uint32(args->xpath, zrt->tableid); +} + +/* * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route */ const void * diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 5f11bbd600..3e89df68fd 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -37,7 +37,6 @@ #include "lib_errors.h" #include "zebra_router.h" -#include "zebra_memory.h" #endif /* defined(HAVE_NETLINK) */ #include "zebra_netns_notify.h" diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 9246283fdf..7edf022892 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -36,7 +36,6 @@ #include "zebra/zebra_nhg_private.h" #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_srte.h" #include "zebra/zserv.h" #include "zebra/rt.h" @@ -2899,6 +2898,8 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_RULE_ADD: @@ -2913,6 +2914,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_IPSET_DELETE: case DPLANE_OP_IPSET_ENTRY_ADD: case DPLANE_OP_IPSET_ENTRY_DELETE: + case DPLANE_OP_NEIGH_TABLE_UPDATE: break; } diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index ae7727ca17..27b8a3ea47 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -28,7 +28,6 @@ #include "zebra_ns.h" #include "zebra_vrf.h" -#include "zebra_memory.h" #include "rt.h" #include "zebra_vxlan.h" #include "debug.h" @@ -41,7 +40,7 @@ extern struct zebra_privs_t zserv_privs; -DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space"); +DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_NS, "Zebra Name Space"); static struct zebra_ns *dzns; diff --git a/zebra/zebra_opaque.c b/zebra/zebra_opaque.c index 1d59e0ab34..244f16302b 100644 --- a/zebra/zebra_opaque.c +++ b/zebra/zebra_opaque.c @@ -24,7 +24,6 @@ #include "lib/stream.h" #include "zebra/debug.h" #include "zebra/zserv.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_opaque.h" /* Mem type */ diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 4b87432ffc..c4004842e6 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -30,7 +30,6 @@ #include "zebra/zebra_pbr.h" #include "zebra/rt.h" #include "zebra/zapi_msg.h" -#include "zebra/zebra_memory.h" #include "zebra/zserv.h" #include "zebra/debug.h" diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 1e7b38086b..bea855d1af 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -1167,8 +1167,6 @@ void zebra_ptm_if_write(struct vty *vty, struct zebra_if *zebra_ifp) #else /* HAVE_BFDD */ -#include "zebra/zebra_memory.h" - /* * Data structures. */ diff --git a/zebra/zebra_ptm_redistribute.c b/zebra/zebra_ptm_redistribute.c index eabc2e005e..537d69fbca 100644 --- a/zebra/zebra_ptm_redistribute.c +++ b/zebra/zebra_ptm_redistribute.c @@ -26,7 +26,6 @@ #include "zebra/zapi_msg.h" #include "zebra/zebra_ptm.h" #include "zebra/zebra_ptm_redistribute.h" -#include "zebra/zebra_memory.h" static int zsend_interface_bfd_update(int cmd, struct zserv *client, struct interface *ifp, struct prefix *dp, diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 6b26192711..82a0e6d015 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -49,7 +49,6 @@ #include "zebra/rt.h" #include "zebra/zapi_msg.h" #include "zebra/zebra_errors.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" @@ -58,6 +57,10 @@ #include "zebra/zapi_msg.h" #include "zebra/zebra_dplane.h" +DEFINE_MGROUP(ZEBRA, "zebra"); + +DEFINE_MTYPE(ZEBRA, RE, "Route Entry"); +DEFINE_MTYPE_STATIC(ZEBRA, RIB_DEST, "RIB destination"); DEFINE_MTYPE_STATIC(ZEBRA, RIB_UPDATE_CTX, "Rib update context object"); /* @@ -800,6 +803,23 @@ int rib_gc_dest(struct route_node *rn) return 1; } +void zebra_rtable_node_cleanup(struct route_table *table, + struct route_node *node) +{ + struct route_entry *re, *next; + + RNODE_FOREACH_RE_SAFE (node, re, next) { + rib_unlink(node, re); + } + + if (node->info) { + rib_dest_t *dest = node->info; + + rnh_list_fini(&dest->nht); + XFREE(MTYPE_RIB_DEST, node->info); + } +} + static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, struct route_entry *new) { @@ -2698,7 +2718,7 @@ void rib_unlink(struct route_node *rn, struct route_entry *re) nexthops_free(re->fib_ng.nexthop); - XFREE(MTYPE_OPAQUE, re->opaque); + zapi_opaque_free(re->opaque); XFREE(MTYPE_RE, re); } @@ -3904,10 +3924,13 @@ static int rib_process_dplane_results(struct thread *thread) case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_NEIGH_IP_INSTALL: + case DPLANE_OP_NEIGH_IP_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_BR_PORT_UPDATE: + case DPLANE_OP_NEIGH_TABLE_UPDATE: case DPLANE_OP_NONE: /* Don't expect this: just return the struct? */ dplane_ctx_fini(&ctx); diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index a6e3f8473f..3b0ef71987 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -48,7 +48,6 @@ #include "zebra/zebra_routemap.h" #include "zebra/zebra_srte.h" #include "zebra/interface.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_errors.h" DEFINE_MTYPE_STATIC(ZEBRA, RNH, "Nexthop tracking object"); diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 17a9bf97f9..6a42c682ad 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -21,7 +21,6 @@ #include <zebra.h> #include "memory.h" -#include "zebra_memory.h" #include "prefix.h" #include "rib.h" #include "vty.h" @@ -358,12 +357,15 @@ DEFPY_YANG( "Match prefix length of IP address\n" "Prefix length\n") { - const char *xpath = "./match-condition[condition='ipv4-prefix-length']"; + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:ipv4-prefix-length']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), - "%s/frr-zebra:ipv4-prefix-length", xpath); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length", + xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); return nb_cli_apply_changes(vty, NULL); @@ -379,7 +381,8 @@ DEFPY_YANG( "Match prefix length of IP address\n" "Prefix length\n") { - const char *xpath = "./match-condition[condition='ipv4-prefix-length']"; + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:ipv4-prefix-length']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -395,12 +398,15 @@ DEFPY_YANG( "Match prefix length of IPv6 address\n" "Prefix length\n") { - const char *xpath = "./match-condition[condition='ipv6-prefix-length']"; + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:ipv6-prefix-length']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), - "%s/frr-zebra:ipv6-prefix-length", xpath); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-zebra-route-map:ipv6-prefix-length", + xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); return nb_cli_apply_changes(vty, NULL); @@ -416,7 +422,8 @@ DEFPY_YANG( "Match prefix length of IPv6 address\n" "Prefix length\n") { - const char *xpath = "./match-condition[condition='ipv6-prefix-length']"; + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:ipv6-prefix-length']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -433,12 +440,14 @@ DEFPY_YANG( "Prefix length\n") { const char *xpath = - "./match-condition[condition='ipv4-next-hop-prefix-length']"; + "./match-condition[condition='frr-zebra-route-map:ipv4-next-hop-prefix-length']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); - snprintf(xpath_value, sizeof(xpath_value), - "%s/frr-zebra:ipv4-prefix-length", xpath); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length", + xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, length_str); return nb_cli_apply_changes(vty, NULL); @@ -455,7 +464,7 @@ DEFPY_YANG( "Prefix length\n") { const char *xpath = - "./match-condition[condition='ipv4-next-hop-prefix-length']"; + "./match-condition[condition='frr-zebra-route-map:ipv4-next-hop-prefix-length']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -469,12 +478,14 @@ DEFPY_YANG( "Match protocol via which the route was learnt\n" FRR_REDIST_HELP_STR_ZEBRA) { - const char *xpath = "./match-condition[condition='source-protocol']"; + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:source-protocol']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); snprintf(xpath_value, sizeof(xpath_value), - "%s/frr-zebra:source-protocol", xpath); + "%s/rmap-match-condition/frr-zebra-route-map:source-protocol", + xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, proto); return nb_cli_apply_changes(vty, NULL); @@ -488,7 +499,8 @@ DEFPY_YANG( "Match protocol via which the route was learnt\n" FRR_REDIST_HELP_STR_ZEBRA) { - const char *xpath = "./match-condition[condition='source-protocol']"; + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:source-protocol']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -502,12 +514,14 @@ DEFPY_YANG( "Match the protocol's instance number\n" "The instance number\n") { - const char *xpath = "./match-condition[condition='source-instance']"; + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:source-instance']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); snprintf(xpath_value, sizeof(xpath_value), - "%s/frr-zebra:source-instance", xpath); + "%s/rmap-match-condition/frr-zebra-route-map:source-instance", + xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, instance_str); return nb_cli_apply_changes(vty, NULL); @@ -520,7 +534,8 @@ DEFPY_YANG( "Match the protocol's instance number\n" "The instance number\n") { - const char *xpath = "./match-condition[condition='source-instance']"; + const char *xpath = + "./match-condition[condition='frr-zebra-route-map:source-instance']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); @@ -537,18 +552,23 @@ DEFPY_YANG( "IPv4 src address\n" "IPv6 src address\n") { - const char *xpath = "./set-action[action='source']"; + const char *xpath = + "./set-action[action='frr-zebra-route-map:src-address']"; char xpath_value[XPATH_MAXLEN]; nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); if (addrv4_str) { - snprintf(xpath_value, sizeof(xpath_value), - "%s/frr-zebra:source-v4", xpath); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-zebra-route-map:ipv4-src-address", + xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addrv4_str); } else { - snprintf(xpath_value, sizeof(xpath_value), - "%s/frr-zebra:source-v6", xpath); + snprintf( + xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-zebra-route-map:ipv6-src-address", + xpath); nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, addrv6_str); } @@ -565,14 +585,15 @@ DEFPY_YANG( "IPv4 address\n" "IPv6 address\n") { - const char *xpath = "./set-action[action='source']"; + const char *xpath = + "./set-action[action='frr-zebra-route-map:src-address']"; nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } -DEFUN (zebra_route_map_timer, +DEFUN_YANG (zebra_route_map_timer, zebra_route_map_timer_cmd, "zebra route-map delay-timer (0-600)", ZEBRA_STR @@ -589,7 +610,7 @@ DEFUN (zebra_route_map_timer, return (CMD_SUCCESS); } -DEFUN (no_zebra_route_map_timer, +DEFUN_YANG (no_zebra_route_map_timer, no_zebra_route_map_timer_cmd, "no zebra route-map delay-timer [(0-600)]", NO_STR diff --git a/zebra/zebra_routemap.h b/zebra/zebra_routemap.h index c016d95875..3f58e14e10 100644 --- a/zebra/zebra_routemap.h +++ b/zebra/zebra_routemap.h @@ -55,4 +55,6 @@ zebra_nht_route_map_check(afi_t afi, int client_proto, const struct prefix *p, #endif extern void zebra_routemap_finish(void); + +extern const struct frr_yang_module_info frr_zebra_route_map_info; #endif diff --git a/zebra/zebra_routemap_nb.c b/zebra/zebra_routemap_nb.c new file mode 100644 index 0000000000..c82c34dd53 --- /dev/null +++ b/zebra/zebra_routemap_nb.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "northbound.h" +#include "libfrr.h" +#include "zebra_routemap_nb.h" + +/* clang-format off */ +const struct frr_yang_module_info frr_zebra_route_map_info = { + .name = "frr-zebra-route-map", + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_ipv4_prefix_length_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_ipv4_prefix_length_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-zebra-route-map:ipv6-prefix-length", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_ipv6_prefix_length_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_ipv6_prefix_length_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-zebra-route-map:source-instance", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_source_instance_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_instance_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-zebra-route-map:source-protocol", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-zebra-route-map:ipv4-src-address", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_ipv4_src_address_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_ipv4_src_address_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-zebra-route-map:ipv6-src-address", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_ipv6_src_address_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_ipv6_src_address_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/zebra/zebra_routemap_nb.h b/zebra/zebra_routemap_nb.h new file mode 100644 index 0000000000..a43f2b2ae9 --- /dev/null +++ b/zebra/zebra_routemap_nb.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_ZEBRA_ROUTEMAP_NB_H_ +#define _FRR_ZEBRA_ROUTEMAP_NB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* prototypes */ +int lib_route_map_entry_match_condition_rmap_match_condition_ipv4_prefix_length_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_ipv4_prefix_length_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_ipv6_prefix_length_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_ipv6_prefix_length_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_source_instance_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_source_instance_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv4_src_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv4_src_address_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv6_src_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv6_src_address_destroy(struct nb_cb_destroy_args *args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/zebra/zebra_routemap_nb_config.c b/zebra/zebra_routemap_nb_config.c new file mode 100644 index 0000000000..8f5660610f --- /dev/null +++ b/zebra/zebra_routemap_nb_config.c @@ -0,0 +1,396 @@ +#include <zebra.h> + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "zebra/rib.h" +#include "zebra/zebra_routemap_nb.h" + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_ipv4_prefix_length_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *length; + int rv; + const char *condition; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + length = yang_dnode_get_string(args->dnode, NULL); + condition = yang_dnode_get_string(args->dnode, + "../../frr-route-map:condition"); + + if (IS_MATCH_IPv4_PREFIX_LEN(condition)) + rhc->rhc_rule = "ip address prefix-len"; + else if (IS_MATCH_IPv4_NH_PREFIX_LEN(condition)) + rhc->rhc_rule = "ip next-hop prefix-len"; + + rhc->rhc_mhook = generic_match_delete; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = generic_match_add(rhc->rhc_rmi, rhc->rhc_rule, + length, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_ipv4_prefix_length_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-zebra-route-map:ipv6-prefix-length + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_ipv6_prefix_length_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *length; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + length = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = generic_match_delete; + rhc->rhc_rule = "ipv6 address prefix-len"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = generic_match_add(rhc->rhc_rmi, "ipv6 address prefix-len", + length, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_ipv6_prefix_length_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; + +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-zebra-route-map:source-instance + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_source_instance_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = generic_match_delete; + rhc->rhc_rule = "source-instance"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = generic_match_add(rhc->rhc_rmi, "source-instance", + type, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_source_instance_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-zebra-route-map:source-protocol + */ +int +lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + type = yang_dnode_get_string(args->dnode, NULL); + if (proto_name2num(type) == -1) { + zlog_warn("%s: invalid protocol: %s", __func__, type); + return NB_ERR_VALIDATION; + } + return NB_OK; + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = generic_match_delete; + rhc->rhc_rule = "source-protocol"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + rv = generic_match_add(rhc->rhc_rmi, "source-protocol", type, + RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int +lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-zebra-route-map:ipv4-src-address + */ +int +lib_route_map_entry_set_action_rmap_set_action_ipv4_src_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + struct interface *pif = NULL; + const char *source; + struct vrf *vrf; + struct prefix p; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + memset(&p, 0, sizeof(p)); + yang_dnode_get_ipv4p(&p, args->dnode, NULL); + if (zebra_check_addr(&p) == 0) { + zlog_warn("%s: invalid IPv4 address: %s", __func__, + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + RB_FOREACH(vrf, vrf_id_head, &vrfs_by_id) { + pif = if_lookup_exact_address(&p.u.prefix4, AF_INET, + vrf->vrf_id); + if (pif != NULL) + break; + } + if (pif == NULL) { + zlog_warn("%s: is not a local address: %s", __func__, + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + source = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "src"; + + rv = generic_set_add(rhc->rhc_rmi, "src", source, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_ipv4_src_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-zebra-route-map:ipv6-src-address + */ +int +lib_route_map_entry_set_action_rmap_set_action_ipv6_src_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + struct interface *pif = NULL; + const char *source; + struct vrf *vrf; + struct prefix p; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + memset(&p, 0, sizeof(p)); + yang_dnode_get_ipv6p(&p, args->dnode, NULL); + if (zebra_check_addr(&p) == 0) { + zlog_warn("%s: invalid IPv6 address: %s", __func__, + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + + RB_FOREACH(vrf, vrf_id_head, &vrfs_by_id) { + pif = if_lookup_exact_address(&p.u.prefix6, AF_INET6, + vrf->vrf_id); + if (pif != NULL) + break; + } + if (pif == NULL) { + zlog_warn("%s: is not a local address: %s", __func__, + yang_dnode_get_string(args->dnode, NULL)); + return NB_ERR_VALIDATION; + } + return NB_OK; + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_APPLY: + /* NOTHING */ + break; + } + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + source = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "src"; + + rv = generic_set_add(rhc->rhc_rmi, "src", source, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int +lib_route_map_entry_set_action_rmap_set_action_ipv6_src_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index 11c1130c4f..5a00f3155d 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -25,7 +25,6 @@ #include "lib/frratomic.h" #include "zebra_router.h" -#include "zebra_memory.h" #include "zebra_pbr.h" #include "zebra_vxlan.h" #include "zebra_mlag.h" @@ -33,6 +32,7 @@ #include "debug.h" DEFINE_MTYPE_STATIC(ZEBRA, RIB_TABLE_INFO, "RIB table info"); +DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_RT_TABLE, "Zebra VRF table"); struct zebra_router zrouter = { .multipath_num = MULTIPATH_NUM, @@ -121,7 +121,7 @@ struct route_table *zebra_router_get_table(struct zebra_vrf *zvrf, if (zrt) return zrt->table; - zrt = XCALLOC(MTYPE_ZEBRA_NS, sizeof(*zrt)); + zrt = XCALLOC(MTYPE_ZEBRA_RT_TABLE, sizeof(*zrt)); zrt->tableid = tableid; zrt->afi = afi; zrt->safi = safi; @@ -185,7 +185,7 @@ static void zebra_router_free_table(struct zebra_router_table *zrt) RB_REMOVE(zebra_router_table_head, &zrouter.tables, zrt); XFREE(MTYPE_RIB_TABLE_INFO, table_info); - XFREE(MTYPE_ZEBRA_NS, zrt); + XFREE(MTYPE_ZEBRA_RT_TABLE, zrt); } void zebra_router_release_table(struct zebra_vrf *zvrf, uint32_t tableid, diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c index e761cd7d04..98158ecc12 100644 --- a/zebra/zebra_srte.c +++ b/zebra/zebra_srte.c @@ -24,7 +24,6 @@ #include "lib/lib_errors.h" #include "zebra/zebra_srte.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_mpls.h" #include "zebra/zebra_rnh.h" #include "zebra/zapi_msg.h" diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 46398f3fb6..212557423b 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -36,7 +36,6 @@ #include "zebra/zebra_vrf.h" #include "zebra/zebra_rnh.h" #include "zebra/router-id.h" -#include "zebra/zebra_memory.h" #include "zebra/interface.h" #include "zebra/zebra_mpls.h" #include "zebra/zebra_vxlan.h" @@ -339,20 +338,6 @@ static int zebra_vrf_update(struct vrf *vrf) return 0; } - -/* Return if this VRF has any FRR configuration or not. - * IMPORTANT: This function needs to be updated when additional configuration - * is added for a VRF. - */ -int zebra_vrf_has_config(struct zebra_vrf *zvrf) -{ - /* EVPN L3-VNI? */ - if (zvrf->l3vni) - return 1; - - return 0; -} - /* Lookup the routing table in a VRF based on both VRF-Id and table-id. * NOTE: Table-id is relevant on two modes: * - case VRF backend is default : on default VRF only @@ -413,23 +398,6 @@ done: return table; } -void zebra_rtable_node_cleanup(struct route_table *table, - struct route_node *node) -{ - struct route_entry *re, *next; - - RNODE_FOREACH_RE_SAFE (node, re, next) { - rib_unlink(node, re); - } - - if (node->info) { - rib_dest_t *dest = node->info; - - rnh_list_fini(&dest->nht); - XFREE(MTYPE_RIB_DEST, node->info); - } -} - static void zebra_rnhtable_node_cleanup(struct route_table *table, struct route_node *node) { diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index ed6376b01f..000b5a7238 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -254,7 +254,6 @@ extern struct zebra_vrf *zebra_vrf_lookup_by_name(const char *); extern struct zebra_vrf *zebra_vrf_alloc(void); extern struct route_table *zebra_vrf_table(afi_t, safi_t, vrf_id_t); -extern int zebra_vrf_has_config(struct zebra_vrf *zvrf); extern void zebra_vrf_init(void); extern void zebra_rtable_node_cleanup(struct route_table *table, diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 3349c18204..283a3e52d6 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -21,7 +21,6 @@ #include <zebra.h> #include "memory.h" -#include "zebra_memory.h" #include "if.h" #include "prefix.h" #include "command.h" @@ -2565,10 +2564,8 @@ DEFUN (default_vrf_vni_mapping, "VNI-ID\n" "Prefix routes only \n") { - int ret = 0; - char err[ERR_STR_SZ]; + char xpath[XPATH_MAXLEN]; struct zebra_vrf *zvrf = NULL; - vni_t vni = strtoul(argv[1]->arg, NULL, 10); int filter = 0; zvrf = vrf_info_lookup(VRF_DEFAULT); @@ -2578,25 +2575,35 @@ DEFUN (default_vrf_vni_mapping, if (argc == 3) filter = 1; - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, - filter, 1); - if (ret != 0) { - vty_out(vty, "%s\n", err); - return CMD_WARNING; + snprintf(xpath, sizeof(xpath), FRR_VRF_KEY_XPATH "/frr-zebra:zebra", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath, sizeof(xpath), + FRR_VRF_KEY_XPATH "/frr-zebra:zebra/l3vni-id", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, argv[1]->arg); + + if (filter) { + snprintf(xpath, sizeof(xpath), + FRR_VRF_KEY_XPATH "/frr-zebra:zebra/prefix-only", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "true"); } - return CMD_SUCCESS; + return nb_cli_apply_changes(vty, NULL); } DEFUN (no_default_vrf_vni_mapping, no_default_vrf_vni_mapping_cmd, - "no vni " CMD_VNI_RANGE, + "no vni " CMD_VNI_RANGE "[prefix-routes-only]", NO_STR "VNI corresponding to DEFAULT VRF\n" - "VNI-ID") + "VNI-ID\n" + "Prefix routes only \n") { - int ret = 0; - char err[ERR_STR_SZ]; + char xpath[XPATH_MAXLEN]; + int filter = 0; vni_t vni = strtoul(argv[2]->arg, NULL, 10); struct zebra_vrf *zvrf = NULL; @@ -2604,13 +2611,32 @@ DEFUN (no_default_vrf_vni_mapping, if (!zvrf) return CMD_WARNING; - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0); - if (ret != 0) { - vty_out(vty, "%s\n", err); + if (argc == 4) + filter = 1; + + if (zvrf->l3vni != vni) { + vty_out(vty, "VNI %d doesn't exist in VRF: %s \n", vni, + zvrf->vrf->name); return CMD_WARNING; } - return CMD_SUCCESS; + snprintf(xpath, sizeof(xpath), + FRR_VRF_KEY_XPATH "/frr-zebra:zebra/l3vni-id", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, argv[2]->arg); + + if (filter) { + snprintf(xpath, sizeof(xpath), + FRR_VRF_KEY_XPATH "/frr-zebra:zebra/prefix-only", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, "true"); + } + + snprintf(xpath, sizeof(xpath), FRR_VRF_KEY_XPATH "/frr-zebra:zebra", + VRF_DEFAULT_NAME); + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); } DEFUN (vrf_vni_mapping, @@ -2638,9 +2664,7 @@ DEFUN (vrf_vni_mapping, nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only", NB_OP_MODIFY, "true"); - nb_cli_apply_changes(vty, NULL); - - return CMD_SUCCESS; + return nb_cli_apply_changes(vty, NULL); } DEFUN (no_vrf_vni_mapping, @@ -2677,9 +2701,7 @@ DEFUN (no_vrf_vni_mapping, nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_DESTROY, NULL); - nb_cli_apply_changes(vty, NULL); - - return CMD_SUCCESS; + return nb_cli_apply_changes(vty, NULL); } /* show vrf */ diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index d95967c55d..4cd3b60a0f 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -46,7 +46,6 @@ #include "zebra/rt_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_l2.h" -#include "zebra/zebra_memory.h" #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" @@ -150,6 +149,11 @@ static int host_rb_entry_compare(const struct host_rb_entry *hle1, } else if (hle1->p.family == AF_INET6) { return memcmp(&hle1->p.u.prefix6, &hle2->p.u.prefix6, IPV6_MAX_BYTELEN); + } else if (hle1->p.family == AF_EVPN) { + /* a single dummy prefix of route_type BGP_EVPN_AD_ROUTE is + * used for all nexthops associated with a non-zero ESI + */ + return 0; } else { zlog_debug("%s: Unexpected family type: %d", __func__, hle1->p.family); diff --git a/zebra/zserv.c b/zebra/zserv.c index 6c5eebe6fe..f89b6fe478 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -638,6 +638,7 @@ static void zserv_client_free(struct zserv *client) vrf_bitmap_free(client->redist_default[afi]); vrf_bitmap_free(client->ridinfo[afi]); + vrf_bitmap_free(client->nhrp_neighinfo[afi]); } /* @@ -760,6 +761,7 @@ static struct zserv *zserv_client_create(int sock) client->redist[afi][i] = vrf_bitmap_init(); client->redist_default[afi] = vrf_bitmap_init(); client->ridinfo[afi] = vrf_bitmap_init(); + client->nhrp_neighinfo[afi] = vrf_bitmap_init(); } /* Add this client to linked list. */ diff --git a/zebra/zserv.h b/zebra/zserv.h index c60799b8ba..203670ac1d 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -137,6 +137,9 @@ struct zserv { /* Router-id information. */ vrf_bitmap_t ridinfo[AFI_MAX]; + /* Router-id information. */ + vrf_bitmap_t nhrp_neighinfo[AFI_MAX]; + bool notify_owner; /* Indicates if client is synchronous. */ |
