diff options
543 files changed, 44315 insertions, 12151 deletions
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..e8a364050a --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +d62a17aedeb0eebdba98238874bb13d62c48dbf9 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1209f52e14..98c7c3d54d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -37,7 +37,7 @@ If applicable, add screenshots to help explain your problem. **Versions** - OS Kernel: [e.g. Linux, OpenBSD, etc] [version] - - FRR Version [version] + - FRR Version: [version] **Additional context** Add any other context about the problem here. diff --git a/bfdd/bfd.c b/bfdd/bfd.c index caa80ed51d..c16912060c 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -88,6 +88,8 @@ static void bfd_profile_set_default(struct bfd_profile *bp) bp->admin_shutdown = true; bp->detection_multiplier = BFD_DEFDETECTMULT; bp->echo_mode = false; + bp->passive = false; + bp->minimum_ttl = BFD_DEF_MHOP_TTL; bp->min_echo_rx = BFD_DEF_REQ_MIN_ECHO; bp->min_rx = BFD_DEFREQUIREDMINRX; bp->min_tx = BFD_DEFDESIREDMINTX; @@ -124,53 +126,12 @@ void bfd_profile_free(struct bfd_profile *bp) XFREE(MTYPE_BFDD_PROFILE, bp); } -/** - * Removes a profile and tests whether it needs to apply the changes or not. - * - * \param bs the BFD session. - * \param apply whether or not to apply configurations immediately. - */ -static void _bfd_profile_remove(struct bfd_session *bs, bool apply) -{ - struct bfd_profile *bp; - - /* No profile applied, nothing to do. */ - bp = bs->profile; - if (bp == NULL) - return; - - /* Remove the profile association. */ - bs->profile = NULL; - - /* Set multiplier to the default. */ - bs->detect_mult = bs->peer_profile.detection_multiplier; - - /* Set timers back to user configuration. */ - bs->timers.desired_min_tx = bs->peer_profile.min_tx; - bs->timers.required_min_rx = bs->peer_profile.min_rx; - - /* We can only apply echo options on single hop sessions. */ - if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { - /* Set default echo timer. */ - bs->timers.required_min_echo = bs->peer_profile.min_echo_rx; - - /* Default is no echo mode. */ - if (apply) - bfd_set_echo(bs, bs->peer_profile.echo_mode); - } - - if (apply) - bfd_set_shutdown(bs, bs->peer_profile.admin_shutdown); -} - void bfd_profile_apply(const char *profname, struct bfd_session *bs) { struct bfd_profile *bp; /* Remove previous profile if any. */ if (bs->profile_name) { - _bfd_profile_remove(bs, false); - /* We are changing profiles. */ if (strcmp(bs->profile_name, profname)) { XFREE(MTYPE_BFDD_PROFILE, bs->profile_name); @@ -182,12 +143,23 @@ void bfd_profile_apply(const char *profname, struct bfd_session *bs) /* Look up new profile to apply. */ bp = bfd_profile_lookup(profname); - if (bp == NULL) - return; /* Point to profile if it exists. */ bs->profile = bp; + /* Apply configuration. */ + bfd_session_apply(bs); +} + +void bfd_session_apply(struct bfd_session *bs) +{ + struct bfd_profile *bp; + uint32_t min_tx = bs->timers.desired_min_tx; + uint32_t min_rx = bs->timers.required_min_rx; + + /* Pick the source of configuration. */ + bp = bs->profile ? bs->profile : &bs->peer_profile; + /* Set multiplier if not the default. */ if (bs->peer_profile.detection_multiplier == BFD_DEFDETECTMULT) bs->detect_mult = bp->detection_multiplier; @@ -219,21 +191,40 @@ void bfd_profile_apply(const char *profname, struct bfd_session *bs) bfd_set_echo(bs, bp->echo_mode); else bfd_set_echo(bs, bs->peer_profile.echo_mode); + } else { + /* Configure the TTL packet filter. */ + if (bs->peer_profile.minimum_ttl == BFD_DEF_MHOP_TTL) + bs->mh_ttl = bp->minimum_ttl; + else + bs->mh_ttl = bs->peer_profile.minimum_ttl; } + /* Toggle 'passive-mode' if default value. */ + if (bs->peer_profile.passive == false) + bfd_set_passive_mode(bs, bp->passive); + else + bfd_set_passive_mode(bs, bs->peer_profile.passive); + /* Toggle 'no shutdown' if default value. */ if (bs->peer_profile.admin_shutdown) bfd_set_shutdown(bs, bp->admin_shutdown); else bfd_set_shutdown(bs, bs->peer_profile.admin_shutdown); + + /* If session interval changed negotiate new timers. */ + if (bs->ses_state == PTM_BFD_UP + && (bs->timers.desired_min_tx != min_tx + || bs->timers.required_min_rx != min_rx)) + bfd_set_polling(bs); } void bfd_profile_remove(struct bfd_session *bs) { /* Remove any previous set profile name. */ XFREE(MTYPE_BFDD_PROFILE, bs->profile_name); + bs->profile = NULL; - _bfd_profile_remove(bs, true); + bfd_session_apply(bs); } void gen_bfd_key(struct bfd_key *key, struct sockaddr_any *peer, @@ -374,8 +365,12 @@ int bfd_session_enable(struct bfd_session *bs) * protocol. */ bs->sock = psock; - bfd_recvtimer_update(bs); - ptm_bfd_start_xmt_timer(bs, false); + + /* Only start timers if we are using active mode. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE) == 0) { + bfd_recvtimer_update(bs); + ptm_bfd_start_xmt_timer(bs, false); + } return 0; } @@ -536,6 +531,12 @@ void ptm_bfd_sess_dn(struct bfd_session *bfd, uint8_t diag) if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_ECHO_ACTIVE)) ptm_bfd_echo_stop(bfd); + /* Stop attempting to transmit or expect control packets if passive. */ + if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_PASSIVE)) { + bfd_recvtimer_delete(bfd); + bfd_xmttimer_delete(bfd); + } + if (old_state != bfd->ses_state) { bfd->stats.session_down++; if (bglobal.debug_peer_event) @@ -758,6 +759,11 @@ static void _bfd_session_update(struct bfd_session *bs, else UNSET_FLAG(bs->flags, BFD_SESS_FLAG_CBIT); + if (bpc->bpc_has_minimum_ttl) { + bs->mh_ttl = bpc->bpc_minimum_ttl; + bs->peer_profile.minimum_ttl = bpc->bpc_minimum_ttl; + } + bs->peer_profile.echo_mode = bpc->bpc_echo; bfd_set_echo(bs, bpc->bpc_echo); @@ -766,6 +772,7 @@ static void _bfd_session_update(struct bfd_session *bs, * the session is disabled. */ bs->peer_profile.admin_shutdown = bpc->bpc_shutdown; + bfd_set_passive_mode(bs, bpc->bpc_passive); bfd_set_shutdown(bs, bpc->bpc_shutdown); /* @@ -986,6 +993,10 @@ static void bs_down_handler(struct bfd_session *bs, int nstate) * bring it up. */ bs->ses_state = PTM_BFD_INIT; + + /* Answer peer with INIT immediately in passive mode. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) + ptm_bfd_snd(bs, 0); break; case PTM_BFD_INIT: @@ -1312,9 +1323,39 @@ void bfd_set_shutdown(struct bfd_session *bs, bool shutdown) bs->ses_state = PTM_BFD_DOWN; control_notify(bs, bs->ses_state); - /* Enable all timers. */ - bfd_recvtimer_update(bs); + /* Enable timers if non passive, otherwise stop them. */ + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) { + bfd_recvtimer_delete(bs); + bfd_xmttimer_delete(bs); + } else { + bfd_recvtimer_update(bs); + bfd_xmttimer_update(bs, bs->xmt_TO); + } + } +} + +void bfd_set_passive_mode(struct bfd_session *bs, bool passive) +{ + if (passive) { + SET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE); + + /* Session is already up and running, nothing to do now. */ + if (bs->ses_state != PTM_BFD_DOWN) + return; + + /* Lets disable the timers since we are now passive. */ + bfd_recvtimer_delete(bs); + bfd_xmttimer_delete(bs); + } else { + UNSET_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE); + + /* Session is already up and running, nothing to do now. */ + if (bs->ses_state != PTM_BFD_DOWN) + return; + + /* Session is down, let it attempt to start the connection. */ bfd_xmttimer_update(bs, bs->xmt_TO); + bfd_recvtimer_update(bs); } } @@ -2011,16 +2052,16 @@ static int bfd_vrf_enable(struct vrf *vrf) if (!bvrf->bg_ev[1]) thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop, &bvrf->bg_ev[1]); - if (!bvrf->bg_ev[2]) + if (!bvrf->bg_ev[2] && bvrf->bg_shop6 != -1) thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop6, &bvrf->bg_ev[2]); - if (!bvrf->bg_ev[3]) + if (!bvrf->bg_ev[3] && bvrf->bg_mhop6 != -1) thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6, &bvrf->bg_ev[3]); if (!bvrf->bg_ev[4]) thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo, &bvrf->bg_ev[4]); - if (!bvrf->bg_ev[5]) + if (!bvrf->bg_ev[5] && bvrf->bg_echov6 != -1) thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6, &bvrf->bg_ev[5]); } @@ -2059,10 +2100,13 @@ static int bfd_vrf_disable(struct vrf *vrf) socket_close(&bvrf->bg_echo); socket_close(&bvrf->bg_shop); socket_close(&bvrf->bg_mhop); - socket_close(&bvrf->bg_shop6); - socket_close(&bvrf->bg_mhop6); + if (bvrf->bg_shop6 != -1) + socket_close(&bvrf->bg_shop6); + if (bvrf->bg_mhop6 != -1) + socket_close(&bvrf->bg_mhop6); socket_close(&bvrf->bg_echo); - socket_close(&bvrf->bg_echov6); + if (bvrf->bg_echov6 != -1) + socket_close(&bvrf->bg_echov6); /* free context */ XFREE(MTYPE_BFDD_VRF, bvrf); diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 492334a670..af3f92d6a8 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -170,6 +170,7 @@ enum bfd_session_flags { BFD_SESS_FLAG_SHUTDOWN = 1 << 7, /* disable BGP peer function */ BFD_SESS_FLAG_CONFIG = 1 << 8, /* Session configured with bfd NB API */ BFD_SESS_FLAG_CBIT = 1 << 9, /* CBIT is set */ + BFD_SESS_FLAG_PASSIVE = 1 << 10, /* Passive mode */ }; /* BFD session hash keys */ @@ -207,6 +208,10 @@ struct bfd_profile { uint32_t min_rx; /** Administrative state. */ bool admin_shutdown; + /** Passive mode. */ + bool passive; + /** Minimum expected TTL value. */ + uint8_t minimum_ttl; /** Echo mode (only applies to single hop). */ bool echo_mode; @@ -328,7 +333,8 @@ TAILQ_HEAD(obslist, bfd_session_observer); #define BFD_DEFREQUIREDMINRX (300 * 1000) /* microseconds. */ #define BFD_DEF_REQ_MIN_ECHO (50 * 1000) /* microseconds. */ #define BFD_DEF_SLOWTX (1000 * 1000) /* microseconds. */ -#define BFD_DEF_MHOP_TTL 5 +/** Minimum multi hop TTL. */ +#define BFD_DEF_MHOP_TTL 254 #define BFD_PKT_LEN 24 /* Length of control packet */ #define BFD_TTL_VAL 255 #define BFD_RCV_TTL_VAL 1 @@ -607,6 +613,23 @@ void bfd_set_echo(struct bfd_session *bs, bool echo); */ void bfd_set_shutdown(struct bfd_session *bs, bool shutdown); +/** + * Set the BFD session passive mode. + * + * \param bs the BFD session. + * \param passive the passive mode. + */ +void bfd_set_passive_mode(struct bfd_session *bs, bool passive); + +/** + * Picks the BFD session configuration from the appropriated source: + * if using the default peer configuration prefer profile (if it exists), + * otherwise use session. + * + * \param bs the BFD session. + */ +void bfd_session_apply(struct bfd_session *bs); + /* BFD hash data structures interface */ void bfd_initialize(void); void bfd_shutdown(void); diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 68bdd89bb7..5cc47d5a44 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -147,6 +147,8 @@ void ptm_bfd_echo_snd(struct bfd_session *bfd) bep.my_discr = htonl(bfd->discrs.my_discr); if (CHECK_FLAG(bfd->flags, BFD_SESS_FLAG_IPV6)) { + if (bvrf->bg_echov6 == -1) + return; sd = bvrf->bg_echov6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; @@ -577,7 +579,7 @@ int bfd_recv_cb(struct thread *t) return 0; } - /* Validate packet TTL. */ + /* Validate single hop packet TTL. */ if ((!is_mhop) && (ttl != BFD_TTL_VAL)) { cp_debug(is_mhop, &peer, &local, ifindex, vrfid, "invalid TTL: %d expected %d", ttl, BFD_TTL_VAL); @@ -630,10 +632,10 @@ int bfd_recv_cb(struct thread *t) * Single hop: set local address that received the packet. */ if (is_mhop) { - if ((BFD_TTL_VAL - bfd->mh_ttl) > BFD_TTL_VAL) { + if (ttl < bfd->mh_ttl) { cp_debug(is_mhop, &peer, &local, ifindex, vrfid, "exceeded max hop count (expected %d, got %d)", - bfd->mh_ttl, BFD_TTL_VAL); + bfd->mh_ttl, ttl); return 0; } } else if (bfd->local_address.sa_sin.sin_family == AF_UNSPEC) { @@ -1145,8 +1147,14 @@ int bp_udp6_shop(const struct vrf *vrf) sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id, vrf->name); } - if (sd == -1) - zlog_fatal("udp6-shop: socket: %s", strerror(errno)); + if (sd == -1) { + if (errno != EAFNOSUPPORT) + zlog_fatal("udp6-shop: socket: %s", strerror(errno)); + else + zlog_warn("udp6-shop: V6 is not supported, continuing"); + + return -1; + } bp_set_ipv6opts(sd); bp_bind_ipv6(sd, BFD_DEFDESTPORT); @@ -1162,8 +1170,14 @@ int bp_udp6_mhop(const struct vrf *vrf) sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf->vrf_id, vrf->name); } - if (sd == -1) - zlog_fatal("udp6-mhop: socket: %s", strerror(errno)); + if (sd == -1) { + if (errno != EAFNOSUPPORT) + zlog_fatal("udp6-mhop: socket: %s", strerror(errno)); + else + zlog_warn("udp6-mhop: V6 is not supported, continuing"); + + return -1; + } bp_set_ipv6opts(sd); bp_bind_ipv6(sd, BFD_DEF_MHOP_DEST_PORT); @@ -1194,8 +1208,15 @@ int bp_echov6_socket(const struct vrf *vrf) frr_with_privs(&bglobal.bfdd_privs) { s = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf->vrf_id, vrf->name); } - if (s == -1) - zlog_fatal("echov6-socket: socket: %s", strerror(errno)); + if (s == -1) { + if (errno != EAFNOSUPPORT) + zlog_fatal("echov6-socket: socket: %s", + strerror(errno)); + else + zlog_warn("echov6-socket: V6 is not supported, continuing"); + + return -1; + } bp_set_ipv6opts(s); bp_bind_ipv6(s, BFD_DEF_ECHO_PORT); diff --git a/bfdd/bfdctl.h b/bfdd/bfdctl.h index 95cfcb1105..e1cff9a31c 100644 --- a/bfdd/bfdctl.h +++ b/bfdd/bfdctl.h @@ -84,11 +84,15 @@ struct bfd_peer_cfg { bool bpc_has_echointerval; uint64_t bpc_echointerval; + bool bpc_has_minimum_ttl; + uint8_t bpc_minimum_ttl; + bool bpc_echo; bool bpc_createonly; bool bpc_shutdown; bool bpc_cbit; + bool bpc_passive; bool bpc_has_profile; char bpc_profile[64]; diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index 0dd021d475..d115684b1c 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -109,7 +109,7 @@ DEFPY_YANG_NOSH( VRF_NAME_STR) { int ret, slen; - char source_str[INET6_ADDRSTRLEN]; + char source_str[INET6_ADDRSTRLEN + 32]; char xpath[XPATH_MAXLEN], xpath_srcaddr[XPATH_MAXLEN + 32]; if (multihop) @@ -168,7 +168,7 @@ DEFPY_YANG( { int slen; char xpath[XPATH_MAXLEN]; - char source_str[INET6_ADDRSTRLEN]; + char source_str[INET6_ADDRSTRLEN + 32]; if (multihop) snprintf(source_str, sizeof(source_str), "[source-addr='%s']", @@ -266,6 +266,63 @@ void bfd_cli_show_shutdown(struct vty *vty, struct lyd_node *dnode, } DEFPY_YANG( + bfd_peer_passive, bfd_peer_passive_cmd, + "[no] passive-mode", + NO_STR + "Don't attempt to start sessions\n") +{ + nb_cli_enqueue_change(vty, "./passive-mode", NB_OP_MODIFY, + no ? "false" : "true"); + return nb_cli_apply_changes(vty, NULL); +} + +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 "); +} + +DEFPY_YANG( + bfd_peer_minimum_ttl, bfd_peer_minimum_ttl_cmd, + "[no] minimum-ttl (1-254)$ttl", + NO_STR + "Expect packets with at least this TTL\n" + "Minimum TTL expected\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_DESTROY, + NULL); + else + nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_MODIFY, + ttl_str); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + no_bfd_peer_minimum_ttl, no_bfd_peer_minimum_ttl_cmd, + "no minimum-ttl", + NO_STR + "Expect packets with at least this TTL\n") +{ + nb_cli_enqueue_change(vty, "./minimum-ttl", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +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)); +} + +DEFPY_YANG( bfd_peer_mult, bfd_peer_mult_cmd, "detect-multiplier (2-255)$multiplier", "Configure peer detection multiplier\n" @@ -462,6 +519,22 @@ ALIAS_YANG(bfd_peer_shutdown, bfd_profile_shutdown_cmd, NO_STR "Disable BFD peer\n") +ALIAS_YANG(bfd_peer_passive, bfd_profile_passive_cmd, + "[no] passive-mode", + NO_STR + "Don't attempt to start sessions\n") + +ALIAS_YANG(bfd_peer_minimum_ttl, bfd_profile_minimum_ttl_cmd, + "[no] minimum-ttl (1-254)$ttl", + NO_STR + "Expect packets with at least this TTL\n" + "Minimum TTL expected\n") + +ALIAS_YANG(no_bfd_peer_minimum_ttl, no_bfd_profile_minimum_ttl_cmd, + "no minimum-ttl", + NO_STR + "Expect packets with at least this TTL\n") + ALIAS_YANG(bfd_peer_echo, bfd_profile_echo_cmd, "[no] echo-mode", NO_STR @@ -530,6 +603,9 @@ bfdd_cli_init(void) install_element(BFD_PEER_NODE, &bfd_peer_echo_cmd); install_element(BFD_PEER_NODE, &bfd_peer_echo_interval_cmd); install_element(BFD_PEER_NODE, &bfd_peer_profile_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_passive_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_minimum_ttl_cmd); + install_element(BFD_PEER_NODE, &no_bfd_peer_minimum_ttl_cmd); /* Profile commands. */ cmd_variable_handler_register(bfd_vars); @@ -546,4 +622,7 @@ bfdd_cli_init(void) install_element(BFD_PROFILE_NODE, &bfd_profile_shutdown_cmd); install_element(BFD_PROFILE_NODE, &bfd_profile_echo_cmd); install_element(BFD_PROFILE_NODE, &bfd_profile_echo_interval_cmd); + install_element(BFD_PROFILE_NODE, &bfd_profile_passive_cmd); + install_element(BFD_PROFILE_NODE, &bfd_profile_minimum_ttl_cmd); + install_element(BFD_PROFILE_NODE, &no_bfd_profile_minimum_ttl_cmd); } diff --git a/bfdd/bfdd_nb.c b/bfdd/bfdd_nb.c index 2ff99ca608..64ba3cf811 100644 --- a/bfdd/bfdd_nb.c +++ b/bfdd/bfdd_nb.c @@ -78,6 +78,21 @@ const struct frr_yang_module_info frr_bfdd_info = { } }, { + .xpath = "/frr-bfdd:bfdd/bfd/profile/passive-mode", + .cbs = { + .modify = bfdd_bfd_profile_passive_mode_modify, + .cli_show = bfd_cli_show_passive, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile/minimum-ttl", + .cbs = { + .modify = bfdd_bfd_profile_minimum_ttl_modify, + .destroy = bfdd_bfd_profile_minimum_ttl_destroy, + .cli_show = bfd_cli_show_minimum_ttl, + } + }, + { .xpath = "/frr-bfdd:bfdd/bfd/profile/echo-mode", .cbs = { .modify = bfdd_bfd_profile_echo_mode_modify, @@ -147,6 +162,13 @@ const struct frr_yang_module_info frr_bfdd_info = { } }, { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/passive-mode", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_passive_mode_modify, + .cli_show = bfd_cli_show_passive, + } + }, + { .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/echo-mode", .cbs = { .modify = bfdd_bfd_sessions_single_hop_echo_mode_modify, @@ -329,6 +351,21 @@ const struct frr_yang_module_info frr_bfdd_info = { } }, { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/passive-mode", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_passive_mode_modify, + .cli_show = bfd_cli_show_passive, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/minimum-ttl", + .cbs = { + .modify = bfdd_bfd_sessions_multi_hop_minimum_ttl_modify, + .destroy = bfdd_bfd_sessions_multi_hop_minimum_ttl_destroy, + .cli_show = bfd_cli_show_minimum_ttl, + } + }, + { .xpath = "/frr-bfdd:bfdd/bfd/sessions/multi-hop/stats/local-discriminator", .cbs = { .get_elem = bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem, diff --git a/bfdd/bfdd_nb.h b/bfdd/bfdd_nb.h index a379c2135e..fbd557b6b1 100644 --- a/bfdd/bfdd_nb.h +++ b/bfdd/bfdd_nb.h @@ -37,6 +37,9 @@ int bfdd_bfd_profile_desired_transmission_interval_modify( int bfdd_bfd_profile_required_receive_interval_modify( struct nb_cb_modify_args *args); int bfdd_bfd_profile_administrative_down_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_profile_passive_mode_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_profile_minimum_ttl_modify(struct nb_cb_modify_args *args); +int bfdd_bfd_profile_minimum_ttl_destroy(struct nb_cb_destroy_args *args); int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args); int bfdd_bfd_profile_desired_echo_transmission_interval_modify( struct nb_cb_modify_args *args); @@ -62,6 +65,8 @@ int bfdd_bfd_sessions_single_hop_required_receive_interval_modify( struct nb_cb_modify_args *args); int bfdd_bfd_sessions_single_hop_administrative_down_modify( struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_passive_mode_modify( + struct nb_cb_modify_args *args); int bfdd_bfd_sessions_single_hop_echo_mode_modify( struct nb_cb_modify_args *args); int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify( @@ -131,6 +136,10 @@ int bfdd_bfd_sessions_multi_hop_required_receive_interval_modify( struct nb_cb_modify_args *args); int bfdd_bfd_sessions_multi_hop_administrative_down_modify( struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify( + struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_multi_hop_minimum_ttl_destroy( + struct nb_cb_destroy_args *args); struct yang_data * bfdd_bfd_sessions_multi_hop_stats_local_discriminator_get_elem( struct nb_cb_get_elem_args *args); @@ -206,5 +215,9 @@ void bfd_cli_show_profile(struct vty *vty, struct lyd_node *dnode, bool show_defaults); void bfd_cli_peer_profile_show(struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void bfd_cli_show_passive(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); +void bfd_cli_show_minimum_ttl(struct vty *vty, struct lyd_node *dnode, + bool show_defaults); #endif /* _FRR_BFDD_NB_H_ */ diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c index 970b5f2d65..0046bc625b 100644 --- a/bfdd/bfdd_nb_config.c +++ b/bfdd/bfdd_nb_config.c @@ -360,6 +360,64 @@ int bfdd_bfd_profile_administrative_down_modify(struct nb_cb_modify_args *args) } /* + * XPath: /frr-bfdd:bfdd/bfd/profile/passive-mode + */ +int bfdd_bfd_profile_passive_mode_modify(struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; + bool passive; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + passive = yang_dnode_get_bool(args->dnode, NULL); + bp = nb_running_get_entry(args->dnode, NULL, true); + if (bp->passive == passive) + return NB_OK; + + bp->passive = passive; + bfd_profile_update(bp); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile/minimum-ttl + */ +int bfdd_bfd_profile_minimum_ttl_modify(struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; + uint8_t minimum_ttl; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + minimum_ttl = yang_dnode_get_uint8(args->dnode, NULL); + bp = nb_running_get_entry(args->dnode, NULL, true); + if (bp->minimum_ttl == minimum_ttl) + return NB_OK; + + bp->minimum_ttl = minimum_ttl; + bfd_profile_update(bp); + + return NB_OK; +} + +int bfdd_bfd_profile_minimum_ttl_destroy(struct nb_cb_destroy_args *args) +{ + struct bfd_profile *bp; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + bp = nb_running_get_entry(args->dnode, NULL, true); + bp->minimum_ttl = BFD_DEF_MHOP_TTL; + bfd_profile_update(bp); + + return NB_OK; +} + +/* * XPath: /frr-bfdd:bfdd/bfd/profile/echo-mode */ int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args) @@ -497,8 +555,8 @@ int bfdd_bfd_sessions_single_hop_detection_multiplier_modify( case NB_EV_APPLY: bs = nb_running_get_entry(args->dnode, NULL, true); - bs->detect_mult = detection_multiplier; bs->peer_profile.detection_multiplier = detection_multiplier; + bfd_session_apply(bs); break; case NB_EV_ABORT: @@ -533,9 +591,8 @@ int bfdd_bfd_sessions_single_hop_desired_transmission_interval_modify( if (tx_interval == bs->timers.desired_min_tx) return NB_OK; - bs->timers.desired_min_tx = tx_interval; bs->peer_profile.min_tx = tx_interval; - bfd_set_polling(bs); + bfd_session_apply(bs); break; case NB_EV_ABORT: @@ -570,9 +627,8 @@ int bfdd_bfd_sessions_single_hop_required_receive_interval_modify( if (rx_interval == bs->timers.required_min_rx) return NB_OK; - bs->timers.required_min_rx = rx_interval; bs->peer_profile.min_rx = rx_interval; - bfd_set_polling(bs); + bfd_session_apply(bs); break; case NB_EV_ABORT: @@ -606,7 +662,37 @@ int bfdd_bfd_sessions_single_hop_administrative_down_modify( bs = nb_running_get_entry(args->dnode, NULL, true); bs->peer_profile.admin_shutdown = shutdown; - bfd_set_shutdown(bs, shutdown); + bfd_session_apply(bs); + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/single-hop/passive-mode + */ +int bfdd_bfd_sessions_single_hop_passive_mode_modify( + struct nb_cb_modify_args *args) +{ + struct bfd_session *bs; + bool passive; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + return NB_OK; + + case NB_EV_APPLY: + break; + + case NB_EV_ABORT: + return NB_OK; + } + + passive = yang_dnode_get_bool(args->dnode, NULL); + + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->peer_profile.passive = passive; + bfd_session_apply(bs); return NB_OK; } @@ -634,7 +720,7 @@ int bfdd_bfd_sessions_single_hop_echo_mode_modify( bs = nb_running_get_entry(args->dnode, NULL, true); bs->peer_profile.echo_mode = echo; - bfd_set_echo(bs, echo); + bfd_session_apply(bs); return NB_OK; } @@ -664,8 +750,8 @@ int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify( if (echo_interval == bs->timers.required_min_echo) return NB_OK; - bs->timers.required_min_echo = echo_interval; bs->peer_profile.min_echo_rx = echo_interval; + bfd_session_apply(bs); break; case NB_EV_ABORT: @@ -689,3 +775,54 @@ int bfdd_bfd_sessions_multi_hop_destroy(struct nb_cb_destroy_args *args) { return bfd_session_destroy(args->event, args->dnode, true); } + +/* + * XPath: /frr-bfdd:bfdd/bfd/sessions/multi-hop/minimum-ttl + */ +int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify( + struct nb_cb_modify_args *args) +{ + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + return NB_OK; + + case NB_EV_APPLY: + break; + + case NB_EV_ABORT: + return NB_OK; + } + + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->peer_profile.minimum_ttl = yang_dnode_get_uint8(args->dnode, NULL); + bfd_session_apply(bs); + + return NB_OK; +} + +int bfdd_bfd_sessions_multi_hop_minimum_ttl_destroy( + struct nb_cb_destroy_args *args) +{ + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + return NB_OK; + + case NB_EV_APPLY: + break; + + case NB_EV_ABORT: + return NB_OK; + } + + bs = nb_running_get_entry(args->dnode, NULL, true); + bs->peer_profile.minimum_ttl = BFD_DEF_MHOP_TTL; + bfd_session_apply(bs); + + return NB_OK; +} diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index 8318ea9665..a3f1638e5f 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -111,6 +111,12 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) vty_out(vty, "\t\tID: %u\n", bs->discrs.my_discr); vty_out(vty, "\t\tRemote ID: %u\n", bs->discrs.remote_discr); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)) + vty_out(vty, "\t\tPassive mode\n"); + else + vty_out(vty, "\t\tActive mode\n"); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) + vty_out(vty, "\t\tMinimum TTL: %d\n", bs->mh_ttl); vty_out(vty, "\t\tStatus: "); switch (bs->ses_state) { @@ -203,6 +209,10 @@ static struct json_object *__display_peer_json(struct bfd_session *bs) json_object_int_add(jo, "id", bs->discrs.my_discr); json_object_int_add(jo, "remote-id", bs->discrs.remote_discr); + json_object_boolean_add(jo, "passive-mode", + CHECK_FLAG(bs->flags, BFD_SESS_FLAG_PASSIVE)); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) + json_object_int_add(jo, "minimum-ttl", bs->mh_ttl); switch (bs->ses_state) { case PTM_BFD_ADM_DOWN: diff --git a/bfdd/ptm_adapter.c b/bfdd/ptm_adapter.c index 8134807a14..48e55bce37 100644 --- a/bfdd/ptm_adapter.c +++ b/bfdd/ptm_adapter.c @@ -303,7 +303,6 @@ static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id, struct bfd_peer_cfg *bpc, struct ptm_client **pc) { uint32_t pid; - uint8_t ttl __attribute__((unused)); size_t ifnamelen; /* @@ -375,7 +374,18 @@ static int _ptm_msg_read(struct stream *msg, int command, vrf_id_t vrf_id, if (bpc->bpc_mhop) { /* Read multihop source address and TTL. */ _ptm_msg_read_address(msg, &bpc->bpc_local); - STREAM_GETC(msg, ttl); + STREAM_GETC(msg, bpc->bpc_minimum_ttl); + if (bpc->bpc_minimum_ttl >= BFD_TTL_VAL + || bpc->bpc_minimum_ttl == 0) { + zlog_warn("%s: received invalid TTL configuration %d", + __func__, bpc->bpc_has_minimum_ttl); + bpc->bpc_minimum_ttl = BFD_DEF_MHOP_TTL; + bpc->bpc_has_minimum_ttl = false; + } else { + bpc->bpc_minimum_ttl = + (BFD_TTL_VAL + 1) - bpc->bpc_minimum_ttl; + bpc->bpc_has_minimum_ttl = true; + } } else { /* If target is IPv6, then we must obtain local address. */ if (bpc->bpc_ipv4 == false) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 948055e375..8817263cef 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -84,6 +84,7 @@ static const struct message attr_str[] = { #endif {BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"}, {BGP_ATTR_PREFIX_SID, "PREFIX_SID"}, + {BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"}, {0}}; static const struct message attr_flag_str[] = { @@ -662,6 +663,8 @@ unsigned int attrhash_key_make(const void *p) MIX(lcommunity_hash_make(attr->lcommunity)); if (attr->ecommunity) MIX(ecommunity_hash_make(attr->ecommunity)); + if (attr->ipv6_ecommunity) + MIX(ecommunity_hash_make(attr->ipv6_ecommunity)); if (attr->cluster) MIX(cluster_hash_key_make(attr->cluster)); if (attr->transit) @@ -700,6 +703,7 @@ bool attrhash_cmp(const void *p1, const void *p2) && attr1->label_index == attr2->label_index && attr1->mp_nexthop_len == attr2->mp_nexthop_len && attr1->ecommunity == attr2->ecommunity + && attr1->ipv6_ecommunity == attr2->ipv6_ecommunity && attr1->lcommunity == attr2->lcommunity && attr1->cluster == attr2->cluster && attr1->transit == attr2->transit @@ -725,7 +729,8 @@ bool attrhash_cmp(const void *p1, const void *p2) && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex && attr1->distance == attr2->distance && srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) - && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn)) + && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn) + && attr1->srte_color == attr2->srte_color) return true; } @@ -829,6 +834,15 @@ struct attr *bgp_attr_intern(struct attr *attr) else attr->ecommunity->refcnt++; } + + if (attr->ipv6_ecommunity) { + if (!attr->ipv6_ecommunity->refcnt) + attr->ipv6_ecommunity = + ecommunity_intern(attr->ipv6_ecommunity); + else + attr->ipv6_ecommunity->refcnt++; + } + if (attr->lcommunity) { if (!attr->lcommunity->refcnt) attr->lcommunity = lcommunity_intern(attr->lcommunity); @@ -1034,6 +1048,10 @@ void bgp_attr_unintern_sub(struct attr *attr) ecommunity_unintern(&attr->ecommunity); UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)); + if (attr->ipv6_ecommunity) + ecommunity_unintern(&attr->ipv6_ecommunity); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES)); + if (attr->lcommunity) lcommunity_unintern(&attr->lcommunity); UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)); @@ -1118,6 +1136,8 @@ void bgp_attr_flush(struct attr *attr) community_free(&attr->community); if (attr->ecommunity && !attr->ecommunity->refcnt) ecommunity_free(&attr->ecommunity); + if (attr->ipv6_ecommunity && !attr->ipv6_ecommunity->refcnt) + ecommunity_free(&attr->ipv6_ecommunity); if (attr->lcommunity && !attr->lcommunity->refcnt) lcommunity_free(&attr->lcommunity); if (attr->cluster && !attr->cluster->refcnt) { @@ -1150,6 +1170,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, bgp_size_t length) { struct peer *const peer = args->peer; + struct attr *const attr = args->attr; const uint8_t flags = args->flags; /* startp and length must be special-cased, as whether or not to * send the attribute data with the NOTIFY depends on the error, @@ -1157,6 +1178,14 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, */ uint8_t *notify_datap = (length > 0 ? args->startp : NULL); + if (bgp_debug_update(peer, NULL, NULL, 1)) { + char attr_str[BUFSIZ] = {0}; + + bgp_dump_attr(attr, attr_str, sizeof(attr_str)); + + zlog_debug("%s: attributes: %s", __func__, attr_str); + } + /* Only relax error handling for eBGP peers */ if (peer->sort != BGP_PEER_EBGP) { bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR, subcode, @@ -1192,6 +1221,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode, case BGP_ATTR_LOCAL_PREF: case BGP_ATTR_COMMUNITIES: case BGP_ATTR_EXT_COMMUNITIES: + case BGP_ATTR_IPV6_EXT_COMMUNITIES: case BGP_ATTR_LARGE_COMMUNITIES: case BGP_ATTR_ORIGINATOR_ID: case BGP_ATTR_CLUSTER_LIST: @@ -1277,6 +1307,8 @@ const uint8_t attr_flags_values[] = { [BGP_ATTR_LARGE_COMMUNITIES] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_IPV6_EXT_COMMUNITIES] = + BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, }; static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1; @@ -1600,16 +1632,20 @@ bgp_attr_local_pref(struct bgp_attr_parser_args *args) external peer, then this attribute MUST be ignored by the receiving speaker. */ if (peer->sort == BGP_PEER_EBGP) { - stream_forward_getp(peer->curr, length); + STREAM_FORWARD_GETP(peer->curr, length); return BGP_ATTR_PARSE_PROCEED; } - attr->local_pref = stream_getl(peer->curr); + STREAM_GETL(peer->curr, attr->local_pref); /* Set the local-pref flag. */ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); return BGP_ATTR_PARSE_PROCEED; + +stream_failure: + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); } /* Atomic aggregate. */ @@ -2255,6 +2291,34 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args) return BGP_ATTR_PARSE_PROCEED; } +/* IPv6 Extended Community attribute. */ +static bgp_attr_parse_ret_t +bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + const bgp_size_t length = args->length; + + if (length == 0) { + attr->ipv6_ecommunity = NULL; + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); + } + + attr->ipv6_ecommunity = + ecommunity_parse_ipv6(stream_pnt(peer->curr), length); + /* XXX: fix ecommunity_parse to use stream API */ + stream_forward_getp(peer->curr, length); + + if (!attr->ipv6_ecommunity) + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES); + + return BGP_ATTR_PARSE_PROCEED; +} + /* Parse Tunnel Encap attribute in an UPDATE */ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ bgp_size_t length, /* IN: attr's length field */ @@ -2964,7 +3028,7 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, size_t lfl = CHECK_FLAG(flag, BGP_ATTR_FLAG_EXTLEN) ? 2 : 1; /* Rewind to end of flag field */ - stream_forward_getp(BGP_INPUT(peer), -(1 + lfl)); + stream_rewind_getp(BGP_INPUT(peer), (1 + lfl)); /* Type */ stream_get(&ndata[0], BGP_INPUT(peer), 1); /* Length */ @@ -3073,6 +3137,9 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, case BGP_ATTR_PMSI_TUNNEL: ret = bgp_attr_pmsi_tunnel(&attr_args); break; + case BGP_ATTR_IPV6_EXT_COMMUNITIES: + ret = bgp_attr_ipv6_ext_communities(&attr_args); + break; default: ret = bgp_attr_unknown(&attr_args); break; diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 1b2c75fbef..e6e953364b 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -24,6 +24,7 @@ #include "mpls.h" #include "bgp_attr_evpn.h" #include "bgpd/bgp_encap_types.h" +#include "srte.h" /* Simple bit mapping. */ #define BITMAP_NBBY 8 @@ -177,6 +178,9 @@ struct attr { /* Extended Communities attribute. */ struct ecommunity *ecommunity; + /* Extended Communities attribute. */ + struct ecommunity *ipv6_ecommunity; + /* Large Communities attribute. */ struct lcommunity *lcommunity; @@ -287,6 +291,9 @@ struct attr { /* EVPN ES */ esi_t esi; + + /* SR-TE Color */ + uint32_t srte_color; }; /* rmap_change_flags definition */ diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 255a7f238b..e9d7c9e8aa 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -2692,7 +2692,8 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, bgp_fs_nlri_get_string((unsigned char *)fs->prefix.ptr, fs->prefix.prefixlen, return_string, - NLRI_STRING_FORMAT_DEBUG, NULL); + NLRI_STRING_FORMAT_DEBUG, NULL, + family2afi(fs->prefix.family)); snprintf(str, size, "FS %s Match{%s}", afi2str(afi), return_string); } else diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index f2aac3646c..79fb7e55e9 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -48,7 +48,12 @@ static struct hash *ecomhash; /* Allocate a new ecommunities. */ struct ecommunity *ecommunity_new(void) { - return XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity)); + struct ecommunity *ecom; + + ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY, + sizeof(struct ecommunity)); + ecom->unit_size = ECOMMUNITY_SIZE; + return ecom; } void ecommunity_strfree(char **s) @@ -83,36 +88,56 @@ static void ecommunity_hash_free(struct ecommunity *ecom) once and whether the new value should replace what is existing or not. */ -bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, - bool unique, bool overwrite) +static bool ecommunity_add_val_internal(struct ecommunity *ecom, + const void *eval, + bool unique, bool overwrite, + uint8_t ecom_size) { int c, ins_idx; + const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval; + const struct ecommunity_val_ipv6 *eval6 = + (struct ecommunity_val_ipv6 *)eval; /* When this is fist value, just add it. */ if (ecom->val == NULL) { ecom->size = 1; - ecom->val = XCALLOC(MTYPE_ECOMMUNITY_VAL, ECOMMUNITY_SIZE); - memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE); + ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, + ecom_length_size(ecom, ecom_size)); + memcpy(ecom->val, eval, ecom_size); return true; } /* If the value already exists in the structure return 0. */ /* check also if the extended community itself exists. */ c = 0; + ins_idx = -1; for (uint8_t *p = ecom->val; c < ecom->size; - p += ECOMMUNITY_SIZE, c++) { + p += ecom_size, c++) { if (unique) { - if (p[0] == eval->val[0] && - p[1] == eval->val[1]) { - if (overwrite) { - memcpy(p, eval->val, ECOMMUNITY_SIZE); - return true; + if (ecom_size == ECOMMUNITY_SIZE) { + if (p[0] == eval4->val[0] && + p[1] == eval4->val[1]) { + if (overwrite) { + memcpy(p, eval4->val, + ecom_size); + return true; + } + return false; + } + } else { + if (p[0] == eval6->val[0] && + p[1] == eval6->val[1]) { + if (overwrite) { + memcpy(p, eval6->val, + ecom_size); + return true; + } + return false; } - return false; } } - int ret = memcmp(p, eval->val, ECOMMUNITY_SIZE); + int ret = memcmp(p, eval, ecom_size); if (ret == 0) return false; if (ret > 0) { @@ -129,61 +154,106 @@ bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, /* Add the value to the structure with numerical sorting. */ ecom->size++; ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, - ecom->size * ECOMMUNITY_SIZE); + ecom_length_size(ecom, ecom_size)); - memmove(ecom->val + ((ins_idx + 1) * ECOMMUNITY_SIZE), - ecom->val + (ins_idx * ECOMMUNITY_SIZE), - (ecom->size - 1 - ins_idx) * ECOMMUNITY_SIZE); - memcpy(ecom->val + (ins_idx * ECOMMUNITY_SIZE), - eval->val, ECOMMUNITY_SIZE); + + memmove(ecom->val + ((ins_idx + 1) * ecom_size), + ecom->val + (ins_idx * ecom_size), + (ecom->size - 1 - ins_idx) * ecom_size); + memcpy(ecom->val + (ins_idx * ecom_size), + eval, ecom_size); return true; } -/* This function takes pointer to Extended Communites strucutre then - create a new Extended Communities structure by uniq and sort each - Extended Communities value. */ -struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom) +/* Add a new Extended Communities value to Extended Communities + * Attribute structure. When the value is already exists in the + * structure, we don't add the value. Newly added value is sorted by + * numerical order. When the value is added to the structure return 1 + * else return 0. + */ +bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, + bool unique, bool overwrite) +{ + return ecommunity_add_val_internal(ecom, (const void *)eval, unique, + overwrite, ECOMMUNITY_SIZE); +} + +bool ecommunity_add_val_ipv6(struct ecommunity *ecom, + struct ecommunity_val_ipv6 *eval, + bool unique, bool overwrite) +{ + return ecommunity_add_val_internal(ecom, (const void *)eval, unique, + overwrite, IPV6_ECOMMUNITY_SIZE); +} + +static struct ecommunity * +ecommunity_uniq_sort_internal(struct ecommunity *ecom, + unsigned short ecom_size) { int i; struct ecommunity *new; - struct ecommunity_val *eval; + const void *eval; if (!ecom) return NULL; new = ecommunity_new(); + new->unit_size = ecom_size; for (i = 0; i < ecom->size; i++) { - eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); - ecommunity_add_val(new, eval, false, false); + eval = (void *)(ecom->val + (i * ecom_size)); + ecommunity_add_val_internal(new, eval, false, false, ecom_size); } return new; } +/* This function takes pointer to Extended Communites strucutre then + * create a new Extended Communities structure by uniq and sort each + * Extended Communities value. + */ +struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom) +{ + return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE); +} + /* Parse Extended Communites Attribute in BGP packet. */ -struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length) +static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt, + unsigned short length, + unsigned short size_ecom) { struct ecommunity tmp; struct ecommunity *new; /* Length check. */ - if (length % ECOMMUNITY_SIZE) + if (length % size_ecom) return NULL; /* Prepare tmporary structure for making a new Extended Communities Attribute. */ - tmp.size = length / ECOMMUNITY_SIZE; + tmp.size = length / size_ecom; tmp.val = pnt; /* Create a new Extended Communities Attribute by uniq and sort each Extended Communities value */ - new = ecommunity_uniq_sort(&tmp); + new = ecommunity_uniq_sort_internal(&tmp, size_ecom); return ecommunity_intern(new); } +struct ecommunity *ecommunity_parse(uint8_t *pnt, + unsigned short length) +{ + return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE); +} + +struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, + unsigned short length) +{ + return ecommunity_parse_internal(pnt, length, + IPV6_ECOMMUNITY_SIZE); +} + /* Duplicate the Extended Communities Attribute structure. */ struct ecommunity *ecommunity_dup(struct ecommunity *ecom) { @@ -191,10 +261,11 @@ struct ecommunity *ecommunity_dup(struct ecommunity *ecom) new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity)); new->size = ecom->size; + new->unit_size = ecom->unit_size; if (new->size) { new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, - ecom->size * ECOMMUNITY_SIZE); - memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE); + ecom->size * ecom->unit_size); + memcpy(new->val, ecom->val, ecom->size * ecom->unit_size); } else new->val = NULL; return new; @@ -216,14 +287,16 @@ struct ecommunity *ecommunity_merge(struct ecommunity *ecom1, if (ecom1->val) ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val, - (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); + (ecom1->size + ecom2->size) * + ecom1->unit_size); else ecom1->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, - (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE); + (ecom1->size + ecom2->size) * + ecom1->unit_size); - memcpy(ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val, - ecom2->size * ECOMMUNITY_SIZE); + memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val, + ecom2->size * ecom1->unit_size); ecom1->size += ecom2->size; return ecom1; @@ -235,9 +308,7 @@ struct ecommunity *ecommunity_intern(struct ecommunity *ecom) struct ecommunity *find; assert(ecom->refcnt == 0); - find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern); - if (find != ecom) ecommunity_free(&ecom); @@ -272,7 +343,7 @@ void ecommunity_unintern(struct ecommunity **ecom) unsigned int ecommunity_hash_make(const void *arg) { const struct ecommunity *ecom = arg; - int size = ecom->size * ECOMMUNITY_SIZE; + int size = ecom->size * ecom->unit_size; return jhash(ecom->val, size, 0x564321ab); } @@ -289,9 +360,12 @@ bool ecommunity_cmp(const void *arg1, const void *arg2) if (ecom1 == NULL || ecom2 == NULL) return false; + if (ecom1->unit_size != ecom2->unit_size) + return false; + return (ecom1->size == ecom2->size - && memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE) - == 0); + && memcmp(ecom1->val, ecom2->val, ecom1->size * + ecom1->unit_size) == 0); } /* Initialize Extended Comminities related hash. */ @@ -314,16 +388,21 @@ enum ecommunity_token { ecommunity_token_rt, ecommunity_token_soo, ecommunity_token_val, + ecommunity_token_rt6, + ecommunity_token_val6, }; -/* - * Encode BGP extended community from passed values. Supports types - * defined in RFC 4360 and well-known sub-types. - */ -static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, - struct in_addr ip, uint32_t val, - struct ecommunity_val *eval) +static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, + int trans, as_t as, + struct in_addr *ip, + struct in6_addr *ip6, + uint32_t val, + void *eval_ptr) { + struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; + struct ecommunity_val_ipv6 *eval6 = + (struct ecommunity_val_ipv6 *)eval_ptr; + assert(eval); if (type == ECOMMUNITY_ENCODE_AS) { if (as > BGP_AS_MAX) @@ -332,6 +411,10 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, || type == ECOMMUNITY_ENCODE_AS4) { if (val > UINT16_MAX) return -1; + } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && + sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 && + (!ip6 || val > UINT16_MAX)) { + return -1; } /* Fill in the values. */ @@ -347,9 +430,14 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, eval->val[6] = (val >> 8) & 0xff; eval->val[7] = val & 0xff; } else if (type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval->val[2], &ip, sizeof(struct in_addr)); + memcpy(&eval->val[2], ip, sizeof(struct in_addr)); eval->val[6] = (val >> 8) & 0xff; eval->val[7] = val & 0xff; + } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && + sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { + memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr)); + eval6->val[18] = (val >> 8) & 0xff; + eval6->val[19] = val & 0xff; } else { eval->val[2] = (as >> 24) & 0xff; eval->val[3] = (as >> 16) & 0xff; @@ -362,9 +450,21 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, return 0; } +/* + * Encode BGP extended community from passed values. Supports types + * defined in RFC 4360 and well-known sub-types. + */ +static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, + struct in_addr ip, uint32_t val, + struct ecommunity_val *eval) +{ + return ecommunity_encode_internal(type, sub_type, trans, as, + &ip, NULL, val, (void *)eval); +} + /* Get next Extended Communities token from the string. */ static const char *ecommunity_gettoken(const char *str, - struct ecommunity_val *eval, + void *eval_ptr, enum ecommunity_token *token) { int ret; @@ -374,11 +474,12 @@ static const char *ecommunity_gettoken(const char *str, const char *p = str; char *endptr; struct in_addr ip; + struct in6_addr ip6; as_t as = 0; uint32_t val = 0; uint8_t ecomm_type; char buf[INET_ADDRSTRLEN + 1]; - + struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; /* Skip white space. */ while (isspace((unsigned char)*p)) { p++; @@ -396,7 +497,10 @@ static const char *ecommunity_gettoken(const char *str, p++; if (tolower((unsigned char)*p) == 't') { p++; - *token = ecommunity_token_rt; + if (*p != '\0' && tolower((int)*p) == '6') + *token = ecommunity_token_rt6; + else + *token = ecommunity_token_rt; return p; } if (isspace((unsigned char)*p) || *p == '\0') { @@ -435,6 +539,7 @@ static const char *ecommunity_gettoken(const char *str, * a) A.B.C.D:MN * b) EF:OPQR * c) GHJK:MN + * d) <IPV6>:MN (only with rt6) * * A.B.C.D: Four Byte IP * EF: Two byte ASN @@ -443,6 +548,38 @@ static const char *ecommunity_gettoken(const char *str, * OPQR: Four byte value * */ + /* IPv6 case : look for last ':' */ + if (*token == ecommunity_token_rt6 || + *token == ecommunity_token_val6) { + char *limit; + + limit = endptr = strrchr(p, ':'); + if (!endptr) + goto error; + + endptr++; + as = strtoul(endptr, &endptr, 10); + if (*endptr != '\0' || as == BGP_AS4_MAX) + goto error; + + memcpy(buf, p, (limit - p)); + buf[limit - p] = '\0'; + ret = inet_pton(AF_INET6, buf, &ip6); + if (ret == 0) + goto error; + + ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP; + if (ecommunity_encode_internal(ecomm_type, + ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6, + 1, 0, NULL, &ip6, as, eval_ptr)) + goto error; + + *token = ecommunity_token_val6; + while (isdigit((int)*p) || *p == ':' || *p == '.') { + p++; + } + return p; + } while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') { if (*p == ':') { if (separator) @@ -508,41 +645,18 @@ error: return p; } -/* Convert string to extended community attribute. - - When type is already known, please specify both str and type. str - should not include keyword such as "rt" and "soo". Type is - ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN. - keyword_included should be zero. - - For example route-map's "set extcommunity" command case: - - "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3" - type = ECOMMUNITY_ROUTE_TARGET - keyword_included = 0 - - "soo 100:1" -> str = "100:1" - type = ECOMMUNITY_SITE_ORIGIN - keyword_included = 0 - - When string includes keyword for each extended community value. - Please specify keyword_included as non-zero value. - - For example standard extcommunity-list case: - - "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1" - type = 0 - keyword_include = 1 -*/ -struct ecommunity *ecommunity_str2com(const char *str, int type, - int keyword_included) +static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, + int keyword_included, + bool is_ipv6_extcomm) { struct ecommunity *ecom = NULL; enum ecommunity_token token = ecommunity_token_unknown; - struct ecommunity_val eval; + struct ecommunity_val_ipv6 eval; int keyword = 0; - while ((str = ecommunity_gettoken(str, &eval, &token))) { + if (is_ipv6_extcomm) + token = ecommunity_token_rt6; + while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) { switch (token) { case ecommunity_token_rt: case ecommunity_token_soo: @@ -553,7 +667,8 @@ struct ecommunity *ecommunity_str2com(const char *str, int type, } keyword = 1; - if (token == ecommunity_token_rt) { + if (token == ecommunity_token_rt || + token == ecommunity_token_rt6) { type = ECOMMUNITY_ROUTE_TARGET; } if (token == ecommunity_token_soo) { @@ -563,8 +678,7 @@ struct ecommunity *ecommunity_str2com(const char *str, int type, case ecommunity_token_val: if (keyword_included) { if (!keyword) { - if (ecom) - ecommunity_free(&ecom); + ecommunity_free(&ecom); return NULL; } keyword = 0; @@ -572,7 +686,24 @@ struct ecommunity *ecommunity_str2com(const char *str, int type, if (ecom == NULL) ecom = ecommunity_new(); eval.val[1] = type; - ecommunity_add_val(ecom, &eval, false, false); + ecommunity_add_val_internal(ecom, (void *)&eval, + false, false, + ecom->unit_size); + break; + case ecommunity_token_val6: + if (keyword_included) { + if (!keyword) { + ecommunity_free(&ecom); + return NULL; + } + keyword = 0; + } + if (ecom == NULL) + ecom = ecommunity_new(); + ecom->unit_size = IPV6_ECOMMUNITY_SIZE; + eval.val[1] = type; + ecommunity_add_val_internal(ecom, (void *)&eval, false, false, + ecom->unit_size); break; case ecommunity_token_unknown: default: @@ -584,16 +715,59 @@ struct ecommunity *ecommunity_str2com(const char *str, int type, return ecom; } -static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, - int type, int sub_type, int format) +/* Convert string to extended community attribute. + * + * When type is already known, please specify both str and type. str + * should not include keyword such as "rt" and "soo". Type is + * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN. + * keyword_included should be zero. + * + * For example route-map's "set extcommunity" command case: + * + * "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3" + * type = ECOMMUNITY_ROUTE_TARGET + * keyword_included = 0 + * + * "soo 100:1" -> str = "100:1" + * type = ECOMMUNITY_SITE_ORIGIN + * keyword_included = 0 + * + * When string includes keyword for each extended community value. + * Please specify keyword_included as non-zero value. + * + * For example standard extcommunity-list case: + * + * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1" + * type = 0 + * keyword_include = 1 + */ +struct ecommunity *ecommunity_str2com(const char *str, int type, + int keyword_included) +{ + return ecommunity_str2com_internal(str, type, + keyword_included, false); +} + +struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type, + int keyword_included) +{ + return ecommunity_str2com_internal(str, type, + keyword_included, true); +} + +static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz, + const uint8_t *pnt, int type, + int sub_type, int format, + unsigned short ecom_size) { int len = 0; const char *prefix; + char buf_local[INET6_ADDRSTRLEN]; /* For parse Extended Community attribute tupple. */ struct ecommunity_as eas; struct ecommunity_ip eip; - + struct ecommunity_ip6 eip6; /* Determine prefix for string, if any. */ switch (format) { @@ -619,11 +793,27 @@ static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val); } else if (type == ECOMMUNITY_ENCODE_AS) { - eas.as = (*pnt++ << 8); - eas.as |= (*pnt++); - pnt = ptr_get_be32(pnt, &eas.val); + if (ecom_size == ECOMMUNITY_SIZE) { + eas.as = (*pnt++ << 8); + eas.as |= (*pnt++); + pnt = ptr_get_be32(pnt, &eas.val); - len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val); + len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, + eas.val); + } else { + /* this is an IPv6 ext community + * first 16 bytes stands for IPv6 addres + */ + memcpy(&eip6.ip, pnt, 16); + pnt += 16; + eip6.val = (*pnt++ << 8); + eip6.val |= (*pnt++); + + inet_ntop(AF_INET6, &eip6.ip, buf_local, + sizeof(buf_local)); + len = snprintf(buf, bufsz, "%s%s:%u", prefix, + buf_local, eip6.val); + } } else if (type == ECOMMUNITY_ENCODE_IP) { memcpy(&eip.ip, pnt, 4); pnt += 4; @@ -640,6 +830,14 @@ static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, return len; } +static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, + int type, int sub_type, int format) +{ + return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type, + sub_type, format, + ECOMMUNITY_SIZE); +} + static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt) { int len = 0; @@ -852,13 +1050,45 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 || type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) { sub_type = *pnt++; - if (sub_type == ECOMMUNITY_REDIRECT_VRF) { - char buf[16] = {}; - ecommunity_rt_soo_str( - buf, sizeof(buf), pnt, - type & ~ECOMMUNITY_ENCODE_TRANS_EXP, - ECOMMUNITY_ROUTE_TARGET, - ECOMMUNITY_FORMAT_DISPLAY); + + if (sub_type == ECOMMUNITY_ROUTE_TARGET) { + char buf[ECOMMUNITY_STRLEN]; + + memset(buf, 0, sizeof(buf)); + ecommunity_rt_soo_str_internal(buf, sizeof(buf), + (const uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + format, + ecom->unit_size); + snprintf(encbuf, sizeof(encbuf), "%s", buf); + } else if (sub_type == + ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { + char buf[64]; + + memset(buf, 0, sizeof(buf)); + ecommunity_rt_soo_str_internal(buf, sizeof(buf), + (const uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + ECOMMUNITY_FORMAT_DISPLAY, + ecom->unit_size); + snprintf(encbuf, sizeof(encbuf), + "FS:redirect VRF %s", buf); + } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) { + char buf[16]; + + memset(buf, 0, sizeof(buf)); + ecommunity_rt_soo_str(buf, sizeof(buf), + (const uint8_t *)pnt, + type & + ~ECOMMUNITY_ENCODE_TRANS_EXP, + ECOMMUNITY_ROUTE_TARGET, + ECOMMUNITY_FORMAT_DISPLAY); + snprintf(encbuf, sizeof(encbuf), + "FS:redirect VRF %s", buf); snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf); } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP) @@ -935,8 +1165,9 @@ bool ecommunity_match(const struct ecommunity *ecom1, /* Every community on com2 needs to be on com1 for this to match */ while (i < ecom1->size && j < ecom2->size) { - if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE, - ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE) + if (memcmp(ecom1->val + i * ecom1->unit_size, + ecom2->val + j * ecom2->unit_size, + ecom2->unit_size) == 0) j++; i++; @@ -957,7 +1188,7 @@ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom, /* If the value already exists in the structure return 0. */ c = 0; - for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { if (p == NULL) { continue; } @@ -984,7 +1215,7 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, * multiple times, handle that. */ c = 0; - for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { if (p[0] == type && p[1] == subtype) found++; } @@ -1001,12 +1232,12 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, /* Strip matching ext community(ies). */ new = XMALLOC(MTYPE_ECOMMUNITY_VAL, - (ecom->size - found) * ECOMMUNITY_SIZE); + (ecom->size - found) * ecom->unit_size); q = new; - for (c = 0, p = ecom->val; c < ecom->size; c++, p += ECOMMUNITY_SIZE) { + for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) { if (!(p[0] == type && p[1] == subtype)) { - memcpy(q, p, ECOMMUNITY_SIZE); - q += ECOMMUNITY_SIZE; + memcpy(q, p, ecom->unit_size); + q += ecom->unit_size; } } XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); @@ -1028,8 +1259,8 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval) if (ecom == NULL || ecom->val == NULL) return false; c = 0; - for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) { - if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) { + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { + if (!memcmp(p, eval->val, ecom->unit_size)) { found = 1; break; } @@ -1039,20 +1270,21 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval) /* Delete the selected value */ ecom->size--; - p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE); + p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size); if (c != 0) - memcpy(p, ecom->val, c * ECOMMUNITY_SIZE); + memcpy(p, ecom->val, c * ecom->unit_size); if ((ecom->size - c) != 0) - memcpy(p + (c)*ECOMMUNITY_SIZE, - ecom->val + (c + 1) * ECOMMUNITY_SIZE, - (ecom->size - c) * ECOMMUNITY_SIZE); + memcpy(p + (c)*ecom->unit_size, + ecom->val + (c + 1) * ecom->unit_size, + (ecom->size - c) * ecom->unit_size); XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); ecom->val = p; return true; } int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, - struct bgp_pbr_entry_action *api) + struct bgp_pbr_entry_action *api, + afi_t afi) { if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) { api->action = ACTION_TRAFFICRATE; @@ -1076,7 +1308,8 @@ int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) { /* must use external function */ return 0; - } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH) { + } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH && + afi == AFI_IP) { /* see draft-ietf-idr-flowspec-redirect-ip-02 * Q1: how come a ext. community can host ipv6 address * Q2 : from cisco documentation: diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 812bcc46e7..fe90efe1f7 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -63,6 +63,10 @@ * 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect */ #define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 0x0c +/* from draft-ietf-idr-flow-spec-v6-09 + * 0x0b Flow-spec Redirect to IPv6 + */ +#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 0x0b /* Low-order octet of the Extended Communities type field for EVPN types */ #define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00 @@ -90,6 +94,7 @@ /* Extended Communities value is eight octet long. */ #define ECOMMUNITY_SIZE 8 +#define IPV6_ECOMMUNITY_SIZE 20 /* Extended Communities type flag. */ #define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40 @@ -99,6 +104,11 @@ struct ecommunity { /* Reference counter. */ unsigned long refcnt; + /* Size of Each Unit of Extended Communities attribute. + * to differentiate between IPv6 ext comm and ext comm + */ + uint8_t unit_size; + /* Size of Extended Communities attribute. */ int size; @@ -119,11 +129,23 @@ struct ecommunity_ip { uint16_t val; }; +struct ecommunity_ip6 { + struct in6_addr ip; + uint16_t val; +}; + /* Extended community value is eight octet. */ struct ecommunity_val { char val[ECOMMUNITY_SIZE]; }; +/* IPv6 Extended community value is eight octet. */ +struct ecommunity_val_ipv6 { + char val[IPV6_ECOMMUNITY_SIZE]; +}; + +#define ecom_length_size(X, Y) ((X)->size * (Y)) + /* * Encode BGP Route Target AS:nn. */ @@ -193,6 +215,8 @@ extern void ecommunity_init(void); extern void ecommunity_finish(void); extern void ecommunity_free(struct ecommunity **); extern struct ecommunity *ecommunity_parse(uint8_t *, unsigned short); +extern struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, + unsigned short length); extern struct ecommunity *ecommunity_dup(struct ecommunity *); extern struct ecommunity *ecommunity_merge(struct ecommunity *, struct ecommunity *); @@ -202,6 +226,8 @@ extern bool ecommunity_cmp(const void *arg1, const void *arg2); extern void ecommunity_unintern(struct ecommunity **); extern unsigned int ecommunity_hash_make(const void *); extern struct ecommunity *ecommunity_str2com(const char *, int, int); +extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type, + int keyword_included); extern char *ecommunity_ecom2str(struct ecommunity *, int, int); extern void ecommunity_strfree(char **s); extern bool ecommunity_match(const struct ecommunity *, @@ -209,9 +235,13 @@ extern bool ecommunity_match(const struct ecommunity *, extern char *ecommunity_str(struct ecommunity *); extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *, uint8_t, uint8_t); + extern bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, bool unique, bool overwrite); +extern bool ecommunity_add_val_ipv6(struct ecommunity *ecom, + struct ecommunity_val_ipv6 *eval, + bool unique, bool overwrite); /* for vpn */ extern struct ecommunity *ecommunity_new(void); @@ -222,7 +252,8 @@ extern bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval); struct bgp_pbr_entry_action; extern int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, - struct bgp_pbr_entry_action *api); + struct bgp_pbr_entry_action *api, + afi_t afi); extern void bgp_compute_aggregate_ecommunity( struct bgp_aggregate *aggregate, diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 4a5d5c3b6e..8c3e54566e 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -726,6 +726,7 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; /* Add Encap */ @@ -788,6 +789,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; /* Add Encap */ @@ -816,6 +818,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_sticky, 0, sizeof(ecom_sticky)); encode_mac_mobility_extcomm(1, seqnum, &eval_sticky); ecom_sticky.size = 1; + ecom_sticky.unit_size = ECOMMUNITY_SIZE; ecom_sticky.val = (uint8_t *)eval_sticky.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_sticky); @@ -832,6 +835,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_default_gw, 0, sizeof(ecom_default_gw)); encode_default_gw_extcomm(&eval_default_gw); ecom_default_gw.size = 1; + ecom_default_gw.unit_size = ECOMMUNITY_SIZE; ecom_default_gw.val = (uint8_t *)eval_default_gw.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_default_gw); @@ -842,6 +846,7 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, memset(&ecom_na, 0, sizeof(ecom_na)); encode_na_flag_extcomm(&eval_na, attr->router_flag, proxy); ecom_na.size = 1; + ecom_na.unit_size = ECOMMUNITY_SIZE; ecom_na.val = (uint8_t *)eval_na.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_na); @@ -871,7 +876,8 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) if (attr->ecommunity) { for (i = 0; i < attr->ecommunity->size; i++) { - pnt = attr->ecommunity->val + (i * 8); + pnt = attr->ecommunity->val + + (i * attr->ecommunity->unit_size); type = *pnt++; sub_type = *pnt++; @@ -879,7 +885,8 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) && sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { ecom_val_ptr = - (attr->ecommunity->val + (i * 8)); + (attr->ecommunity->val + + (i * attr->ecommunity->unit_size)); break; } } @@ -887,12 +894,14 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr) /* Update the existing MM ecommunity */ if (ecom_val_ptr) { - memcpy(ecom_val_ptr, eval.val, sizeof(char) * ECOMMUNITY_SIZE); + memcpy(ecom_val_ptr, eval.val, sizeof(char) + * attr->ecommunity->unit_size); } /* Add MM to existing */ else { memset(&ecom_tmp, 0, sizeof(ecom_tmp)); ecom_tmp.size = 1; + ecom_tmp.unit_size = ECOMMUNITY_SIZE; ecom_tmp.val = (uint8_t *)eval.val; if (attr->ecommunity) @@ -2713,9 +2722,9 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf, struct vrf_irt_node *irt; /* Only deal with RTs */ - pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + pnt = (ecom->val + (i * ecom->unit_size)); eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); + + (i * ecom->unit_size)); type = *pnt++; sub_type = *pnt++; if (sub_type != ECOMMUNITY_ROUTE_TARGET) @@ -2737,7 +2746,7 @@ static int is_route_matching_for_vrf(struct bgp *bgp_vrf, if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + memcpy(&eval_tmp, eval, ecom->unit_size); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_vrf_import_rt(&eval_tmp); } @@ -2780,9 +2789,9 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, struct irt_node *irt; /* Only deal with RTs */ - pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + pnt = (ecom->val + (i * ecom->unit_size)); eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); + + (i * ecom->unit_size)); type = *pnt++; sub_type = *pnt++; if (sub_type != ECOMMUNITY_ROUTE_TARGET) @@ -2804,7 +2813,7 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + memcpy(&eval_tmp, eval, ecom->unit_size); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); } @@ -2836,7 +2845,7 @@ static int bgp_evpn_route_rmac_self_check(struct bgp *bgp_vrf, char buf1[PREFIX_STRLEN]; char attr_str[BUFSIZ] = {0}; - bgp_dump_attr(pi->attr, attr_str, BUFSIZ); + bgp_dump_attr(pi->attr, attr_str, sizeof(attr_str)); zlog_debug("%s: bgp %u prefix %s with attr %s - DENIED due to self mac", __func__, bgp_vrf->vrf_id, @@ -3220,9 +3229,9 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, struct bgp_evpn_es *es; /* Only deal with RTs */ - pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + pnt = (ecom->val + (i * ecom->unit_size)); eval = (struct ecommunity_val *)(ecom->val - + (i * ECOMMUNITY_SIZE)); + + (i * ecom->unit_size)); type = *pnt++; sub_type = *pnt++; if (sub_type != ECOMMUNITY_ROUTE_TARGET) @@ -3261,7 +3270,7 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi, if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_AS4 || type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE); + memcpy(&eval_tmp, eval, ecom->unit_size); mask_ecom_global_admin(&eval_tmp, eval); irt = lookup_import_rt(bgp, &eval_tmp); vrf_irt = lookup_vrf_import_rt(&eval_tmp); @@ -3590,11 +3599,12 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, uint32_t addpath_id) { struct prefix_rd prd; - struct prefix_evpn p; - struct bgp_route_evpn evpn; + struct prefix_evpn p = {}; + struct bgp_route_evpn evpn = {}; uint8_t ipaddr_len; uint8_t macaddr_len; - mpls_label_t label[BGP_MAX_LABELS]; /* holds the VNI(s) as in packet */ + /* holds the VNI(s) as in packet */ + mpls_label_t label[BGP_MAX_LABELS] = {}; uint32_t num_labels = 0; uint32_t eth_tag; int ret; @@ -3613,60 +3623,61 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, return -1; } - memset(&evpn, 0, sizeof(evpn)); + struct stream *pkt = stream_new(psize); + stream_put(pkt, pfx, psize); /* Make prefix_rd */ prd.family = AF_UNSPEC; prd.prefixlen = 64; - memcpy(&prd.val, pfx, 8); - pfx += 8; + + STREAM_GET(&prd.val, pkt, 8); /* Make EVPN prefix. */ - memset(&p, 0, sizeof(struct prefix_evpn)); p.family = AF_EVPN; p.prefixlen = EVPN_ROUTE_PREFIXLEN; p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE; /* Copy Ethernet Seg Identifier */ if (attr) { - memcpy(&attr->esi, pfx, sizeof(esi_t)); + STREAM_GET(&attr->esi, pkt, sizeof(esi_t)); + if (bgp_evpn_is_esi_local(&attr->esi)) attr->es_flags |= ATTR_ES_IS_LOCAL; else attr->es_flags &= ~ATTR_ES_IS_LOCAL; + } else { + STREAM_FORWARD_GETP(pkt, sizeof(esi_t)); } - pfx += sizeof(esi_t); /* Copy Ethernet Tag */ - memcpy(ð_tag, pfx, 4); + STREAM_GET(ð_tag, pkt, 4); p.prefix.macip_addr.eth_tag = ntohl(eth_tag); - pfx += 4; /* Get the MAC Addr len */ - macaddr_len = *pfx++; + STREAM_GETC(pkt, macaddr_len); /* Get the MAC Addr */ if (macaddr_len == (ETH_ALEN * 8)) { - memcpy(&p.prefix.macip_addr.mac.octet, pfx, ETH_ALEN); - pfx += ETH_ALEN; + STREAM_GET(&p.prefix.macip_addr.mac.octet, pkt, ETH_ALEN); } else { flog_err( EC_BGP_EVPN_ROUTE_INVALID, "%u:%s - Rx EVPN Type-2 NLRI with unsupported MAC address length %d", peer->bgp->vrf_id, peer->host, macaddr_len); - return -1; + goto fail; } /* Get the IP. */ - ipaddr_len = *pfx++; + STREAM_GETC(pkt, ipaddr_len); + if (ipaddr_len != 0 && ipaddr_len != IPV4_MAX_BITLEN && ipaddr_len != IPV6_MAX_BITLEN) { flog_err( EC_BGP_EVPN_ROUTE_INVALID, "%u:%s - Rx EVPN Type-2 NLRI with unsupported IP address length %d", peer->bgp->vrf_id, peer->host, ipaddr_len); - return -1; + goto fail; } if (ipaddr_len) { @@ -3674,25 +3685,17 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, p.prefix.macip_addr.ip.ipa_type = (ipaddr_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 : IPADDR_V6; - memcpy(&p.prefix.macip_addr.ip.ip.addr, pfx, ipaddr_len); + STREAM_GET(&p.prefix.macip_addr.ip.ip.addr, pkt, ipaddr_len); } - pfx += ipaddr_len; /* Get the VNI(s). Stored as bytes here. */ + STREAM_GET(&label[0], pkt, BGP_LABEL_BYTES); num_labels++; - memset(label, 0, sizeof(label)); - memcpy(&label[0], pfx, BGP_LABEL_BYTES); - pfx += BGP_LABEL_BYTES; - psize -= (33 + ipaddr_len); + /* Do we have a second VNI? */ - if (psize) { + if (STREAM_READABLE(pkt)) { num_labels++; - memcpy(&label[1], pfx, BGP_LABEL_BYTES); - /* - * If in future, we are required to access additional fields, - * we MUST increment pfx by BGP_LABEL_BYTES in before reading - * the next field - */ + STREAM_GET(&label[1], pkt, BGP_LABEL_BYTES); } /* Process the route. */ @@ -3704,6 +3707,16 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi, ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, &label[0], num_labels, &evpn); + goto done; + +fail: +stream_failure: + flog_err(EC_BGP_EVPN_ROUTE_INVALID, + "%u:%s - Rx EVPN Type-2 NLRI - corrupt, discarding", + peer->bgp->vrf_id, peer->host); + ret = -1; +done: + stream_free(pkt); return ret; } @@ -5190,7 +5203,8 @@ int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) char attr_str[BUFSIZ] = {0}; bgp_dump_attr(pi->attr, - attr_str, BUFSIZ); + attr_str, + sizeof(attr_str)); zlog_debug( "%u: prefix %pRN with attr %s - DENIED due to martian or self nexthop", diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index eb65c43bb9..934c22d168 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -523,6 +523,7 @@ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; attr->ecommunity = ecommunity_dup(&ecom_encap); @@ -532,6 +533,7 @@ static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es, es_get_system_mac(&es->esi, &mac); encode_es_rt_extcomm(&eval_es_rt, &mac); ecom_es_rt.size = 1; + ecom_es_rt.unit_size = ECOMMUNITY_SIZE; ecom_es_rt.val = (uint8_t *)eval_es_rt.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_es_rt); @@ -775,6 +777,7 @@ static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; attr->ecommunity = ecommunity_dup(&ecom_encap); @@ -782,6 +785,7 @@ static void bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es *es, encode_esi_label_extcomm(&eval_esi_label, false /*single_active*/); ecom_esi_label.size = 1; + ecom_esi_label.unit_size = ECOMMUNITY_SIZE; ecom_esi_label.val = (uint8_t *)eval_esi_label.val; attr->ecommunity = ecommunity_merge(attr->ecommunity, &ecom_esi_label); @@ -818,6 +822,7 @@ static void bgp_evpn_type1_evi_route_extcomm_build(struct bgp_evpn_es *es, memset(&ecom_encap, 0, sizeof(ecom_encap)); encode_encap_extcomm(tnl_type, &eval); ecom_encap.size = 1; + ecom_encap.unit_size = ECOMMUNITY_SIZE; ecom_encap.val = (uint8_t *)eval.val; attr->ecommunity = ecommunity_dup(&ecom_encap); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 3a198b20f6..15ecffdc72 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -4002,6 +4002,7 @@ DEFUN(show_bgp_l2vpn_evpn_route, "Display Detailed Information\n" "Specify Route type\n" "EAD (Type-1) route\n" + "EAD (Type-1) route\n" "MAC-IP (Type-2) route\n" "MAC-IP (Type-2) route\n" "Multicast (Type-3) route\n" diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index 17c41636de..341cfe9d07 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -33,7 +33,8 @@ #include "bgpd/bgp_debug.h" #include "bgpd/bgp_errors.h" -static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) +static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len, + afi_t afi) { uint32_t offset = 0; int type; @@ -48,7 +49,15 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len) ret = bgp_flowspec_ip_address( BGP_FLOWSPEC_VALIDATE_ONLY, nlri_content + offset, - len - offset, NULL, &error); + len - offset, NULL, &error, + afi, NULL); + break; + case FLOWSPEC_FLOW_LABEL: + if (afi == AFI_IP) + return -1; + ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content + offset, + len - offset, NULL, &error); break; case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: @@ -103,11 +112,6 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, afi = packet->afi; safi = packet->safi; - if (afi == AFI_IP6) { - flog_err(EC_LIB_DEVELOPMENT, "BGP flowspec IPv6 not supported"); - return BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED; - } - if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT_EXTENDED) { flog_err(EC_BGP_FLOWSPEC_PACKET, "BGP flowspec nlri length maximum reached (%u)", @@ -137,7 +141,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, psize); return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } - if (bgp_fs_nlri_validate(pnt, psize) < 0) { + if (bgp_fs_nlri_validate(pnt, psize, afi) < 0) { flog_err( EC_BGP_FLOWSPEC_PACKET, "Bad flowspec format or NLRI options not supported"); @@ -147,6 +151,7 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, p.prefixlen = 0; /* Flowspec encoding is in bytes */ p.u.prefix_flowspec.prefixlen = psize; + p.u.prefix_flowspec.family = afi2family(afi); temp = XCALLOC(MTYPE_TMP, psize); memcpy(temp, pnt, psize); p.u.prefix_flowspec.ptr = (uintptr_t) temp; @@ -161,7 +166,8 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, p.u.prefix_flowspec.ptr, p.u.prefix_flowspec.prefixlen, return_string, - NLRI_STRING_FORMAT_MIN, NULL); + NLRI_STRING_FORMAT_MIN, NULL, + afi); snprintf(ec_string, sizeof(ec_string), "EC{none}"); if (attr && attr->ecommunity) { diff --git a/bgpd/bgp_flowspec.h b/bgpd/bgp_flowspec.h index 94c571f2fc..d1a59a672d 100644 --- a/bgpd/bgp_flowspec.h +++ b/bgpd/bgp_flowspec.h @@ -41,7 +41,8 @@ extern int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi, extern void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, char *return_string, int format, - json_object *json_path); + json_object *json_path, + afi_t afi); extern void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, struct bgp_path_info *path, int display, diff --git a/bgpd/bgp_flowspec_private.h b/bgpd/bgp_flowspec_private.h index cec244c165..757a8ae2e9 100644 --- a/bgpd/bgp_flowspec_private.h +++ b/bgpd/bgp_flowspec_private.h @@ -41,5 +41,6 @@ #define FLOWSPEC_PKT_LEN 10 #define FLOWSPEC_DSCP 11 #define FLOWSPEC_FRAGMENT 12 +#define FLOWSPEC_FLOW_LABEL 13 /* For IPv6 only */ #endif /* _FRR_BGP_FLOWSPEC_PRIVATE_H */ diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 764d2f87ae..90e9236385 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -76,6 +76,7 @@ static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len, return ret; } + bool bgp_flowspec_contains_prefix(const struct prefix *pfs, struct prefix *input, int prefix_check) { @@ -84,6 +85,7 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs, int ret = 0, error = 0; uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr; size_t len = pfs->u.prefix_flowspec.prefixlen; + afi_t afi = family2afi(pfs->u.prefix_flowspec.family); struct prefix compare; error = 0; @@ -98,7 +100,8 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs, BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, nlri_content+offset, len - offset, - &compare, &error); + &compare, &error, + afi, NULL); if (ret <= 0) break; if (prefix_check && @@ -115,6 +118,16 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs, &compare.u.prefix6.s6_addr)) return true; break; + case FLOWSPEC_FLOW_LABEL: + if (afi == AFI_IP) { + error = -1; + continue; + } + ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY, + nlri_content+offset, + len - offset, + NULL, &error); + break; case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: case FLOWSPEC_DEST_PORT: @@ -158,13 +171,16 @@ bool bgp_flowspec_contains_prefix(const struct prefix *pfs, int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, - void *result, int *error) + void *result, int *error, + afi_t afi, + uint8_t *ipv6_offset) { char *display = (char *)result; /* for return_string */ struct prefix *prefix = (struct prefix *)result; uint32_t offset = 0; struct prefix prefix_local; int psize; + uint8_t prefix_offset = 0; *error = 0; memset(&prefix_local, 0, sizeof(struct prefix)); @@ -172,8 +188,13 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, prefix_local.prefixlen = nlri_ptr[offset]; psize = PSIZE(prefix_local.prefixlen); offset++; - /* TODO Flowspec IPv6 Support */ - prefix_local.family = AF_INET; + prefix_local.family = afi2family(afi); + if (prefix_local.family == AF_INET6) { + prefix_offset = nlri_ptr[offset]; + if (ipv6_offset) + *ipv6_offset = prefix_offset; + offset++; + } /* Prefix length check. */ if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8) *error = -1; @@ -189,11 +210,30 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, offset += psize; switch (type) { case BGP_FLOWSPEC_RETURN_STRING: - prefix2str(&prefix_local, display, - BGP_FLOWSPEC_STRING_DISPLAY_MAX); + if (prefix_local.family == AF_INET6) { + char str[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; + int ret; + + prefix2str(&prefix_local, str, + BGP_FLOWSPEC_STRING_DISPLAY_MAX); + ret = snprintf(display, BGP_FLOWSPEC_STRING_DISPLAY_MAX, + "%s/off %u", + str, prefix_offset); + if (ret < 0) { + *error = -1; + break; + } + } else + prefix2str(&prefix_local, display, + BGP_FLOWSPEC_STRING_DISPLAY_MAX); break; case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE: - PREFIX_COPY_IPV4(prefix, &prefix_local) + if (prefix) { + if (prefix_local.family == AF_INET) + PREFIX_COPY_IPV4(prefix, &prefix_local) + else + PREFIX_COPY_IPV6(prefix, &prefix_local) + } break; case BGP_FLOWSPEC_VALIDATE_ONLY: default: @@ -417,7 +457,8 @@ int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, } int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, - struct bgp_pbr_entry_main *bpem) + struct bgp_pbr_entry_main *bpem, + afi_t afi) { int offset = 0, error = 0; struct prefix *prefix; @@ -425,6 +466,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, uint8_t *match_num; uint8_t bitmask = 0; int ret = 0, type; + uint8_t *prefix_offset; while (offset < len - 1 && error >= 0) { type = nlri_content[offset]; @@ -436,15 +478,18 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, if (type == FLOWSPEC_DEST_PREFIX) { bitmask |= PREFIX_DST_PRESENT; prefix = &bpem->dst_prefix; + prefix_offset = &bpem->dst_prefix_offset; } else { bitmask |= PREFIX_SRC_PRESENT; prefix = &bpem->src_prefix; + prefix_offset = &bpem->src_prefix_offset; } ret = bgp_flowspec_ip_address( BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE, nlri_content + offset, len - offset, - prefix, &error); + prefix, &error, + afi, prefix_offset); if (error < 0) flog_err(EC_BGP_FLOWSPEC_PACKET, "%s: flowspec_ip_address error %d", @@ -456,11 +501,30 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, if (prefix->family == AF_INET && prefix->u.prefix4.s_addr == INADDR_ANY) bpem->match_bitmask_iprule |= bitmask; + else if (prefix->family == AF_INET6 + && !memcmp(&prefix->u.prefix6, + &in6addr_any, + sizeof(struct in6_addr))) + bpem->match_bitmask_iprule |= bitmask; else bpem->match_bitmask |= bitmask; } offset += ret; break; + case FLOWSPEC_FLOW_LABEL: + if (afi == AFI_IP) { + error = -1; + continue; + } + match_num = &(bpem->match_flowlabel_num); + mval = (struct bgp_pbr_match_val *) + &(bpem->flow_label); + offset += bgp_flowspec_call_non_opaque_decode( + nlri_content + offset, + len - offset, + mval, match_num, + &error); + break; case FLOWSPEC_IP_PROTOCOL: match_num = &(bpem->match_protocol_num); mval = (struct bgp_pbr_match_val *) @@ -583,7 +647,8 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, bpem->match_packet_length_num || bpem->match_icmp_code_num || bpem->match_icmp_type_num || bpem->match_port_num || bpem->match_src_port_num || bpem->match_dst_port_num || - bpem->match_protocol_num || bpem->match_bitmask) + bpem->match_protocol_num || bpem->match_bitmask || + bpem->match_flowlabel_num) bpem->type = BGP_PBR_IPSET; else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) || (bpem->match_bitmask_iprule & PREFIX_DST_PRESENT)) @@ -599,7 +664,7 @@ int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, /* return 1 if FS entry invalid or no NH IP */ bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, - struct prefix *p) + struct prefix *p, afi_t afi) { struct bgp_pbr_entry_main api; int i; @@ -615,9 +680,15 @@ bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, api_action = &api.actions[i]; if (api_action->action != ACTION_REDIRECT_IP) continue; - p->family = AF_INET; - p->prefixlen = IPV4_MAX_BITLEN; - p->u.prefix4 = api_action->u.zr.redirect_ip_v4; + p->family = afi2family(afi); + if (afi == AFI_IP) { + p->prefixlen = IPV4_MAX_BITLEN; + p->u.prefix4 = api_action->u.zr.redirect_ip_v4; + } else { + p->prefixlen = IPV6_MAX_BITLEN; + memcpy(&p->u.prefix6, &api_action->u.zr.redirect_ip_v6, + sizeof(struct in6_addr)); + } return false; } return true; diff --git a/bgpd/bgp_flowspec_util.h b/bgpd/bgp_flowspec_util.h index 0e78c7a53c..6cc4854d7f 100644 --- a/bgpd/bgp_flowspec_util.h +++ b/bgpd/bgp_flowspec_util.h @@ -39,7 +39,8 @@ extern int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type, extern int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, uint32_t max_len, - void *result, int *error); + void *result, int *error, + afi_t afi, uint8_t *ipv6_offset); extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, uint8_t *nlri_ptr, @@ -48,13 +49,14 @@ extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, struct bgp_pbr_entry_main; extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len, - struct bgp_pbr_entry_main *bpem); + struct bgp_pbr_entry_main *bpem, + afi_t afi); extern bool bgp_flowspec_contains_prefix(const struct prefix *pfs, struct prefix *input, int prefix_check); extern bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi, - struct prefix *nh); + struct prefix *nh, afi_t afi); #endif /* _FRR_BGP_FLOWSPEC_UTIL_H */ diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c index e309fa948e..798bce8219 100644 --- a/bgpd/bgp_flowspec_vty.c +++ b/bgpd/bgp_flowspec_vty.c @@ -50,6 +50,7 @@ static const struct message bgp_flowspec_display_large[] = { {FLOWSPEC_PKT_LEN, "Packet Length"}, {FLOWSPEC_DSCP, "DSCP field"}, {FLOWSPEC_FRAGMENT, "Packet Fragment"}, + {FLOWSPEC_FLOW_LABEL, "Packet Flow Label"}, {0} }; @@ -66,6 +67,7 @@ static const struct message bgp_flowspec_display_min[] = { {FLOWSPEC_PKT_LEN, "pktlen"}, {FLOWSPEC_DSCP, "dscp"}, {FLOWSPEC_FRAGMENT, "pktfrag"}, + {FLOWSPEC_FLOW_LABEL, "flwlbl"}, {0} }; @@ -93,7 +95,8 @@ static const struct message bgp_flowspec_display_min[] = { */ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, char *return_string, int format, - json_object *json_path) + json_object *json_path, + afi_t afi) { uint32_t offset = 0; int type; @@ -127,7 +130,8 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, type_util, nlri_content+offset, len - offset, - local_string, &error); + local_string, &error, + afi, NULL); if (ret <= 0) break; if (json_path) { @@ -145,6 +149,7 @@ void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len, len_string -= len_written; ptr += len_written; break; + case FLOWSPEC_FLOW_LABEL: case FLOWSPEC_IP_PROTOCOL: case FLOWSPEC_PORT: case FLOWSPEC_DEST_PORT: @@ -258,66 +263,99 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p, { struct attr *attr; char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; - char *s; + char *s1 = NULL, *s2 = NULL; json_object *json_nlri_path = NULL; json_object *json_ecom_path = NULL; json_object *json_time_path = NULL; char timebuf[BGP_UPTIME_LEN]; + struct bgp_dest *dest = NULL; - /* Print prefix */ - if (p != NULL) { - if (p->family != AF_FLOWSPEC) - return; - if (json_paths) { - if (display == NLRI_STRING_FORMAT_JSON) - json_nlri_path = json_object_new_object(); - else - json_nlri_path = json_paths; - } - if (display == NLRI_STRING_FORMAT_LARGE && path) - vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n", - path->flags); - bgp_fs_nlri_get_string((unsigned char *) - p->u.prefix_flowspec.ptr, - p->u.prefix_flowspec.prefixlen, - return_string, - display, - json_nlri_path); - if (display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "%s", return_string); - else if (display == NLRI_STRING_FORMAT_DEBUG) - vty_out(vty, "%s", return_string); - else if (display == NLRI_STRING_FORMAT_MIN) - vty_out(vty, " %-30s", return_string); - else if (json_paths && display == NLRI_STRING_FORMAT_JSON) - json_object_array_add(json_paths, json_nlri_path); + if (path) + dest = path->net; + if (dest) + bgp_dest_get_bgp_table_info(dest); + if (p == NULL || p->family != AF_FLOWSPEC) + return; + if (json_paths) { + if (display == NLRI_STRING_FORMAT_JSON) + json_nlri_path = json_object_new_object(); + else + json_nlri_path = json_paths; } + if (display == NLRI_STRING_FORMAT_LARGE && path) + vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n", + path->flags); + bgp_fs_nlri_get_string((unsigned char *) + p->u.prefix_flowspec.ptr, + p->u.prefix_flowspec.prefixlen, + return_string, + display, + json_nlri_path, + family2afi(p->u.prefix_flowspec + .family)); + if (display == NLRI_STRING_FORMAT_LARGE) + vty_out(vty, "%s", return_string); + else if (display == NLRI_STRING_FORMAT_DEBUG) + vty_out(vty, "%s", return_string); + else if (display == NLRI_STRING_FORMAT_MIN) + vty_out(vty, " %-30s", return_string); + else if (json_paths && display == NLRI_STRING_FORMAT_JSON) + json_object_array_add(json_paths, json_nlri_path); if (!path) return; - if (path->attr->ecommunity) { + if (path->attr && + (path->attr->ecommunity || path->attr->ipv6_ecommunity)) { /* Print attribute */ attr = path->attr; - s = ecommunity_ecom2str(attr->ecommunity, - ECOMMUNITY_FORMAT_ROUTE_MAP, 0); - if (!s) + if (attr->ecommunity) + s1 = ecommunity_ecom2str(attr->ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + if (attr->ipv6_ecommunity) + s2 = ecommunity_ecom2str(attr->ipv6_ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + if (!s1 && !s2) return; if (display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "\t%s\n", s); + vty_out(vty, "\t%s%s%s\n", s1 ? s1 : "", + s2 && s1 ? " " : "", s2 ? s2 : ""); else if (display == NLRI_STRING_FORMAT_MIN) - vty_out(vty, "%s", s); + vty_out(vty, "%s%s", s1 ? s1 : "", s2 ? s2 : ""); else if (json_paths) { json_ecom_path = json_object_new_object(); - json_object_string_add(json_ecom_path, - "ecomlist", s); + if (s1) + json_object_string_add(json_ecom_path, + "ecomlist", s1); + if (s2) + json_object_string_add(json_ecom_path, + "ecom6list", s2); if (display == NLRI_STRING_FORMAT_JSON) json_object_array_add(json_paths, json_ecom_path); } - if (attr->nexthop.s_addr != 0 && - display == NLRI_STRING_FORMAT_LARGE) - vty_out(vty, "\tNLRI NH %-16s\n", - inet_ntoa(attr->nexthop)); - XFREE(MTYPE_ECOMMUNITY_STR, s); + if (display == NLRI_STRING_FORMAT_LARGE) { + char local_buff[INET6_ADDRSTRLEN]; + + local_buff[0] = '\0'; + if (p->u.prefix_flowspec.family == AF_INET && + attr->nexthop.s_addr != 0) + inet_ntop(AF_INET, + &attr->nexthop.s_addr, + local_buff, + INET6_ADDRSTRLEN); + else if (p->u.prefix_flowspec.family == AF_INET6 && + attr->mp_nexthop_len != 0 && + attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV4 && + attr->mp_nexthop_len != BGP_ATTR_NHLEN_VPNV4) + inet_ntop(AF_INET6, + &attr->mp_nexthop_global, + local_buff, + INET6_ADDRSTRLEN); + if (local_buff[0] != '\0') + vty_out(vty, "\tNLRI NH %s\n", + local_buff); + } + XFREE(MTYPE_ECOMMUNITY_STR, s1); + XFREE(MTYPE_ECOMMUNITY_STR, s2); } peer_uptime(path->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL); if (display == NLRI_STRING_FORMAT_LARGE) { @@ -469,10 +507,17 @@ int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, struct bgp_pbr_interface_head *head; bool bgp_pbr_interface_any; - if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC || afi != AFI_IP) + if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC) + return 0; + if (afi == AFI_IP) { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4; + } else if (afi == AFI_IP6) { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); + bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv6; + } else { return 0; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4; + } if (!RB_EMPTY(bgp_pbr_interface_head, head) || !bgp_pbr_interface_any) declare_node = true; @@ -483,7 +528,8 @@ int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp, } static int bgp_fs_local_install_interface(struct bgp *bgp, - const char *no, const char *ifname) + const char *no, const char *ifname, + afi_t afi) { struct bgp_pbr_interface *pbr_if; struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; @@ -492,14 +538,19 @@ static int bgp_fs_local_install_interface(struct bgp *bgp, if (!bgp_pbr_cfg) return CMD_SUCCESS; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4); + if (afi == AFI_IP) { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4); + } else { + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); + bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv6); + } if (no) { if (!ifname) { if (*bgp_pbr_interface_any) { *bgp_pbr_interface_any = false; /* remove all other interface list */ - bgp_pbr_reset(bgp, AFI_IP); + bgp_pbr_reset(bgp, afi); } return CMD_SUCCESS; } @@ -523,7 +574,7 @@ static int bgp_fs_local_install_interface(struct bgp *bgp, if (!*bgp_pbr_interface_any) { /* remove all other interface list */ - bgp_pbr_reset(bgp, AFI_IP); + bgp_pbr_reset(bgp, afi); *bgp_pbr_interface_any = true; } } @@ -543,7 +594,8 @@ DEFUN (bgp_fs_local_install_ifname, char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ? argv[idx]->arg : NULL; - return bgp_fs_local_install_interface(bgp, no, ifname); + return bgp_fs_local_install_interface(bgp, no, ifname, + bgp_node_afi(vty)); } extern int bgp_flowspec_display_match_per_ip(afi_t afi, struct bgp_table *rib, @@ -581,4 +633,5 @@ void bgp_flowspec_vty_init(void) install_element(ENABLE_NODE, &no_debug_bgp_flowspec_cmd); install_element(CONFIG_NODE, &no_debug_bgp_flowspec_cmd); install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd); + install_element(BGP_FLOWSPECV6_NODE, &bgp_fs_local_install_ifname_cmd); } diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index ab3b88da7a..14dcf2b593 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -304,8 +304,8 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) ? "accept" : ""), peer->host, peer->fd, from_peer->fd); - bgp_stop(peer); - bgp_stop(from_peer); + BGP_EVENT_ADD(peer, BGP_Stop); + BGP_EVENT_ADD(from_peer, BGP_Stop); return NULL; } if (from_peer->status > Active) { @@ -1105,6 +1105,13 @@ void bgp_fsm_change_status(struct peer *peer, int status) peer->ostatus = peer->status; peer->status = status; + /* Reset received keepalives counter on every FSM change */ + peer->rtt_keepalive_rcv = 0; + + /* Fire backward transition hook if that's the case */ + if (peer->ostatus > peer->status) + hook_call(peer_backward_transition, peer); + /* Save event that caused status change. */ peer->last_major_event = peer->cur_event; @@ -1268,8 +1275,6 @@ int bgp_stop(struct peer *peer) peer->host); update_group_remove_peer_afs(peer); - hook_call(peer_backward_transition, peer); - /* Reset peer synctime */ peer->synctime = 0; } @@ -1548,6 +1553,8 @@ int bgp_start(struct peer *peer) peer->host); if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) peer->last_reset = PEER_DOWN_USER_SHUTDOWN; + else if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN)) + peer->last_reset = PEER_DOWN_USER_SHUTDOWN; else if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) peer->last_reset = PEER_DOWN_PFX_COUNT; return -1; @@ -1673,9 +1680,6 @@ static int bgp_fsm_open(struct peer *peer) /* Send keepalive and make keepalive timer */ bgp_keepalive_send(peer); - /* Reset holdtimer value. */ - BGP_TIMER_OFF(peer->t_holdtime); - return 0; } @@ -1873,6 +1877,30 @@ static int bgp_establish(struct peer *peer) } } + if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) { + if ((bgp_peer_gr_mode_get(peer) == PEER_GR) + || ((bgp_peer_gr_mode_get(peer) == PEER_GLOBAL_INHERIT) + && (bgp_global_gr_mode_get(peer->bgp) == GLOBAL_GR))) { + FOREACH_AFI_SAFI (afi, safi) + /* Send route processing complete + message to RIB */ + bgp_zebra_update( + afi, safi, peer->bgp->vrf_id, + ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE); + } + } else { + /* Peer sends R-bit. In this case, we need to send + * ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE to Zebra. */ + if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_BIT_RCV)) { + FOREACH_AFI_SAFI (afi, safi) + /* Send route processing complete + message to RIB */ + bgp_zebra_update( + afi, safi, peer->bgp->vrf_id, + ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE); + } + } + peer->nsf_af_count = nsf_af_count; if (nsf_af_count) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 314337d194..5ef3cf736d 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -101,11 +101,9 @@ void encode_label(mpls_label_t label, mpls_label_t *label_pnt) int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, struct bgp_nlri *packet) { - uint8_t *pnt; - uint8_t *lim; struct prefix p; - int psize = 0; - int prefixlen; + uint8_t psize = 0; + uint8_t prefixlen; uint16_t type; struct rd_as rd_as; struct rd_ip rd_ip; @@ -115,13 +113,14 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, safi_t safi; int addpath_encoded; uint32_t addpath_id; + int ret = 0; /* Make prefix_rd */ prd.family = AF_UNSPEC; prd.prefixlen = 64; - pnt = packet->nlri; - lim = pnt + packet->length; + struct stream *data = stream_new(packet->length); + stream_put(data, packet->nlri, packet->length); afi = packet->afi; safi = packet->safi; addpath_id = 0; @@ -132,23 +131,26 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, PEER_CAP_ADDPATH_AF_TX_RCV)); #define VPN_PREFIXLEN_MIN_BYTES (3 + 8) /* label + RD */ - for (; pnt < lim; pnt += psize) { + while (STREAM_READABLE(data) > 0) { /* Clear prefix structure. */ memset(&p, 0, sizeof(struct prefix)); if (addpath_encoded) { - - /* When packet overflow occurs return immediately. */ - if (pnt + BGP_ADDPATH_ID_LEN > lim) - return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; - - memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN); + STREAM_GET(&addpath_id, data, BGP_ADDPATH_ID_LEN); addpath_id = ntohl(addpath_id); - pnt += BGP_ADDPATH_ID_LEN; + } + + if (STREAM_READABLE(data) < 1) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no prefix length)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; } /* Fetch prefix length. */ - prefixlen = *pnt++; + STREAM_GETC(data, prefixlen); p.family = afi2family(packet->afi); psize = PSIZE(prefixlen); @@ -157,16 +159,18 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (prefix length %d less than VPN min length)", peer->host, prefixlen); - return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; + ret = BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH; + goto done; } /* sanity check against packet data */ - if ((pnt + psize) > lim) { + if (STREAM_READABLE(data) < psize) { flog_err( EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (prefix length %d exceeds packet size %u)", - peer->host, prefixlen, (uint)(lim - pnt)); - return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; + peer->host, prefixlen, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; + goto done; } /* sanity check against storage for the IP address portion */ @@ -177,7 +181,8 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, peer->host, prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8, sizeof(p.u)); - return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; } /* Sanity check against max bitlen of the address family */ @@ -188,30 +193,48 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, peer->host, prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8, p.family, prefix_blen(&p)); - return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; } /* Copy label to prefix. */ - memcpy(&label, pnt, BGP_LABEL_BYTES); + if (STREAM_READABLE(data) < BGP_LABEL_BYTES) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no label)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; + } + + STREAM_GET(&label, data, BGP_LABEL_BYTES); bgp_set_valid_label(&label); /* Copy routing distinguisher to rd. */ - memcpy(&prd.val, pnt + BGP_LABEL_BYTES, 8); + if (STREAM_READABLE(data) < 8) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no RD)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + goto done; + } + STREAM_GET(&prd.val, data, 8); /* Decode RD type. */ - type = decode_rd_type(pnt + BGP_LABEL_BYTES); + type = decode_rd_type(prd.val); switch (type) { case RD_TYPE_AS: - decode_rd_as(pnt + 5, &rd_as); + decode_rd_as(&prd.val[2], &rd_as); break; case RD_TYPE_AS4: - decode_rd_as4(pnt + 5, &rd_as); + decode_rd_as4(&prd.val[2], &rd_as); break; case RD_TYPE_IP: - decode_rd_ip(pnt + 5, &rd_ip); + decode_rd_ip(&prd.val[2], &rd_ip); break; #ifdef ENABLE_BGP_VNC @@ -224,11 +247,9 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, break; /* just report */ } - p.prefixlen = - prefixlen - - VPN_PREFIXLEN_MIN_BYTES * 8; /* exclude label & RD */ - memcpy(p.u.val, pnt + VPN_PREFIXLEN_MIN_BYTES, - psize - VPN_PREFIXLEN_MIN_BYTES); + /* exclude label & RD */ + p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8; + STREAM_GET(p.u.val, data, psize - VPN_PREFIXLEN_MIN_BYTES); if (attr) { bgp_update(peer, &p, addpath_id, attr, packet->afi, @@ -241,15 +262,27 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, } } /* Packet length consistency check. */ - if (pnt != lim) { + if (STREAM_READABLE(data) != 0) { flog_err( EC_BGP_UPDATE_RCV, "%s [Error] Update packet error / VPN (%td data remaining after parsing)", - peer->host, lim - pnt); + peer->host, STREAM_READABLE(data)); return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } - return 0; + goto done; + +stream_failure: + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error / VPN (NLRI of size %u - length error)", + peer->host, packet->length); + ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; + +done: + stream_free(data); + return ret; + #undef VPN_PREFIXLEN_MIN_BYTES } @@ -391,12 +424,11 @@ static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2) if (!e1 || !e2) return false; - for (i = 0; i < e1->size; ++i) { for (j = 0; j < e2->size; ++j) { - if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE), - e2->val + (j * ECOMMUNITY_SIZE), - ECOMMUNITY_SIZE)) { + if (!memcmp(e1->val + (i * e1->unit_size), + e2->val + (j * e2->unit_size), + e1->unit_size)) { return true; } @@ -2456,6 +2488,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey) { struct listnode *mnode, *mnnode; struct bgp *bgp; + afi_t afi = AFI_IP; + + if (eckey->unit_size == IPV6_ECOMMUNITY_SIZE) + afi = AFI_IP6; for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { struct ecommunity *ec; @@ -2463,7 +2499,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey) if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF) continue; - ec = bgp->vpn_policy[AFI_IP].import_redirect_rtlist; + ec = bgp->vpn_policy[afi].import_redirect_rtlist; + + if (ec && eckey->unit_size != ec->unit_size) + continue; if (ecom_intersect(ec, eckey)) return bgp->vrf_id; diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 06aec9412c..cae11ae7bd 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -160,12 +160,26 @@ static int bgp_md5_set_password(struct peer *peer, const char *password) */ frr_with_privs(&bgpd_privs) { for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener)) - if (listener->su.sa.sa_family - == peer->su.sa.sa_family) { + if (listener->su.sa.sa_family == + peer->su.sa.sa_family) { uint16_t prefixlen = peer->su.sa.sa_family == AF_INET - ? IPV4_MAX_PREFIXLEN - : IPV6_MAX_PREFIXLEN; + ? IPV4_MAX_PREFIXLEN + : IPV6_MAX_PREFIXLEN; + + /* + * if we have stored a BGP vrf instance in the + * listener it must match the bgp instance in + * the peer otherwise the peer bgp instance + * must be the default vrf or a view instance + */ + if (!listener->bgp) { + if (peer->bgp->vrf_id != VRF_DEFAULT + && peer->bgp->inst_type + != BGP_INSTANCE_TYPE_VIEW) + continue; + } else if (listener->bgp != peer->bgp) + continue; ret = bgp_md5_set_socket(listener->fd, &peer->su, prefixlen, @@ -176,7 +190,7 @@ static int bgp_md5_set_password(struct peer *peer, const char *password) return ret; } -int bgp_md5_set_prefix(struct prefix *p, const char *password) +int bgp_md5_set_prefix(struct bgp *bgp, struct prefix *p, const char *password) { int ret = 0; union sockunion su; @@ -186,7 +200,9 @@ int bgp_md5_set_prefix(struct prefix *p, const char *password) /* Set or unset the password on the listen socket(s). */ frr_with_privs(&bgpd_privs) { for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener)) - if (listener->su.sa.sa_family == p->family) { + if (listener->su.sa.sa_family == p->family + && ((bgp->vrf_id == VRF_DEFAULT) + || (listener->bgp == bgp))) { prefix2sockunion(p, &su); ret = bgp_md5_set_socket(listener->fd, &su, p->prefixlen, @@ -198,9 +214,9 @@ int bgp_md5_set_prefix(struct prefix *p, const char *password) return ret; } -int bgp_md5_unset_prefix(struct prefix *p) +int bgp_md5_unset_prefix(struct bgp *bgp, struct prefix *p) { - return bgp_md5_set_prefix(p, NULL); + return bgp_md5_set_prefix(bgp, p, NULL); } int bgp_md5_set(struct peer *peer) @@ -459,7 +475,8 @@ static int bgp_accept(struct thread *thread) return -1; } - if (CHECK_FLAG(peer1->flags, PEER_FLAG_SHUTDOWN)) { + if (CHECK_FLAG(peer1->flags, PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(peer1->bgp->flags, BGP_FLAG_SHUTDOWN)) { if (bgp_debug_neighbor_events(peer1)) zlog_debug( "[Event] connection from %s rejected(%s:%u:%s) due to admin shutdown", @@ -811,8 +828,9 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen, listener->fd = sock; listener->name = XSTRDUP(MTYPE_BGP_LISTENER, bgp->name); - /* this socket needs a change of ns. record bgp back pointer */ - if (bgp->vrf_id != VRF_DEFAULT && vrf_is_backend_netns()) + /* this socket is in a vrf record bgp back pointer */ + if (bgp->vrf_id != VRF_DEFAULT + && bgp->inst_type != BGP_INSTANCE_TYPE_VIEW) listener->bgp = bgp; memcpy(&listener->su, sa, salen); diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 018efbc08e..0b5cc17523 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -31,8 +31,9 @@ extern void bgp_close(void); extern int bgp_connect(struct peer *); extern int bgp_getsockname(struct peer *); -extern int bgp_md5_set_prefix(struct prefix *p, const char *password); -extern int bgp_md5_unset_prefix(struct prefix *p); +extern int bgp_md5_set_prefix(struct bgp *bgp, struct prefix *p, + const char *password); +extern int bgp_md5_unset_prefix(struct bgp *bgp, struct prefix *p); extern int bgp_md5_set(struct peer *); extern int bgp_md5_unset(struct peer *); extern int bgp_set_socket_ttl(struct peer *, int fd); diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 5cc0d60529..ed026a2fff 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -35,7 +35,6 @@ #include "filter.h" #include "bgpd/bgpd.h" -#include "bgpd/bgp_table.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_nexthop.h" @@ -48,10 +47,20 @@ DEFINE_MTYPE_STATIC(BGPD, MARTIAN_STRING, "BGP Martian Address Intf String"); -char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size) +int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a, + const struct bgp_nexthop_cache *b) { - prefix2str(bgp_dest_get_prefix(bnc->dest), buf, size); - return buf; + if (a->srte_color < b->srte_color) + return -1; + if (a->srte_color > b->srte_color) + return 1; + + return prefix_cmp(&a->prefix, &b->prefix); +} + +const char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size) +{ + return prefix2str(&bnc->prefix, buf, size); } void bnc_nexthop_free(struct bgp_nexthop_cache *bnc) @@ -59,32 +68,62 @@ void bnc_nexthop_free(struct bgp_nexthop_cache *bnc) nexthops_free(bnc->nexthop); } -struct bgp_nexthop_cache *bnc_new(void) +struct bgp_nexthop_cache *bnc_new(struct bgp_nexthop_cache_head *tree, + struct prefix *prefix, uint32_t srte_color) { struct bgp_nexthop_cache *bnc; bnc = XCALLOC(MTYPE_BGP_NEXTHOP_CACHE, sizeof(struct bgp_nexthop_cache)); + bnc->prefix = *prefix; + bnc->srte_color = srte_color; + bnc->tree = tree; LIST_INIT(&(bnc->paths)); + bgp_nexthop_cache_add(tree, bnc); + return bnc; } +bool bnc_existing_for_prefix(struct bgp_nexthop_cache *bnc) +{ + struct bgp_nexthop_cache *bnc_tmp; + + frr_each (bgp_nexthop_cache, bnc->tree, bnc_tmp) { + if (bnc_tmp == bnc) + continue; + if (prefix_cmp(&bnc->prefix, &bnc_tmp->prefix) == 0) + return true; + } + return false; +} + void bnc_free(struct bgp_nexthop_cache *bnc) { bnc_nexthop_free(bnc); + bgp_nexthop_cache_del(bnc->tree, bnc); XFREE(MTYPE_BGP_NEXTHOP_CACHE, bnc); } +struct bgp_nexthop_cache *bnc_find(struct bgp_nexthop_cache_head *tree, + struct prefix *prefix, uint32_t srte_color) +{ + struct bgp_nexthop_cache bnc = {}; + + if (!tree) + return NULL; + + bnc.prefix = *prefix; + bnc.srte_color = srte_color; + return bgp_nexthop_cache_find(tree, &bnc); +} + /* Reset and free all BGP nexthop cache. */ -static void bgp_nexthop_cache_reset(struct bgp_table *table) +static void bgp_nexthop_cache_reset(struct bgp_nexthop_cache_head *tree) { - struct bgp_dest *dest; struct bgp_nexthop_cache *bnc; - for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { - bnc = bgp_dest_get_bgp_nexthop_info(dest); - if (!bnc) - continue; + while (bgp_nexthop_cache_count(tree) > 0) { + bnc = bgp_nexthop_cache_first(tree); while (!LIST_EMPTY(&(bnc->paths))) { struct bgp_path_info *path = LIST_FIRST(&(bnc->paths)); @@ -93,8 +132,6 @@ static void bgp_nexthop_cache_reset(struct bgp_table *table) } bnc_free(bnc); - bgp_dest_set_bgp_nexthop_info(dest, NULL); - bgp_dest_unlock_node(dest); } } @@ -773,20 +810,21 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp, } static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, - struct bgp_dest *dest, struct bgp_nexthop_cache *bnc, bool specific) { char buf[PREFIX2STR_BUFFER]; time_t tbuf; struct peer *peer; - const struct prefix *p = bgp_dest_get_prefix(dest); peer = (struct peer *)bnc->nht_info; + if (bnc->srte_color) + vty_out(vty, " SR-TE color %u -", bnc->srte_color); if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) { vty_out(vty, " %s valid [IGP metric %d], #paths %d", - inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf)), + inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, + buf, sizeof(buf)), bnc->metric, bnc->path_count); if (peer) vty_out(vty, ", peer %s", peer->host); @@ -794,7 +832,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, bgp_show_nexthops_detail(vty, bgp, bnc); } else { vty_out(vty, " %s invalid, #paths %d", - inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf)), + inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix, + buf, sizeof(buf)), bnc->path_count); if (peer) vty_out(vty, ", peer %s", peer->host); @@ -816,29 +855,21 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp, static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, bool import_table) { - struct bgp_dest *dest; struct bgp_nexthop_cache *bnc; afi_t afi; - struct bgp_table **table; + struct bgp_nexthop_cache_head(*tree)[AFI_MAX]; if (import_table) vty_out(vty, "Current BGP import check cache:\n"); else vty_out(vty, "Current BGP nexthop cache:\n"); if (import_table) - table = bgp->import_check_table; + tree = &bgp->import_check_table; else - table = bgp->nexthop_cache_table; + tree = &bgp->nexthop_cache_table; for (afi = AFI_IP; afi < AFI_MAX; afi++) { - if (!table || !table[afi]) - continue; - for (dest = bgp_table_top(table[afi]); dest; - dest = bgp_route_next(dest)) { - bnc = bgp_dest_get_bgp_nexthop_info(dest); - if (!bnc) - continue; - bgp_show_nexthop(vty, bgp, dest, bnc, false); - } + frr_each (bgp_nexthop_cache, &(*tree)[afi], bnc) + bgp_show_nexthop(vty, bgp, bnc, false); } } @@ -859,27 +890,21 @@ static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name, if (nhopip_str) { struct prefix nhop; - struct bgp_table **table; - struct bgp_dest *dest; + struct bgp_nexthop_cache_head (*tree)[AFI_MAX]; struct bgp_nexthop_cache *bnc; if (!str2prefix(nhopip_str, &nhop)) { vty_out(vty, "nexthop address is malformed\n"); return CMD_WARNING; } - table = import_table ? \ - bgp->import_check_table : bgp->nexthop_cache_table; - dest = bgp_node_lookup(table[family2afi(nhop.family)], &nhop); - if (!dest) { - vty_out(vty, "specified nexthop is not found\n"); - return CMD_SUCCESS; - } - bnc = bgp_dest_get_bgp_nexthop_info(dest); + tree = import_table ? &bgp->import_check_table + : &bgp->nexthop_cache_table; + bnc = bnc_find(tree[family2afi(nhop.family)], &nhop, 0); if (!bnc) { vty_out(vty, "specified nexthop does not have entry\n"); return CMD_SUCCESS; } - bgp_show_nexthop(vty, bgp, dest, bnc, true); + bgp_show_nexthop(vty, bgp, bnc, true); } else bgp_show_nexthops(vty, bgp, import_table); @@ -966,12 +991,10 @@ void bgp_scan_init(struct bgp *bgp) afi_t afi; for (afi = AFI_IP; afi < AFI_MAX; afi++) { - bgp->nexthop_cache_table[afi] = - bgp_table_init(bgp, afi, SAFI_UNICAST); + bgp_nexthop_cache_init(&bgp->nexthop_cache_table[afi]); + bgp_nexthop_cache_init(&bgp->import_check_table[afi]); bgp->connected_table[afi] = bgp_table_init(bgp, afi, SAFI_UNICAST); - bgp->import_check_table[afi] = - bgp_table_init(bgp, afi, SAFI_UNICAST); } } @@ -988,16 +1011,12 @@ void bgp_scan_finish(struct bgp *bgp) for (afi = AFI_IP; afi < AFI_MAX; afi++) { /* Only the current one needs to be reset. */ - bgp_nexthop_cache_reset(bgp->nexthop_cache_table[afi]); - bgp_table_unlock(bgp->nexthop_cache_table[afi]); - bgp->nexthop_cache_table[afi] = NULL; + bgp_nexthop_cache_reset(&bgp->nexthop_cache_table[afi]); + bgp_nexthop_cache_reset(&bgp->import_check_table[afi]); bgp->connected_table[afi]->route_table->cleanup = bgp_connected_cleanup; bgp_table_unlock(bgp->connected_table[afi]); bgp->connected_table[afi] = NULL; - - bgp_table_unlock(bgp->import_check_table[afi]); - bgp->import_check_table[afi] = NULL; } } diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index 416ab2a739..c4b913faf4 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -24,6 +24,7 @@ #include "if.h" #include "queue.h" #include "prefix.h" +#include "bgp_table.h" #define NEXTHOP_FAMILY(nexthop_len) \ (((nexthop_len) == 4 || (nexthop_len) == 12 \ @@ -36,8 +37,13 @@ #define BGP_MP_NEXTHOP_FAMILY NEXTHOP_FAMILY +PREDECL_RBTREE_UNIQ(bgp_nexthop_cache); + /* BGP nexthop cache value structure. */ struct bgp_nexthop_cache { + /* RB-tree entry. */ + struct bgp_nexthop_cache_item entry; + /* IGP route's metric. */ uint32_t metric; @@ -61,13 +67,22 @@ struct bgp_nexthop_cache { #define BGP_NEXTHOP_METRIC_CHANGED (1 << 1) #define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2) - struct bgp_dest *dest; + /* Back pointer to the cache tree this entry belongs to. */ + struct bgp_nexthop_cache_head *tree; + + uint32_t srte_color; + struct prefix prefix; void *nht_info; /* In BGP, peer session */ LIST_HEAD(path_list, bgp_path_info) paths; unsigned int path_count; struct bgp *bgp; }; +extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a, + const struct bgp_nexthop_cache *b); +DECLARE_RBTREE_UNIQ(bgp_nexthop_cache, struct bgp_nexthop_cache, entry, + bgp_nexthop_cache_compare); + /* Own tunnel-ip address structure */ struct tip_addr { struct in_addr addr; @@ -79,6 +94,12 @@ struct bgp_addrv6 { struct list *ifp_name_list; }; +/* Forward declaration(s). */ +struct peer; +struct update_subgroup; +struct bgp_dest; +struct attr; + extern void bgp_connected_add(struct bgp *bgp, struct connected *c); extern void bgp_connected_delete(struct bgp *bgp, struct connected *c); extern bool bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop, @@ -94,10 +115,16 @@ extern int bgp_config_write_scan_time(struct vty *); extern bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type, uint8_t sub_type, struct attr *attr, struct bgp_dest *dest); -extern struct bgp_nexthop_cache *bnc_new(void); +extern struct bgp_nexthop_cache *bnc_new(struct bgp_nexthop_cache_head *tree, + struct prefix *prefix, + uint32_t srte_color); +extern bool bnc_existing_for_prefix(struct bgp_nexthop_cache *bnc); extern void bnc_free(struct bgp_nexthop_cache *bnc); +extern struct bgp_nexthop_cache *bnc_find(struct bgp_nexthop_cache_head *tree, + struct prefix *prefix, + uint32_t srte_color); extern void bnc_nexthop_free(struct bgp_nexthop_cache *bnc); -extern char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size); +extern const char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size); extern void bgp_scan_init(struct bgp *bgp); extern void bgp_scan_finish(struct bgp *bgp); extern void bgp_scan_vty_init(void); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index a74b5f91ac..9573d118e5 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -72,15 +72,14 @@ static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc) if (LIST_EMPTY(&(bnc->paths)) && !bnc->nht_info) { if (BGP_DEBUG(nht, NHT)) { char buf[PREFIX2STR_BUFFER]; - zlog_debug("bgp_unlink_nexthop: freeing bnc %s(%s)", + zlog_debug("bgp_unlink_nexthop: freeing bnc %s(%u)(%s)", bnc_str(bnc, buf, PREFIX2STR_BUFFER), - bnc->bgp->name_pretty); + bnc->srte_color, bnc->bgp->name_pretty); } - unregister_zebra_rnh(bnc, - CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE)); - bgp_dest_set_bgp_nexthop_info(bnc->dest, NULL); - bgp_dest_unlock_node(bnc->dest); - bnc->dest = NULL; + /* only unregister if this is the last nh for this prefix*/ + if (!bnc_existing_for_prefix(bnc)) + unregister_zebra_rnh( + bnc, CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE)); bnc_free(bnc); } } @@ -100,16 +99,13 @@ void bgp_unlink_nexthop(struct bgp_path_info *path) void bgp_unlink_nexthop_by_peer(struct peer *peer) { struct prefix p; - struct bgp_dest *dest; struct bgp_nexthop_cache *bnc; afi_t afi = family2afi(peer->su.sa.sa_family); if (!sockunion2hostprefix(&peer->su, &p)) return; - dest = bgp_node_get(peer->bgp->nexthop_cache_table[afi], &p); - - bnc = bgp_dest_get_bgp_nexthop_info(dest); + bnc = bnc_find(&peer->bgp->nexthop_cache_table[afi], &p, 0); if (!bnc) return; @@ -127,11 +123,11 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, afi_t afi, struct bgp_path_info *pi, struct peer *peer, int connected) { - struct bgp_dest *dest; + struct bgp_nexthop_cache_head *tree = NULL; struct bgp_nexthop_cache *bnc; struct prefix p; + uint32_t srte_color = 0; int is_bgp_static_route = 0; - const struct prefix *bnc_p; if (pi) { is_bgp_static_route = ((pi->type == ZEBRA_ROUTE_BGP) @@ -155,6 +151,8 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, * addr */ if (make_prefix(afi, pi, &p) < 0) return 1; + + srte_color = pi->attr->srte_color; } else if (peer) { if (!sockunion2hostprefix(&peer->su, &p)) { if (BGP_DEBUG(nht, NHT)) { @@ -168,29 +166,24 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, return 0; if (is_bgp_static_route) - dest = bgp_node_get(bgp_nexthop->import_check_table[afi], &p); + tree = &bgp_nexthop->import_check_table[afi]; else - dest = bgp_node_get(bgp_nexthop->nexthop_cache_table[afi], &p); + tree = &bgp_nexthop->nexthop_cache_table[afi]; - bnc = bgp_dest_get_bgp_nexthop_info(dest); + bnc = bnc_find(tree, &p, srte_color); if (!bnc) { - bnc = bnc_new(); - bgp_dest_set_bgp_nexthop_info(dest, bnc); - bnc->dest = dest; + bnc = bnc_new(tree, &p, srte_color); bnc->bgp = bgp_nexthop; - bgp_dest_lock_node(dest); if (BGP_DEBUG(nht, NHT)) { char buf[PREFIX2STR_BUFFER]; - zlog_debug("Allocated bnc %s(%s) peer %p", + zlog_debug("Allocated bnc %s(%u)(%s) peer %p", bnc_str(bnc, buf, PREFIX2STR_BUFFER), - bnc->bgp->name_pretty, peer); + bnc->srte_color, bnc->bgp->name_pretty, + peer); } } - bnc_p = bgp_dest_get_prefix(bnc->dest); - - bgp_dest_unlock_node(dest); if (is_bgp_static_route) { SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); @@ -236,7 +229,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); SET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); } else if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED) - && !is_default_host_route(bnc_p)) + && !is_default_host_route(&bnc->prefix)) register_zebra_rnh(bnc, is_bgp_static_route); if (pi && pi->nexthop != bnc) { @@ -269,7 +262,6 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer) { - struct bgp_dest *dest; struct bgp_nexthop_cache *bnc; struct prefix p; @@ -279,26 +271,15 @@ void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer) if (!sockunion2hostprefix(&peer->su, &p)) return; - dest = bgp_node_lookup( - peer->bgp->nexthop_cache_table[family2afi(p.family)], &p); - if (!dest) { - if (BGP_DEBUG(nht, NHT)) - zlog_debug( - "Cannot find connected NHT node for peer %s(%s)", - peer->host, peer->bgp->name_pretty); - return; - } - - bnc = bgp_dest_get_bgp_nexthop_info(dest); + bnc = bnc_find(&peer->bgp->nexthop_cache_table[family2afi(p.family)], + &p, 0); if (!bnc) { if (BGP_DEBUG(nht, NHT)) zlog_debug( - "Cannot find connected NHT node for peer %s(%s) on route_node as expected", + "Cannot find connected NHT node for peer %s(%s)", peer->host, peer->bgp->name_pretty); - bgp_dest_unlock_node(dest); return; } - bgp_dest_unlock_node(dest); if (bnc->nht_info != peer) { if (BGP_DEBUG(nht, NHT)) @@ -317,95 +298,40 @@ void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer) "Freeing connected NHT node %p for peer %s(%s)", bnc, peer->host, bnc->bgp->name_pretty); unregister_zebra_rnh(bnc, 0); - bgp_dest_set_bgp_nexthop_info(bnc->dest, NULL); - bgp_dest_unlock_node(bnc->dest); bnc_free(bnc); } } -void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) +static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, + struct zapi_route *nhr) { - struct bgp_dest *dest = NULL; - struct bgp_nexthop_cache *bnc; struct nexthop *nexthop; struct nexthop *oldnh; struct nexthop *nhlist_head = NULL; struct nexthop *nhlist_tail = NULL; int i; - struct bgp *bgp; - struct zapi_route nhr; - - bgp = bgp_lookup_by_vrf_id(vrf_id); - if (!bgp) { - flog_err( - EC_BGP_NH_UPD, - "parse nexthop update: instance not found for vrf_id %u", - vrf_id); - return; - } - if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { - if (BGP_DEBUG(nht, NHT)) - zlog_debug("%s[%s]: Failure to decode nexthop update", - __func__, bgp->name_pretty); - return; - } - - if (command == ZEBRA_NEXTHOP_UPDATE) - dest = bgp_node_lookup( - bgp->nexthop_cache_table[family2afi(nhr.prefix.family)], - &nhr.prefix); - else if (command == ZEBRA_IMPORT_CHECK_UPDATE) - dest = bgp_node_lookup( - bgp->import_check_table[family2afi(nhr.prefix.family)], - &nhr.prefix); - - if (!dest) { - if (BGP_DEBUG(nht, NHT)) { - char buf[PREFIX2STR_BUFFER]; - prefix2str(&nhr.prefix, buf, sizeof(buf)); - zlog_debug("parse nexthop update(%s(%s)): rn not found", - buf, bgp->name_pretty); - } - return; - } - - bnc = bgp_dest_get_bgp_nexthop_info(dest); - if (!bnc) { - if (BGP_DEBUG(nht, NHT)) { - char buf[PREFIX2STR_BUFFER]; - - prefix2str(&nhr.prefix, buf, sizeof(buf)); - zlog_debug( - "parse nexthop update(%s(%s)): bnc node info not found", - buf, bgp->name_pretty); - } - bgp_dest_unlock_node(dest); - return; - } - - bgp_dest_unlock_node(dest); bnc->last_update = bgp_clock(); bnc->change_flags = 0; /* debug print the input */ if (BGP_DEBUG(nht, NHT)) { char buf[PREFIX2STR_BUFFER]; - prefix2str(&nhr.prefix, buf, sizeof(buf)); + prefix2str(&nhr->prefix, buf, sizeof(buf)); zlog_debug( - "%s(%u): Rcvd NH update %s - metric %d/%d #nhops %d/%d flags 0x%x", - bnc->bgp->name_pretty, vrf_id, buf, nhr.metric, - bnc->metric, nhr.nexthop_num, bnc->nexthop_num, - bnc->flags); + "%s(%u): Rcvd NH update %s(%u) - metric %d/%d #nhops %d/%d flags 0x%x", + bnc->bgp->name_pretty, bnc->bgp->vrf_id, buf, + bnc->srte_color, nhr->metric, bnc->metric, + nhr->nexthop_num, bnc->nexthop_num, bnc->flags); } - if (nhr.metric != bnc->metric) + if (nhr->metric != bnc->metric) bnc->change_flags |= BGP_NEXTHOP_METRIC_CHANGED; - if (nhr.nexthop_num != bnc->nexthop_num) + if (nhr->nexthop_num != bnc->nexthop_num) bnc->change_flags |= BGP_NEXTHOP_CHANGED; - if (nhr.nexthop_num) { + if (nhr->nexthop_num) { struct peer *peer = bnc->nht_info; /* notify bgp fsm if nbr ip goes from invalid->valid */ @@ -413,15 +339,15 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); bnc->flags |= BGP_NEXTHOP_VALID; - bnc->metric = nhr.metric; - bnc->nexthop_num = nhr.nexthop_num; + bnc->metric = nhr->metric; + bnc->nexthop_num = nhr->nexthop_num; bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID; /* check below */ - for (i = 0; i < nhr.nexthop_num; i++) { + for (i = 0; i < nhr->nexthop_num; i++) { int num_labels = 0; - nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]); + nexthop = nexthop_from_zapi_nexthop(&nhr->nexthops[i]); /* * Turn on RA for the v6 nexthops @@ -431,7 +357,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) if (peer && !peer->ifp && CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE) - && nhr.prefix.family == AF_INET6 + && nhr->prefix.family == AF_INET6 && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { struct interface *ifp; @@ -485,7 +411,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) bnc->nexthop = nhlist_head; } else { bnc->flags &= ~BGP_NEXTHOP_VALID; - bnc->nexthop_num = nhr.nexthop_num; + bnc->nexthop_num = nhr->nexthop_num; /* notify bgp fsm if nbr ip goes from valid->invalid */ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); @@ -497,26 +423,88 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) evaluate_paths(bnc); } +void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) +{ + struct bgp_nexthop_cache_head *tree = NULL; + struct bgp_nexthop_cache *bnc; + struct bgp *bgp; + struct zapi_route nhr; + afi_t afi; + + bgp = bgp_lookup_by_vrf_id(vrf_id); + if (!bgp) { + flog_err( + EC_BGP_NH_UPD, + "parse nexthop update: instance not found for vrf_id %u", + vrf_id); + return; + } + + if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { + if (BGP_DEBUG(nht, NHT)) + zlog_debug("%s[%s]: Failure to decode nexthop update", + __PRETTY_FUNCTION__, bgp->name_pretty); + return; + } + + afi = family2afi(nhr.prefix.family); + if (command == ZEBRA_NEXTHOP_UPDATE) + tree = &bgp->nexthop_cache_table[afi]; + else if (command == ZEBRA_IMPORT_CHECK_UPDATE) + tree = &bgp->import_check_table[afi]; + + bnc = bnc_find(tree, &nhr.prefix, nhr.srte_color); + if (!bnc) { + if (BGP_DEBUG(nht, NHT)) { + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&nhr.prefix, buf, sizeof(buf)); + zlog_debug( + "parse nexthop update(%s(%u)(%s)): bnc info not found", + buf, nhr.srte_color, bgp->name_pretty); + } + return; + } + + bgp_process_nexthop_update(bnc, &nhr); + + /* + * HACK: if any BGP route is dependant on an SR-policy that doesn't + * exist, zebra will never send NH updates relative to that policy. In + * that case, whenever we receive an update about a colorless NH, update + * the corresponding colorful NHs that share the same endpoint but that + * are inactive. This ugly hack should work around the problem at the + * cost of a performance pernalty. Long term, what should be done is to + * make zebra's RNH subsystem aware of SR-TE colors (like bgpd is), + * which should provide a better infrastructure to solve this issue in + * a more efficient and elegant way. + */ + if (nhr.srte_color == 0) { + struct bgp_nexthop_cache *bnc_iter; + + frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], + bnc_iter) { + if (!prefix_same(&bnc->prefix, &bnc_iter->prefix) + || bnc_iter->srte_color == 0 + || CHECK_FLAG(bnc_iter->flags, BGP_NEXTHOP_VALID)) + continue; + + bgp_process_nexthop_update(bnc_iter, &nhr); + } + } +} + /* * Cleanup nexthop registration and status information for BGP nexthops * pertaining to this VRF. This is invoked upon VRF deletion. */ void bgp_cleanup_nexthops(struct bgp *bgp) { - afi_t afi; - struct bgp_dest *dest; - struct bgp_nexthop_cache *bnc; - - for (afi = AFI_IP; afi < AFI_MAX; afi++) { - if (!bgp->nexthop_cache_table[afi]) - continue; - - for (dest = bgp_table_top(bgp->nexthop_cache_table[afi]); dest; - dest = bgp_route_next(dest)) { - bnc = bgp_dest_get_bgp_nexthop_info(dest); - if (!bnc) - continue; + for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) { + struct bgp_nexthop_cache *bnc; + frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], + bnc) { /* Clear relevant flags. */ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); @@ -544,7 +532,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) if (!pi->peer) return -1; return bgp_flowspec_get_first_nh(pi->peer->bgp, - pi, p); + pi, p, afi); } memset(p, 0, sizeof(struct prefix)); switch (afi) { @@ -609,7 +597,6 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) */ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command) { - const struct prefix *p; bool exact_match = false; int ret; @@ -631,23 +618,18 @@ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command) "%s: We have not connected yet, cannot send nexthops", __func__); } - p = bgp_dest_get_prefix(bnc->dest); if ((command == ZEBRA_NEXTHOP_REGISTER || command == ZEBRA_IMPORT_ROUTE_REGISTER) && (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED) || CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH))) exact_match = true; - if (BGP_DEBUG(zebra, ZEBRA)) { - char buf[PREFIX2STR_BUFFER]; - - prefix2str(p, buf, PREFIX2STR_BUFFER); - zlog_debug("%s: sending cmd %s for %s (vrf %s)", - __func__, zserv_command_string(command), buf, - bnc->bgp->name_pretty); - } + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: sending cmd %s for %pFX (vrf %s)", __func__, + zserv_command_string(command), &bnc->prefix, + bnc->bgp->name_pretty); - ret = zclient_send_rnh(zclient, command, p, exact_match, + ret = zclient_send_rnh(zclient, command, &bnc->prefix, exact_match, bnc->bgp->vrf_id); /* TBD: handle the failure */ if (ret < 0) @@ -725,8 +707,8 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) char buf[PREFIX2STR_BUFFER]; bnc_str(bnc, buf, PREFIX2STR_BUFFER); zlog_debug( - "NH update for %s %s flags 0x%x chgflags 0x%x - evaluate paths", - buf, bnc->bgp->name_pretty, bnc->flags, + "NH update for %s(%u)(%s) - flags 0x%x chgflags 0x%x - evaluate paths", + buf, bnc->srte_color, bnc->bgp->name_pretty, bnc->flags, bnc->change_flags); } @@ -814,7 +796,8 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc) path->extra->igpmetric = 0; if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) - || CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED)) + || CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) + || path->attr->srte_color != 0) SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED); path_valid = !!CHECK_FLAG(path->flags, BGP_PATH_VALID); @@ -901,21 +884,11 @@ void path_nh_map(struct bgp_path_info *path, struct bgp_nexthop_cache *bnc, */ void bgp_nht_register_nexthops(struct bgp *bgp) { - struct bgp_dest *dest; - struct bgp_nexthop_cache *bnc; - afi_t afi; - - for (afi = AFI_IP; afi < AFI_MAX; afi++) { - if (!bgp->nexthop_cache_table[afi]) - continue; - - for (dest = bgp_table_top(bgp->nexthop_cache_table[afi]); dest; - dest = bgp_route_next(dest)) { - bnc = bgp_dest_get_bgp_nexthop_info(dest); - - if (!bnc) - continue; + for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) { + struct bgp_nexthop_cache *bnc; + frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], + bnc) { register_zebra_rnh(bnc, 0); } } @@ -924,7 +897,6 @@ void bgp_nht_register_nexthops(struct bgp *bgp) void bgp_nht_reg_enhe_cap_intfs(struct peer *peer) { struct bgp *bgp; - struct bgp_dest *dest; struct bgp_nexthop_cache *bnc; struct nexthop *nhop; struct interface *ifp; @@ -934,10 +906,6 @@ void bgp_nht_reg_enhe_cap_intfs(struct peer *peer) return; bgp = peer->bgp; - - if (!bgp->nexthop_cache_table[AFI_IP6]) - return; - if (!sockunion2hostprefix(&peer->su, &p)) { zlog_warn("%s: Unable to convert sockunion to prefix for %s", __func__, peer->host); @@ -946,11 +914,8 @@ void bgp_nht_reg_enhe_cap_intfs(struct peer *peer) if (p.family != AF_INET6) return; - dest = bgp_node_lookup(bgp->nexthop_cache_table[AFI_IP6], &p); - if (!dest) - return; - bnc = bgp_dest_get_bgp_nexthop_info(dest); + bnc = bnc_find(&bgp->nexthop_cache_table[AFI_IP6], &p, 0); if (!bnc) return; @@ -973,7 +938,6 @@ void bgp_nht_reg_enhe_cap_intfs(struct peer *peer) void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer) { struct bgp *bgp; - struct bgp_dest *dest; struct bgp_nexthop_cache *bnc; struct nexthop *nhop; struct interface *ifp; @@ -984,9 +948,6 @@ void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer) bgp = peer->bgp; - if (!bgp->nexthop_cache_table[AFI_IP6]) - return; - if (!sockunion2hostprefix(&peer->su, &p)) { zlog_warn("%s: Unable to convert sockunion to prefix for %s", __func__, peer->host); @@ -996,11 +957,7 @@ void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer) if (p.family != AF_INET6) return; - dest = bgp_node_lookup(bgp->nexthop_cache_table[AFI_IP6], &p); - if (!dest) - return; - - bnc = bgp_dest_get_bgp_nexthop_info(dest); + bnc = bnc_find(&bgp->nexthop_cache_table[AFI_IP6], &p, 0); if (!bnc) return; diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 732c8e6753..6cfcb9cc3d 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -1102,6 +1102,9 @@ int bgp_open_option_parse(struct peer *peer, uint8_t length, int *mp_capability) zlog_debug("%s rcv OPEN w/ OPTION parameter len: %u", peer->host, length); + /* Unset any previously received GR capability. */ + UNSET_FLAG(peer->cap, PEER_CAP_RESTART_RCV); + while (stream_get_getp(s) < end) { uint8_t opt_type; uint8_t opt_length; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 7692a200ee..15dba37667 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -198,6 +198,8 @@ void bgp_check_update_delay(struct bgp *bgp) PEER_FLAG_CONFIG_NODE) && !CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN) + && !CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_SHUTDOWN) && !peer->update_delay_over) { if (bgp_debug_neighbor_events(peer)) zlog_debug( @@ -1214,7 +1216,6 @@ static int bgp_open_receive(struct peer *peer, bgp_size_t size) * "Bad BGP Identifier". */ if (remote_id.s_addr == INADDR_ANY - || IPV4_CLASS_DE(ntohl(remote_id.s_addr)) || (peer->sort == BGP_PEER_IBGP && ntohl(peer->local_id.s_addr) == ntohl(remote_id.s_addr))) { if (bgp_debug_neighbor_events(peer)) @@ -1431,6 +1432,27 @@ static int bgp_keepalive_receive(struct peer *peer, bgp_size_t size) bgp_update_implicit_eors(peer); + peer->rtt = sockopt_tcp_rtt(peer->fd); + + /* If the peer's RTT is higher than expected, shutdown + * the peer automatically. + */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_RTT_SHUTDOWN) + && peer->rtt > peer->rtt_expected) { + + peer->rtt_keepalive_rcv++; + + if (peer->rtt_keepalive_rcv > peer->rtt_keepalive_conf) { + zlog_warn( + "%s shutdown due to high round-trip-time (%dms > %dms)", + peer->host, peer->rtt, peer->rtt_expected); + peer_flag_set(peer, PEER_FLAG_SHUTDOWN); + } + } else { + if (peer->rtt_keepalive_rcv) + peer->rtt_keepalive_rcv--; + } + return Receive_KEEPALIVE_message; } @@ -1574,7 +1596,8 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW || BGP_DEBUG(update, UPDATE_IN) || BGP_DEBUG(update, UPDATE_PREFIX)) { - ret = bgp_dump_attr(&attr, peer->rcvd_attr_str, BUFSIZ); + ret = bgp_dump_attr(&attr, peer->rcvd_attr_str, + sizeof(peer->rcvd_attr_str)); peer->stat_upd_7606++; diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 7c3e8cd70e..f6e5c196ca 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -42,6 +42,10 @@ DEFINE_MTYPE_STATIC(BGPD, PBR_RULE, "PBR rule") DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context") DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value") +/* chain strings too long to fit in one line */ +#define FSPEC_ACTION_EXCEED_LIMIT "flowspec actions exceeds limit" +#define IPV6_FRAGMENT_INVALID "fragment not valid for IPv6 for this implementation" + RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface, id_entry, bgp_pbr_interface_compare); struct bgp_pbr_interface_head ifaces_by_name_ipv4 = @@ -168,35 +172,62 @@ static int bgp_pbr_match_walkcb(struct hash_bucket *bucket, void *arg) return HASHWALK_CONTINUE; } -static int sprintf_bgp_pbr_match_val(char *str, struct bgp_pbr_match_val *mval, - const char *prepend) +static int snprintf_bgp_pbr_match_val(char *str, int len, + struct bgp_pbr_match_val *mval, + const char *prepend) { char *ptr = str; + int delta; - if (prepend) - ptr += sprintf(ptr, "%s", prepend); - else { - if (mval->unary_operator & OPERATOR_UNARY_OR) - ptr += sprintf(ptr, ", or "); - if (mval->unary_operator & OPERATOR_UNARY_AND) - ptr += sprintf(ptr, ", and "); - } - if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) - ptr += sprintf(ptr, "<"); - if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) - ptr += sprintf(ptr, ">"); - if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) - ptr += sprintf(ptr, "="); - if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) - ptr += sprintf(ptr, "match"); - ptr += sprintf(ptr, " %u", mval->value); + if (prepend) { + delta = snprintf(ptr, len, "%s", prepend); + ptr += delta; + len -= delta; + } else { + if (mval->unary_operator & OPERATOR_UNARY_OR) { + delta = snprintf(ptr, len, ", or "); + ptr += delta; + len -= delta; + } + if (mval->unary_operator & OPERATOR_UNARY_AND) { + delta = snprintf(ptr, len, ", and "); + ptr += delta; + len -= delta; + } + } + if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) { + delta = snprintf(ptr, len, "<"); + ptr += delta; + len -= delta; + } + if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) { + delta = snprintf(ptr, len, ">"); + ptr += delta; + len -= delta; + } + if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) { + delta = snprintf(ptr, len, "="); + ptr += delta; + len -= delta; + } + if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) { + delta = snprintf(ptr, len, "match"); + ptr += delta; + len -= delta; + } + ptr += snprintf(ptr, len, " %u", mval->value); return (int)(ptr - str); } -#define INCREMENT_DISPLAY(_ptr, _cnt) do { \ - if (_cnt) \ - (_ptr) += sprintf((_ptr), "; "); \ - _cnt++; \ +#define INCREMENT_DISPLAY(_ptr, _cnt, _len) do { \ + int sn_delta; \ + \ + if (_cnt) { \ + sn_delta = snprintf((_ptr), (_len), "; ");\ + (_len) -= sn_delta; \ + (_ptr) += sn_delta; \ + } \ + (_cnt)++; \ } while (0) /* this structure can be used for port range, @@ -222,6 +253,7 @@ struct bgp_pbr_val_mask { struct bgp_pbr_filter { uint8_t type; vrf_id_t vrf_id; + uint8_t family; struct prefix *src; struct prefix *dst; uint8_t bitmask_iprule; @@ -231,6 +263,7 @@ struct bgp_pbr_filter { struct bgp_pbr_range_port *dst_port; struct bgp_pbr_val_mask *tcp_flags; struct bgp_pbr_val_mask *dscp; + struct bgp_pbr_val_mask *flow_label; struct bgp_pbr_val_mask *pkt_len_val; struct bgp_pbr_val_mask *fragment; }; @@ -242,6 +275,7 @@ struct bgp_pbr_filter { struct bgp_pbr_or_filter { struct list *tcpflags; struct list *dscp; + struct list *flowlabel; struct list *pkt_len; struct list *fragment; struct list *icmp_type; @@ -268,6 +302,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite( TCP_HEADER_ALL_FLAGS & ~(value); } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FLOW_LABEL || type_entry == FLOWSPEC_PKT_LEN || type_entry == FLOWSPEC_FRAGMENT) { and_valmask->val = value; @@ -282,6 +317,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite( TCP_HEADER_ALL_FLAGS & ~(value); } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FLOW_LABEL || type_entry == FLOWSPEC_FRAGMENT || type_entry == FLOWSPEC_PKT_LEN) { and_valmask->val = value; @@ -296,7 +332,7 @@ static bool bgp_pbr_extract_enumerate_unary_opposite( /* TCP : FIN and SYN -> val = ALL; mask = 3 * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST) - * other variables type: dscp, pkt len, fragment + * other variables type: dscp, pkt len, fragment, flow label * - value is copied in bgp_pbr_val_mask->val value * - if negate form is identifierd, bgp_pbr_val_mask->mask set to 1 */ @@ -351,6 +387,7 @@ static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[], and_valmask->mask |= TCP_HEADER_ALL_FLAGS & list[i].value; } else if (type_entry == FLOWSPEC_DSCP || + type_entry == FLOWSPEC_FLOW_LABEL || type_entry == FLOWSPEC_ICMP_TYPE || type_entry == FLOWSPEC_ICMP_CODE || type_entry == FLOWSPEC_FRAGMENT || @@ -490,9 +527,17 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) api->match_protocol_num); return 0; } + if (api->src_prefix_offset > 0 || + api->dst_prefix_offset > 0) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match prefix offset:" + "implementation does not support it."); + return 0; + } if (api->match_protocol_num == 1 && api->protocol[0].value != PROTOCOL_UDP && api->protocol[0].value != PROTOCOL_ICMP && + api->protocol[0].value != PROTOCOL_ICMPV6 && api->protocol[0].value != PROTOCOL_TCP) { if (BGP_DEBUG(pbr, PBR)) zlog_debug("BGP: match protocol operations:protocol (%d) not supported. ignoring", @@ -575,6 +620,27 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) return 0; } } + if (api->match_flowlabel_num) { + if (api->afi == AFI_IP) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match Flow Label operations:" + "Not for IPv4."); + return 0; + } + if (!bgp_pbr_extract_enumerate(api->flow_label, + api->match_flowlabel_num, + OPERATOR_UNARY_OR | OPERATOR_UNARY_AND, + NULL, FLOWSPEC_FLOW_LABEL)) { + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match FlowLabel operations:" + "too complex. ignoring."); + return 0; + } + if (BGP_DEBUG(pbr, PBR)) + zlog_debug("BGP: match FlowLabel operations " + "not supported. ignoring."); + return 0; + } if (api->match_fragment_num) { char fail_str[64]; bool success; @@ -598,6 +664,18 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) "Value not valid (%d) for this implementation", api->fragment[i].value); } + if (api->afi == AFI_IP6 && + api->fragment[i].value == 1) { + success = false; + snprintf(fail_str, sizeof(fail_str), + "IPv6 dont fragment match invalid (%d)", + api->fragment[i].value); + } + } + if (api->afi == AFI_IP6) { + success = false; + snprintf(fail_str, sizeof(fail_str), + "%s", IPV6_FRAGMENT_INVALID); } } else snprintf(fail_str, sizeof(fail_str), @@ -643,7 +721,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) if (api->actions[i].action == ACTION_MARKING) { if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); - zlog_warn("PBR: iprule set DSCP %u not supported", + zlog_warn("PBR: iprule set DSCP/Flow Label %u not supported", api->actions[i].u.marking_dscp); } } @@ -668,6 +746,7 @@ static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api) } /* return -1 if build or validation failed */ + int bgp_pbr_build_and_validate_entry(const struct prefix *p, struct bgp_path_info *path, struct bgp_pbr_entry_main *api) @@ -679,13 +758,13 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, struct bgp_pbr_entry_action *api_action; struct prefix *src = NULL, *dst = NULL; int valid_prefix = 0; - afi_t afi = AFI_IP; struct bgp_pbr_entry_action *api_action_redirect_ip = NULL; bool discard_action_found = false; + afi_t afi = family2afi(p->u.prefix_flowspec.family); /* extract match from flowspec entries */ ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr, - p->u.prefix_flowspec.prefixlen, api); + p->u.prefix_flowspec.prefixlen, api, afi); if (ret < 0) return -1; /* extract actiosn from flowspec ecom list */ @@ -699,8 +778,10 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, if (BGP_DEBUG(pbr, PBR_ERROR)) flog_err( EC_BGP_FLOWSPEC_PACKET, - "%s: flowspec actions exceeds limit (max %u)", - __func__, action_count); + "%s: %s (max %u)", + __func__, + FSPEC_ACTION_EXCEED_LIMIT, + action_count); break; } api_action = &api->actions[action_count - 1]; @@ -737,9 +818,10 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, * do not overwrite * draft-ietf-idr-flowspec-redirect */ - if (api_action_redirect_ip) { - if (api_action_redirect_ip->u.zr - .redirect_ip_v4.s_addr + if (api_action_redirect_ip && + p->u.prefix_flowspec.family == AF_INET) { + if (api_action_redirect_ip->u + .zr.redirect_ip_v4.s_addr != INADDR_ANY) continue; if (path->attr->nexthop.s_addr @@ -751,13 +833,44 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, api_action_redirect_ip->u.zr.duplicate = ecom_eval->val[7]; continue; - } else { + } else if (api_action_redirect_ip && + p->u.prefix_flowspec.family == AF_INET6) { + if (memcmp(&api_action_redirect_ip->u + .zr.redirect_ip_v6, + &in6addr_any, + sizeof(struct in6_addr))) + continue; + if (path->attr->mp_nexthop_len == 0 || + path->attr->mp_nexthop_len == + BGP_ATTR_NHLEN_IPV4 || + path->attr->mp_nexthop_len == + BGP_ATTR_NHLEN_VPNV4) + continue; + memcpy(&api_action_redirect_ip->u + .zr.redirect_ip_v6, + &path->attr->mp_nexthop_global, + sizeof(struct in6_addr)); + api_action_redirect_ip->u.zr.duplicate + = ecom_eval->val[7]; + continue; + } else if (p->u.prefix_flowspec.family == + AF_INET) { api_action->action = ACTION_REDIRECT_IP; api_action->u.zr.redirect_ip_v4.s_addr = path->attr->nexthop.s_addr; api_action->u.zr.duplicate = ecom_eval->val[7]; api_action_redirect_ip = api_action; + } else if (p->u.prefix_flowspec.family == + AF_INET6) { + api_action->action = ACTION_REDIRECT_IP; + memcpy(&api_action->u + .zr.redirect_ip_v6, + &path->attr->mp_nexthop_global, + sizeof(struct in6_addr)); + api_action->u.zr.duplicate + = ecom_eval->val[7]; + api_action_redirect_ip = api_action; } } else if ((ecom_eval->val[0] == (char)ECOMMUNITY_ENCODE_IP) && @@ -789,16 +902,56 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p, (char)ECOMMUNITY_ENCODE_TRANS_EXP) continue; ret = ecommunity_fill_pbr_action(ecom_eval, - api_action); + api_action, + afi); if (ret != 0) continue; - if ((api_action->action == ACTION_TRAFFICRATE) && - api->actions[i].u.r.rate == 0) + if ((api_action->action == ACTION_TRAFFICRATE) + && api->actions[i].u.r.rate == 0) discard_action_found = true; } api->action_num++; } } + if (path && path->attr && path->attr->ipv6_ecommunity) { + struct ecommunity_val_ipv6 *ipv6_ecom_eval; + + ecom = path->attr->ipv6_ecommunity; + for (i = 0; i < ecom->size; i++) { + ipv6_ecom_eval = (struct ecommunity_val_ipv6 *) + (ecom->val + (i * ecom->unit_size)); + action_count++; + if (action_count > ACTIONS_MAX_NUM) { + if (BGP_DEBUG(pbr, PBR_ERROR)) + flog_err( + EC_BGP_FLOWSPEC_PACKET, + "%s: flowspec actions exceeds limit (max %u)", + __func__, action_count); + break; + } + api_action = &api->actions[action_count - 1]; + if ((ipv6_ecom_eval->val[1] == + (char)ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) && + (ipv6_ecom_eval->val[0] == + (char)ECOMMUNITY_ENCODE_TRANS_EXP)) { + struct ecommunity *eckey = ecommunity_new(); + struct ecommunity_val_ipv6 ecom_copy; + + eckey->unit_size = IPV6_ECOMMUNITY_SIZE; + memcpy(&ecom_copy, ipv6_ecom_eval, + sizeof(struct ecommunity_val_ipv6)); + ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET; + ecommunity_add_val_ipv6(eckey, &ecom_copy, + false, false); + api_action->action = ACTION_REDIRECT; + api_action->u.redirect_vrf = + get_first_vrf_for_redirect_with_rt( + eckey); + ecommunity_free(&eckey); + api->action_num++; + } + } + } /* if ECOMMUNITY_TRAFFIC_RATE = 0 as action * then reduce the API action list to that action */ @@ -969,11 +1122,13 @@ uint32_t bgp_pbr_match_hash_key(const void *arg) key = jhash_1word(pbm->vrf_id, 0x4312abde); key = jhash_1word(pbm->flags, key); + key = jhash_1word(pbm->family, key); key = jhash(&pbm->pkt_len_min, 2, key); key = jhash(&pbm->pkt_len_max, 2, key); key = jhash(&pbm->tcp_flags, 2, key); key = jhash(&pbm->tcp_mask_flags, 2, key); key = jhash(&pbm->dscp_value, 1, key); + key = jhash(&pbm->flow_label, 2, key); key = jhash(&pbm->fragment, 1, key); key = jhash(&pbm->protocol, 1, key); return jhash_1word(pbm->type, key); @@ -989,6 +1144,9 @@ bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) if (r1->vrf_id != r2->vrf_id) return false; + if (r1->family != r2->family) + return false; + if (r1->type != r2->type) return false; @@ -1013,6 +1171,9 @@ bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2) if (r1->dscp_value != r2->dscp_value) return false; + if (r1->flow_label != r2->flow_label) + return false; + if (r1->fragment != r2->fragment) return false; @@ -1121,6 +1282,7 @@ uint32_t bgp_pbr_action_hash_key(const void *arg) pbra = arg; key = jhash_1word(pbra->table_id, 0x4312abde); key = jhash_1word(pbra->fwmark, key); + key = jhash_1word(pbra->afi, key); return key; } @@ -1138,6 +1300,9 @@ bool bgp_pbr_action_hash_equal(const void *arg1, const void *arg2) if (r1->vrf_id != r2->vrf_id) return false; + if (r1->afi != r2->afi) + return false; + if (memcmp(&r1->nh, &r2->nh, sizeof(struct nexthop))) return false; @@ -1244,6 +1409,7 @@ void bgp_pbr_cleanup(struct bgp *bgp) if (bgp->bgp_pbr_cfg == NULL) return; bgp_pbr_reset(bgp, AFI_IP); + bgp_pbr_reset(bgp, AFI_IP6); XFREE(MTYPE_PBR, bgp->bgp_pbr_cfg); } @@ -1274,132 +1440,227 @@ void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api) char *ptr = return_string; char buff[64]; int nb_items = 0; + int delta, len = sizeof(return_string); - ptr += sprintf(ptr, "MATCH : "); + delta = snprintf(ptr, sizeof(return_string), "MATCH : "); + len -= delta; + ptr += delta; if (api->match_bitmask & PREFIX_SRC_PRESENT) { struct prefix *p = &(api->src_prefix); - ptr += sprintf(ptr, "@src %s", prefix2str(p, buff, 64)); - INCREMENT_DISPLAY(ptr, nb_items); + if (api->src_prefix_offset) + delta = snprintf(ptr, len, "@src %s/off%u", + prefix2str(p, buff, 64), + api->src_prefix_offset); + else + delta = snprintf(ptr, len, "@src %s", + prefix2str(p, buff, 64)); + len -= delta; + ptr += delta; + INCREMENT_DISPLAY(ptr, nb_items, len); } if (api->match_bitmask & PREFIX_DST_PRESENT) { struct prefix *p = &(api->dst_prefix); - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@dst %s", prefix2str(p, buff, 64)); + INCREMENT_DISPLAY(ptr, nb_items, len); + if (api->dst_prefix_offset) + delta = snprintf(ptr, len, "@dst %s/off%u", + prefix2str(p, buff, 64), + api->dst_prefix_offset); + else + delta = snprintf(ptr, len, "@dst %s", + prefix2str(p, buff, 64)); + len -= delta; + ptr += delta; } if (api->match_protocol_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_protocol_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->protocol[i], - i > 0 ? NULL : "@proto "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_protocol_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->protocol[i], + i > 0 ? NULL : "@proto "); + len -= delta; + ptr += delta; + } if (api->match_src_port_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_src_port_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->src_port[i], - i > 0 ? NULL : "@srcport "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_src_port_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->src_port[i], + i > 0 ? NULL : "@srcport "); + len -= delta; + ptr += delta; + } if (api->match_dst_port_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_dst_port_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->dst_port[i], - i > 0 ? NULL : "@dstport "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_dst_port_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dst_port[i], + i > 0 ? NULL : "@dstport "); + len -= delta; + ptr += delta; + } if (api->match_port_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_port_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->port[i], - i > 0 ? NULL : "@port "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_port_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->port[i], + i > 0 ? NULL : "@port "); + len -= delta; + ptr += delta; + } if (api->match_icmp_type_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_icmp_type_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_type[i], - i > 0 ? NULL : "@icmptype "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_icmp_type_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_type[i], + i > 0 ? NULL : "@icmptype "); + len -= delta; + ptr += delta; + } if (api->match_icmp_code_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_icmp_code_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->icmp_code[i], - i > 0 ? NULL : "@icmpcode "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_icmp_code_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_code[i], + i > 0 ? NULL : "@icmpcode "); + len -= delta; + ptr += delta; + } if (api->match_packet_length_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_packet_length_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->packet_length[i], - i > 0 ? NULL : "@plen "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_packet_length_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, + &api->packet_length[i], + i > 0 ? NULL : "@plen "); + len -= delta; + ptr += delta; + } if (api->match_dscp_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_dscp_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->dscp[i], - i > 0 ? NULL : "@dscp "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_dscp_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dscp[i], + i > 0 ? NULL : "@dscp "); + len -= delta; + ptr += delta; + } + + if (api->match_flowlabel_num) + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_flowlabel_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, + &api->flow_label[i], + i > 0 ? NULL : "@flowlabel "); + len -= delta; + ptr += delta; + } if (api->match_tcpflags_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_tcpflags_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->tcpflags[i], - i > 0 ? NULL : "@tcpflags "); + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_tcpflags_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->tcpflags[i], + i > 0 ? NULL : "@tcpflags "); + len -= delta; + ptr += delta; + } if (api->match_fragment_num) - INCREMENT_DISPLAY(ptr, nb_items); - for (i = 0; i < api->match_fragment_num; i++) - ptr += sprintf_bgp_pbr_match_val(ptr, &api->fragment[i], - i > 0 ? NULL : "@fragment "); - if (!nb_items) + INCREMENT_DISPLAY(ptr, nb_items, len); + for (i = 0; i < api->match_fragment_num; i++) { + delta = snprintf_bgp_pbr_match_val(ptr, len, &api->fragment[i], + i > 0 ? NULL : "@fragment "); + len -= delta; + ptr += delta; + } + + len = sizeof(return_string); + if (!nb_items) { ptr = return_string; - else - ptr += sprintf(ptr, "; "); - if (api->action_num) - ptr += sprintf(ptr, "SET : "); + } else { + len -= (ptr - return_string); + delta = snprintf(ptr, len, "; "); + len -= delta; + ptr += delta; + } + if (api->action_num) { + delta = snprintf(ptr, len, "SET : "); + len -= delta; + ptr += delta; + } nb_items = 0; for (i = 0; i < api->action_num; i++) { switch (api->actions[i].action) { case ACTION_TRAFFICRATE: - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@set rate %f", - api->actions[i].u.r.rate); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@set rate %f", + api->actions[i].u.r.rate); + len -= delta; + ptr += delta; break; case ACTION_TRAFFIC_ACTION: - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@action "); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@action "); + len -= delta; + ptr += delta; if (api->actions[i].u.za.filter - & TRAFFIC_ACTION_TERMINATE) - ptr += sprintf(ptr, - " terminate (apply filter(s))"); + & TRAFFIC_ACTION_TERMINATE) { + delta = snprintf(ptr, len, + " terminate (apply filter(s))"); + len -= delta; + ptr += delta; + } if (api->actions[i].u.za.filter - & TRAFFIC_ACTION_DISTRIBUTE) - ptr += sprintf(ptr, " distribute"); + & TRAFFIC_ACTION_DISTRIBUTE) { + delta = snprintf(ptr, len, " distribute"); + len -= delta; + ptr += delta; + } if (api->actions[i].u.za.filter - & TRAFFIC_ACTION_SAMPLE) - ptr += sprintf(ptr, " sample"); + & TRAFFIC_ACTION_SAMPLE) { + delta = snprintf(ptr, len, " sample"); + len -= delta; + ptr += delta; + } break; - case ACTION_REDIRECT_IP: - INCREMENT_DISPLAY(ptr, nb_items); - char local_buff[INET_ADDRSTRLEN]; + case ACTION_REDIRECT_IP: { + char local_buff[INET6_ADDRSTRLEN]; + void *ptr_ip; - if (inet_ntop(AF_INET, - &api->actions[i].u.zr.redirect_ip_v4, - local_buff, INET_ADDRSTRLEN) != NULL) - ptr += sprintf(ptr, + INCREMENT_DISPLAY(ptr, nb_items, len); + if (api->afi == AF_INET) + ptr_ip = &api->actions[i].u.zr.redirect_ip_v4; + else + ptr_ip = &api->actions[i].u.zr.redirect_ip_v6; + if (inet_ntop(afi2family(api->afi), + ptr_ip, local_buff, + INET6_ADDRSTRLEN) != NULL) { + delta = snprintf(ptr, len, "@redirect ip nh %s", local_buff); + len -= delta; + ptr += delta; + } break; + } case ACTION_REDIRECT: { struct vrf *vrf; vrf = vrf_lookup_by_id(api->actions[i].u.redirect_vrf); - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@redirect vrf %s(%u)", - VRF_LOGNAME(vrf), - api->actions[i].u.redirect_vrf); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@redirect vrf %s(%u)", + VRF_LOGNAME(vrf), + api->actions[i].u.redirect_vrf); + len -= delta; + ptr += delta; break; } case ACTION_MARKING: - INCREMENT_DISPLAY(ptr, nb_items); - ptr += sprintf(ptr, "@set dscp %u", - api->actions[i].u.marking_dscp); + INCREMENT_DISPLAY(ptr, nb_items, len); + delta = snprintf(ptr, len, "@set dscp/flowlabel %u", + api->actions[i].u.marking_dscp); + len -= delta; + ptr += delta; break; default: break; @@ -1494,7 +1755,7 @@ static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa, if (bpa->installed && bpa->table_id != 0) { bgp_send_pbr_rule_action(bpa, NULL, false); bgp_zebra_announce_default(bpa->bgp, &(bpa->nh), - AFI_IP, + bpa->afi, bpa->table_id, false); bpa->installed = false; @@ -1573,6 +1834,8 @@ static int bgp_pbr_get_remaining_entry(struct hash_bucket *bucket, void *arg) bpm_temp->pkt_len_min != bpm->pkt_len_min || bpm_temp->pkt_len_max != bpm->pkt_len_max || bpm_temp->dscp_value != bpm->dscp_value || + bpm_temp->flow_label != bpm->flow_label || + bpm_temp->family != bpm->family || bpm_temp->fragment != bpm->fragment) return HASHWALK_CONTINUE; @@ -1644,16 +1907,17 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit( return; } + temp.family = bpf->family; if (bpf->src) { temp.flags |= MATCH_IP_SRC_SET; prefix_copy(&temp2.src, bpf->src); } else - temp2.src.family = AF_INET; + temp2.src.family = bpf->family; if (bpf->dst) { temp.flags |= MATCH_IP_DST_SET; prefix_copy(&temp2.dst, bpf->dst); } else - temp2.dst.family = AF_INET; + temp2.dst.family = bpf->family; if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) { if (bpf->protocol == IPPROTO_ICMP) temp.flags |= MATCH_ICMP_SET; @@ -1696,6 +1960,14 @@ static void bgp_pbr_policyroute_remove_from_zebra_unit( temp.flags |= MATCH_DSCP_SET; temp.dscp_value = bpf->dscp->val; } + if (bpf->flow_label) { + if (bpf->flow_label->mask) + temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET; + else + temp.flags |= MATCH_FLOW_LABEL_SET; + temp.flow_label = bpf->flow_label->val; + } + if (bpf->fragment) { if (bpf->fragment->mask) temp.flags |= MATCH_FRAGMENT_INVERSE_SET; @@ -1742,6 +2014,8 @@ static uint8_t bgp_pbr_next_type_entry(uint8_t type_entry) if (type_entry == FLOWSPEC_TCP_FLAGS) return FLOWSPEC_DSCP; if (type_entry == FLOWSPEC_DSCP) + return FLOWSPEC_FLOW_LABEL; + if (type_entry == FLOWSPEC_FLOW_LABEL) return FLOWSPEC_PKT_LEN; if (type_entry == FLOWSPEC_PKT_LEN) return FLOWSPEC_FRAGMENT; @@ -1835,6 +2109,9 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive( } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) { orig_list = bpof->dscp; target_val = &bpf->dscp; + } else if (type_entry == FLOWSPEC_FLOW_LABEL && bpof->flowlabel) { + orig_list = bpof->flowlabel; + target_val = &bpf->flow_label; } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) { orig_list = bpof->pkt_len; target_val = &bpf->pkt_len_val; @@ -1872,6 +2149,9 @@ static void bgp_pbr_policyroute_remove_from_zebra( else if (bpof->dscp) bgp_pbr_policyroute_remove_from_zebra_recursive( bgp, path, bpf, bpof, FLOWSPEC_DSCP); + else if (bpof->flowlabel) + bgp_pbr_policyroute_remove_from_zebra_recursive( + bgp, path, bpf, bpof, FLOWSPEC_FLOW_LABEL); else if (bpof->pkt_len) bgp_pbr_policyroute_remove_from_zebra_recursive( bgp, path, bpf, bpof, FLOWSPEC_PKT_LEN); @@ -1888,6 +2168,8 @@ static void bgp_pbr_policyroute_remove_from_zebra( list_delete_all_node(bpof->tcpflags); if (bpof->dscp) list_delete_all_node(bpof->dscp); + if (bpof->flowlabel) + list_delete_all_node(bpof->flowlabel); if (bpof->pkt_len) list_delete_all_node(bpof->pkt_len); if (bpof->fragment) @@ -1979,6 +2261,15 @@ static void bgp_pbr_dump_entry(struct bgp_pbr_filter *bpf, bool add) ? "!" : "", bpf->dscp->val); } + if (bpf->flow_label) { + snprintf(buffer + remaining_len, + sizeof(buffer) + - remaining_len, + "%s flow_label %d", + bpf->flow_label->mask + ? "!" : "", + bpf->flow_label->val); + } zlog_debug("BGP: %s FS PBR from %s to %s, %s %s", add ? "adding" : "removing", bpf->src == NULL ? "<all>" : @@ -2027,6 +2318,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (nh) memcpy(&temp3.nh, nh, sizeof(struct nexthop)); temp3.vrf_id = bpf->vrf_id; + temp3.afi = family2afi(bpf->family); bpa = hash_get(bgp->pbr_action_hash, &temp3, bgp_pbr_action_alloc_intern); @@ -2085,7 +2377,8 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (!bpa->installed && !bpa->install_in_progress) { bgp_send_pbr_rule_action(bpa, NULL, true); bgp_zebra_announce_default(bgp, nh, - AFI_IP, bpa->table_id, true); + bpa->afi, + bpa->table_id, true); } /* ip rule add */ if (bpr && !bpr->installed) @@ -2111,6 +2404,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, /* then look for bpm */ memset(&temp, 0, sizeof(temp)); temp.vrf_id = bpf->vrf_id; + temp.family = bpf->family; if (bpf->src) temp.flags |= MATCH_IP_SRC_SET; if (bpf->dst) @@ -2162,6 +2456,13 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, temp.flags |= MATCH_DSCP_SET; temp.dscp_value = bpf->dscp->val; } + if (bpf->flow_label) { + if (bpf->flow_label->mask) + temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET; + else + temp.flags |= MATCH_FLOW_LABEL_SET; + temp.flow_label = bpf->flow_label->val; + } if (bpf->fragment) { if (bpf->fragment->mask) temp.flags |= MATCH_FRAGMENT_INVERSE_SET; @@ -2179,7 +2480,8 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (bpm->unique == 0) { bpm->unique = ++bgp_pbr_match_counter_unique; /* 0 value is forbidden */ - sprintf(bpm->ipset_name, "match%p", bpm); + snprintf(bpm->ipset_name, sizeof(bpm->ipset_name), + "match%p", bpm); bpm->entry_hash = hash_create_size(8, bgp_pbr_match_entry_hash_key, bgp_pbr_match_entry_hash_equal, @@ -2197,11 +2499,11 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (bpf->src) prefix_copy(&temp2.src, bpf->src); else - temp2.src.family = AF_INET; + temp2.src.family = bpf->family; if (bpf->dst) prefix_copy(&temp2.dst, bpf->dst); else - temp2.dst.family = AF_INET; + temp2.dst.family = bpf->family; temp2.src_port_min = src_port ? src_port->min_port : 0; temp2.dst_port_min = dst_port ? dst_port->min_port : 0; temp2.src_port_max = src_port ? src_port->max_port : 0; @@ -2248,7 +2550,7 @@ static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp, if (!bpa->installed && !bpa->install_in_progress) { bgp_send_pbr_rule_action(bpa, NULL, true); bgp_zebra_announce_default(bgp, nh, - AFI_IP, bpa->table_id, true); + bpa->afi, bpa->table_id, true); } /* ipset create */ @@ -2400,8 +2702,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, bpf.type = api->type; memset(&nh, 0, sizeof(struct nexthop)); nh.vrf_id = VRF_UNKNOWN; - if (api->match_protocol_num) + if (api->match_protocol_num) { proto = (uint8_t)api->protocol[0].value; + if (api->afi == AF_INET6 && proto == IPPROTO_ICMPV6) + proto = IPPROTO_ICMP; + } /* if match_port is selected, then either src or dst port will be parsed * but not both at the same time */ @@ -2510,6 +2815,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, bpf.protocol = proto; bpf.src_port = srcp; bpf.dst_port = dstp; + bpf.family = afi2family(api->afi); if (!add) { bgp_pbr_policyroute_remove_from_zebra(bgp, path, &bpf, &bpof); return; @@ -2559,10 +2865,18 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, */ break; case ACTION_REDIRECT_IP: - nh.type = NEXTHOP_TYPE_IPV4; - nh.gate.ipv4.s_addr = - api->actions[i].u.zr.redirect_ip_v4.s_addr; nh.vrf_id = api->vrf_id; + if (api->afi == AFI_IP) { + nh.type = NEXTHOP_TYPE_IPV4; + nh.gate.ipv4.s_addr = + api->actions[i].u.zr. + redirect_ip_v4.s_addr; + } else { + nh.type = NEXTHOP_TYPE_IPV6; + memcpy(&nh.gate.ipv6, + &api->actions[i].u.zr.redirect_ip_v6, + sizeof(struct in6_addr)); + } bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof, &nh, &rate); /* XXX combination with REDIRECT_VRF @@ -2571,8 +2885,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, continue_loop = 0; break; case ACTION_REDIRECT: + if (api->afi == AFI_IP) + nh.type = NEXTHOP_TYPE_IPV4; + else + nh.type = NEXTHOP_TYPE_IPV6; nh.vrf_id = api->actions[i].u.redirect_vrf; - nh.type = NEXTHOP_TYPE_IPV4; bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof, &nh, &rate); continue_loop = 0; @@ -2580,7 +2897,7 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, case ACTION_MARKING: if (BGP_DEBUG(pbr, PBR)) { bgp_pbr_print_policy_route(api); - zlog_warn("PBR: Set DSCP %u Ignored", + zlog_warn("PBR: Set DSCP/FlowLabel %u Ignored", api->actions[i].u.marking_dscp); } break; @@ -2598,8 +2915,6 @@ void bgp_pbr_update_entry(struct bgp *bgp, const struct prefix *p, { struct bgp_pbr_entry_main api; - if (afi == AFI_IP6) - return; /* IPv6 not supported */ if (safi != SAFI_FLOWSPEC) return; /* not supported */ /* Make Zebra API structure. */ @@ -2649,10 +2964,12 @@ void bgp_pbr_reset(struct bgp *bgp, afi_t afi) struct bgp_pbr_interface_head *head; struct bgp_pbr_interface *pbr_if; - if (!bgp_pbr_cfg || afi != AFI_IP) + if (!bgp_pbr_cfg) return; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - + if (afi == AFI_IP) + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + else + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); while (!RB_EMPTY(bgp_pbr_interface_head, head)) { pbr_if = RB_ROOT(bgp_pbr_interface_head, head); RB_REMOVE(bgp_pbr_interface_head, head, pbr_if); diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h index 043853a157..379ac40672 100644 --- a/bgpd/bgp_pbr.h +++ b/bgpd/bgp_pbr.h @@ -79,6 +79,7 @@ struct bgp_pbr_entry_action { vrf_id_t redirect_vrf; struct _pbr_redirect_ip { struct in_addr redirect_ip_v4; + struct in6_addr redirect_ip_v6; uint8_t duplicate; } zr; uint8_t marking_dscp; @@ -114,13 +115,17 @@ struct bgp_pbr_entry_main { uint8_t match_dscp_num; uint8_t match_tcpflags_num; uint8_t match_fragment_num; + uint8_t match_flowlabel_num; struct prefix src_prefix; struct prefix dst_prefix; + uint8_t src_prefix_offset; + uint8_t dst_prefix_offset; #define PROTOCOL_UDP 17 #define PROTOCOL_TCP 6 #define PROTOCOL_ICMP 1 +#define PROTOCOL_ICMPV6 58 struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX]; @@ -129,6 +134,7 @@ struct bgp_pbr_entry_main { struct bgp_pbr_match_val icmp_code[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val packet_length[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val dscp[BGP_PBR_MATCH_VAL_MAX]; + struct bgp_pbr_match_val flow_label[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX]; struct bgp_pbr_match_val fragment[BGP_PBR_MATCH_VAL_MAX]; @@ -154,6 +160,8 @@ extern int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a, struct bgp_pbr_config { struct bgp_pbr_interface_head ifaces_by_name_ipv4; bool pbr_interface_any_ipv4; + struct bgp_pbr_interface_head ifaces_by_name_ipv6; + bool pbr_interface_any_ipv6; }; extern struct bgp_pbr_config *bgp_pbr_cfg; @@ -179,6 +187,7 @@ struct bgp_pbr_match { uint32_t type; uint32_t flags; + uint8_t family; uint16_t pkt_len_min; uint16_t pkt_len_max; @@ -187,6 +196,7 @@ struct bgp_pbr_match { uint8_t dscp_value; uint8_t fragment; uint8_t protocol; + uint16_t flow_label; vrf_id_t vrf_id; @@ -254,6 +264,7 @@ struct bgp_pbr_action { bool install_in_progress; uint32_t refcnt; struct bgp *bgp; + afi_t afi; }; extern struct bgp_pbr_rule *bgp_pbr_rule_lookup(vrf_id_t vrf_id, diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 80ffa18424..8eaee36c2e 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3056,16 +3056,58 @@ static int bgp_maximum_prefix_restart_timer(struct thread *thread) return 0; } +static uint32_t bgp_filtered_routes_count(struct peer *peer, afi_t afi, + safi_t safi) +{ + uint32_t count = 0; + bool filtered = false; + struct bgp_dest *dest; + struct bgp_adj_in *ain; + struct attr attr = {}; + struct bgp_table *table = peer->bgp->rib[afi][safi]; + + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + for (ain = dest->adj_in; ain; ain = ain->next) { + const struct prefix *rn_p = bgp_dest_get_prefix(dest); + + attr = *ain->attr; + + if (bgp_input_filter(peer, rn_p, &attr, afi, safi) + == FILTER_DENY) + filtered = true; + + if (bgp_input_modifier( + peer, rn_p, &attr, afi, safi, + ROUTE_MAP_IN_NAME(&peer->filter[afi][safi]), + NULL, 0, NULL) + == RMAP_DENY) + filtered = true; + + if (filtered) + count++; + + bgp_attr_undup(&attr, ain->attr); + } + } + + return count; +} + bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, int always) { iana_afi_t pkt_afi; iana_safi_t pkt_safi; + uint32_t pcount = (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_FORCE)) + ? bgp_filtered_routes_count(peer, afi, safi) + + peer->pcount[afi][safi] + : peer->pcount[afi][safi]; if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) return false; - if (peer->pcount[afi][safi] > peer->pmax[afi][safi]) { + if (pcount > peer->pmax[afi][safi]) { if (CHECK_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT) && !always) @@ -3073,8 +3115,8 @@ bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, zlog_info( "%%MAXPFXEXCEED: No. of %s prefix received from %s %u exceed, limit %u", - get_afi_safi_str(afi, safi, false), peer->host, - peer->pcount[afi][safi], peer->pmax[afi][safi]); + get_afi_safi_str(afi, safi, false), peer->host, pcount, + peer->pmax[afi][safi]); SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT); if (CHECK_FLAG(peer->af_flags[afi][safi], @@ -3125,8 +3167,7 @@ bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, UNSET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT); - if (peer->pcount[afi][safi] - > (peer->pmax[afi][safi] * peer->pmax_threshold[afi][safi] / 100)) { + if (pcount > (pcount * peer->pmax_threshold[afi][safi] / 100)) { if (CHECK_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD) && !always) @@ -3134,8 +3175,8 @@ bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi, zlog_info( "%%MAXPFX: No. of %s prefix received from %s reaches %u, max %u", - get_afi_safi_str(afi, safi, false), peer->host, - peer->pcount[afi][safi], peer->pmax[afi][safi]); + get_afi_safi_str(afi, safi, false), peer->host, pcount, + peer->pmax[afi][safi]); SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_THRESHOLD); } else @@ -3564,8 +3605,20 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, goto filtered; } + /* Update Overlay Index */ + if (afi == AFI_L2VPN) { + overlay_index_update(&new_attr, + evpn == NULL ? NULL : &evpn->gw_ip); + } + attr_new = bgp_attr_intern(&new_attr); + /* If maximum prefix count is configured and current prefix + * count exeed it. + */ + if (bgp_maximum_prefix_overflow(peer, afi, safi, 0)) + return -1; + /* If the update is implicit withdraw. */ if (pi) { pi->uptime = bgp_clock(); @@ -3803,12 +3856,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } } #endif - /* Update Overlay Index */ - if (afi == AFI_L2VPN) { - overlay_index_update( - pi->attr, - evpn == NULL ? NULL : &evpn->gw_ip); - } /* Update bgp route dampening information. */ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) @@ -4036,11 +4083,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } #endif - /* If maximum prefix count is configured and current prefix - count exeed it. */ - if (bgp_maximum_prefix_overflow(peer, afi, safi, 0)) - return -1; - /* If this is an EVPN route, process for import. */ if (safi == SAFI_EVPN && CHECK_FLAG(new->flags, BGP_PATH_VALID)) bgp_evpn_import_route(bgp, afi, safi, p, new); @@ -9981,11 +10023,14 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, if (dest_p->family == AF_FLOWSPEC) { char retstr[BGP_FLOWSPEC_STRING_DISPLAY_MAX]; + bgp_fs_nlri_get_string( (unsigned char *) dest_p->u.prefix_flowspec.ptr, dest_p->u.prefix_flowspec.prefixlen, - retstr, NLRI_STRING_FORMAT_MIN, NULL); + retstr, NLRI_STRING_FORMAT_MIN, NULL, + family2afi(dest_p->u + .prefix_flowspec.family)); if (first) vty_out(vty, "\"%s/%d\": ", retstr, dest_p->u.prefix_flowspec diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 97153cfb72..09cc775d47 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -594,10 +594,14 @@ route_match_prefix_list_flowspec(afi_t afi, struct prefix_list *plist, memset(&api, 0, sizeof(api)); + if (family2afi(p->u.prefix_flowspec.family) != afi) + return RMAP_NOMATCH; + /* extract match from flowspec entries */ ret = bgp_flowspec_match_rules_fill( (uint8_t *)p->u.prefix_flowspec.ptr, - p->u.prefix_flowspec.prefixlen, &api); + p->u.prefix_flowspec.prefixlen, &api, + afi); if (ret < 0) return RMAP_NOMATCH; if (api.match_bitmask & PREFIX_DST_PRESENT || @@ -1664,6 +1668,45 @@ static const struct route_map_rule_cmd route_match_tag_cmd = { route_map_rule_tag_free, }; +static enum route_map_cmd_result_t +route_set_srte_color(void *rule, const struct prefix *prefix, + route_map_object_t type, void *object) +{ + uint32_t *srte_color = rule; + struct bgp_path_info *path; + + if (type != RMAP_BGP) + return RMAP_OKAY; + + path = object; + + path->attr->srte_color = *srte_color; + path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR); + + return RMAP_OKAY; +} + +/* Route map `sr-te color' compile function */ +static void *route_set_srte_color_compile(const char *arg) +{ + uint32_t *color; + + color = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); + *color = atoi(arg); + + return color; +} + +/* Free route map's compiled `sr-te color' value. */ +static void route_set_srte_color_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for sr-te color set. */ +struct route_map_rule_cmd route_set_srte_color_cmd = { + "sr-te color", route_set_srte_color, route_set_srte_color_compile, + route_set_srte_color_free}; /* Set nexthop to object. ojbect must be pointer to struct attr. */ struct rmap_ip_nexthop_set { @@ -2602,6 +2645,7 @@ route_set_ecommunity_lb(void *rule, const struct prefix *prefix, ecommunity_free(&old_ecom); } else { ecom_lb.size = 1; + ecom_lb.unit_size = ECOMMUNITY_SIZE; ecom_lb.val = (uint8_t *)lb_eval.val; new_ecom = ecommunity_dup(&ecom_lb); } @@ -5681,6 +5725,9 @@ void bgp_route_map_init(void) route_map_match_tag_hook(generic_match_add); route_map_no_match_tag_hook(generic_match_delete); + route_map_set_srte_color_hook(generic_set_add); + route_map_no_set_srte_color_hook(generic_set_delete); + route_map_set_ip_nexthop_hook(generic_set_add); route_map_no_set_ip_nexthop_hook(generic_set_delete); @@ -5723,6 +5770,7 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_vrl_source_vrf_cmd); route_map_install_set(&route_set_table_id_cmd); + route_map_install_set(&route_set_srte_color_cmd); route_map_install_set(&route_set_ip_nexthop_cmd); route_map_install_set(&route_set_local_pref_cmd); route_map_install_set(&route_set_weight_cmd); diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index ecdcaa38b8..76bf2db05b 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -781,6 +781,9 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) */ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest; dest = bgp_route_next(dest)) { + if (!bgp_dest_has_bgp_path_info_data(dest)) + continue; + ret = route_map_apply(peer->default_rmap[afi][safi].map, bgp_dest_get_prefix(dest), RMAP_BGP, &bpi_rmap); diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 5df9e3f23f..f706b834fe 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -837,7 +837,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) memset(send_attr_str, 0, BUFSIZ); send_attr_printed = 0; bgp_dump_attr(adv->baa->attr, send_attr_str, - BUFSIZ); + sizeof(send_attr_str)); } } @@ -1147,7 +1147,7 @@ void subgroup_default_update_packet(struct update_subgroup *subgrp, attrstr[0] = '\0'; - bgp_dump_attr(attr, attrstr, BUFSIZ); + bgp_dump_attr(attr, attrstr, sizeof(attrstr)); if (addpath_encode) snprintf(tx_id_buf, sizeof(tx_id_buf), diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 0268b7ec9d..d80667699a 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -3600,6 +3600,50 @@ DEFUN (bgp_default_shutdown, return CMD_SUCCESS; } +DEFPY(bgp_shutdown_msg, bgp_shutdown_msg_cmd, "bgp shutdown message MSG...", + BGP_STR + "Administrative shutdown of the BGP instance\n" + "Add a shutdown message (RFC 8203)\n" + "Shutdown message\n") +{ + char *msgstr = NULL; + + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (argc > 3) + msgstr = argv_concat(argv, argc, 3); + + bgp_shutdown_enable(bgp, msgstr); + XFREE(MTYPE_TMP, msgstr); + + return CMD_SUCCESS; +} + +DEFPY(bgp_shutdown, bgp_shutdown_cmd, "bgp shutdown", + BGP_STR "Administrative shutdown of the BGP instance\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp_shutdown_enable(bgp, NULL); + + return CMD_SUCCESS; +} + +DEFPY(no_bgp_shutdown, no_bgp_shutdown_cmd, "no bgp shutdown", + NO_STR BGP_STR "Administrative shutdown of the BGP instance\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp_shutdown_disable(bgp); + + return CMD_SUCCESS; +} + +ALIAS(no_bgp_shutdown, no_bgp_shutdown_msg_cmd, + "no bgp shutdown message MSG...", NO_STR BGP_STR + "Administrative shutdown of the BGP instance\n" + "Add a shutdown message (RFC 8203)\n" "Shutdown message\n") + DEFUN (neighbor_remote_as, neighbor_remote_as_cmd, "neighbor <A.B.C.D|X:X::X:X|WORD> remote-as <(1-4294967295)|internal|external>", @@ -4436,6 +4480,64 @@ ALIAS(no_neighbor_shutdown_msg, no_neighbor_shutdown_cmd, NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Administratively shut down this neighbor\n") +DEFUN(neighbor_shutdown_rtt, + neighbor_shutdown_rtt_cmd, + "neighbor <A.B.C.D|X:X::X:X|WORD> shutdown rtt (1-65535) [count (1-255)]", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Administratively shut down this neighbor\n" + "Shutdown if round-trip-time is higher than expected\n" + "Round-trip-time in milliseconds\n" + "Specify the number of keepalives before shutdown\n" + "The number of keepalives with higher RTT to shutdown\n") +{ + int idx_peer = 1; + int idx_rtt = 4; + int idx_count = 0; + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + peer->rtt_expected = strtol(argv[idx_rtt]->arg, NULL, 10); + + if (argv_find(argv, argc, "count", &idx_count)) + peer->rtt_keepalive_conf = + strtol(argv[idx_count + 1]->arg, NULL, 10); + + return peer_flag_set_vty(vty, argv[idx_peer]->arg, + PEER_FLAG_RTT_SHUTDOWN); +} + +DEFUN(no_neighbor_shutdown_rtt, + no_neighbor_shutdown_rtt_cmd, + "no neighbor <A.B.C.D|X:X::X:X|WORD> shutdown rtt [(1-65535) [count (1-255)]]", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Administratively shut down this neighbor\n" + "Shutdown if round-trip-time is higher than expected\n" + "Round-trip-time in milliseconds\n" + "Specify the number of keepalives before shutdown\n" + "The number of keepalives with higher RTT to shutdown\n") +{ + int idx_peer = 2; + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); + + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + peer->rtt_expected = 0; + peer->rtt_keepalive_conf = 1; + + return peer_flag_unset_vty(vty, argv[idx_peer]->arg, + PEER_FLAG_RTT_SHUTDOWN); +} + /* neighbor capability dynamic. */ DEFUN (neighbor_capability_dynamic, neighbor_capability_dynamic_cmd, @@ -6704,7 +6806,8 @@ static int peer_maximum_prefix_set_vty(struct vty *vty, const char *ip_str, afi_t afi, safi_t safi, const char *num_str, const char *threshold_str, int warning, - const char *restart_str) + const char *restart_str, + const char *force_str) { int ret; struct peer *peer; @@ -6728,7 +6831,7 @@ static int peer_maximum_prefix_set_vty(struct vty *vty, const char *ip_str, restart = 0; ret = peer_maximum_prefix_set(peer, afi, safi, max, threshold, warning, - restart); + restart, force_str ? true : false); return bgp_vty_return(vty, ret); } @@ -6799,172 +6902,220 @@ DEFUN(no_neighbor_maximum_prefix_out, return CMD_SUCCESS; } -/* Maximum number of prefix configuration. prefix count is different - for each peer configuration. So this configuration can be set for +/* Maximum number of prefix configuration. Prefix count is different + for each peer configuration. So this configuration can be set for each peer configuration. */ DEFUN (neighbor_maximum_prefix, neighbor_maximum_prefix_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295)", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" - "maximum no. of prefix limit\n") + "maximum no. of prefix limit\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - argv[idx_number]->arg, NULL, 0, NULL); + argv[idx_number]->arg, NULL, 0, NULL, force); } ALIAS_HIDDEN(neighbor_maximum_prefix, neighbor_maximum_prefix_hidden_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295)", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" - "maximum no. of prefix limit\n") + "maximum no. of prefix limit\n" + "Force checking all received routes not only accepted\n") DEFUN (neighbor_maximum_prefix_threshold, neighbor_maximum_prefix_threshold_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100)", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" - "Threshold value (%) at which to generate a warning msg\n") + "Threshold value (%) at which to generate a warning msg\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; int idx_number_2 = 4; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - argv[idx_number]->arg, argv[idx_number_2]->arg, 0, NULL); + argv[idx_number]->arg, argv[idx_number_2]->arg, 0, NULL, force); } ALIAS_HIDDEN( neighbor_maximum_prefix_threshold, neighbor_maximum_prefix_threshold_hidden_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100)", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" - "Threshold value (%) at which to generate a warning msg\n") + "Threshold value (%) at which to generate a warning msg\n" + "Force checking all received routes not only accepted\n") DEFUN (neighbor_maximum_prefix_warning, neighbor_maximum_prefix_warning_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - argv[idx_number]->arg, NULL, 1, NULL); + argv[idx_number]->arg, NULL, 1, NULL, force); } ALIAS_HIDDEN( neighbor_maximum_prefix_warning, neighbor_maximum_prefix_warning_hidden_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") DEFUN (neighbor_maximum_prefix_threshold_warning, neighbor_maximum_prefix_threshold_warning_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" "Threshold value (%) at which to generate a warning msg\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; int idx_number_2 = 4; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - argv[idx_number]->arg, argv[idx_number_2]->arg, 1, NULL); + argv[idx_number]->arg, argv[idx_number_2]->arg, 1, NULL, force); } ALIAS_HIDDEN( neighbor_maximum_prefix_threshold_warning, neighbor_maximum_prefix_threshold_warning_hidden_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" "Threshold value (%) at which to generate a warning msg\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") DEFUN (neighbor_maximum_prefix_restart, neighbor_maximum_prefix_restart_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535)", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" "Restart bgp connection after limit is exceeded\n" - "Restart interval in minutes\n") + "Restart interval in minutes\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; int idx_number_2 = 5; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), - argv[idx_number]->arg, NULL, 0, argv[idx_number_2]->arg); + argv[idx_number]->arg, NULL, 0, argv[idx_number_2]->arg, force); } ALIAS_HIDDEN( neighbor_maximum_prefix_restart, neighbor_maximum_prefix_restart_hidden_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535)", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefix accept from this peer\n" "maximum no. of prefix limit\n" "Restart bgp connection after limit is exceeded\n" - "Restart interval in minutes\n") + "Restart interval in minutes\n" + "Force checking all received routes not only accepted\n") DEFUN (neighbor_maximum_prefix_threshold_restart, neighbor_maximum_prefix_threshold_restart_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535)", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefixes to accept from this peer\n" "maximum no. of prefix limit\n" "Threshold value (%) at which to generate a warning msg\n" "Restart bgp connection after limit is exceeded\n" - "Restart interval in minutes\n") + "Restart interval in minutes\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 1; int idx_number = 3; int idx_number_2 = 4; int idx_number_3 = 6; + int idx_force = 0; + char *force = NULL; + + if (argv_find(argv, argc, "force", &idx_force)) + force = argv[idx_force]->arg; + return peer_maximum_prefix_set_vty( vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty), argv[idx_number]->arg, argv[idx_number_2]->arg, 0, - argv[idx_number_3]->arg); + argv[idx_number_3]->arg, force); } ALIAS_HIDDEN( neighbor_maximum_prefix_threshold_restart, neighbor_maximum_prefix_threshold_restart_hidden_cmd, - "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535)", + "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535) [force]", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefixes to accept from this peer\n" "maximum no. of prefix limit\n" "Threshold value (%) at which to generate a warning msg\n" "Restart bgp connection after limit is exceeded\n" - "Restart interval in minutes\n") + "Restart interval in minutes\n" + "Force checking all received routes not only accepted\n") DEFUN (no_neighbor_maximum_prefix, no_neighbor_maximum_prefix_cmd, - "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only]]", + "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only] [force]]", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 @@ -6973,7 +7124,8 @@ DEFUN (no_neighbor_maximum_prefix, "Threshold value (%) at which to generate a warning msg\n" "Restart bgp connection after limit is exceeded\n" "Restart interval in minutes\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") { int idx_peer = 2; return peer_maximum_prefix_unset_vty(vty, argv[idx_peer]->arg, @@ -6983,14 +7135,15 @@ DEFUN (no_neighbor_maximum_prefix, ALIAS_HIDDEN( no_neighbor_maximum_prefix, no_neighbor_maximum_prefix_hidden_cmd, - "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only]]", + "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only] [force]]", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Maximum number of prefixes to accept from this peer\n" "maximum no. of prefix limit\n" "Threshold value (%) at which to generate a warning msg\n" "Restart bgp connection after limit is exceeded\n" "Restart interval in minutes\n" - "Only give warning message when limit is exceeded\n") + "Only give warning message when limit is exceeded\n" + "Force checking all received routes not only accepted\n") /* "neighbor allowas-in" */ @@ -7281,15 +7434,20 @@ DEFPY( } static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, - struct ecommunity **list) + struct ecommunity **list, bool is_rt6) { struct ecommunity *ecom = NULL; struct ecommunity *ecomadd; for (; argc; --argc, ++argv) { - - ecomadd = ecommunity_str2com(argv[0]->arg, - ECOMMUNITY_ROUTE_TARGET, 0); + if (is_rt6) + ecomadd = ecommunity_str2com_ipv6(argv[0]->arg, + ECOMMUNITY_ROUTE_TARGET, + 0); + else + ecomadd = ecommunity_str2com(argv[0]->arg, + ECOMMUNITY_ROUTE_TARGET, + 0); if (!ecomadd) { vty_out(vty, "Malformed community-list value\n"); if (ecom) @@ -7369,10 +7527,10 @@ DEFPY (af_rd_vpn_export, int ret; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; if (yes) { ret = str2prefix_rd(rd_str, &prd); @@ -7430,10 +7588,10 @@ DEFPY (af_label_vpn_export, mpls_label_t label = MPLS_LABEL_NONE; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; /* If "no ...", squash trailing parameter */ if (!yes) @@ -7525,7 +7683,6 @@ DEFPY (af_nexthop_vpn_export, vty_out(vty, "%% Nexthop required\n"); return CMD_WARNING_CONFIG_FAILED; } - if (!sockunion2hostprefix(nexthop_su, &p)) return CMD_WARNING_CONFIG_FAILED; } @@ -7591,10 +7748,10 @@ DEFPY (af_rt_vpn_imexport, vpn_policy_direction_t dir; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) @@ -7609,7 +7766,7 @@ DEFPY (af_rt_vpn_imexport, vty_out(vty, "%% Missing RTLIST\n"); return CMD_WARNING_CONFIG_FAILED; } - ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom); + ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, false); if (ret != CMD_SUCCESS) { return ret; } @@ -7671,10 +7828,10 @@ DEFPY (af_route_map_vpn_imexport, vpn_policy_direction_t dir; afi_t afi; int idx = 0; - int yes = 1; + bool yes = true; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) @@ -7913,12 +8070,12 @@ DEFPY (bgp_imexport_vpn, afi_t afi; safi_t safi; int idx = 0; - int yes = 1; + bool yes = true; int flag; vpn_policy_direction_t dir; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type && BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) { @@ -7966,34 +8123,44 @@ DEFPY (bgp_imexport_vpn, DEFPY (af_routetarget_import, af_routetarget_import_cmd, - "[no] <rt|route-target> redirect import RTLIST...", + "[no] <rt|route-target|route-target6|rt6> redirect import RTLIST...", NO_STR "Specify route target list\n" "Specify route target list\n" + "Specify route target list\n" + "Specify route target list\n" "Flow-spec redirect type route target\n" "Import routes to this address-family\n" - "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|IPV6:MN)\n") { VTY_DECLVAR_CONTEXT(bgp, bgp); int ret; struct ecommunity *ecom = NULL; afi_t afi; - int idx = 0; - int yes = 1; + int idx = 0, idx_unused = 0; + bool yes = true; + bool rt6 = false; if (argv_find(argv, argc, "no", &idx)) - yes = 0; + yes = false; + + if (argv_find(argv, argc, "rt6", &idx_unused) || + argv_find(argv, argc, "route-target6", &idx_unused)) + rt6 = true; afi = vpn_policy_getafi(vty, bgp, false); if (afi == AFI_MAX) return CMD_WARNING_CONFIG_FAILED; + if (rt6 && afi != AFI_IP6) + return CMD_WARNING_CONFIG_FAILED; + if (yes) { if (!argv_find(argv, argc, "RTLIST", &idx)) { vty_out(vty, "%% Missing RTLIST\n"); return CMD_WARNING_CONFIG_FAILED; } - ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom); + ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, rt6); if (ret != CMD_SUCCESS) return ret; } @@ -9277,7 +9444,9 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, json_object_int_add(json_peer, "pfxSnt", (PAF_SUBGRP(paf))->scount); - if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) + if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_SHUTDOWN)) json_object_string_add(json_peer, "state", "Idle (Admin)"); else if (peer->afc_recv[afi][safi]) @@ -9396,7 +9565,10 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, ->scount); } } else { - if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) + if (CHECK_FLAG(peer->flags, + PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(peer->bgp->flags, + BGP_FLAG_SHUTDOWN)) vty_out(vty, " Idle (Admin)"); else if (CHECK_FLAG( peer->sflags, @@ -10893,7 +11065,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, if (use_json) { /* Administrative shutdown. */ - if (CHECK_FLAG(p->flags, PEER_FLAG_SHUTDOWN)) + if (CHECK_FLAG(p->flags, PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(p->bgp->flags, BGP_FLAG_SHUTDOWN)) json_object_boolean_true_add(json_neigh, "adminShutDown"); @@ -10999,7 +11172,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } } else { /* Administrative shutdown. */ - if (CHECK_FLAG(p->flags, PEER_FLAG_SHUTDOWN)) + if (CHECK_FLAG(p->flags, PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(p->bgp->flags, BGP_FLAG_SHUTDOWN)) vty_out(vty, " Administratively shut down\n"); /* BGP Version. */ @@ -12695,12 +12869,11 @@ static void bgp_show_all_instances_neighbors_vty(struct vty *vty, use_json, json); } json_object_free(json); + json = NULL; } - if (use_json) { + if (use_json) vty_out(vty, "}\n"); - json_object_free(json); - } else if (!nbr_output) vty_out(vty, "%% BGP instance not found\n"); } @@ -13573,7 +13746,8 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group) if (listcount(group->peer)) { vty_out(vty, " Peer-group members:\n"); for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { - if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) + if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN) + || CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN)) peer_status = "Idle (Admin)"; else if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) @@ -14524,7 +14698,13 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, ECOMMUNITY_FORMAT_ROUTE_MAP, ECOMMUNITY_ROUTE_TARGET); - vty_out(vty, "%*srt redirect import %s\n", indent, "", b); + if (bgp->vpn_policy[afi].import_redirect_rtlist->unit_size + != ECOMMUNITY_SIZE) + vty_out(vty, "%*srt6 redirect import %s\n", + indent, "", b); + else + vty_out(vty, "%*srt redirect import %s\n", + indent, "", b); XFREE(MTYPE_ECOMMUNITY_STR, b); } } @@ -14707,6 +14887,10 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, vty_out(vty, " neighbor %s shutdown\n", addr); } + if (peergroup_flag_check(peer, PEER_FLAG_RTT_SHUTDOWN)) + vty_out(vty, " neighbor %s shutdown rtt %u count %u\n", addr, + peer->rtt_expected, peer->rtt_keepalive_conf); + /* bfd */ if (peer->bfd_info) { if (!peer_group_active(peer) || !g_peer->bfd_info) { @@ -15054,6 +15238,9 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, if (peer->pmax_restart[afi][safi]) vty_out(vty, " restart %u", peer->pmax_restart[afi][safi]); + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_MAX_PREFIX_FORCE)) + vty_out(vty, " force"); vty_out(vty, "\n"); } @@ -15509,6 +15696,10 @@ int bgp_config_write(struct vty *vty) if (bgp->autoshutdown) vty_out(vty, " bgp default shutdown\n"); + /* BGP instance administrative shutdown */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN)) + vty_out(vty, " bgp shutdown\n"); + /* IPv4 unicast configuration. */ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); @@ -15972,6 +16163,12 @@ void bgp_vty_init(void) /* "bgp default shutdown" command */ install_element(BGP_NODE, &bgp_default_shutdown_cmd); + /* "bgp shutdown" commands */ + install_element(BGP_NODE, &bgp_shutdown_cmd); + install_element(BGP_NODE, &bgp_shutdown_msg_cmd); + install_element(BGP_NODE, &no_bgp_shutdown_cmd); + install_element(BGP_NODE, &no_bgp_shutdown_msg_cmd); + /* "neighbor remote-as" commands. */ install_element(BGP_NODE, &neighbor_remote_as_cmd); install_element(BGP_NODE, &neighbor_interface_config_cmd); @@ -16493,6 +16690,8 @@ void bgp_vty_init(void) install_element(BGP_NODE, &no_neighbor_shutdown_cmd); install_element(BGP_NODE, &neighbor_shutdown_msg_cmd); install_element(BGP_NODE, &no_neighbor_shutdown_msg_cmd); + install_element(BGP_NODE, &neighbor_shutdown_rtt_cmd); + install_element(BGP_NODE, &no_neighbor_shutdown_rtt_cmd); /* "neighbor capability extended-nexthop" commands.*/ install_element(BGP_NODE, &neighbor_capability_enhe_cmd); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 87936f1dd6..15bd6d33b8 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1265,6 +1265,9 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, api.tableid = info->attr->rmap_table_id; } + if (CHECK_FLAG(info->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) + SET_FLAG(api.message, ZAPI_MESSAGE_SRTE); + /* Metric is currently based on the best-path only */ metric = info->attr->med; @@ -1303,6 +1306,11 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, continue; } api_nh = &api.nexthops[valid_nh_count]; + + if (CHECK_FLAG(info->attr->flag, + ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) + api_nh->srte_color = info->attr->srte_color; + if (nh_family == AF_INET) { if (bgp_debug_zebra(&api.prefix)) { if (mpinfo->extra) { @@ -2356,7 +2364,10 @@ static void bgp_encode_pbr_rule_action(struct stream *s, struct bgp_pbr_rule *pbr) { struct prefix pfx; + uint8_t fam = AF_INET; + if (pbra && pbra->nh.type == NEXTHOP_TYPE_IPV6) + fam = AF_INET6; stream_putl(s, 0); /* seqno unused */ if (pbr) stream_putl(s, pbr->priority); @@ -2376,7 +2387,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s, memcpy(&pfx, &(pbr->src), sizeof(struct prefix)); else { memset(&pfx, 0, sizeof(pfx)); - pfx.family = AF_INET; + pfx.family = fam; } stream_putc(s, pfx.family); stream_putc(s, pfx.prefixlen); @@ -2388,7 +2399,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s, memcpy(&pfx, &(pbr->dst), sizeof(struct prefix)); else { memset(&pfx, 0, sizeof(pfx)); - pfx.family = AF_INET; + pfx.family = fam; } stream_putc(s, pfx.family); stream_putc(s, pfx.prefixlen); @@ -2412,7 +2423,7 @@ static void bgp_encode_pbr_ipset_match(struct stream *s, { stream_putl(s, pbim->unique); stream_putl(s, pbim->type); - + stream_putc(s, pbim->family); stream_put(s, pbim->ipset_name, ZEBRA_IPSET_NAME_SIZE); } @@ -2461,6 +2472,7 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, stream_putl(s, bpa->fwmark); stream_put(s, pbm->ipset_name, ZEBRA_IPSET_NAME_SIZE); + stream_putc(s, pbm->family); stream_putw(s, pbm->pkt_len_min); stream_putw(s, pbm->pkt_len_max); stream_putw(s, pbm->tcp_flags); @@ -2468,6 +2480,7 @@ static void bgp_encode_pbr_iptable_match(struct stream *s, stream_putc(s, pbm->dscp_value); stream_putc(s, pbm->fragment); stream_putc(s, pbm->protocol); + stream_putw(s, pbm->flow_label); } /* BGP has established connection with Zebra. */ @@ -2979,7 +2992,8 @@ void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime, pbrime->install_in_progress = true; } -static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) +static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s, + uint8_t family) { struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; struct bgp_pbr_interface_head *head; @@ -2988,8 +3002,10 @@ static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) if (!bgp_pbr_cfg) return; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - + if (family == AF_INET) + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + else + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { ifp = if_lookup_by_name(pbr_if->name, bgp->vrf_id); if (ifp) @@ -2997,7 +3013,7 @@ static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s) } } -static int bgp_pbr_get_ifnumber(struct bgp *bgp) +static int bgp_pbr_get_ifnumber(struct bgp *bgp, uint8_t family) { struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg; struct bgp_pbr_interface_head *head; @@ -3006,8 +3022,10 @@ static int bgp_pbr_get_ifnumber(struct bgp *bgp) if (!bgp_pbr_cfg) return 0; - head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); - + if (family == AF_INET) + head = &(bgp_pbr_cfg->ifaces_by_name_ipv4); + else + head = &(bgp_pbr_cfg->ifaces_by_name_ipv6); RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) { if (if_lookup_by_name(pbr_if->name, bgp->vrf_id)) cnt++; @@ -3038,10 +3056,10 @@ void bgp_send_pbr_iptable(struct bgp_pbr_action *pba, VRF_DEFAULT); bgp_encode_pbr_iptable_match(s, pba, pbm); - nb_interface = bgp_pbr_get_ifnumber(pba->bgp); + nb_interface = bgp_pbr_get_ifnumber(pba->bgp, pbm->family); stream_putl(s, nb_interface); if (nb_interface) - bgp_encode_pbr_interface_list(pba->bgp, s); + bgp_encode_pbr_interface_list(pba->bgp, s, pbm->family); stream_putw_at(s, 0, stream_get_endp(s)); ret = zclient_send_message(zclient); if (install) { @@ -3063,14 +3081,14 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, struct zapi_route api; struct prefix p; - if (!nh || nh->type != NEXTHOP_TYPE_IPV4 + if (!nh || (nh->type != NEXTHOP_TYPE_IPV4 + && nh->type != NEXTHOP_TYPE_IPV6) || nh->vrf_id == VRF_UNKNOWN) return; memset(&p, 0, sizeof(struct prefix)); - /* default route */ - if (afi != AFI_IP) + if (afi != AFI_IP && afi != AFI_IP6) return; - p.family = AF_INET; + p.family = afi2family(afi); memset(&api, 0, sizeof(api)); api.vrf_id = bgp->vrf_id; api.type = ZEBRA_ROUTE_BGP; @@ -3086,7 +3104,7 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); /* redirect IP */ - if (nh->gate.ipv4.s_addr != INADDR_ANY) { + if (afi == AFI_IP && nh->gate.ipv4.s_addr != INADDR_ANY) { char buff[PREFIX_STRLEN]; api_nh->vrf_id = nh->vrf_id; @@ -3095,7 +3113,25 @@ void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh, inet_ntop(AF_INET, &(nh->gate.ipv4), buff, INET_ADDRSTRLEN); if (BGP_DEBUG(zebra, ZEBRA)) - zlog_info("BGP: %s default route to %s table %d (redirect IP)", + zlog_debug("BGP: %s default route to %s table %d (redirect IP)", + announce ? "adding" : "withdrawing", + buff, table_id); + zclient_route_send(announce ? ZEBRA_ROUTE_ADD + : ZEBRA_ROUTE_DELETE, + zclient, &api); + } else if (afi == AFI_IP6 && + memcmp(&nh->gate.ipv6, + &in6addr_any, sizeof(struct in6_addr))) { + char buff[PREFIX_STRLEN]; + + api_nh->vrf_id = nh->vrf_id; + memcpy(&api_nh->gate.ipv6, &nh->gate.ipv6, + sizeof(struct in6_addr)); + api_nh->type = NEXTHOP_TYPE_IPV6; + + inet_ntop(AF_INET6, &(nh->gate.ipv6), buff, INET_ADDRSTRLEN); + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("BGP: %s default route to %s table %d (redirect IP)", announce ? "adding" : "withdrawing", buff, table_id); zclient_route_send(announce ? ZEBRA_ROUTE_ADD diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index c9e6fd2ac0..d638c6686e 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1604,6 +1604,9 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, /* Default TTL set. */ peer->ttl = (peer->sort == BGP_PEER_IBGP) ? MAXTTL : BGP_DEFAULT_TTL; + /* Default configured keepalives count for shutdown rtt command */ + peer->rtt_keepalive_conf = 1; + SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE); if (afi && safi) { @@ -2284,9 +2287,9 @@ int peer_delete(struct peer *peer) /* Password configuration */ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD)) { XFREE(MTYPE_PEER_PASSWORD, peer->password); - if (!accept_peer && !BGP_PEER_SU_UNSPEC(peer) - && !CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + && !CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) + && !CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_NEIGHBOR)) bgp_md5_unset(peer); } @@ -2668,7 +2671,7 @@ int peer_group_listen_range_add(struct peer_group *group, struct prefix *range) /* Update passwords for new ranges */ if (group->conf->password) - bgp_md5_set_prefix(prefix, group->conf->password); + bgp_md5_set_prefix(group->bgp, prefix, group->conf->password); return 0; } @@ -2715,7 +2718,7 @@ int peer_group_listen_range_del(struct peer_group *group, struct prefix *range) /* Remove passwords for deleted ranges */ if (group->conf->password) - bgp_md5_unset_prefix(prefix); + bgp_md5_unset_prefix(group->bgp, prefix); return 0; } @@ -3865,6 +3868,7 @@ struct peer_flag_action { static const struct peer_flag_action peer_flag_action_list[] = { {PEER_FLAG_PASSIVE, 0, peer_change_reset}, {PEER_FLAG_SHUTDOWN, 0, peer_change_reset}, + {PEER_FLAG_RTT_SHUTDOWN, 0, peer_change_none}, {PEER_FLAG_DONT_CAPABILITY, 0, peer_change_none}, {PEER_FLAG_OVERRIDE_CAPABILITY, 0, peer_change_none}, {PEER_FLAG_STRICT_CAP_MATCH, 0, peer_change_none}, @@ -3902,6 +3906,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { {PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset}, {PEER_FLAG_MAX_PREFIX, 0, peer_change_none}, {PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none}, + {PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none}, {PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out}, {PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out}, {PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out}, @@ -3966,6 +3971,7 @@ static void peer_flag_modify_action(struct peer *peer, uint32_t flag) peer_nsf_stop(peer); UNSET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); + if (peer->t_pmax_restart) { BGP_TIMER_OFF(peer->t_pmax_restart); if (bgp_debug_neighbor_events(peer)) @@ -3974,9 +3980,6 @@ static void peer_flag_modify_action(struct peer *peer, uint32_t flag) peer->host); } - if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) - peer_nsf_stop(peer); - if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { char *msg = peer->tx_shutdown_message; size_t msglen; @@ -4022,6 +4025,65 @@ static void peer_flag_modify_action(struct peer *peer, uint32_t flag) bgp_session_reset(peer); } +/* Enable global administrative shutdown of all peers of BGP instance */ +void bgp_shutdown_enable(struct bgp *bgp, const char *msg) +{ + struct peer *peer; + struct listnode *node; + + /* do nothing if already shut down */ + if (CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN)) + return; + + /* informational log message */ + zlog_info("Enabled administrative shutdown on BGP instance AS %u", + bgp->as); + + /* iterate through peers of BGP instance */ + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + /* continue, if peer is already in administrative shutdown. */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) + continue; + + /* send a RFC 4486 notification message if necessary */ + if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) { + if (msg) + bgp_notify_send_with_data( + peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN, + (uint8_t *)(msg), strlen(msg)); + else + bgp_notify_send( + peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); + } + + /* reset start timer to initial value */ + peer->v_start = BGP_INIT_START_TIMER; + + /* trigger a RFC 4271 ManualStop event */ + BGP_EVENT_ADD(peer, BGP_Stop); + } + + /* set the BGP instances shutdown flag */ + SET_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN); +} + +/* Disable global administrative shutdown of all peers of BGP instance */ +void bgp_shutdown_disable(struct bgp *bgp) +{ + /* do nothing if not shut down. */ + if (!CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN)) + return; + + /* informational log message */ + zlog_info("Disabled administrative shutdown on BGP instance AS %u", + bgp->as); + + /* clear the BGP instances shutdown flag */ + UNSET_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN); +} + /* Change specified peer flag. */ static int peer_flag_modify(struct peer *peer, uint32_t flag, int set) { @@ -4384,7 +4446,7 @@ int peer_ebgp_multihop_set(struct peer *peer, int ttl) peer->ttl = ttl; if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { - if (peer->fd >= 0 && peer->sort != BGP_PEER_IBGP) { + if (peer->sort != BGP_PEER_IBGP) { if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send(peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); @@ -5564,9 +5626,9 @@ int peer_password_set(struct peer *peer, const char *password) struct prefix *lr; for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP], ln, lr)) - bgp_md5_set_prefix(lr, password); + bgp_md5_set_prefix(peer->bgp, lr, password); for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP6], ln, lr)) - bgp_md5_set_prefix(lr, password); + bgp_md5_set_prefix(peer->bgp, lr, password); return ret; } @@ -5602,7 +5664,6 @@ int peer_password_unset(struct peer *peer) /* Attempt to uninstall password on socket. */ if (!BGP_PEER_SU_UNSPEC(peer)) bgp_md5_unset(peer); - /* Skip peer-group mechanics for regular peers. */ return 0; } @@ -5637,9 +5698,9 @@ int peer_password_unset(struct peer *peer) struct prefix *lr; for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP], ln, lr)) - bgp_md5_unset_prefix(lr); + bgp_md5_unset_prefix(peer->bgp, lr); for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP6], ln, lr)) - bgp_md5_unset_prefix(lr); + bgp_md5_unset_prefix(peer->bgp, lr); return 0; } @@ -6451,13 +6512,19 @@ int peer_unsuppress_map_unset(struct peer *peer, afi_t afi, safi_t safi) int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi, uint32_t max, uint8_t threshold, int warning, - uint16_t restart) + uint16_t restart, bool force) { struct peer *member; struct listnode *node, *nnode; /* Set flags and configuration on peer. */ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX); + + if (force) + peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE); + else + peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE); + if (warning) peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING); else @@ -6492,6 +6559,14 @@ int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi, member->pmax[afi][safi] = max; member->pmax_threshold[afi][safi] = threshold; member->pmax_restart[afi][safi] = restart; + + if (force) + SET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_FORCE); + else + UNSET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_FORCE); + if (warning) SET_FLAG(member->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); @@ -6513,6 +6588,8 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi) if (peer_group_active(peer)) { peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX); peer_af_flag_inherit(peer, afi, safi, + PEER_FLAG_MAX_PREFIX_FORCE); + peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING); PEER_ATTR_INHERIT(peer, peer->group, pmax[afi][safi]); PEER_ATTR_INHERIT(peer, peer->group, pmax_threshold[afi][safi]); @@ -6523,6 +6600,7 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi) /* Remove flags and configuration from peer. */ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX); + peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE); peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING); peer->pmax[afi][safi] = 0; peer->pmax_threshold[afi][safi] = 0; @@ -6547,6 +6625,8 @@ int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi) UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); UNSET_FLAG(member->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_FORCE); + UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); member->pmax[afi][safi] = 0; member->pmax_threshold[afi][safi] = 0; @@ -6753,7 +6833,8 @@ int peer_ttl_security_hops_unset(struct peer *peer) */ int peer_clear(struct peer *peer, struct listnode **nnode) { - if (!CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN) + || !CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN)) { if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) { UNSET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); if (peer->t_pmax_restart) { diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 966de87830..8707ebacb6 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -42,6 +42,7 @@ #include "vxlan.h" #include "bgp_labelpool.h" #include "bgp_addpath_types.h" +#include "bgp_nexthop.h" #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -452,6 +453,9 @@ struct bgp { #define BGP_FLAG_EBGP_REQUIRES_POLICY (1 << 25) #define BGP_FLAG_SHOW_NEXTHOP_HOSTNAME (1 << 26) +/* This flag is set if the instance is in administrative shutdown */ +#define BGP_FLAG_SHUTDOWN (1 << 27) + enum global_mode GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE] [BGP_GLOBAL_GR_EVENT_CMD]; enum global_mode global_gr_present_state; @@ -479,11 +483,11 @@ struct bgp { /* BGP per AF peer count */ uint32_t af_peer_count[AFI_MAX][SAFI_MAX]; - /* Route table for next-hop lookup cache. */ - struct bgp_table *nexthop_cache_table[AFI_MAX]; + /* Tree for next-hop lookup cache. */ + struct bgp_nexthop_cache_head nexthop_cache_table[AFI_MAX]; - /* Route table for import-check */ - struct bgp_table *import_check_table[AFI_MAX]; + /* Tree for import-check */ + struct bgp_nexthop_cache_head import_check_table[AFI_MAX]; struct bgp_table *connected_table[AFI_MAX]; @@ -964,6 +968,9 @@ struct peer { int fd; /* File descriptor */ int ttl; /* TTL of TCP connection to the peer. */ int rtt; /* Estimated round-trip-time from TCP_INFO */ + int rtt_expected; /* Expected round-trip-time for a peer */ + uint8_t rtt_keepalive_rcv; /* Received count for RTT shutdown */ + uint8_t rtt_keepalive_conf; /* Configured count for RTT shutdown */ int gtsm_hops; /* minimum hopcount to peer */ char *desc; /* Description of the peer. */ unsigned short port; /* Destination port for peer */ @@ -993,41 +1000,41 @@ struct peer { /* Capability flags (reset in bgp_stop) */ uint32_t cap; -#define PEER_CAP_REFRESH_ADV (1 << 0) /* refresh advertised */ -#define PEER_CAP_REFRESH_OLD_RCV (1 << 1) /* refresh old received */ -#define PEER_CAP_REFRESH_NEW_RCV (1 << 2) /* refresh rfc received */ -#define PEER_CAP_DYNAMIC_ADV (1 << 3) /* dynamic advertised */ -#define PEER_CAP_DYNAMIC_RCV (1 << 4) /* dynamic received */ -#define PEER_CAP_RESTART_ADV (1 << 5) /* restart advertised */ -#define PEER_CAP_RESTART_RCV (1 << 6) /* restart received */ -#define PEER_CAP_AS4_ADV (1 << 7) /* as4 advertised */ -#define PEER_CAP_AS4_RCV (1 << 8) /* as4 received */ -#define PEER_CAP_RESTART_BIT_ADV (1 << 9) /* sent restart state */ -#define PEER_CAP_RESTART_BIT_RCV (1 << 10) /* peer restart state */ -#define PEER_CAP_ADDPATH_ADV (1 << 11) /* addpath advertised */ -#define PEER_CAP_ADDPATH_RCV (1 << 12) /* addpath received */ -#define PEER_CAP_ENHE_ADV (1 << 13) /* Extended nexthop advertised */ -#define PEER_CAP_ENHE_RCV (1 << 14) /* Extended nexthop received */ -#define PEER_CAP_HOSTNAME_ADV (1 << 15) /* hostname advertised */ -#define PEER_CAP_HOSTNAME_RCV (1 << 16) /* hostname received */ +#define PEER_CAP_REFRESH_ADV (1U << 0) /* refresh advertised */ +#define PEER_CAP_REFRESH_OLD_RCV (1U << 1) /* refresh old received */ +#define PEER_CAP_REFRESH_NEW_RCV (1U << 2) /* refresh rfc received */ +#define PEER_CAP_DYNAMIC_ADV (1U << 3) /* dynamic advertised */ +#define PEER_CAP_DYNAMIC_RCV (1U << 4) /* dynamic received */ +#define PEER_CAP_RESTART_ADV (1U << 5) /* restart advertised */ +#define PEER_CAP_RESTART_RCV (1U << 6) /* restart received */ +#define PEER_CAP_AS4_ADV (1U << 7) /* as4 advertised */ +#define PEER_CAP_AS4_RCV (1U << 8) /* as4 received */ +#define PEER_CAP_RESTART_BIT_ADV (1U << 9) /* sent restart state */ +#define PEER_CAP_RESTART_BIT_RCV (1U << 10) /* peer restart state */ +#define PEER_CAP_ADDPATH_ADV (1U << 11) /* addpath advertised */ +#define PEER_CAP_ADDPATH_RCV (1U << 12) /* addpath received */ +#define PEER_CAP_ENHE_ADV (1U << 13) /* Extended nexthop advertised */ +#define PEER_CAP_ENHE_RCV (1U << 14) /* Extended nexthop received */ +#define PEER_CAP_HOSTNAME_ADV (1U << 15) /* hostname advertised */ +#define PEER_CAP_HOSTNAME_RCV (1U << 16) /* hostname received */ /* Capability flags (reset in bgp_stop) */ uint32_t af_cap[AFI_MAX][SAFI_MAX]; -#define PEER_CAP_ORF_PREFIX_SM_ADV (1 << 0) /* send-mode advertised */ -#define PEER_CAP_ORF_PREFIX_RM_ADV (1 << 1) /* receive-mode advertised */ -#define PEER_CAP_ORF_PREFIX_SM_RCV (1 << 2) /* send-mode received */ -#define PEER_CAP_ORF_PREFIX_RM_RCV (1 << 3) /* receive-mode received */ -#define PEER_CAP_ORF_PREFIX_SM_OLD_RCV (1 << 4) /* send-mode received */ -#define PEER_CAP_ORF_PREFIX_RM_OLD_RCV (1 << 5) /* receive-mode received */ -#define PEER_CAP_RESTART_AF_RCV (1 << 6) /* graceful restart afi/safi received */ -#define PEER_CAP_RESTART_AF_PRESERVE_RCV (1 << 7) /* graceful restart afi/safi F-bit received */ -#define PEER_CAP_ADDPATH_AF_TX_ADV (1 << 8) /* addpath tx advertised */ -#define PEER_CAP_ADDPATH_AF_TX_RCV (1 << 9) /* addpath tx received */ -#define PEER_CAP_ADDPATH_AF_RX_ADV (1 << 10) /* addpath rx advertised */ -#define PEER_CAP_ADDPATH_AF_RX_RCV (1 << 11) /* addpath rx received */ -#define PEER_CAP_ENHE_AF_ADV (1 << 12) /* Extended nexthopi afi/safi advertised */ -#define PEER_CAP_ENHE_AF_RCV (1 << 13) /* Extended nexthop afi/safi received */ -#define PEER_CAP_ENHE_AF_NEGO (1 << 14) /* Extended nexthop afi/safi negotiated */ +#define PEER_CAP_ORF_PREFIX_SM_ADV (1U << 0) /* send-mode advertised */ +#define PEER_CAP_ORF_PREFIX_RM_ADV (1U << 1) /* receive-mode advertised */ +#define PEER_CAP_ORF_PREFIX_SM_RCV (1U << 2) /* send-mode received */ +#define PEER_CAP_ORF_PREFIX_RM_RCV (1U << 3) /* receive-mode received */ +#define PEER_CAP_ORF_PREFIX_SM_OLD_RCV (1U << 4) /* send-mode received */ +#define PEER_CAP_ORF_PREFIX_RM_OLD_RCV (1U << 5) /* receive-mode received */ +#define PEER_CAP_RESTART_AF_RCV (1U << 6) /* graceful restart afi/safi received */ +#define PEER_CAP_RESTART_AF_PRESERVE_RCV (1U << 7) /* graceful restart afi/safi F-bit received */ +#define PEER_CAP_ADDPATH_AF_TX_ADV (1U << 8) /* addpath tx advertised */ +#define PEER_CAP_ADDPATH_AF_TX_RCV (1U << 9) /* addpath tx received */ +#define PEER_CAP_ADDPATH_AF_RX_ADV (1U << 10) /* addpath rx advertised */ +#define PEER_CAP_ADDPATH_AF_RX_RCV (1U << 11) /* addpath rx received */ +#define PEER_CAP_ENHE_AF_ADV (1U << 12) /* Extended nexthopi afi/safi advertised */ +#define PEER_CAP_ENHE_AF_RCV (1U << 13) /* Extended nexthop afi/safi received */ +#define PEER_CAP_ENHE_AF_NEGO (1U << 14) /* Extended nexthop afi/safi negotiated */ /* Global configuration flags. */ /* @@ -1086,34 +1093,35 @@ struct peer { * flags_invert) must be respected. */ uint32_t flags; -#define PEER_FLAG_PASSIVE (1 << 0) /* passive mode */ -#define PEER_FLAG_SHUTDOWN (1 << 1) /* shutdown */ -#define PEER_FLAG_DONT_CAPABILITY (1 << 2) /* dont-capability */ -#define PEER_FLAG_OVERRIDE_CAPABILITY (1 << 3) /* override-capability */ -#define PEER_FLAG_STRICT_CAP_MATCH (1 << 4) /* strict-match */ -#define PEER_FLAG_DYNAMIC_CAPABILITY (1 << 5) /* dynamic capability */ -#define PEER_FLAG_DISABLE_CONNECTED_CHECK (1 << 6) /* disable-connected-check */ -#define PEER_FLAG_LOCAL_AS_NO_PREPEND (1 << 7) /* local-as no-prepend */ -#define PEER_FLAG_LOCAL_AS_REPLACE_AS (1 << 8) /* local-as no-prepend replace-as */ -#define PEER_FLAG_DELETE (1 << 9) /* mark the peer for deleting */ -#define PEER_FLAG_CONFIG_NODE (1 << 10) /* the node to update configs on */ -#define PEER_FLAG_LONESOUL (1 << 11) -#define PEER_FLAG_DYNAMIC_NEIGHBOR (1 << 12) /* dynamic neighbor */ -#define PEER_FLAG_CAPABILITY_ENHE (1 << 13) /* Extended next-hop (rfc 5549)*/ -#define PEER_FLAG_IFPEER_V6ONLY (1 << 14) /* if-based peer is v6 only */ -#define PEER_FLAG_IS_RFAPI_HD (1 << 15) /* attached to rfapi HD */ -#define PEER_FLAG_ENFORCE_FIRST_AS (1 << 16) /* enforce-first-as */ -#define PEER_FLAG_ROUTEADV (1 << 17) /* route advertise */ -#define PEER_FLAG_TIMER (1 << 18) /* keepalive & holdtime */ -#define PEER_FLAG_TIMER_CONNECT (1 << 19) /* connect timer */ -#define PEER_FLAG_PASSWORD (1 << 20) /* password */ -#define PEER_FLAG_LOCAL_AS (1 << 21) /* local-as */ -#define PEER_FLAG_UPDATE_SOURCE (1 << 22) /* update-source */ +#define PEER_FLAG_PASSIVE (1U << 0) /* passive mode */ +#define PEER_FLAG_SHUTDOWN (1U << 1) /* shutdown */ +#define PEER_FLAG_DONT_CAPABILITY (1U << 2) /* dont-capability */ +#define PEER_FLAG_OVERRIDE_CAPABILITY (1U << 3) /* override-capability */ +#define PEER_FLAG_STRICT_CAP_MATCH (1U << 4) /* strict-match */ +#define PEER_FLAG_DYNAMIC_CAPABILITY (1U << 5) /* dynamic capability */ +#define PEER_FLAG_DISABLE_CONNECTED_CHECK (1U << 6) /* disable-connected-check */ +#define PEER_FLAG_LOCAL_AS_NO_PREPEND (1U << 7) /* local-as no-prepend */ +#define PEER_FLAG_LOCAL_AS_REPLACE_AS (1U << 8) /* local-as no-prepend replace-as */ +#define PEER_FLAG_DELETE (1U << 9) /* mark the peer for deleting */ +#define PEER_FLAG_CONFIG_NODE (1U << 10) /* the node to update configs on */ +#define PEER_FLAG_LONESOUL (1U << 11) +#define PEER_FLAG_DYNAMIC_NEIGHBOR (1U << 12) /* dynamic neighbor */ +#define PEER_FLAG_CAPABILITY_ENHE (1U << 13) /* Extended next-hop (rfc 5549)*/ +#define PEER_FLAG_IFPEER_V6ONLY (1U << 14) /* if-based peer is v6 only */ +#define PEER_FLAG_IS_RFAPI_HD (1U << 15) /* attached to rfapi HD */ +#define PEER_FLAG_ENFORCE_FIRST_AS (1U << 16) /* enforce-first-as */ +#define PEER_FLAG_ROUTEADV (1U << 17) /* route advertise */ +#define PEER_FLAG_TIMER (1U << 18) /* keepalive & holdtime */ +#define PEER_FLAG_TIMER_CONNECT (1U << 19) /* connect timer */ +#define PEER_FLAG_PASSWORD (1U << 20) /* password */ +#define PEER_FLAG_LOCAL_AS (1U << 21) /* local-as */ +#define PEER_FLAG_UPDATE_SOURCE (1U << 22) /* update-source */ /* BGP-GR Peer related flags */ -#define PEER_FLAG_GRACEFUL_RESTART_HELPER (1 << 23) /* Helper */ -#define PEER_FLAG_GRACEFUL_RESTART (1 << 24) /* Graceful Restart */ -#define PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT (1 << 25) /* Global-Inherit */ +#define PEER_FLAG_GRACEFUL_RESTART_HELPER (1U << 23) /* Helper */ +#define PEER_FLAG_GRACEFUL_RESTART (1U << 24) /* Graceful Restart */ +#define PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT (1U << 25) /* Global-Inherit */ +#define PEER_FLAG_RTT_SHUTDOWN (1U << 26) /* shutdown rtt */ /* *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART @@ -1127,9 +1135,9 @@ struct peer { uint8_t nsf_af_count; uint8_t peer_gr_new_status_flag; -#define PEER_GRACEFUL_RESTART_NEW_STATE_HELPER (1 << 0) -#define PEER_GRACEFUL_RESTART_NEW_STATE_RESTART (1 << 1) -#define PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT (1 << 2) +#define PEER_GRACEFUL_RESTART_NEW_STATE_HELPER (1U << 0) +#define PEER_GRACEFUL_RESTART_NEW_STATE_RESTART (1U << 1) +#define PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT (1U << 2) /* outgoing message sent in CEASE_ADMIN_SHUTDOWN notify */ char *tx_shutdown_message; @@ -1151,32 +1159,33 @@ struct peer { uint32_t af_flags_override[AFI_MAX][SAFI_MAX]; uint32_t af_flags_invert[AFI_MAX][SAFI_MAX]; uint32_t af_flags[AFI_MAX][SAFI_MAX]; -#define PEER_FLAG_SEND_COMMUNITY (1 << 0) /* send-community */ -#define PEER_FLAG_SEND_EXT_COMMUNITY (1 << 1) /* send-community ext. */ -#define PEER_FLAG_NEXTHOP_SELF (1 << 2) /* next-hop-self */ -#define PEER_FLAG_REFLECTOR_CLIENT (1 << 3) /* reflector-client */ -#define PEER_FLAG_RSERVER_CLIENT (1 << 4) /* route-server-client */ -#define PEER_FLAG_SOFT_RECONFIG (1 << 5) /* soft-reconfiguration */ -#define PEER_FLAG_AS_PATH_UNCHANGED (1 << 6) /* transparent-as */ -#define PEER_FLAG_NEXTHOP_UNCHANGED (1 << 7) /* transparent-next-hop */ -#define PEER_FLAG_MED_UNCHANGED (1 << 8) /* transparent-next-hop */ -#define PEER_FLAG_DEFAULT_ORIGINATE (1 << 9) /* default-originate */ -#define PEER_FLAG_REMOVE_PRIVATE_AS (1 << 10) /* remove-private-as */ -#define PEER_FLAG_ALLOWAS_IN (1 << 11) /* set allowas-in */ -#define PEER_FLAG_ORF_PREFIX_SM (1 << 12) /* orf capability send-mode */ -#define PEER_FLAG_ORF_PREFIX_RM (1 << 13) /* orf capability receive-mode */ -#define PEER_FLAG_MAX_PREFIX (1 << 14) /* maximum prefix */ -#define PEER_FLAG_MAX_PREFIX_WARNING (1 << 15) /* maximum prefix warning-only */ -#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1 << 16) /* leave link-local nexthop unchanged */ -#define PEER_FLAG_FORCE_NEXTHOP_SELF (1 << 17) /* next-hop-self force */ -#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL (1 << 18) /* remove-private-as all */ -#define PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE (1 << 19) /* remove-private-as replace-as */ -#define PEER_FLAG_AS_OVERRIDE (1 << 20) /* as-override */ -#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE (1 << 21) /* remove-private-as all replace-as */ -#define PEER_FLAG_WEIGHT (1 << 24) /* weight */ -#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1 << 25) /* allowas-in origin */ -#define PEER_FLAG_SEND_LARGE_COMMUNITY (1 << 26) /* Send large Communities */ -#define PEER_FLAG_MAX_PREFIX_OUT (1 << 27) /* outgoing maximum prefix */ +#define PEER_FLAG_SEND_COMMUNITY (1U << 0) /* send-community */ +#define PEER_FLAG_SEND_EXT_COMMUNITY (1U << 1) /* send-community ext. */ +#define PEER_FLAG_NEXTHOP_SELF (1U << 2) /* next-hop-self */ +#define PEER_FLAG_REFLECTOR_CLIENT (1U << 3) /* reflector-client */ +#define PEER_FLAG_RSERVER_CLIENT (1U << 4) /* route-server-client */ +#define PEER_FLAG_SOFT_RECONFIG (1U << 5) /* soft-reconfiguration */ +#define PEER_FLAG_AS_PATH_UNCHANGED (1U << 6) /* transparent-as */ +#define PEER_FLAG_NEXTHOP_UNCHANGED (1U << 7) /* transparent-next-hop */ +#define PEER_FLAG_MED_UNCHANGED (1U << 8) /* transparent-next-hop */ +#define PEER_FLAG_DEFAULT_ORIGINATE (1U << 9) /* default-originate */ +#define PEER_FLAG_REMOVE_PRIVATE_AS (1U << 10) /* remove-private-as */ +#define PEER_FLAG_ALLOWAS_IN (1U << 11) /* set allowas-in */ +#define PEER_FLAG_ORF_PREFIX_SM (1U << 12) /* orf capability send-mode */ +#define PEER_FLAG_ORF_PREFIX_RM (1U << 13) /* orf capability receive-mode */ +#define PEER_FLAG_MAX_PREFIX (1U << 14) /* maximum prefix */ +#define PEER_FLAG_MAX_PREFIX_WARNING (1U << 15) /* maximum prefix warning-only */ +#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1U << 16) /* leave link-local nexthop unchanged */ +#define PEER_FLAG_FORCE_NEXTHOP_SELF (1U << 17) /* next-hop-self force */ +#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL (1U << 18) /* remove-private-as all */ +#define PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE (1U << 19) /* remove-private-as replace-as */ +#define PEER_FLAG_AS_OVERRIDE (1U << 20) /* as-override */ +#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE (1U << 21) /* remove-private-as all replace-as */ +#define PEER_FLAG_WEIGHT (1U << 24) /* weight */ +#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1U << 25) /* allowas-in origin */ +#define PEER_FLAG_SEND_LARGE_COMMUNITY (1U << 26) /* Send large Communities */ +#define PEER_FLAG_MAX_PREFIX_OUT (1U << 27) /* outgoing maximum prefix */ +#define PEER_FLAG_MAX_PREFIX_FORCE (1U << 28) /* maximum-prefix <num> force */ enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX]; @@ -1191,22 +1200,22 @@ struct peer { /* Peer status flags. */ uint16_t sflags; -#define PEER_STATUS_ACCEPT_PEER (1 << 0) /* accept peer */ -#define PEER_STATUS_PREFIX_OVERFLOW (1 << 1) /* prefix-overflow */ -#define PEER_STATUS_CAPABILITY_OPEN (1 << 2) /* capability open send */ -#define PEER_STATUS_HAVE_ACCEPT (1 << 3) /* accept peer's parent */ -#define PEER_STATUS_GROUP (1 << 4) /* peer-group conf */ -#define PEER_STATUS_NSF_MODE (1 << 5) /* NSF aware peer */ -#define PEER_STATUS_NSF_WAIT (1 << 6) /* wait comeback peer */ +#define PEER_STATUS_ACCEPT_PEER (1U << 0) /* accept peer */ +#define PEER_STATUS_PREFIX_OVERFLOW (1U << 1) /* prefix-overflow */ +#define PEER_STATUS_CAPABILITY_OPEN (1U << 2) /* capability open send */ +#define PEER_STATUS_HAVE_ACCEPT (1U << 3) /* accept peer's parent */ +#define PEER_STATUS_GROUP (1U << 4) /* peer-group conf */ +#define PEER_STATUS_NSF_MODE (1U << 5) /* NSF aware peer */ +#define PEER_STATUS_NSF_WAIT (1U << 6) /* wait comeback peer */ /* Peer status af flags (reset in bgp_stop) */ uint16_t af_sflags[AFI_MAX][SAFI_MAX]; -#define PEER_STATUS_ORF_PREFIX_SEND (1 << 0) /* prefix-list send peer */ -#define PEER_STATUS_ORF_WAIT_REFRESH (1 << 1) /* wait refresh received peer */ -#define PEER_STATUS_PREFIX_THRESHOLD (1 << 2) /* exceed prefix-threshold */ -#define PEER_STATUS_PREFIX_LIMIT (1 << 3) /* exceed prefix-limit */ -#define PEER_STATUS_EOR_SEND (1 << 4) /* end-of-rib send to peer */ -#define PEER_STATUS_EOR_RECEIVED (1 << 5) /* end-of-rib received from peer */ +#define PEER_STATUS_ORF_PREFIX_SEND (1U << 0) /* prefix-list send peer */ +#define PEER_STATUS_ORF_WAIT_REFRESH (1U << 1) /* wait refresh received peer */ +#define PEER_STATUS_PREFIX_THRESHOLD (1U << 2) /* exceed prefix-threshold */ +#define PEER_STATUS_PREFIX_LIMIT (1U << 3) /* exceed prefix-limit */ +#define PEER_STATUS_EOR_SEND (1U << 4) /* end-of-rib send to peer */ +#define PEER_STATUS_EOR_RECEIVED (1U << 5) /* end-of-rib received from peer */ /* Configured timer values. */ _Atomic uint32_t holdtime; @@ -1240,9 +1249,9 @@ struct peer { /* Thread flags. */ _Atomic uint32_t thread_flags; -#define PEER_THREAD_WRITES_ON (1 << 0) -#define PEER_THREAD_READS_ON (1 << 1) -#define PEER_THREAD_KEEPALIVES_ON (1 << 2) +#define PEER_THREAD_WRITES_ON (1U << 0) +#define PEER_THREAD_READS_ON (1U << 1) +#define PEER_THREAD_KEEPALIVES_ON (1U << 2) /* workqueues */ struct work_queue *clear_node_queue; @@ -1333,11 +1342,11 @@ struct peer { * whether the filter in filter (struct bgp_filter) is peer-specific. */ uint8_t filter_override[AFI_MAX][SAFI_MAX][FILTER_MAX]; -#define PEER_FT_DISTRIBUTE_LIST (1 << 0) /* distribute-list */ -#define PEER_FT_FILTER_LIST (1 << 1) /* filter-list */ -#define PEER_FT_PREFIX_LIST (1 << 2) /* prefix-list */ -#define PEER_FT_ROUTE_MAP (1 << 3) /* route-map */ -#define PEER_FT_UNSUPPRESS_MAP (1 << 4) /* unsuppress-map */ +#define PEER_FT_DISTRIBUTE_LIST (1U << 0) /* distribute-list */ +#define PEER_FT_FILTER_LIST (1U << 1) /* filter-list */ +#define PEER_FT_PREFIX_LIST (1U << 2) /* prefix-list */ +#define PEER_FT_ROUTE_MAP (1U << 3) /* route-map */ +#define PEER_FT_UNSUPPRESS_MAP (1U << 4) /* unsuppress-map */ /* ORF Prefix-list */ struct prefix_list *orf_plist[AFI_MAX][SAFI_MAX]; @@ -1348,7 +1357,7 @@ struct peer { /* Track if we printed the attribute in debugs */ int rcvd_attr_printed; - /* Prefix count. */ + /* Accepted prefix count */ uint32_t pcount[AFI_MAX][SAFI_MAX]; /* Max prefix count. */ @@ -1368,39 +1377,39 @@ struct peer { /* peer reset cause */ uint8_t last_reset; -#define PEER_DOWN_RID_CHANGE 1 /* bgp router-id command */ -#define PEER_DOWN_REMOTE_AS_CHANGE 2 /* neighbor remote-as command */ -#define PEER_DOWN_LOCAL_AS_CHANGE 3 /* neighbor local-as command */ -#define PEER_DOWN_CLID_CHANGE 4 /* bgp cluster-id command */ -#define PEER_DOWN_CONFED_ID_CHANGE 5 /* bgp confederation id command */ -#define PEER_DOWN_CONFED_PEER_CHANGE 6 /* bgp confederation peer command */ -#define PEER_DOWN_RR_CLIENT_CHANGE 7 /* neighbor rr-client command */ -#define PEER_DOWN_RS_CLIENT_CHANGE 8 /* neighbor rs-client command */ -#define PEER_DOWN_UPDATE_SOURCE_CHANGE 9 /* neighbor update-source command */ -#define PEER_DOWN_AF_ACTIVATE 10 /* neighbor activate command */ -#define PEER_DOWN_USER_SHUTDOWN 11 /* neighbor shutdown command */ -#define PEER_DOWN_USER_RESET 12 /* clear ip bgp command */ -#define PEER_DOWN_NOTIFY_RECEIVED 13 /* notification received */ -#define PEER_DOWN_NOTIFY_SEND 14 /* notification send */ -#define PEER_DOWN_CLOSE_SESSION 15 /* tcp session close */ -#define PEER_DOWN_NEIGHBOR_DELETE 16 /* neghbor delete */ -#define PEER_DOWN_RMAP_BIND 17 /* neghbor peer-group command */ -#define PEER_DOWN_RMAP_UNBIND 18 /* no neighbor peer-group command */ -#define PEER_DOWN_CAPABILITY_CHANGE 19 /* neighbor capability command */ -#define PEER_DOWN_PASSIVE_CHANGE 20 /* neighbor passive command */ -#define PEER_DOWN_MULTIHOP_CHANGE 21 /* neighbor multihop command */ -#define PEER_DOWN_NSF_CLOSE_SESSION 22 /* NSF tcp session close */ -#define PEER_DOWN_V6ONLY_CHANGE 23 /* if-based peering v6only toggled */ -#define PEER_DOWN_BFD_DOWN 24 /* BFD down */ -#define PEER_DOWN_IF_DOWN 25 /* Interface down */ -#define PEER_DOWN_NBR_ADDR_DEL 26 /* Peer address lost */ -#define PEER_DOWN_WAITING_NHT 27 /* Waiting for NHT to resolve */ -#define PEER_DOWN_NBR_ADDR 28 /* Waiting for peer IPv6 IP Addr */ -#define PEER_DOWN_VRF_UNINIT 29 /* Associated VRF is not init yet */ -#define PEER_DOWN_NOAFI_ACTIVATED 30 /* No AFI/SAFI activated for peer */ -#define PEER_DOWN_AS_SETS_REJECT 31 /* Reject routes with AS_SET */ -#define PEER_DOWN_WAITING_OPEN 32 /* Waiting for open to succeed */ -#define PEER_DOWN_PFX_COUNT 33 /* Reached received prefix count */ +#define PEER_DOWN_RID_CHANGE 1U /* bgp router-id command */ +#define PEER_DOWN_REMOTE_AS_CHANGE 2U /* neighbor remote-as command */ +#define PEER_DOWN_LOCAL_AS_CHANGE 3U /* neighbor local-as command */ +#define PEER_DOWN_CLID_CHANGE 4U /* bgp cluster-id command */ +#define PEER_DOWN_CONFED_ID_CHANGE 5U /* bgp confederation id command */ +#define PEER_DOWN_CONFED_PEER_CHANGE 6U /* bgp confederation peer command */ +#define PEER_DOWN_RR_CLIENT_CHANGE 7U /* neighbor rr-client command */ +#define PEER_DOWN_RS_CLIENT_CHANGE 8U /* neighbor rs-client command */ +#define PEER_DOWN_UPDATE_SOURCE_CHANGE 9U /* neighbor update-source command */ +#define PEER_DOWN_AF_ACTIVATE 10U /* neighbor activate command */ +#define PEER_DOWN_USER_SHUTDOWN 11U /* neighbor shutdown command */ +#define PEER_DOWN_USER_RESET 12U /* clear ip bgp command */ +#define PEER_DOWN_NOTIFY_RECEIVED 13U /* notification received */ +#define PEER_DOWN_NOTIFY_SEND 14U /* notification send */ +#define PEER_DOWN_CLOSE_SESSION 15U /* tcp session close */ +#define PEER_DOWN_NEIGHBOR_DELETE 16U /* neghbor delete */ +#define PEER_DOWN_RMAP_BIND 17U /* neghbor peer-group command */ +#define PEER_DOWN_RMAP_UNBIND 18U /* no neighbor peer-group command */ +#define PEER_DOWN_CAPABILITY_CHANGE 19U /* neighbor capability command */ +#define PEER_DOWN_PASSIVE_CHANGE 20U /* neighbor passive command */ +#define PEER_DOWN_MULTIHOP_CHANGE 21U /* neighbor multihop command */ +#define PEER_DOWN_NSF_CLOSE_SESSION 22U /* NSF tcp session close */ +#define PEER_DOWN_V6ONLY_CHANGE 23U /* if-based peering v6only toggled */ +#define PEER_DOWN_BFD_DOWN 24U /* BFD down */ +#define PEER_DOWN_IF_DOWN 25U /* Interface down */ +#define PEER_DOWN_NBR_ADDR_DEL 26U /* Peer address lost */ +#define PEER_DOWN_WAITING_NHT 27U /* Waiting for NHT to resolve */ +#define PEER_DOWN_NBR_ADDR 28U /* Waiting for peer IPv6 IP Addr */ +#define PEER_DOWN_VRF_UNINIT 29U /* Associated VRF is not init yet */ +#define PEER_DOWN_NOAFI_ACTIVATED 30U /* No AFI/SAFI activated for peer */ +#define PEER_DOWN_AS_SETS_REJECT 31U /* Reject routes with AS_SET */ +#define PEER_DOWN_WAITING_OPEN 32U /* Waiting for open to succeed */ +#define PEER_DOWN_PFX_COUNT 33U /* Reached received prefix count */ /* * Remember to update peer_down_str in bgp_fsm.c when you add * a new value to the last_reset reason @@ -1411,15 +1420,15 @@ struct peer { /* The kind of route-map Flags.*/ uint16_t rmap_type; -#define PEER_RMAP_TYPE_IN (1 << 0) /* neighbor route-map in */ -#define PEER_RMAP_TYPE_OUT (1 << 1) /* neighbor route-map out */ -#define PEER_RMAP_TYPE_NETWORK (1 << 2) /* network route-map */ -#define PEER_RMAP_TYPE_REDISTRIBUTE (1 << 3) /* redistribute route-map */ -#define PEER_RMAP_TYPE_DEFAULT (1 << 4) /* default-originate route-map */ -#define PEER_RMAP_TYPE_NOSET (1 << 5) /* not allow to set commands */ -#define PEER_RMAP_TYPE_IMPORT (1 << 6) /* neighbor route-map import */ -#define PEER_RMAP_TYPE_EXPORT (1 << 7) /* neighbor route-map export */ -#define PEER_RMAP_TYPE_AGGREGATE (1 << 8) /* aggregate-address route-map */ +#define PEER_RMAP_TYPE_IN (1U << 0) /* neighbor route-map in */ +#define PEER_RMAP_TYPE_OUT (1U << 1) /* neighbor route-map out */ +#define PEER_RMAP_TYPE_NETWORK (1U << 2) /* network route-map */ +#define PEER_RMAP_TYPE_REDISTRIBUTE (1U << 3) /* redistribute route-map */ +#define PEER_RMAP_TYPE_DEFAULT (1U << 4) /* default-originate route-map */ +#define PEER_RMAP_TYPE_NOSET (1U << 5) /* not allow to set commands */ +#define PEER_RMAP_TYPE_IMPORT (1U << 6) /* neighbor route-map import */ +#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; @@ -1459,7 +1468,8 @@ DECLARE_QOBJ_TYPE(peer) /* Check if suppress start/restart of sessions to peer. */ #define BGP_PEER_START_SUPPRESSED(P) \ (CHECK_FLAG((P)->flags, PEER_FLAG_SHUTDOWN) \ - || CHECK_FLAG((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + || CHECK_FLAG((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW) \ + || CHECK_FLAG((P)->bgp->flags, BGP_FLAG_SHUTDOWN)) #define PEER_PASSWORD_MINLEN (1) #define PEER_PASSWORD_MAXLEN (80) @@ -1529,8 +1539,10 @@ struct bgp_nlri { #define BGP_ATTR_AS_PATHLIMIT 21 #define BGP_ATTR_PMSI_TUNNEL 22 #define BGP_ATTR_ENCAP 23 +#define BGP_ATTR_IPV6_EXT_COMMUNITIES 25 #define BGP_ATTR_LARGE_COMMUNITIES 32 #define BGP_ATTR_PREFIX_SID 40 +#define BGP_ATTR_SRTE_COLOR 51 #ifdef ENABLE_BGP_VNC_ATTR #define BGP_ATTR_VNC 255 #endif @@ -1919,7 +1931,7 @@ extern int peer_password_unset(struct peer *); extern int peer_unsuppress_map_unset(struct peer *, afi_t, safi_t); extern int peer_maximum_prefix_set(struct peer *, afi_t, safi_t, uint32_t, - uint8_t, int, uint16_t); + uint8_t, int, uint16_t, bool force); extern int peer_maximum_prefix_unset(struct peer *, afi_t, safi_t); extern int peer_clear(struct peer *, struct listnode **); @@ -1946,6 +1958,9 @@ extern struct peer_af *peer_af_create(struct peer *, afi_t, safi_t); extern struct peer_af *peer_af_find(struct peer *, afi_t, safi_t); extern int peer_af_delete(struct peer *, afi_t, safi_t); +extern void bgp_shutdown_enable(struct bgp *bgp, const char *msg); +extern void bgp_shutdown_disable(struct bgp *bgp); + extern void bgp_close(void); extern void bgp_free(struct bgp *); void bgp_gr_apply_running_config(void); diff --git a/bgpd/valgrind.supp b/bgpd/valgrind.supp index ed236a6dc5..31f2477a58 100644 --- a/bgpd/valgrind.supp +++ b/bgpd/valgrind.supp @@ -1,17 +1,18 @@ { - <libyang_0.16.46> + <zlog_keep_working_at_exit> Memcheck:Leak + match-leak-kinds: reachable fun:calloc - fun:_dlerror_run - fun:dlopen@@GLIBC_2.2.5 - fun:ly_load_plugins_dir - fun:ly_load_plugins + fun:qcalloc + fun:zlog_target_clone } { - <zlog_keep_working_at_exit> + <libyang1_1.0.184> Memcheck:Leak match-leak-kinds: reachable fun:calloc - fun:qcalloc - fun:zlog_target_clone + fun:_dlerror_run + fun:dlopen@@GLIBC_2.2.5 + obj:/usr/lib/x86_64-linux-gnu/libyang.so.1.9.2 + fun:ly_load_plugins } diff --git a/configure.ac b/configure.ac index 36a5ce2428..715efbcdae 100755 --- a/configure.ac +++ b/configure.ac @@ -610,10 +610,6 @@ AC_ARG_ENABLE([cumulus], AS_HELP_STRING([--enable-cumulus], [enable Cumulus Switch Special Extensions])) AC_ARG_ENABLE([datacenter], AS_HELP_STRING([--enable-datacenter], [enable Compilation for Data Center Extensions])) -AC_ARG_ENABLE([fuzzing], - AS_HELP_STRING([--enable-fuzzing], [enable ability to fuzz various parts of FRR])) -AC_ARG_ENABLE([netlink_fuzzing], - AS_HELP_STRING([--enable-netlink-fuzzing], [enable ability to fuzz netlink listening socket in zebra])) AC_ARG_ENABLE([rr-semantics], AS_HELP_STRING([--disable-rr-semantics], [disable the v6 Route Replace semantics])) AC_ARG_ENABLE([protobuf], @@ -718,14 +714,6 @@ else DFLT_NAME="traditional" fi -if test "$enable_fuzzing" = "yes" ; then - AC_DEFINE([HANDLE_ZAPI_FUZZING], [1], [Compile extensions to use with a fuzzer]) -fi - -if test "$enable_netlink_fuzzing" = "yes" ; then - AC_DEFINE([HANDLE_NETLINK_FUZZING], [1], [Compile extensions to use with a fuzzer for netlink]) -fi - if test "$enable_cumulus" = "yes" ; then AC_DEFINE([HAVE_CUMULUS], [1], [Compile Special Cumulus Code in]) fi diff --git a/doc/developer/grpc.rst b/doc/developer/grpc.rst new file mode 100644 index 0000000000..8029a08b73 --- /dev/null +++ b/doc/developer/grpc.rst @@ -0,0 +1,249 @@ +.. _grpc-dev: + +*************** +Northbound gRPC +*************** + +.. _grpc-languages-bindings: + +Programming Language Bindings +============================= + +The gRPC supported programming language bindings can be found here: +https://grpc.io/docs/languages/ + +After picking a programming language that supports gRPC bindings, the +next step is to generate the FRR northbound bindings. To generate the +northbound bindings you'll need the programming language binding +generator tools and those are language specific. + +Next sections will use Ruby as an example for writing scripts to use +the northbound. + + +.. _grpc-ruby-generate: + +Generating Ruby FRR Bindings +---------------------------- + +Generating FRR northbound bindings for Ruby example: + +:: + + # Install the required gems: + # - grpc: the gem that will talk with FRR's gRPC plugin. + # - grpc-tools: the gem that provides the code generator. + gem install grpc + gem install grpc-tools + + # Create your project/scripts directory: + mkdir /tmp/frr-ruby + + # Go to FRR's grpc directory: + cd grpc + + # Generate the ruby bindings: + grpc_tools_ruby_protoc \ + --ruby_out=/tmp/frr-ruby \ + --grpc_out=/tmp/frr-ruby \ + frr-northbound.proto + + +.. _grpc-ruby-if-sample: + +Using Ruby To Get Interfaces State +---------------------------------- + +Here is a sample script to print all interfaces FRR discovered: + +:: + + require 'frr-northbound_services_pb' + + # Create the connection with FRR's gRPC: + stub = Frr::Northbound::Stub.new('localhost:50051', :this_channel_is_insecure) + + # Create a new state request to get interface state: + request = Frr::GetRequest.new + request.type = :STATE + request.path.push('/frr-interface:lib') + + # Ask FRR. + response = stub.get(request) + + # Print the response. + response.each do |result| + result.data.data.each_line do |line| + puts line + end + end + + +.. note:: + + The generated files will assume that they are in the search path (e.g. + inside gem) so you'll need to either edit it to use ``require_relative`` or + tell Ruby where to look for them. For simplicity we'll use ``-I .`` to tell + it is in the current directory. + + +The previous script will output something like this: + +:: + + $ cd /tmp/frr-ruby + # Add `-I.` so ruby finds the FRR generated file locally. + $ ruby -I. interface.rb + { + "frr-interface:lib": { + "interface": [ + { + "name": "eth0", + "vrf": "default", + "state": { + "if-index": 2, + "mtu": 1500, + "mtu6": 1500, + "speed": 1000, + "metric": 0, + "phy-address": "11:22:33:44:55:66" + }, + "frr-zebra:zebra": { + "state": { + "up-count": 0, + "down-count": 0 + } + } + }, + { + "name": "lo", + "vrf": "default", + "state": { + "if-index": 1, + "mtu": 0, + "mtu6": 65536, + "speed": 0, + "metric": 0, + "phy-address": "00:00:00:00:00:00" + }, + "frr-zebra:zebra": { + "state": { + "up-count": 0, + "down-count": 0 + } + } + } + ] + } + } + + +.. _grpc-ruby-bfd-profile-sample: + +Using Ruby To Create BFD Profiles +--------------------------------- + +In this example you'll learn how to edit configuration using JSON +and programmatic (XPath) format. + +:: + + require 'frr-northbound_services_pb' + + # Create the connection with FRR's gRPC: + stub = Frr::Northbound::Stub.new('localhost:50051', :this_channel_is_insecure) + + # Create a new candidate configuration change. + new_candidate = stub.create_candidate(Frr::CreateCandidateRequest.new) + + # Use JSON to configure. + request = Frr::LoadToCandidateRequest.new + request.candidate_id = new_candidate.candidate_id + request.type = :MERGE + request.config = Frr::DataTree.new + request.config.encoding = :JSON + request.config.data = <<-EOJ + { + "frr-bfdd:bfdd": { + "bfd": { + "profile": [ + { + "name": "test-prof", + "detection-multiplier": 4, + "required-receive-interval": 800000 + } + ] + } + } + } + EOJ + + # Load configuration to candidate. + stub.load_to_candidate(request) + + # Commit candidate. + stub.commit( + Frr::CommitRequest.new( + candidate_id: new_candidate.candidate_id, + phase: :ALL, + comment: 'create test-prof' + ) + ) + + # + # Now lets delete the previous profile and create a new one. + # + + # Create a new candidate configuration change. + new_candidate = stub.create_candidate(Frr::CreateCandidateRequest.new) + + # Edit the configuration candidate. + request = Frr::EditCandidateRequest.new + request.candidate_id = new_candidate.candidate_id + + # Delete previously created profile. + request.delete.push( + Frr::PathValue.new( + path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof']", + ) + ) + + # Add new profile with two configurations. + request.update.push( + Frr::PathValue.new( + path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof-2']/detection-multiplier", + value: 5.to_s + ) + ) + request.update.push( + Frr::PathValue.new( + path: "/frr-bfdd:bfdd/bfd/profile[name='test-prof-2']/desired-transmission-interval", + value: 900_000.to_s + ) + ) + + # Modify the candidate. + stub.edit_candidate(request) + + # Commit the candidate configuration. + stub.commit( + Frr::CommitRequest.new( + candidate_id: new_candidate.candidate_id, + phase: :ALL, + comment: 'replace test-prof with test-prof-2' + ) + ) + + +And here is the new FRR configuration: + +:: + + $ sudo vtysh -c 'show running-config' + ... + bfd + profile test-prof-2 + detect-multiplier 5 + transmit-interval 900 + ! + ! diff --git a/doc/developer/index.rst b/doc/developer/index.rst index 26b590c876..1f803b3772 100644 --- a/doc/developer/index.rst +++ b/doc/developer/index.rst @@ -12,6 +12,7 @@ FRRouting Developer's Guide testing bgpd fpm + grpc ospf zebra vtysh diff --git a/doc/developer/ospf-sr.rst b/doc/developer/ospf-sr.rst index 070465db5b..efe9b1b12f 100644 --- a/doc/developer/ospf-sr.rst +++ b/doc/developer/ospf-sr.rst @@ -1,8 +1,7 @@ OSPF Segment Routing ==================== -This is an EXPERIMENTAL support of draft -`draft-ietf-ospf-segment-routing-extensions-24`. +This is an EXPERIMENTAL support of `RFC 8665`. DON'T use it for production network. Supported Features @@ -10,12 +9,13 @@ Supported Features * Automatic computation of Primary and Backup Adjacency SID with Cisco experimental remote IP address -* SRGB configuration +* SRGB & SRLB configuration * Prefix configuration for Node SID with optional NO-PHP flag (Linux kernel support both mode) * Node MSD configuration (with Linux Kernel >= 4.10 a maximum of 32 labels could be stack) * Automatic provisioning of MPLS table +* Equal Cost Multi-Path (ECMP) * Static route configuration with label stack up to 32 labels Interoperability @@ -243,16 +243,16 @@ Routing. router ospf ospf router-id 192.168.1.11 capability opaque - mpls-te on - mpls-te router-address 192.168.1.11 - router-info area 0.0.0.0 segment-routing on segment-routing global-block 10000 19999 + segment-routing local-block 5000 5999 segment-routing node-msd 8 segment-routing prefix 192.168.1.11/32 index 1100 -The first segment-routing statement enable it. The Second one set the SRGB, -third line the MSD and finally, set the Prefix SID index for a given prefix. +The first segment-routing statement enables it. The second and third one set +the SRGB and SRLB respectively, fourth line the MSD and finally, set the +Prefix SID index for a given prefix. + Note that only prefix of Loopback interface could be configured with a Prefix SID. It is possible to add `no-php-flag` at the end of the prefix command to disable Penultimate Hop Popping. This advertises to peers that they MUST NOT pop @@ -265,9 +265,6 @@ Known limitations * Only single Area is supported. ABR is not yet supported * Only SPF algorithm is supported * Extended Prefix Range is not supported -* MPLS table are not flush at startup. Thus, restarting zebra process is - mandatory to remove old MPLS entries in the data plane after a crash of - ospfd daemon * With NO Penultimate Hop Popping, it is not possible to express a Segment Path with an Adjacency SID due to the impossibility for the Linux Kernel to perform double POP instruction. diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 6dab244a03..03b4b5a3e2 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -28,6 +28,7 @@ dev_RSTFILES = \ doc/developer/cli.rst \ doc/developer/conf.py \ doc/developer/frr-release-procedure.rst \ + doc/developer/grpc.rst \ doc/developer/hooks.rst \ doc/developer/include-compile.rst \ doc/developer/index.rst \ diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst index 6a73803d01..d51cbc9a14 100644 --- a/doc/developer/zebra.rst +++ b/doc/developer/zebra.rst @@ -382,3 +382,75 @@ Zebra Protocol Commands +------------------------------------+-------+ | ZEBRA_OPAQUE_UNREGISTER | 109 | +------------------------------------+-------+ +| ZEBRA_NEIGH_DISCOVER | 110 | ++------------------------------------+-------+ + +Dataplane batching +================== + +Dataplane batching is an optimization feature that reduces the processing +time involved in the user space to kernel space transition for every message we +want to send. + +Design +----------- + +With our dataplane abstraction, we create a queue of dataplane context objects +for the messages we want to send to the kernel. In a separate pthread, we +loop over this queue and send the context objects to the appropriate +dataplane. A batching enhancement tightly integrates with the dataplane +context objects so they are able to be batch sent to dataplanes that support +it. + +There is one main change in the dataplane code. It does not call +kernel-dependent functions one-by-one, but instead it hands a list of work down +to the kernel level for processing. + +Netlink +^^^^^^^ + +At the moment, this is the only dataplane that allows for batch sending +messages to it. + +When messages must be sent to the kernel, they are consecutively added +to the batch represented by the `struct nl_batch`. Context objects are firstly +encoded to their binary representation. All the encoding functions use the same +interface: take a context object, a buffer and a size of the buffer as an +argument. It is important that they should handle a situation in which a message +wouldn't fit in the buffer and return a proper error. To achieve a zero-copy +(in the user space only) messages are encoded to the same buffer which will +be passed to the kernel. Hence, we can theoretically hit the boundary of the +buffer. + +Messages stored in the batch are sent if one of the conditions occurs: + +- When an encoding function returns the buffer overflow error. The context + object that caused this error is re-added to the new, empty batch. + +- When the size of the batch hits certain limit. + +- When the namespace of a currently being processed context object is + different from all the previous ones. They have to be sent through + distinct sockets, so the messages cannot share the same buffer. + +- After the last message from the list is processed. + +As mentioned earlier, there is a special threshold which is smaller than +the size of the underlying buffer. It prevents the overflow error and thus +eliminates the case, in which a message is encoded twice. + +The buffer used in the batching is global, since allocating that big amount of +memory every time wouldn't be most effective. However, its size can be changed +dynamically, using hidden vtysh command: +``zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)``. This feature is +only used in tests and shouldn't be utilized in any other place. + +For every failed message in the batch, the kernel responds with an error +message. Error messages are kept in the same order as they were sent, so parsing the +response is straightforward. We use the two pointer technique to match +requests with responses and then set appropriate status of dataplane context +objects. There is also a global receive buffer and it is assumed that whatever +the kernel sends it will fit in this buffer. The payload of netlink error messages +consists of a error code and the original netlink message of the request, so +the batch response won't be bigger than the batch request increased by +some space for the headers. diff --git a/doc/figures/fig_dmvpn_topologies.png b/doc/figures/fig_dmvpn_topologies.png Binary files differnew file mode 100644 index 0000000000..a0dcc3e67d --- /dev/null +++ b/doc/figures/fig_dmvpn_topologies.png diff --git a/doc/figures/nodes.dot b/doc/figures/nodes.dot index b548b5529a..4ce147b2c4 100644 --- a/doc/figures/nodes.dot +++ b/doc/figures/nodes.dot @@ -47,7 +47,7 @@ digraph climodes { CONFIG_NODE -> OSPF_NODE [ label="router ospf [(1-65535)] [vrf NAME]" ]; CONFIG_NODE -> OSPF6_NODE [ label="router ospf6" ]; CONFIG_NODE -> LDP_NODE [ label="mpls ldp" ]; - CONFIG_NODE -> ISIS_NODE [ label="router isis WORD" ]; + CONFIG_NODE -> ISIS_NODE [ label="router isis WORD [vrf NAME]" ]; CONFIG_NODE -> RMAP_NODE [ label="route-map WORD <deny|permit> (1-65535)" ]; CONFIG_NODE -> PW_NODE [ label="pseudowire IFNAME" ]; CONFIG_NODE -> VTY_NODE [ label="line vty" ]; diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 47792317ad..86b0c28002 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -171,6 +171,33 @@ BFD peers and profiles share the same BFD session configuration commands. 'administrative down' message is sent to the remote peer. +.. index:: [no] passive-mode +.. clicmd:: [no] passive-mode + + Mark session as passive: a passive session will not attempt to start + the connection and will wait for control packets from peer before it + begins replying. + + This feature is useful when you have a router that acts as the + central node of a star network and you want to avoid sending BFD + control packets you don't need to. + + The default is active-mode (or ``no passive-mode``). + +.. index:: [no] minimum-ttl (1-254) +.. clicmd:: [no] minimum-ttl (1-254) + + For multi hop sessions only: configure the minimum expected TTL for + an incoming BFD control packet. + + This feature serves the purpose of thightening the packet validation + requirements to avoid receiving BFD control packets from other + sessions. + + The default value is 254 (which means we only expect one hop between + this system and the peer). + + BFD Peer Specific Commands -------------------------- diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index db0776adba..25eaa577c1 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -900,6 +900,25 @@ BGP GR Peer Mode Commands at the peer level. +.. _bgp-shutdown: + +Administrative Shutdown +----------------------- + +.. index:: [no] bgp shutdown [message MSG...] +.. clicmd:: [no] bgp shutdown [message MSG...] + + Administrative shutdown of all peers of a bgp instance. Drop all BGP peers, + but preserve their configurations. The peers are notified in accordance with + `RFC 8203 <https://tools.ietf.org/html/rfc8203/>`_ by sending a + ``NOTIFICATION`` message with error code ``Cease`` and subcode + ``Administrative Shutdown`` prior to terminating connections. This global + shutdown is independent of the neighbor shutdown, meaning that individually + shut down peers will not be affected by lifting it. + + An optional shutdown message `MSG` can be specified. + + .. _bgp-network: Networks @@ -1197,14 +1216,14 @@ Defining Peers The time in milliseconds that BGP will delay before deciding what peers can be put into an update-group together in order to generate a single update for them. The default time is 1000. - + .. _bgp-configuring-peers: Configuring Peers ^^^^^^^^^^^^^^^^^ -.. index:: [no] neighbor PEER shutdown [message MSG...] -.. clicmd:: [no] neighbor PEER shutdown [message MSG...] +.. index:: [no] neighbor PEER shutdown [message MSG...] [rtt (1-65535) [count (1-255)]] +.. clicmd:: [no] neighbor PEER shutdown [message MSG...] [rtt (1-65535) [count (1-255)]] Shutdown the peer. We can delete the neighbor's configuration by ``no neighbor PEER remote-as ASN`` but all configuration of the neighbor @@ -1213,6 +1232,12 @@ Configuring Peers Optionally you can specify a shutdown message `MSG`. + Also, you can specify optionally _rtt_ in milliseconds to automatically + shutdown the peer if round-trip-time becomes higher than defined. + + Additional _count_ parameter is the number of keepalive messages to count + before shutdown the peer if round-trip-time becomes higher than defined. + .. index:: [no] neighbor PEER disable-connected-check .. clicmd:: [no] neighbor PEER disable-connected-check @@ -1222,6 +1247,11 @@ Configuring Peers .. index:: [no] neighbor PEER ebgp-multihop .. clicmd:: [no] neighbor PEER ebgp-multihop + Specifying ``ebgp-multihop`` allows sessions with eBGP neighbors to + establish when they are multiple hops away. When the neighbor is not + directly connected and this knob is not enabled, the session will not + establish. + .. index:: [no] neighbor PEER description ... .. clicmd:: [no] neighbor PEER description ... @@ -1306,8 +1336,8 @@ Configuring Peers This command specifies a default `weight` value for the neighbor's routes. -.. index:: [no] neighbor PEER maximum-prefix NUMBER -.. clicmd:: [no] neighbor PEER maximum-prefix NUMBER +.. index:: [no] neighbor PEER maximum-prefix NUMBER [force] +.. clicmd:: [no] neighbor PEER maximum-prefix NUMBER [force] Sets a maximum number of prefixes we can receive from a given peer. If this number is exceeded, the BGP session will be destroyed. @@ -1319,6 +1349,11 @@ Configuring Peers granular and offers much smarter matching criterion than number of received prefixes, making it more suited to implementing policy. + If _force_ is set, then ALL prefixes are counted for maximum instead of + accepted only. This is useful for cases where an inbound filter is applied, + but you want maximum-prefix to act on ALL (including filtered) prefixes. This + option requires `soft-reconfiguration inbound` to be enabled for the peer. + .. index:: [no] neighbor PEER maximum-prefix-out NUMBER .. clicmd:: [no] neighbor PEER maximum-prefix-out NUMBER diff --git a/doc/user/conf.py b/doc/user/conf.py index d8a188b152..1f6f050bcf 100644 --- a/doc/user/conf.py +++ b/doc/user/conf.py @@ -16,6 +16,7 @@ import sys import os import re import pygments +import sphinx from sphinx.highlighting import lexers # If extensions (or modules to document with autodoc) are in another directory, @@ -359,14 +360,36 @@ texinfo_documents = [ with open('../extra/frrlexer.py', 'rb') as lex: frrlexerpy = lex.read() +# Parse version string into int array +def vparse(s): + a = [] + + for c in s: + if c != '.': + a.append(int(c)) + + while len(a) < 3: + a.append(0) + + return a[:3] + # 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') + # css overrides for HTML theme - app.add_stylesheet('overrides.css') - app.add_javascript('overrides.js') + # Note sphinx version differences + sver = vparse(sphinx.__version__) + + if sver < vparse('1.8.0') : + app.add_stylesheet('overrides.css') + app.add_javascript('overrides.js') + else: + app.add_css_file('overrides.css') + app.add_js_file('overrides.js') + # load Pygments lexer for FRR config syntax # # NB: in Pygments 2.2+ this can be done with `load_lexer_from_file`, but we diff --git a/doc/user/flowspec.rst b/doc/user/flowspec.rst index b274afe8a2..d3eb25a7c7 100644 --- a/doc/user/flowspec.rst +++ b/doc/user/flowspec.rst @@ -22,6 +22,9 @@ more or less complex combination of the following: - Layer 3 information: DSCP value, Protocol type, packet length, fragmentation. - Misc layer 4 TCP flags. +Note that if originally Flowspec defined IPv4 rules, this is also possible to use +IPv6 address-family. The same set of combinations as defined for IPv4 can be used. + A combination of the above rules is applied for traffic filtering. This is encoded as part of specific BGP extended communities and the action can range from the obvious rerouting (to nexthop or to separate VRF) to shaping, or @@ -31,6 +34,7 @@ The following IETF drafts and RFCs have been used to implement FRR Flowspec: - :rfc:`5575` - [Draft-IETF-IDR-Flowspec-redirect-IP]_ +- [Draft-IETF-IDR-Flow-Spec-V6]_ .. _design-principles-flowspec: @@ -108,9 +112,13 @@ As of today, it is only possible to configure Flowspec on the default VRF. router bgp <AS> neighbor <A.B.C.D> remote-as <remoteAS> + neighbor <A:B::C:D> remote-as <remoteAS2> address-family ipv4 flowspec neighbor <A.B.C.D> activate - exit + exit + address-family ipv6 flowspec + neighbor <A:B::C:D> activate + exit exit You can see Flowspec entries, by using one of the following show commands: @@ -118,6 +126,8 @@ You can see Flowspec entries, by using one of the following show commands: .. index:: show bgp ipv4 flowspec [detail | A.B.C.D] .. clicmd:: show bgp ipv4 flowspec [detail | A.B.C.D] +.. index:: show bgp ipv6 flowspec [detail | A:B::C:D] +.. clicmd:: show bgp ipv6 flowspec [detail | A:B::C:D] Per-interface configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -183,6 +193,28 @@ interfaces are created with private IP addressing scheme. exit exit +Similarly, it is possible to do the same for IPv6 flowspec rules, by using +an IPv6 extended community. The format is defined on :rfc:`5701`, and that +community contains an IPv6 address encoded in the attribute, and matches the +locally configured imported route target IPv6 defined under the appropriate +BGP VRF instance. Below example defines an IPv6 extended community containing +`E:F::G:H` address followed by 2 bytes chosen by admin ( here `JJ`). + +.. code-block:: frr + + router bgp <ASx> + neighbor <A:B::C:D> remote-as <ASz> + address-family ipv6 flowspec + neighbor A:B::C:D activate + exit + exit + router bgp <ASy> vrf vrf2 + address-family ipv6 unicast + rt6 redirect import <E:F::G:H:JJ> + exit + exit + + Flowspec monitoring & troubleshooting ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -348,4 +380,5 @@ inside FRRouting. .. [Draft-IETF-IDR-Flowspec-redirect-IP] <https://tools.ietf.org/id/draft-ietf-idr-flowspec-redirect-ip-02.txt> .. [Draft-IETF-IDR-Flowspec-Interface-Set] <https://tools.ietf.org/id/draft-ietf-idr-flowspec-interfaceset-03.txt> +.. [Draft-IETF-IDR-Flow-Spec-V6] <https://tools.ietf.org/id/draft-ietf-idr-flow-spec-v6-10.txt> .. [Presentation] <https://docs.google.com/presentation/d/1ekQygUAG5yvQ3wWUyrw4Wcag0LgmbW1kV02IWcU4iUg/edit#slide=id.g378f0e1b5e_1_44> diff --git a/doc/user/grpc.rst b/doc/user/grpc.rst new file mode 100644 index 0000000000..d6767fc866 --- /dev/null +++ b/doc/user/grpc.rst @@ -0,0 +1,66 @@ +.. _grpc: + +*************** +Northbound gRPC +*************** + +.. program:: configure + +*gRPC* provides a combined front end to all FRR daemons using the YANG +northbound. It is currently disabled by default due its experimental +stage, but it can be enabled with :option:`--enable-grpc` option in the +configure script. + + +.. _grpc-features: + +Northbound gRPC Features +======================== + +* Get/set configuration using JSON/XML/XPath encondings. +* Execute YANG RPC calls. +* Lock/unlock configuration. +* Create/edit/load/update/commit candidate configuration. +* List/get transactions. + + +.. note:: + + There is currently no support for YANG notifications. + + +.. note:: + + You can find more information on how to code programs to interact + with FRR by reading the gRPC Programming Language Bindings section + in the `developer's documentation + <http://docs.frrouting.org/projects/dev-guide/en/latest/grpc.html>`_. + + +.. _grpc-config: + +Daemon gRPC Configuration +========================= + +The *gRPC* module accepts the following run time option: + +- ``port``: the port to listen to (defaults to ``50051``). + + +.. note:: + + At the moment only localhost connections with no SSL/TLS are + supported. + + +To configure FRR daemons to listen to gRPC you need to append the +following parameter to the daemon's command line: ``-M grpc`` +(optionally ``-M grpc:PORT`` to specify listening port). + +To do that in production you need to edit the ``/etc/frr/daemons`` file +so the daemons get started with the command line argument. Example: + +:: + + # other daemons... + bfdd_options=" --daemon -A 127.0.0.1 -M grpc" diff --git a/doc/user/index.rst b/doc/user/index.rst index 7bb86e563b..8ac997f8dd 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -23,6 +23,7 @@ Basics basic vtysh + grpc filter routemap ipv6 diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 622af67b0f..0fd33eace8 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -255,12 +255,6 @@ options from the list below. mind. Specifically turn on -g3 -O0 for compiling options and add inclusion of grammar sandbox. -.. option:: --enable-fuzzing - - Turn on some compile options to allow you to run fuzzing tools against the - system. This flag is intended as a developer only tool and should not be - used for normal operations. - .. option:: --disable-snmp Build without SNMP support. @@ -528,34 +522,13 @@ Additional kernel modules are also needed to support MPLS forwarding. features can be found in http://schd.ws/hosted_files/ossna2017/fe/vrf-tutorial-oss.pdf. - The following impacts how BGP TCP sockets are managed across VRFs: - - .. code-block:: shell - - net.ipv4.tcp_l3mdev_accept=0 - - With this setting a BGP TCP socket is opened per VRF. This setting - ensures that other TCP services, such as SSH, provided for non-VRF - purposes are blocked from VRF associated Linux interfaces. - - .. code-block:: shell - - net.ipv4.tcp_l3mdev_accept=1 - - With this setting a single BGP TCP socket is shared across the - system. This setting exposes any TCP service running on the system, - e.g., SSH, to all VRFs. Generally this setting is not used in - environments where VRFs are used to support multiple administrative - groups. + A separate BGP TCP socket is opened per VRF. **Important note** as of June 2018, Kernel versions 4.14-4.18 have a known bug where VRF-specific TCP sockets are not properly handled. When running these kernel versions, if unable to establish any VRF BGP - adjacencies, either downgrade to 4.13 or set - 'net.ipv4.tcp_l3mdev_accept=1'. The fix for this issue is planned to be - included in future kernel versions. So upgrading your kernel may also - address this issue. - + adjacencies, downgrade to 4.13. The issue was fixed in 4.14.57, 4.17.9 + and more recent kernel versions. Building ^^^^^^^^ diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index 200d00821f..8cbbe0809f 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -33,8 +33,8 @@ ISIS router To start the ISIS process you have to specify the ISIS router. As of this writing, *isisd* does not support multiple ISIS processes. -.. index:: [no] router isis WORD -.. clicmd:: [no] router isis WORD +.. index:: [no] router isis WORD [vrf NAME] +.. clicmd:: [no] router isis WORD [vrf NAME] Enable or disable the ISIS process by specifying the ISIS domain with 'WORD'. *isisd* does not yet support multiple ISIS processes but you must @@ -202,8 +202,8 @@ ISIS interface .. _ip-router-isis-word: -.. index:: [no] <ip|ipv6> router isis WORD -.. clicmd:: [no] <ip|ipv6> router isis WORD +.. index:: [no] <ip|ipv6> router isis WORD [vrf NAME] +.. clicmd:: [no] <ip|ipv6> router isis WORD [vrf NAME] Activate ISIS adjacency on this interface. Note that the name of ISIS instance must be the same as the one used to configure the ISIS process (see @@ -418,8 +418,8 @@ Showing ISIS information Show topology IS-IS paths to Intermediate Systems, globally, in area (level-1) or domain (level-2). -.. index:: show ip route isis -.. clicmd:: show ip route isis +.. index:: show isis route [level-1|level-2] +.. clicmd:: show isis route [level-1|level-2] Show the ISIS routing table, as determined by the most recent SPF calculation. @@ -751,3 +751,22 @@ A Segment Routing configuration, with IPv4, IPv6, SRGB and MSD configuration. segment-routing prefix 2001:db8:1000::1/128 index 101 explicit-null ! +ISIS Vrf Configuration Examples +=============================== + +A simple vrf example: + +.. code-block:: frr + + ! + interface eth0 vrf RED + ip router isis FOO vrf RED + isis network point-to-point + isis circuit-type level-2-only + ! + router isis FOO vrf RED + net 47.0023.0000.0000.0000.0000.0000.0000.1900.0004.00 + metric-style wide + is-type level-2-only + + diff --git a/doc/user/nhrpd.rst b/doc/user/nhrpd.rst index 8d3bea7c94..9caeb0eedb 100644 --- a/doc/user/nhrpd.rst +++ b/doc/user/nhrpd.rst @@ -227,5 +227,206 @@ Show NHRP Configuration Example ===================== -FIXME +.. figure:: ../figures/fig_dmvpn_topologies.png + :alt: image + + image + +IPSec configurration example +---------------------------- + +This changes required on all nodes as HUB and Spokes. + +ipsec.conf file + +.. code-block:: shell + + config setup + conn dmvpn + authby=secret + auto=add + keyexchange=ikev2 + ike=aes256-aes256-sha256-modp2048 + esp=aes256-aes256-sha256-modp2048 + dpdaction=clear + dpddelay=300s + left=%any + leftid=%any + right=%any + rightid=%any + leftprotoport=gre + rightprotoport=gre + type=transport + keyingtries=%forever + +ipsec.secrets file + +.. code-block:: shell + + %any : PSK "some_s3cret!" + + +HUB configuration example +------------------------- + +Creating gre interface + +.. code-block:: console + + ip tunnel add gre1 mode gre key 42 ttl 64 + ip addr add 10.0.0.254/32 dev gre1 + ip link set gre1 up + +Adding iptables rules to provide possibility shortcut tunnels and connect spokes directly + +.. code-block:: shell + + iptables -A FORWARD -i gre1 -o gre1 \\ + -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \\ + --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 --hashlimit-dstmask 24 \\ + --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128 + +FRR config on HUB + +.. code-block:: frr + + nhrp nflog-group 1 + ! + interface gre1 + description DMVPN Tunnel Interface + ip address 10.0.0.254/32 + ip nhrp network-id 1 + ip nhrp redirect + ip nhrp registration no-unique + ip nhrp shortcut + tunnel protection vici profile dmvpn + tunnel source eth0 + ! + router bgp 65000 + bgp router-id 10.0.0.254 + no bgp ebgp-requires-policy + neighbor SPOKES peer-group + neighbor SPOKES disable-connected-check + neighbor 10.0.0.1 remote-as 65001 + neighbor 10.0.0.1 peer-group SPOKES + neighbor 10.0.0.2 remote-as 65002 + neighbor 10.0.0.2 peer-group SPOKES + neighbor 10.0.0.3 remote-as 65003 + neighbor 10.0.0.3 peer-group SPOKES + ! + address-family ipv4 unicast + network 172.16.0.0/24 + redistribute nhrp + exit-address-family + +Spoke1 configuration +-------------------- + +Creating gre interface + +.. code-block:: console + + ip tunnel add gre1 mode gre key 42 ttl 64 + ip addr add 10.0.0.1/32 dev gre1 + ip link set gre1 up + + +FRR config on Spoke1 + +.. code-block:: frr + + interface gre1 + description DMVPN Tunnel Interface + ip address 10.0.0.1/32 + ip nhrp network-id 1 + ip nhrp nhs dynamic nbma 198.51.100.1 + ip nhrp redirect + ip nhrp registration no-unique + ip nhrp shortcut + no link-detect + tunnel protection vici profile dmvpn + tunnel source eth0 + ! + router bgp 65001 + no bgp ebgp-requires-policy + neighbor 10.0.0.254 remote-as 65000 + neighbor 10.0.0.254 disable-connected-check + ! + address-family ipv4 unicast + network 172.16.1.0/24 + exit-address-family + + +Spoke2 configuration +-------------------- + +Creating gre interface + +.. code-block:: console + + ip tunnel add gre1 mode gre key 42 ttl 64 + ip addr add 10.0.0.1/32 dev gre1 + ip link set gre1 up + +FRR config on Spoke2 + +.. code-block:: frr + + interface gre1 + description DMVPN Tunnel Interface + ip address 10.0.0.2/32 + ip nhrp network-id 1 + ip nhrp nhs dynamic nbma 198.51.100.1 + ip nhrp redirect + ip nhrp registration no-unique + ip nhrp shortcut + no link-detect + tunnel protection vici profile dmvpn + tunnel source eth0 + ! + router bgp 65002 + no bgp ebgp-requires-policy + neighbor 10.0.0.254 remote-as 65000 + neighbor 10.0.0.254 disable-connected-check + ! + address-family ipv4 unicast + network 172.16.2.0/24 + exit-address-family + + +Spoke3 configuration +-------------------- + +Creating gre interface + +.. code-block:: console + + ip tunnel add gre1 mode gre key 42 ttl 64 + ip addr add 10.0.0.3/32 dev gre1 + ip link set gre1 up + +FRR config on Spoke3 + +.. code-block:: frr + + interface gre1 + description DMVPN Tunnel Interface + ip address 10.0.0.3/32 + ip nhrp network-id 1 + ip nhrp nhs dynamic nbma 198.51.100.1 + ip nhrp redirect + ip nhrp registration no-unique + ip nhrp shortcut + no link-detect + tunnel protection vici profile dmvpn + tunnel source eth0 + ! + router bgp 65003 + no bgp ebgp-requires-policy + neighbor 10.0.0.254 remote-as 65000 + neighbor 10.0.0.254 disable-connected-check + ! + address-family ipv4 unicast + network 172.16.3.0/24 + exit-address-family diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index 16eaf3be2a..010526b637 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -310,6 +310,17 @@ To start OSPF process you have to specify the OSPF router. In some cases it may be more convenient to enable OSPF on a per interface/subnet basis (:clicmd:`ip ospf area AREA [ADDR]`). +.. index:: proactive-arp +.. clicmd:: proactive-arp + +.. index:: no proactive-arp +.. clicmd:: no proactive-arp + + This command enables or disables sending ARP requests to update neighbor + table entries. It speeds up convergence for /32 networks on a P2P + connection. + + This feature is enabled by default. .. _ospf-area: @@ -1071,8 +1082,8 @@ Router Information Segment Routing =============== -This is an EXPERIMENTAL support of Segment Routing as per draft -`draft-ietf-ospf-segment-routing-extensions-24.txt` for MPLS dataplane. +This is an EXPERIMENTAL support of Segment Routing as per `RFC 8665` for MPLS +dataplane. .. index:: [no] segment-routing on .. clicmd:: [no] segment-routing on @@ -1085,7 +1096,13 @@ This is an EXPERIMENTAL support of Segment Routing as per draft .. clicmd:: [no] segment-routing global-block (0-1048575) (0-1048575) Fix the Segment Routing Global Block i.e. the label range used by MPLS to - store label in the MPLS FIB. + store label in the MPLS FIB for Prefix SID. + +.. index:: [no] segment-routing local-block (0-1048575) (0-1048575) +.. clicmd:: [no] segment-routing local-block (0-1048575) (0-1048575) + + Fix the Segment Routing Local Block i.e. the label range used by MPLS to + store label in the MPLS FIB for Adjacency SID. .. index:: [no] segment-routing node-msd (1-16) .. clicmd:: [no] segment-routing node-msd (1-16) @@ -1093,13 +1110,15 @@ This is an EXPERIMENTAL support of Segment Routing as per draft Fix the Maximum Stack Depth supported by the router. The value depend of the MPLS dataplane. E.g. for Linux kernel, since version 4.13 it is 32. -.. index:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag] -.. clicmd:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag] +.. index:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag|explicit-null] +.. clicmd:: [no] segment-routing prefix A.B.C.D/M [index (0-65535)|no-php-flag|explicit-null] Set the Segment Routing index for the specified prefix. Note that, only prefix with /32 corresponding to a loopback interface are currently supported. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR - node to request to its neighbor to not pop the label. + node to request to its neighbor to not pop the label. The 'explicit-null' means that + neighbor nodes must swap the incoming label by the MPLS Explicit Null label + before delivering the packet. .. index:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json] .. clicmd:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json] diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index f557cbe022..fa5fc248a8 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -329,6 +329,12 @@ Route Map Set Command Set the BGP table to a given table identifier +.. index:: set sr-te color (1-4294967295) +.. clicmd:: set sr-te color (1-4294967295) + + Set the color of a SR-TE Policy to be applied to a learned route. The SR-TE + Policy is uniquely determined by the color and the BGP nexthop. + .. _route-map-call-command: Route Map Call Command diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst index 76bdc48dc0..57ef141c7e 100644 --- a/doc/user/sharp.rst +++ b/doc/user/sharp.rst @@ -78,7 +78,8 @@ keyword. At present, no sharp commands will be preserved in the config. The nexthop or import choice chooses the type of nexthop we are asking zebra to watch for us. This choice affects zebra's decision on what matches. Connected tells zebra whether or not that we want the route - matched against to be a static or connected route. The no form of + matched against to be a static or connected route for the nexthop keyword, + for the import keyword connected means exact match. The no form of the command obviously turns this watching off. .. index:: sharp data nexthop @@ -137,3 +138,9 @@ keyword. At present, no sharp commands will be preserved in the config. Remove a test zapi client session that was created with the specified session id. + +.. index:: sharp neigh discover +.. clicmd:: sharp neigh discover [vrf NAME] <A.B.C.D|X:X::X:X> IFNAME + + Send an ARP/NDP request to trigger the addition of a neighbor in the ARP + table. diff --git a/doc/user/static.rst b/doc/user/static.rst index 09bdc9cbea..6302d1b148 100644 --- a/doc/user/static.rst +++ b/doc/user/static.rst @@ -128,3 +128,15 @@ but this time, the route command will apply to the VRF. ip route 10.0.0.0/24 10.0.0.2 exit-vrf + +SR-TE Route Commands +==================== + +It is possible to specify a route using a SR-TE policy configured in Zebra. + +e.g. to use the SR-TE policy with endpoint 6.6.6.6 and color 123 to reach the +network 9.9.9.9/24: + +.. code-block:: frr + + ip route 9.9.9.9/24 6.6.6.6 color 123 diff --git a/doc/user/subdir.am b/doc/user/subdir.am index eb7b7ced52..dd7a193e34 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -15,6 +15,7 @@ user_RSTFILES = \ doc/user/filter.rst \ doc/user/frr-reload.rst \ doc/user/glossary.rst \ + doc/user/grpc.rst \ doc/user/index.rst \ doc/user/installation.rst \ doc/user/ipv6.rst \ diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 7b028fd674..a9ab162b40 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -1050,23 +1050,34 @@ Many routing protocols require a router-id to be configured. To have a consistent router-id across all daemons, the following commands are available to configure and display the router-id: -.. index:: [no] router-id A.B.C.D -.. clicmd:: [no] router-id A.B.C.D +.. index:: [no] [ip] router-id A.B.C.D +.. clicmd:: [no] [ip] router-id A.B.C.D Allow entering of the router-id. This command also works under the - vrf subnode, to allow router-id's per vrf. + vrf subnode, to allow router-id's per vrf. -.. index:: [no] router-id A.B.C.D vrf NAME -.. clicmd:: [no] router-id A.B.C.D vrf NAME +.. index:: [no] [ip] router-id A.B.C.D vrf NAME +.. clicmd:: [no] [ip] router-id A.B.C.D vrf NAME Configure the router-id of this router from the configure NODE. A show run of this command will display the router-id command under the vrf sub node. This command is deprecated and will be removed at some point in time in the future. - -.. index:: show router-id [vrf NAME] -.. clicmd:: show router-id [vrf NAME] + +.. index:: show [ip] router-id [vrf NAME] +.. clicmd:: show [ip] router-id [vrf NAME] Display the user configured router-id. +For protocols requiring an IPv6 router-id, the following commands are available: + +.. index:: [no] ipv6 router-id X:X::X:X +.. clicmd:: [no] ipv6 router-id X:X::X:X + + Configure the IPv6 router-id of this router. Like its IPv4 counterpart, + this command works under the vrf subnode, to allow router-id's per vrf. + +.. index:: show ipv6 router-id [vrf NAME] +.. clicmd:: show ipv6 router-id [vrf NAME] + Display the user configured IPv6 router-id. diff --git a/eigrpd/eigrp_dump.c b/eigrpd/eigrp_dump.c index 0874ce3f19..97de73116b 100644 --- a/eigrpd/eigrp_dump.c +++ b/eigrpd/eigrp_dump.c @@ -154,7 +154,7 @@ void show_ip_eigrp_interface_header(struct vty *vty, struct eigrp *eigrp) { vty_out(vty, - "\nEIGRP interfaces for AS(%d)\n\n %-10s %-10s %-10s %-6s %-12s %-7s %-14s %-12s %-8s %-8s %-8s\n %-39s %-12s %-7s %-14s %-12s %-8s\n", + "\nEIGRP interfaces for AS(%d)\n\n%-16s %-10s %-10s %-6s %-12s %-7s %-14s %-12s %-8s %-8s %-8s\n %-44s %-12s %-7s %-14s %-12s %-8s\n", eigrp->AS, "Interface", "Bandwidth", "Delay", "Peers", "Xmit Queue", "Mean", "Pacing Time", "Multicast", "Pending", "Hello", "Holdtime", "", "Un/Reliable", "SRTT", "Un/Reliable", @@ -164,7 +164,7 @@ void show_ip_eigrp_interface_header(struct vty *vty, struct eigrp *eigrp) void show_ip_eigrp_interface_sub(struct vty *vty, struct eigrp *eigrp, struct eigrp_interface *ei) { - vty_out(vty, "%-11s ", IF_NAME(ei)); + vty_out(vty, "%-16s ", IF_NAME(ei)); vty_out(vty, "%-11u", ei->params.bandwidth); vty_out(vty, "%-11u", ei->params.delay); vty_out(vty, "%-7u", ei->nbrs->count); diff --git a/eigrpd/eigrp_vty.c b/eigrpd/eigrp_vty.c index e4a499425b..4426cf67e9 100644 --- a/eigrpd/eigrp_vty.c +++ b/eigrpd/eigrp_vty.c @@ -101,6 +101,24 @@ static struct eigrp *eigrp_vty_get_eigrp(struct vty *vty, const char *vrf_name) return eigrp_lookup(vrf->vrf_id); } +static void eigrp_topology_helper(struct vty *vty, struct eigrp *eigrp, + const char *all) +{ + struct eigrp_prefix_entry *tn; + struct route_node *rn; + + show_ip_eigrp_topology_header(vty, eigrp); + + for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + tn = rn->info; + eigrp_vty_display_prefix_entry(vty, eigrp, tn, + all ? true : false); + } +} + DEFPY (show_ip_eigrp_topology_all, show_ip_eigrp_topology_all_cmd, "show ip eigrp [vrf NAME] topology [all-links$all]", @@ -112,28 +130,30 @@ DEFPY (show_ip_eigrp_topology_all, "Show all links in topology table\n") { struct eigrp *eigrp; - struct eigrp_prefix_entry *tn; - struct route_node *rn; - eigrp = eigrp_vty_get_eigrp(vty, vrf); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + struct vrf *v; - show_ip_eigrp_topology_header(vty, eigrp); + RB_FOREACH (v, vrf_name_head, &vrfs_by_name) { + eigrp = eigrp_lookup(v->vrf_id); + if (!eigrp) + continue; - for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) { - if (!rn->info) - continue; + vty_out(vty, "VRF %s:\n", v->name); - tn = rn->info; - eigrp_vty_display_prefix_entry(vty, eigrp, tn, - all ? true : false); + eigrp_topology_helper(vty, eigrp, all); + } + } else { + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + eigrp_topology_helper(vty, eigrp, all); } return CMD_SUCCESS; - } DEFPY (show_ip_eigrp_topology, @@ -152,6 +172,11 @@ DEFPY (show_ip_eigrp_topology, struct route_node *rn; struct prefix cmp; + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + vty_out(vty, "Specifying vrf `all` for a particular address/prefix makes no sense\n"); + return CMD_SUCCESS; + } + eigrp = eigrp_vty_get_eigrp(vty, vrf); if (eigrp == NULL) { vty_out(vty, " EIGRP Routing Process not enabled\n"); @@ -187,6 +212,24 @@ DEFPY (show_ip_eigrp_topology, return CMD_SUCCESS; } +static void eigrp_interface_helper(struct vty *vty, struct eigrp *eigrp, + const char *ifname, const char *detail) +{ + struct eigrp_interface *ei; + struct listnode *node; + + if (!ifname) + show_ip_eigrp_interface_header(vty, eigrp); + + for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { + if (!ifname || strcmp(ei->ifp->name, ifname) == 0) { + show_ip_eigrp_interface_sub(vty, eigrp, ei); + if (detail) + show_ip_eigrp_interface_detail(vty, eigrp, ei); + } + } +} + DEFPY (show_ip_eigrp_interfaces, show_ip_eigrp_interfaces_cmd, "show ip eigrp [vrf NAME] interfaces [IFNAME] [detail]$detail", @@ -198,28 +241,52 @@ DEFPY (show_ip_eigrp_interfaces, "Interface name to look at\n" "Detailed information\n") { - struct eigrp_interface *ei; struct eigrp *eigrp; - struct listnode *node; - eigrp = eigrp_vty_get_eigrp(vty, vrf); - if (eigrp == NULL) { - vty_out(vty, "EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + struct vrf *vrf; + + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + eigrp = eigrp_lookup(vrf->vrf_id); + if (!eigrp) + continue; + + vty_out(vty, "VRF %s:\n", vrf->name); + + eigrp_interface_helper(vty, eigrp, ifname, detail); + } + } else { + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, "EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; + } + + eigrp_interface_helper(vty, eigrp, ifname, detail); } - if (!ifname) - show_ip_eigrp_interface_header(vty, eigrp); + + return CMD_SUCCESS; +} + +static void eigrp_neighbors_helper(struct vty *vty, struct eigrp *eigrp, + const char *ifname, const char *detail) +{ + struct eigrp_interface *ei; + struct listnode *node, *node2, *nnode2; + struct eigrp_neighbor *nbr; + + show_ip_eigrp_neighbor_header(vty, eigrp); for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { if (!ifname || strcmp(ei->ifp->name, ifname) == 0) { - show_ip_eigrp_interface_sub(vty, eigrp, ei); - if (detail) - show_ip_eigrp_interface_detail(vty, eigrp, ei); + for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { + if (detail || (nbr->state == EIGRP_NEIGHBOR_UP)) + show_ip_eigrp_neighbor_sub(vty, nbr, + !!detail); + } } } - - return CMD_SUCCESS; } DEFPY (show_ip_eigrp_neighbors, @@ -234,26 +301,27 @@ DEFPY (show_ip_eigrp_neighbors, "Detailed Information\n") { struct eigrp *eigrp; - struct eigrp_interface *ei; - struct listnode *node, *node2, *nnode2; - struct eigrp_neighbor *nbr; - eigrp = eigrp_vty_get_eigrp(vty, vrf); - if (eigrp == NULL) { - vty_out(vty, " EIGRP Routing Process not enabled\n"); - return CMD_SUCCESS; - } + if (vrf && strncmp(vrf, "all", sizeof("all")) == 0) { + struct vrf *vrf; - show_ip_eigrp_neighbor_header(vty, eigrp); + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + eigrp = eigrp_lookup(vrf->vrf_id); + if (!eigrp) + continue; - for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { - if (!ifname || strcmp(ei->ifp->name, ifname) == 0) { - for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) { - if (detail || (nbr->state == EIGRP_NEIGHBOR_UP)) - show_ip_eigrp_neighbor_sub(vty, nbr, - !!detail); - } + vty_out(vty, "VRF %s:\n", vrf->name); + + eigrp_neighbors_helper(vty, eigrp, ifname, detail); + } + } else { + eigrp = eigrp_vty_get_eigrp(vty, vrf); + if (eigrp == NULL) { + vty_out(vty, " EIGRP Routing Process not enabled\n"); + return CMD_SUCCESS; } + + eigrp_neighbors_helper(vty, eigrp, ifname, detail); } return CMD_SUCCESS; diff --git a/grpc/frr-northbound.proto b/grpc/frr-northbound.proto index d070d715e8..32544ba694 100644 --- a/grpc/frr-northbound.proto +++ b/grpc/frr-northbound.proto @@ -1,20 +1,27 @@ // -// Copyright (C) 2019 NetDEF, Inc. -// Renato Westphal +// Copyright 2019 FRRouting // -// 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. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: // -// 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. +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. // -// 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 +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. // syntax = "proto3"; @@ -280,6 +287,9 @@ message CommitResponse { // ID of the created configuration transaction (when the phase is APPLY // or ALL). uint32 transaction_id = 1; + + // Human-readable error or warning message(s). + string error_message = 2; } // diff --git a/isisd/fabricd.c b/isisd/fabricd.c index e9dfd46010..1a081bbea6 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -221,7 +221,10 @@ struct fabricd *fabricd_new(struct isis_area *area) rv->area = area; rv->initial_sync_state = FABRICD_SYNC_PENDING; - rv->spftree = isis_spftree_new(area); + rv->spftree = + isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1], + area->isis->sysid, ISIS_LEVEL2, SPFTREE_IPV4, + SPF_TYPE_FORWARD, F_SPFTREE_HOPCOUNT_METRIC); rv->neighbors = skiplist_new(0, neighbor_entry_list_cmp, neighbor_entry_del_void); rv->neighbors_neighbors = hash_create(neighbor_entry_hash_key, @@ -475,7 +478,7 @@ void fabricd_run_spf(struct isis_area *area) if (!f) return; - isis_run_hopcount_spf(area, isis->sysid, f->spftree); + isis_run_hopcount_spf(area, area->isis->sysid, f->spftree); neighbors_neighbors_update(f); fabricd_bump_tier_calculation_timer(f); } diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 425627485a..f7cdd58f72 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -43,15 +43,12 @@ #include "isisd/isis_dynhn.h" #include "isisd/isis_pdu.h" #include "isisd/isis_lsp.h" -#include "isisd/isis_spf.h" #include "isisd/isis_events.h" #include "isisd/isis_mt.h" #include "isisd/isis_tlvs.h" #include "isisd/fabricd.h" #include "isisd/isis_nb.h" -extern struct isis *isis; - static struct isis_adjacency *adj_alloc(const uint8_t *id) { struct isis_adjacency *adj; @@ -94,6 +91,7 @@ struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa, } } adj->adj_sids = list_new(); + listnode_add(circuit->area->adjacency_list, adj); return adj; } @@ -123,42 +121,21 @@ struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa, return NULL; } -bool isis_adj_exists(const struct isis_area *area, int level, - const uint8_t *sysid) +struct isis_adjacency *isis_adj_find(const struct isis_area *area, int level, + const uint8_t *sysid) { - struct isis_circuit *circuit; + struct isis_adjacency *adj; struct listnode *node; - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { - struct isis_adjacency *adj; - struct listnode *anode; - struct list *adjdb; - - switch (circuit->circ_type) { - case CIRCUIT_T_BROADCAST: - adjdb = circuit->u.bc.adjdb[level - 1]; - if (!adjdb) - continue; - - for (ALL_LIST_ELEMENTS_RO(adjdb, anode, adj)) { - if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) - return true; - } - break; - case CIRCUIT_T_P2P: - adj = circuit->u.p2p.neighbor; - if (!adj) - break; + for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) { + if (!(adj->level & level)) + continue; - if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) - return true; - break; - default: - break; - } + if (!memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) + return adj; } - return false; + return NULL; } DEFINE_HOOK(isis_adj_state_change_hook, (struct isis_adjacency *adj), (adj)) @@ -174,9 +151,6 @@ void isis_delete_adj(void *arg) if (adj->adj_state != ISIS_ADJ_DOWN) adj->adj_state = ISIS_ADJ_DOWN; - /* remove from SPF trees */ - spftree_area_adj_del(adj->circuit->area, adj); - hook_call(isis_adj_state_change_hook, adj); XFREE(MTYPE_ISIS_ADJACENCY_INFO, adj->area_addresses); @@ -186,6 +160,7 @@ void isis_delete_adj(void *arg) adj_mt_finish(adj); list_delete(&adj->adj_sids); + listnode_delete(adj->circuit->area->adjacency_list, adj); XFREE(MTYPE_ISIS_ADJACENCY, adj); return; } diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index d61fbbd751..3c3a211a52 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -113,8 +113,8 @@ struct isis_adjacency *isis_adj_lookup(const uint8_t *sysid, struct list *adjdb); struct isis_adjacency *isis_adj_lookup_snpa(const uint8_t *ssnpa, struct list *adjdb); -bool isis_adj_exists(const struct isis_area *area, int level, - const uint8_t *sysid); +struct isis_adjacency *isis_adj_find(const struct isis_area *area, int level, + const uint8_t *sysid); struct isis_adjacency *isis_new_adj(const uint8_t *id, const uint8_t *snpa, int level, struct isis_circuit *circuit); void isis_delete_adj(void *adj); diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c index 5729994baa..f81dd6cf51 100644 --- a/isisd/isis_bfd.c +++ b/isisd/isis_bfd.c @@ -195,6 +195,14 @@ static int isis_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) struct listnode *anode; struct isis_area *area; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfid(vrf_id); + + if (isis == NULL) { + zlog_warn(" %s : ISIS routing instance not found", __func__); + return -1; + } if (IS_DEBUG_BFD) zlog_debug("ISIS-BFD: Got neighbor replay request, resending neighbors."); diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 253ba22667..1214c01a12 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -224,10 +224,17 @@ struct isis_circuit *circuit_scan_by_ifp(struct interface *ifp) struct isis_area *area; struct listnode *node; struct isis_circuit *circuit; + struct isis *isis = NULL; if (ifp->info) return (struct isis_circuit *)ifp->info; + isis = isis_lookup_by_vrfid(ifp->vrf_id); + if (isis == NULL) { + zlog_warn(" %s : ISIS routing instance not found", __func__); + return NULL; + } + if (isis->area_list) { for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { circuit = @@ -618,7 +625,8 @@ int isis_circuit_up(struct isis_circuit *circuit) } if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - circuit->circuit_id = isis_circuit_id_gen(isis, circuit->interface); + circuit->circuit_id = isis_circuit_id_gen(circuit->area->isis, + circuit->interface); if (!circuit->circuit_id) { flog_err( EC_ISIS_CONFIG, @@ -802,7 +810,8 @@ void isis_circuit_down(struct isis_circuit *circuit) circuit->lsp_regenerate_pending[0] = 0; circuit->lsp_regenerate_pending[1] = 0; - _ISIS_CLEAR_FLAG(isis->circuit_ids_used, circuit->circuit_id); + _ISIS_CLEAR_FLAG(circuit->area->isis->circuit_ids_used, + circuit->circuit_id); circuit->circuit_id = 0; } else if (circuit->circ_type == CIRCUIT_T_P2P) { isis_delete_adj(circuit->u.p2p.neighbor); @@ -1011,6 +1020,14 @@ static int isis_interface_config_write(struct vty *vty) struct isis_area *area; struct isis_circuit *circuit; int i; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfid(vrf->vrf_id); + + if (isis == NULL) { + vty_out(vty, "ISIS routing instance not found"); + return 0; + } FOR_ALL_INTERFACES (vrf, ifp) { /* IF name */ @@ -1227,21 +1244,24 @@ static int isis_interface_config_write(struct vty *vty) #else static int isis_interface_config_write(struct vty *vty) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf = NULL; int write = 0; - struct interface *ifp; - struct lyd_node *dnode; - FOR_ALL_INTERFACES (vrf, ifp) { - dnode = yang_dnode_get( - running_config->dnode, - "/frr-interface:lib/interface[name='%s'][vrf='%s']", - ifp->name, vrf->name); - if (dnode == NULL) - continue; + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + struct interface *ifp; - write++; - nb_cli_show_dnode_cmds(vty, dnode, false); + FOR_ALL_INTERFACES (vrf, ifp) { + struct lyd_node *dnode; + dnode = yang_dnode_get( + running_config->dnode, + "/frr-interface:lib/interface[name='%s'][vrf='%s']", + ifp->name, vrf->name); + if (dnode == NULL) + continue; + + write++; + nb_cli_show_dnode_cmds(vty, dnode, false); + } } return write; } @@ -1251,6 +1271,7 @@ struct isis_circuit *isis_circuit_create(struct isis_area *area, struct interface *ifp) { struct isis_circuit *circuit = circuit_scan_by_ifp(ifp); + if (circuit && circuit->area) return NULL; circuit = isis_csm_state_change(ISIS_ENABLE, circuit, area); @@ -1429,7 +1450,7 @@ int isis_if_delete_hook(struct interface *ifp) /* Clean up the circuit data */ if (ifp && ifp->info) { circuit = ifp->info; - isis_csm_state_change(IF_DOWN_FROM_Z, circuit, circuit->area); + isis_csm_state_change(IF_DOWN_FROM_Z, circuit, ifp); } return 0; @@ -1437,10 +1458,15 @@ int isis_if_delete_hook(struct interface *ifp) static int isis_ifp_create(struct interface *ifp) { - if (if_is_operative(ifp)) + struct vrf *vrf = NULL; + + if (if_is_operative(ifp)) { + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (vrf) + isis_global_instance_create(vrf->name); isis_csm_state_change(IF_UP_FROM_Z, circuit_scan_by_ifp(ifp), ifp); - + } hook_call(isis_if_new_hook, ifp); return 0; diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 7c380fb0d9..da358f411b 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -50,16 +50,18 @@ struct metric { }; struct isis_bcast_info { - uint8_t snpa[ETH_ALEN]; /* SNPA of this circuit */ - char run_dr_elect[2]; /* Should we run dr election ? */ - struct thread *t_run_dr[2]; /* DR election thread */ - struct thread *t_send_lan_hello[2]; /* send LAN IIHs in this thread */ - struct list *adjdb[2]; /* adjacency dbs */ - struct list *lan_neighs[2]; /* list of lx neigh snpa */ - char is_dr[2]; /* Are we level x DR ? */ + uint8_t snpa[ETH_ALEN]; /* SNPA of this circuit */ + char run_dr_elect[ISIS_LEVELS]; /* Should we run dr election ? */ + struct thread *t_run_dr[ISIS_LEVELS]; /* DR election thread */ + struct thread *t_send_lan_hello[ISIS_LEVELS]; /* send LAN IIHs in this + thread */ + struct list *adjdb[ISIS_LEVELS]; /* adjacency dbs */ + struct list *lan_neighs[ISIS_LEVELS]; /* list of lx neigh snpa */ + char is_dr[ISIS_LEVELS]; /* Are we level x DR ? */ uint8_t l1_desig_is[ISIS_SYS_ID_LEN + 1]; /* level-1 DR */ uint8_t l2_desig_is[ISIS_SYS_ID_LEN + 1]; /* level-2 DR */ - struct thread *t_refresh_pseudo_lsp[2]; /* refresh pseudo-node LSPs */ + struct thread *t_refresh_pseudo_lsp[ISIS_LEVELS]; /* refresh pseudo-node + LSPs */ }; struct isis_p2p_info { @@ -86,10 +88,11 @@ struct isis_circuit { * Threads */ struct thread *t_read; - struct thread *t_send_csnp[2]; - struct thread *t_send_psnp[2]; + struct thread *t_send_csnp[ISIS_LEVELS]; + struct thread *t_send_psnp[ISIS_LEVELS]; struct isis_tx_queue *tx_queue; - struct isis_circuit_arg level_arg[2]; /* used as argument for threads */ + struct isis_circuit_arg + level_arg[ISIS_LEVELS]; /* used as argument for threads */ /* there is no real point in two streams, just for programming kicker */ int (*rx)(struct isis_circuit *circuit, uint8_t *ssnpa); @@ -107,7 +110,7 @@ struct isis_circuit { struct isis_bcast_info bc; struct isis_p2p_info p2p; } u; - uint8_t priority[2]; /* l1/2 IS configured priority */ + uint8_t priority[ISIS_LEVELS]; /* l1/2 IS configured priority */ int pad_hellos; /* add padding to Hello PDUs ? */ char ext_domain; /* externalDomain (boolean) */ int lsp_regenerate_pending[ISIS_LEVELS]; @@ -117,12 +120,12 @@ struct isis_circuit { struct isis_passwd passwd; /* Circuit rx/tx password */ int is_type; /* circuit is type == level of circuit * differentiated from circuit type (media) */ - uint32_t hello_interval[2]; /* hello-interval in seconds */ - uint16_t hello_multiplier[2]; /* hello-multiplier */ - uint16_t csnp_interval[2]; /* csnp-interval in seconds */ - uint16_t psnp_interval[2]; /* psnp-interval in seconds */ - uint8_t metric[2]; - uint32_t te_metric[2]; + uint32_t hello_interval[ISIS_LEVELS]; /* hello-interval in seconds */ + uint16_t hello_multiplier[ISIS_LEVELS]; /* hello-multiplier */ + uint16_t csnp_interval[ISIS_LEVELS]; /* csnp-interval in seconds */ + uint16_t psnp_interval[ISIS_LEVELS]; /* psnp-interval in seconds */ + uint8_t metric[ISIS_LEVELS]; + uint32_t te_metric[ISIS_LEVELS]; struct isis_ext_subtlvs *ext; /* Extended parameters (TE + Adj SID */ int ip_router; /* Route IP ? */ int is_passive; /* Is Passive ? */ @@ -131,7 +134,7 @@ struct isis_circuit { int ipv6_router; /* Route IPv6 ? */ struct list *ipv6_link; /* our link local IPv6 addresses */ struct list *ipv6_non_link; /* our non-link local IPv6 addresses */ - uint16_t upadjcount[2]; + uint16_t upadjcount[ISIS_LEVELS]; #define ISIS_CIRCUIT_FLAPPED_AFTER_SPF 0x01 uint8_t flags; bool disable_threeway_adj; @@ -143,8 +146,8 @@ struct isis_circuit { uint32_t init_failures; /* intialisationFailures */ uint32_t ctrl_pdus_rxed; /* controlPDUsReceived */ uint32_t ctrl_pdus_txed; /* controlPDUsSent */ - uint32_t - desig_changes[2]; /* lanLxDesignatedIntermediateSystemChanges */ + uint32_t desig_changes[ISIS_LEVELS]; /* lanLxDesignatedIntermediateSystemChanges + */ uint32_t rej_adjacencies; /* rejectedAdjacencies */ /* * Counters as in ietf-isis@2019-09-09.yang diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index cd75116c50..31fe41db82 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -46,23 +46,33 @@ /* * XPath: /frr-isisd:isis/instance */ -DEFPY_YANG_NOSH(router_isis, router_isis_cmd, "router isis WORD$tag", - ROUTER_STR - "ISO IS-IS\n" - "ISO Routing area tag\n") +DEFPY_YANG_NOSH(router_isis, router_isis_cmd, + "router isis WORD$tag [vrf NAME$vrf_name]", + ROUTER_STR + "ISO IS-IS\n" + "ISO Routing area tag\n" VRF_CMD_HELP_STR) { int ret; char base_xpath[XPATH_MAXLEN]; + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + snprintf(base_xpath, XPATH_MAXLEN, - "/frr-isisd:isis/instance[area-tag='%s']", tag); + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']", tag, + vrf_name); nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); /* default value in yang for is-type is level-1, but in FRR * the first instance is assigned is-type level-1-2. We * need to make sure to set it in the yang model so that it * is consistent with what FRR sees. */ - if (listcount(isis->area_list) == 0) + + if (!im) { + return CMD_SUCCESS; + } + + if (listcount(im->isis) == 0) nb_cli_enqueue_change(vty, "./is-type", NB_OP_MODIFY, "level-1-2"); ret = nb_cli_apply_changes(vty, base_xpath); @@ -72,25 +82,30 @@ DEFPY_YANG_NOSH(router_isis, router_isis_cmd, "router isis WORD$tag", return ret; } -DEFPY_YANG(no_router_isis, no_router_isis_cmd, "no router isis WORD$tag", - NO_STR ROUTER_STR - "ISO IS-IS\n" - "ISO Routing area tag\n") +DEFPY_YANG(no_router_isis, no_router_isis_cmd, + "no router isis WORD$tag [vrf NAME$vrf_name]", + NO_STR ROUTER_STR + "ISO IS-IS\n" + "ISO Routing area tag\n" VRF_CMD_HELP_STR) { char temp_xpath[XPATH_MAXLEN]; struct listnode *node, *nnode; struct isis_circuit *circuit = NULL; struct isis_area *area = NULL; - if (!yang_dnode_exists(vty->candidate_config->dnode, - "/frr-isisd:isis/instance[area-tag='%s']", - tag)) { + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + + if (!yang_dnode_exists( + vty->candidate_config->dnode, + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']", tag, + vrf_name)) { vty_out(vty, "ISIS area %s not found.\n", tag); return CMD_ERR_NOTHING_TODO; } nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); - area = isis_area_lookup(tag); + area = isis_area_lookup_by_vrf(tag, vrf_name); if (area && area->circuit_list && listcount(area->circuit_list)) { for (ALL_LIST_ELEMENTS(area->circuit_list, node, nnode, circuit)) { @@ -109,15 +124,23 @@ DEFPY_YANG(no_router_isis, no_router_isis_cmd, "no router isis WORD$tag", } return nb_cli_apply_changes( - vty, "/frr-isisd:isis/instance[area-tag='%s']", tag); + vty, "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']", tag, + vrf_name); } void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { + const char *vrf = NULL; + + vrf = yang_dnode_get_string(dnode, "./vrf"); + vty_out(vty, "!\n"); - vty_out(vty, "router isis %s\n", + vty_out(vty, "router isis %s ", yang_dnode_get_string(dnode, "./area-tag")); + if (!strmatch(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, "vrf %s", yang_dnode_get_string(dnode, "./vrf")); + vty_out(vty, "\n"); } /* @@ -126,41 +149,62 @@ void cli_show_router_isis(struct vty *vty, struct lyd_node *dnode, * XPath: /frr-interface:lib/interface/frr-isisd:isis/ipv6-routing * XPath: /frr-isisd:isis/instance */ -DEFPY_YANG(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - "IS-IS routing protocol\n" - "Routing process tag\n") +DEFPY_YANG(ip_router_isis, ip_router_isis_cmd, + "ip router isis WORD$tag [vrf NAME$vrf_name]", + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + "IS-IS routing protocol\n" + "Routing process tag\n" VRF_CMD_HELP_STR) { char temp_xpath[XPATH_MAXLEN]; const char *circ_type; - struct isis_area *area; + struct isis_area *area = NULL; struct interface *ifp; + struct vrf *vrf; /* area will be created if it is not present. make sure the yang model * is synced with FRR and call the appropriate NB cb. */ - area = isis_area_lookup(tag); + + if (!im) { + return CMD_SUCCESS; + } + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + + if (!vrf_name && ifp->vrf_id == VRF_DEFAULT) + vrf_name = VRF_DEFAULT_NAME; + + if (ifp->vrf_id != VRF_DEFAULT) { + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (vrf && !vrf_name) + vrf_name = vrf->name; + } + area = isis_area_lookup_by_vrf(tag, vrf_name); if (!area) { + isis_global_instance_create(vrf_name); snprintf(temp_xpath, XPATH_MAXLEN, - "/frr-isisd:isis/instance[area-tag='%s']", tag); + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']", + tag, vrf_name); nb_cli_enqueue_change(vty, temp_xpath, NB_OP_CREATE, tag); - snprintf(temp_xpath, XPATH_MAXLEN, - "/frr-isisd:isis/instance[area-tag='%s']/is-type", - tag); - nb_cli_enqueue_change( - vty, temp_xpath, NB_OP_MODIFY, - listcount(isis->area_list) == 0 ? "level-1-2" : NULL); + snprintf( + temp_xpath, XPATH_MAXLEN, + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']/is-type", + tag, vrf_name); + nb_cli_enqueue_change(vty, temp_xpath, NB_OP_MODIFY, + listcount(im->isis) == 0 ? "level-1-2" + : NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis", NB_OP_CREATE, NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis/area-tag", NB_OP_MODIFY, tag); + + nb_cli_enqueue_change(vty, "./frr-isisd:isis/vrf", NB_OP_MODIFY, + vrf_name); nb_cli_enqueue_change(vty, "./frr-isisd:isis/ipv4-routing", NB_OP_MODIFY, "true"); nb_cli_enqueue_change( vty, "./frr-isisd:isis/circuit-type", NB_OP_MODIFY, - listcount(isis->area_list) == 0 ? "level-1-2" - : "level-1"); + listcount(im->isis) == 0 ? "level-1-2" : "level-1"); } else { /* area exists, circuit type defaults to its area's is_type */ switch (area->is_type) { @@ -181,6 +225,9 @@ DEFPY_YANG(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis/area-tag", NB_OP_MODIFY, tag); + nb_cli_enqueue_change(vty, "./frr-isisd:isis/vrf", NB_OP_MODIFY, + vrf_name); + nb_cli_enqueue_change(vty, "./frr-isisd:isis/ipv4-routing", NB_OP_MODIFY, "true"); nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type", @@ -188,7 +235,6 @@ DEFPY_YANG(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", } /* check if the interface is a loopback and if so set it as passive */ - ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); if (ifp && if_is_loopback(ifp)) nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", NB_OP_MODIFY, "true"); @@ -196,41 +242,61 @@ DEFPY_YANG(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag", return nb_cli_apply_changes(vty, NULL); } -DEFPY_YANG(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - "IS-IS routing protocol\n" - "Routing process tag\n") +DEFPY_YANG(ip6_router_isis, ip6_router_isis_cmd, + "ipv6 router isis WORD$tag [vrf NAME$vrf_name]", + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + "IS-IS routing protocol\n" + "Routing process tag\n" VRF_CMD_HELP_STR) { char temp_xpath[XPATH_MAXLEN]; const char *circ_type; - struct isis_area *area; struct interface *ifp; + struct isis_area *area; + struct vrf *vrf; /* area will be created if it is not present. make sure the yang model * is synced with FRR and call the appropriate NB cb. */ - area = isis_area_lookup(tag); + + if (!im) + return CMD_SUCCESS; + + ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); + if (!vrf_name && ifp->vrf_id == VRF_DEFAULT) + vrf_name = VRF_DEFAULT_NAME; + + if (ifp->vrf_id != VRF_DEFAULT) { + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (vrf && !vrf_name) + vrf_name = vrf->name; + } + area = isis_area_lookup_by_vrf(tag, vrf_name); if (!area) { + isis_global_instance_create(vrf_name); snprintf(temp_xpath, XPATH_MAXLEN, - "/frr-isisd:isis/instance[area-tag='%s']", tag); + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']", + tag, vrf_name); nb_cli_enqueue_change(vty, temp_xpath, NB_OP_CREATE, tag); - snprintf(temp_xpath, XPATH_MAXLEN, - "/frr-isisd:isis/instance[area-tag='%s']/is-type", - tag); - nb_cli_enqueue_change( - vty, temp_xpath, NB_OP_MODIFY, - listcount(isis->area_list) == 0 ? "level-1-2" : NULL); + snprintf( + temp_xpath, XPATH_MAXLEN, + "/frr-isisd:isis/instance[area-tag='%s'][vrf='%s']/is-type", + tag, vrf_name); + nb_cli_enqueue_change(vty, temp_xpath, NB_OP_MODIFY, + listcount(im->isis) == 0 ? "level-1-2" + : NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis", NB_OP_CREATE, NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis/area-tag", NB_OP_MODIFY, tag); + nb_cli_enqueue_change(vty, "./frr-isisd:isis/vrf", NB_OP_MODIFY, + vrf_name); + nb_cli_enqueue_change(vty, "./frr-isisd:isis/ipv6-routing", NB_OP_MODIFY, "true"); nb_cli_enqueue_change( vty, "./frr-isisd:isis/circuit-type", NB_OP_MODIFY, - listcount(isis->area_list) == 0 ? "level-1-2" - : "level-1"); + listcount(im->isis) == 0 ? "level-1-2" : "level-1"); } else { /* area exists, circuit type defaults to its area's is_type */ switch (area->is_type) { @@ -251,6 +317,8 @@ DEFPY_YANG(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", NULL); nb_cli_enqueue_change(vty, "./frr-isisd:isis/area-tag", NB_OP_MODIFY, tag); + nb_cli_enqueue_change(vty, "./frr-isisd:isis/vrf", NB_OP_MODIFY, + vrf_name); nb_cli_enqueue_change(vty, "./frr-isisd:isis/ipv6-routing", NB_OP_MODIFY, "true"); nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type", @@ -258,7 +326,6 @@ DEFPY_YANG(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", } /* check if the interface is a loopback and if so set it as passive */ - ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false); if (ifp && if_is_loopback(ifp)) nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive", NB_OP_MODIFY, "true"); @@ -267,13 +334,13 @@ DEFPY_YANG(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag", } DEFPY_YANG(no_ip_router_isis, no_ip_router_isis_cmd, - "no <ip|ipv6>$ip router isis [WORD]$tag", - NO_STR - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - "IP router interface commands\n" - "IS-IS routing protocol\n" - "Routing process tag\n") + "no <ip|ipv6>$ip router isis [WORD]$tag [vrf NAME$vrf_name]", + NO_STR + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + "IP router interface commands\n" + "IS-IS routing protocol\n" + "Routing process tag\n") { const struct lyd_node *dnode; @@ -309,19 +376,33 @@ DEFPY_YANG(no_ip_router_isis, no_ip_router_isis_cmd, void cli_show_ip_isis_ipv4(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "../vrf"); + if (!yang_dnode_get_bool(dnode, NULL)) vty_out(vty, " no"); - vty_out(vty, " ip router isis %s\n", + 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); + vty_out(vty, "\n"); } void cli_show_ip_isis_ipv6(struct vty *vty, struct lyd_node *dnode, bool show_defaults) { + const char *vrf; + + vrf = yang_dnode_get_string(dnode, "../vrf"); + if (!yang_dnode_get_bool(dnode, NULL)) vty_out(vty, " no"); - vty_out(vty, " ipv6 router isis %s\n", + vty_out(vty, " ipv6 router isis %s ", yang_dnode_get_string(dnode, "../area-tag")); + if (!strmatch(vrf, VRF_DEFAULT_NAME)) + vty_out(vty, "vrf %s", vrf); + vty_out(vty, "\n"); } /* @@ -1154,7 +1235,7 @@ DEFPY_YANG(isis_default_originate, isis_default_originate_cmd, "Distribute default route into level-2\n" "Always advertise default route\n" "Metric for default route\n" - "ISIS default metric\n" + "IS-IS default metric\n" "Route map reference\n" "Pointer to route-map entries\n") { @@ -1228,7 +1309,7 @@ DEFPY_YANG(isis_redistribute, isis_redistribute_cmd, "Redistribute into level-1\n" "Redistribute into level-2\n" "Metric for redistributed routes\n" - "ISIS default metric\n" + "IS-IS default metric\n" "Route map reference\n" "Pointer to route-map entries\n") { diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c index 7aae326a4f..929b4c26e8 100644 --- a/isisd/isis_csm.c +++ b/isisd/isis_csm.c @@ -48,8 +48,6 @@ #include "isisd/isis_events.h" #include "isisd/isis_errors.h" -extern struct isis *isis; - static const char *const csm_statestr[] = {"C_STATE_NA", "C_STATE_INIT", "C_STATE_CONF", "C_STATE_UP"}; @@ -66,6 +64,7 @@ struct isis_circuit * isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) { int old_state; + struct isis *isis = NULL; old_state = circuit ? circuit->state : C_STATE_NA; if (IS_DEBUG_EVENTS) @@ -86,6 +85,13 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) case IF_UP_FROM_Z: circuit = isis_circuit_new(); isis_circuit_if_add(circuit, (struct interface *)arg); + isis = isis_lookup_by_vrfid(circuit->interface->vrf_id); + if (isis == NULL) { + zlog_warn( + " %s : ISIS routing instance not found", + __func__); + break; + } listnode_add(isis->init_circ_list, circuit); circuit->state = C_STATE_INIT; break; @@ -111,7 +117,8 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) circuit->state = C_STATE_UP; isis_event_circuit_state_change(circuit, circuit->area, 1); - listnode_delete(isis->init_circ_list, circuit); + listnode_delete(circuit->area->isis->init_circ_list, + circuit); break; case IF_UP_FROM_Z: assert(circuit); @@ -122,6 +129,14 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) break; case IF_DOWN_FROM_Z: isis_circuit_if_del(circuit, (struct interface *)arg); + isis = isis_lookup_by_vrfid(circuit->interface->vrf_id); + if (isis == NULL) { + zlog_warn( + "%s : ISIS routing instance not found", + __func__); + break; + } + listnode_delete(isis->init_circ_list, circuit); isis_circuit_del(circuit); circuit = NULL; @@ -174,6 +189,15 @@ isis_csm_state_change(int event, struct isis_circuit *circuit, void *arg) circuit->state = C_STATE_INIT; isis_event_circuit_state_change( circuit, (struct isis_area *)arg, 0); + + isis = isis_lookup_by_vrfid(circuit->interface->vrf_id); + if (isis == NULL) { + zlog_warn( + "%s : ISIS routing instance not found", + __func__); + break; + } + listnode_add(isis->init_circ_list, circuit); break; case IF_DOWN_FROM_Z: diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c index 8df1304866..318fb9fab8 100644 --- a/isisd/isis_dr.c +++ b/isisd/isis_dr.c @@ -225,7 +225,7 @@ int isis_dr_resign(struct isis_circuit *circuit, int level) THREAD_TIMER_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); circuit->lsp_regenerate_pending[level - 1] = 0; - memcpy(id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(id) = circuit->circuit_id; LSP_FRAGMENT(id) = 0; lsp_purge_pseudo(id, circuit, level); @@ -278,7 +278,8 @@ int isis_dr_commence(struct isis_circuit *circuit, int level) /* there was a dr elected, purge its LSPs from the db */ lsp_purge_pseudo(old_dr, circuit, level); } - memcpy(circuit->u.bc.l1_desig_is, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(circuit->u.bc.l1_desig_is, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id; @@ -299,7 +300,8 @@ int isis_dr_commence(struct isis_circuit *circuit, int level) /* there was a dr elected, purge its LSPs from the db */ lsp_purge_pseudo(old_dr, circuit, level); } - memcpy(circuit->u.bc.l2_desig_is, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(circuit->u.bc.l2_desig_is, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id; diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index 921e23d33a..244f388c26 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -45,33 +45,48 @@ extern struct host host; struct list *dyn_cache = NULL; static int dyn_cache_cleanup(struct thread *); -void dyn_cache_init(void) +void dyn_cache_init(struct isis *isis) { if (dyn_cache == NULL) dyn_cache = list_new(); - thread_add_timer(master, dyn_cache_cleanup, NULL, 120, - &isis->t_dync_clean); + if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) + thread_add_timer(master, dyn_cache_cleanup, isis, 120, + &isis->t_dync_clean); return; } +void dyn_cache_cleanup_all(void) +{ + struct listnode *node, *nnode; + struct isis_dynhn *dyn; + + for (ALL_LIST_ELEMENTS(dyn_cache, node, nnode, dyn)) { + list_delete_node(dyn_cache, node); + XFREE(MTYPE_ISIS_DYNHN, dyn); + } +} + static int dyn_cache_cleanup(struct thread *thread) { struct listnode *node, *nnode; struct isis_dynhn *dyn; time_t now = time(NULL); + struct isis *isis = NULL; + + isis = THREAD_ARG(thread); isis->t_dync_clean = NULL; for (ALL_LIST_ELEMENTS(dyn_cache, node, nnode, dyn)) { if ((now - dyn->refresh) < MAX_LSP_LIFETIME) continue; - list_delete_node(dyn_cache, node); XFREE(MTYPE_ISIS_DYNHN, dyn); } - thread_add_timer(master, dyn_cache_cleanup, NULL, 120, - &isis->t_dync_clean); + thread_add_timer(master, dyn_cache_cleanup, isis, 120, + &isis->t_dync_clean); + return ISIS_OK; } @@ -132,11 +147,14 @@ void isis_dynhn_remove(const uint8_t *id) * 2 0000.0000.0002 bar-gw * * 0000.0000.0004 this-gw */ -void dynhn_print_all(struct vty *vty) +void dynhn_print_all(struct vty *vty, struct isis *isis) { struct listnode *node; struct isis_dynhn *dyn; + vty_out(vty, "vrf : %s\n", isis->name); + if (!isis->sysid_set) + return; vty_out(vty, "Level System ID Dynamic Hostname\n"); for (ALL_LIST_ELEMENTS_RO(dyn_cache, node, dyn)) { vty_out(vty, "%-7d", dyn->level); diff --git a/isisd/isis_dynhn.h b/isisd/isis_dynhn.h index 27133bd3fd..973fde8307 100644 --- a/isisd/isis_dynhn.h +++ b/isisd/isis_dynhn.h @@ -30,11 +30,12 @@ struct isis_dynhn { int level; }; -void dyn_cache_init(void); +void dyn_cache_init(struct isis *isis); +void dyn_cache_cleanup_all(void); void isis_dynhn_insert(const uint8_t *id, const char *hostname, int level); void isis_dynhn_remove(const uint8_t *id); struct isis_dynhn *dynhn_find_by_id(const uint8_t *id); struct isis_dynhn *dynhn_find_by_name(const char *hostname); -void dynhn_print_all(struct vty *vty); +void dynhn_print_all(struct vty *vty, struct isis *isis); #endif /* _ZEBRA_ISIS_DYNHN_H */ diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index bf15163cbf..1af6f417dc 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -338,13 +338,17 @@ void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno) static void lsp_purge_add_poi(struct isis_lsp *lsp, const uint8_t *sender) { + if (lsp->area == NULL) + return; + if (!lsp->area->purge_originator) return; /* add purge originator identification */ if (!lsp->tlvs) lsp->tlvs = isis_alloc_tlvs(); - isis_tlvs_set_purge_originator(lsp->tlvs, isis->sysid, sender); + isis_tlvs_set_purge_originator(lsp->tlvs, lsp->area->isis->sysid, + sender); isis_tlvs_set_dynamic_hostname(lsp->tlvs, cmd_hostname_get()); } @@ -591,7 +595,8 @@ static void lsp_set_time(struct isis_lsp *lsp) stream_putw_at(lsp->pdu, 10, lsp->hdr.rem_lifetime); } -void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag) +void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag, + struct isis *isis) { struct isis_dynhn *dyn = NULL; char id[SYSID_STRLEN]; @@ -607,6 +612,7 @@ void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag) snprintf(id, sizeof(id), "%.14s", cmd_hostname_get()); else memcpy(id, sysid_print(lsp_id), 15); + if (frag) sprintf(dest, "%s.%02x-%02x", id, LSP_PSEUDO_ID(lsp_id), LSP_FRAGMENT(lsp_id)); @@ -638,13 +644,14 @@ static const char *lsp_bits2string(uint8_t lsp_bits, char *buf, size_t buf_size) } /* this function prints the lsp on show isis database */ -void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost) +void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, + struct isis *isis) { char LSPid[255]; char age_out[8]; char b[200]; - lspid_print(lsp->hdr.lsp_id, LSPid, dynhost, 1); + lspid_print(lsp->hdr.lsp_id, LSPid, dynhost, 1, isis); vty_out(vty, "%-21s%c ", LSPid, lsp->own_lsp ? '*' : ' '); vty_out(vty, "%5hu ", lsp->hdr.pdu_len); vty_out(vty, "0x%08x ", lsp->hdr.seqno); @@ -658,9 +665,10 @@ void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost) vty_out(vty, "%s\n", lsp_bits2string(lsp->hdr.lsp_bits, b, sizeof(b))); } -void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost) +void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost, + struct isis *isis) { - lsp_print(lsp, vty, dynhost); + lsp_print(lsp, vty, dynhost, isis); if (lsp->tlvs) vty_multiline(vty, " ", "%s", isis_format_tlvs(lsp->tlvs)); vty_out(vty, "\n"); @@ -668,19 +676,19 @@ void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost) /* print all the lsps info in the local lspdb */ int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, - char dynhost) + char dynhost, struct isis *isis) { struct isis_lsp *lsp; int lsp_count = 0; if (detail == ISIS_UI_LEVEL_BRIEF) { frr_each (lspdb, head, lsp) { - lsp_print(lsp, vty, dynhost); + lsp_print(lsp, vty, dynhost, isis); lsp_count++; } } else if (detail == ISIS_UI_LEVEL_DETAIL) { frr_each (lspdb, head, lsp) { - lsp_print_detail(lsp, vty, dynhost); + lsp_print_detail(lsp, vty, dynhost, isis); lsp_count++; } } @@ -913,10 +921,10 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) } /* Add Router Capability TLV. */ - if (isis->router_id != 0) { + if (area->isis->router_id != 0) { struct isis_router_cap cap = {}; - cap.router_id.s_addr = isis->router_id; + cap.router_id.s_addr = area->isis->router_id; /* Add SR Sub-TLVs if SR is enabled. */ if (area->srdb.enabled) { @@ -954,8 +962,8 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) * into LSP. TE router ID will be the same if MPLS-TE * is not activate or MPLS-TE router-id not specified */ - if (isis->router_id != 0) { - struct in_addr id = {.s_addr = isis->router_id}; + if (area->isis->router_id != 0) { + struct in_addr id = {.s_addr = area->isis->router_id}; inet_ntop(AF_INET, &id, buf, sizeof(buf)); lsp_debug("ISIS (%s): Adding router ID %s as IPv4 tlv.", area->area_tag, buf); @@ -1210,7 +1218,8 @@ int lsp_generate(struct isis_area *area, int level) return ISIS_ERROR; memset(&lspid, 0, ISIS_SYS_ID_LEN + 2); - memcpy(&lspid, isis->sysid, ISIS_SYS_ID_LEN); + + memcpy(&lspid, area->isis->sysid, ISIS_SYS_ID_LEN); /* only builds the lsp if the area shares the level */ oldlsp = lsp_search(&area->lspdb[level - 1], lspid); @@ -1281,9 +1290,8 @@ static int lsp_regenerate(struct isis_area *area, int level) return ISIS_ERROR; head = &area->lspdb[level - 1]; - memset(lspid, 0, ISIS_SYS_ID_LEN + 2); - memcpy(lspid, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(lspid, area->isis->sysid, ISIS_SYS_ID_LEN); lsp = lsp_search(head, lspid); @@ -1404,7 +1412,7 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, all_pseudo ? "" : "not ", func, file, line); - memcpy(id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(id, area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(id) = LSP_FRAGMENT(id) = 0; now = time(NULL); @@ -1525,7 +1533,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, */ uint8_t ne_id[ISIS_SYS_ID_LEN + 1]; - memcpy(ne_id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(ne_id, area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(ne_id) = 0; if (circuit->area->oldmetric) { @@ -1537,7 +1545,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, } if (circuit->area->newmetric) { isis_tlvs_add_extended_reach(lsp->tlvs, ISIS_MT_IPV4_UNICAST, - ne_id, 0, circuit->ext); + ne_id, 0, NULL); lsp_debug( "ISIS (%s): Adding %s.%02x as te-style neighbor (self)", area->area_tag, sysid_print(ne_id), @@ -1579,7 +1587,7 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, if (circuit->area->newmetric) { isis_tlvs_add_extended_reach(lsp->tlvs, ISIS_MT_IPV4_UNICAST, - ne_id, 0, circuit->ext); + ne_id, 0, NULL); lsp_debug( "ISIS (%s): Adding %s.%02x as te-style neighbor (peer)", area->area_tag, sysid_print(ne_id), @@ -1603,7 +1611,7 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) || (circuit->u.bc.is_dr[level - 1] == 0)) return ISIS_ERROR; - memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(lsp_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN); LSP_FRAGMENT(lsp_id) = 0; LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id; @@ -1663,7 +1671,7 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) || (circuit->u.bc.is_dr[level - 1] == 0)) return ISIS_ERROR; - memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(lsp_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id; LSP_FRAGMENT(lsp_id) = 0; @@ -1720,7 +1728,7 @@ static int lsp_l1_refresh_pseudo(struct thread *thread) if ((circuit->u.bc.is_dr[0] == 0) || (circuit->is_type & IS_LEVEL_1) == 0) { - memcpy(id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(id) = circuit->circuit_id; LSP_FRAGMENT(id) = 0; lsp_purge_pseudo(id, circuit, IS_LEVEL_1); @@ -1742,7 +1750,7 @@ static int lsp_l2_refresh_pseudo(struct thread *thread) if ((circuit->u.bc.is_dr[1] == 0) || (circuit->is_type & IS_LEVEL_2) == 0) { - memcpy(id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(id) = circuit->circuit_id; LSP_FRAGMENT(id) = 0; lsp_purge_pseudo(id, circuit, IS_LEVEL_2); @@ -1770,7 +1778,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) area->area_tag, circuit_t2string(level), circuit->interface->name); - memcpy(lsp_id, isis->sysid, ISIS_SYS_ID_LEN); + memcpy(lsp_id, area->isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(lsp_id) = circuit->circuit_id; LSP_FRAGMENT(lsp_id) = 0; now = time(NULL); @@ -2038,6 +2046,160 @@ static int lsp_handle_adj_state_change(struct isis_adjacency *adj) return 0; } +/* + * Iterate over all IP reachability TLVs in a LSP (all fragments) of the given + * address-family and MT-ID. + */ +int isis_lsp_iterate_ip_reach(struct isis_lsp *lsp, int family, uint16_t mtid, + lsp_ip_reach_iter_cb cb, void *arg) +{ + bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id); + struct isis_lsp *frag; + struct listnode *node; + + if (lsp->hdr.seqno == 0 || lsp->hdr.rem_lifetime == 0) + return LSP_ITER_CONTINUE; + + /* Parse main LSP. */ + if (lsp->tlvs) { + if (!fabricd && !pseudo_lsp && family == AF_INET + && mtid == ISIS_MT_IPV4_UNICAST) { + struct isis_item_list *reachs[] = { + &lsp->tlvs->oldstyle_ip_reach, + &lsp->tlvs->oldstyle_ip_reach_ext}; + + for (unsigned int i = 0; i < array_size(reachs); i++) { + struct isis_oldstyle_ip_reach *r; + + for (r = (struct isis_oldstyle_ip_reach *) + reachs[i] + ->head; + r; r = r->next) { + bool external = i ? true : false; + + if ((*cb)((struct prefix *)&r->prefix, + r->metric, external, NULL, + arg) + == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + } + + if (!pseudo_lsp && family == AF_INET) { + struct isis_item_list *ipv4_reachs; + + if (mtid == ISIS_MT_IPV4_UNICAST) + ipv4_reachs = &lsp->tlvs->extended_ip_reach; + else + ipv4_reachs = isis_lookup_mt_items( + &lsp->tlvs->mt_ip_reach, mtid); + + struct isis_extended_ip_reach *r; + for (r = ipv4_reachs ? (struct isis_extended_ip_reach *) + ipv4_reachs->head + : NULL; + r; r = r->next) { + if ((*cb)((struct prefix *)&r->prefix, + r->metric, false, r->subtlvs, arg) + == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + + if (!pseudo_lsp && family == AF_INET6) { + struct isis_item_list *ipv6_reachs; + struct isis_ipv6_reach *r; + + if (mtid == ISIS_MT_IPV4_UNICAST) + ipv6_reachs = &lsp->tlvs->ipv6_reach; + else + ipv6_reachs = isis_lookup_mt_items( + &lsp->tlvs->mt_ipv6_reach, mtid); + + for (r = ipv6_reachs ? (struct isis_ipv6_reach *) + ipv6_reachs->head + : NULL; + r; r = r->next) { + if ((*cb)((struct prefix *)&r->prefix, + r->metric, r->external, r->subtlvs, + arg) + == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + } + + /* Parse LSP fragments. */ + for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) { + if (!frag->tlvs) + continue; + + isis_lsp_iterate_ip_reach(frag, family, mtid, cb, arg); + } + + return LSP_ITER_CONTINUE; +} + +/* + * Iterate over all IS reachability TLVs in a LSP (all fragments) of the given + * MT-ID. + */ +int isis_lsp_iterate_is_reach(struct isis_lsp *lsp, uint16_t mtid, + lsp_is_reach_iter_cb cb, void *arg) +{ + bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id); + struct isis_lsp *frag; + struct listnode *node; + struct isis_item *head; + struct isis_item_list *te_neighs; + + if (lsp->hdr.seqno == 0 || lsp->hdr.rem_lifetime == 0) + return LSP_ITER_CONTINUE; + + /* Parse main LSP. */ + if (lsp->tlvs) { + if (pseudo_lsp || mtid == ISIS_MT_IPV4_UNICAST) { + head = lsp->tlvs->oldstyle_reach.head; + for (struct isis_oldstyle_reach *reach = + (struct isis_oldstyle_reach *)head; + reach; reach = reach->next) { + if ((*cb)(reach->id, reach->metric, true, NULL, + arg) + == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + + if (pseudo_lsp || mtid == ISIS_MT_IPV4_UNICAST) + te_neighs = &lsp->tlvs->extended_reach; + else + te_neighs = + isis_get_mt_items(&lsp->tlvs->mt_reach, mtid); + if (te_neighs) { + head = te_neighs->head; + for (struct isis_extended_reach *reach = + (struct isis_extended_reach *)head; + reach; reach = reach->next) { + if ((*cb)(reach->id, reach->metric, false, + reach->subtlvs, arg) + == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + } + + /* Parse LSP fragments. */ + for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) { + if (!frag->tlvs) + continue; + + isis_lsp_iterate_is_reach(frag, mtid, cb, arg); + } + + return LSP_ITER_CONTINUE; +} + void lsp_init(void) { hook_register(isis_adj_state_change_hook, diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index 4cbca5d517..0783036e49 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -29,6 +29,7 @@ PREDECL_RBTREE_UNIQ(lspdb) +struct isis; /* Structure for isis_lsp, this structure will only support the fixed * System ID (Currently 6) (atleast for now). In order to support more * We will have to split the header into two parts, and for readability @@ -115,14 +116,37 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, struct isis_tlvs *tlvs, struct stream *stream, struct isis_area *area, int level, bool confusion); void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno); -void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag); -void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost); -void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost); +void lspid_print(uint8_t *lsp_id, char *dest, char dynhost, char frag, + struct isis *isis); +void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, + struct isis *isis); +void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost, + struct isis *isis); int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, - char dynhost); + char dynhost, struct isis *isis); /* sets SRMflags for all active circuits of an lsp */ void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set); +#define LSP_ITER_CONTINUE 0 +#define LSP_ITER_STOP -1 + +/* Callback used by isis_lsp_iterate_ip_reach() function. */ +struct isis_subtlvs; +typedef int (*lsp_ip_reach_iter_cb)(const struct prefix *prefix, + uint32_t metric, bool external, + struct isis_subtlvs *subtlvs, void *arg); + +/* Callback used by isis_lsp_iterate_is_reach() function. */ +typedef int (*lsp_is_reach_iter_cb)(const uint8_t *id, uint32_t metric, + bool oldmetric, + struct isis_ext_subtlvs *subtlvs, + void *arg); + +int isis_lsp_iterate_ip_reach(struct isis_lsp *lsp, int family, uint16_t mtid, + lsp_ip_reach_iter_cb cb, void *arg); +int isis_lsp_iterate_is_reach(struct isis_lsp *lsp, uint16_t mtid, + lsp_is_reach_iter_cb cb, void *arg); + #define lsp_flood(lsp, circuit) \ _lsp_flood((lsp), (circuit), __func__, __FILE__, __LINE__) void _lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit, diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 2317b166c6..26f5227aae 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -101,6 +101,7 @@ void sigusr1(void); static __attribute__((__noreturn__)) void terminate(int i) { + isis_terminate(); isis_sr_term(); isis_zebra_stop(); exit(i); @@ -233,14 +234,14 @@ int main(int argc, char **argv, char **envp) } /* thread master */ - master = frr_init(); - + isis_master_init(frr_init()); + master = im->master; /* * initializations */ isis_error_init(); access_list_init(); - vrf_init(NULL, NULL, NULL, NULL, NULL); + isis_vrf_init(); prefix_list_init(); isis_init(); isis_circuit_init(); @@ -250,7 +251,7 @@ int main(int argc, char **argv, char **envp) #ifndef FABRICD isis_cli_init(); #endif /* ifdef FABRICD */ - isis_spf_cmds_init(); + isis_spf_init(); isis_redist_init(); isis_route_map_init(); isis_mpls_te_init(); @@ -259,7 +260,7 @@ int main(int argc, char **argv, char **envp) mt_init(); /* create the global 'isis' instance */ - isis_new(1, VRF_DEFAULT); + isis_global_instance_create(VRF_DEFAULT_NAME); isis_zebra_init(master, instance); isis_bfd_init(); diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index 86725173c4..6e9cbaf98e 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -440,12 +440,14 @@ struct in_addr newprefix2inaddr(uint8_t *prefix_start, uint8_t prefix_masklen) const char *print_sys_hostname(const uint8_t *sysid) { struct isis_dynhn *dyn; + struct isis *isis = NULL; if (!sysid) return "nullsysid"; /* For our system ID return our host name */ - if (memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0) + isis = isis_lookup_by_sysid(sysid); + if (isis && !CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) return cmd_hostname_get(); dyn = dynhn_find_by_id(sysid); diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c index 2b8b02e3f1..33b0b4d02c 100644 --- a/isisd/isis_nb.c +++ b/isisd/isis_nb.c @@ -551,6 +551,13 @@ const struct frr_yang_module_info frr_isisd_info = { }, }, { + .xpath = "/frr-interface:lib/interface/frr-isisd:isis/vrf", + .cbs = { + .modify = lib_interface_isis_vrf_modify, + }, + }, + + { .xpath = "/frr-interface:lib/interface/frr-isisd:isis/circuit-type", .cbs = { .cli_show = cli_show_ip_isis_circ_type, diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h index a9401bc86a..a79cb8ff57 100644 --- a/isisd/isis_nb.h +++ b/isisd/isis_nb.h @@ -168,6 +168,7 @@ int isis_instance_mpls_te_router_address_destroy( int lib_interface_isis_create(struct nb_cb_create_args *args); int lib_interface_isis_destroy(struct nb_cb_destroy_args *args); int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args); +int lib_interface_isis_vrf_modify(struct nb_cb_modify_args *args); int lib_interface_isis_ipv4_routing_modify(struct nb_cb_modify_args *args); int lib_interface_isis_ipv6_routing_modify(struct nb_cb_modify_args *args); int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args); diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 6873c652f2..170fe92c28 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -53,16 +53,19 @@ int isis_instance_create(struct nb_cb_create_args *args) { struct isis_area *area; const char *area_tag; + const char *vrf_name; if (args->event != NB_EV_APPLY) return NB_OK; - + vrf_name = yang_dnode_get_string(args->dnode, "./vrf"); area_tag = yang_dnode_get_string(args->dnode, "./area-tag"); - area = isis_area_lookup(area_tag); + isis_global_instance_create(vrf_name); + area = isis_area_lookup_by_vrf(area_tag, vrf_name); if (area) return NB_ERR_INCONSISTENCY; - area = isis_area_create(area_tag); + area = isis_area_create(area_tag, vrf_name); + /* save area in dnode to avoid looking it up all the time */ nb_running_set_entry(args->dnode, area); @@ -75,9 +78,8 @@ int isis_instance_destroy(struct nb_cb_destroy_args *args) if (args->event != NB_EV_APPLY) return NB_OK; - area = nb_running_unset_entry(args->dnode); - isis_area_destroy(area->area_tag); + isis_area_destroy(area); return NB_OK; } @@ -113,6 +115,9 @@ 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); + if (area == NULL) + return NB_ERR_VALIDATION; addr.addr_len = dotformat2buff(buff, net_title); memcpy(addr.area_addr, buff, addr.addr_len); if (addr.area_addr[addr.addr_len - 1] != 0) { @@ -121,9 +126,9 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) "nsel byte (last byte) in area address must be 0"); return NB_ERR_VALIDATION; } - if (isis->sysid_set) { + if (area->isis->sysid_set) { /* Check that the SystemID portions match */ - if (memcmp(isis->sysid, GETSYSID((&addr)), + if (memcmp(area->isis->sysid, GETSYSID((&addr)), ISIS_SYS_ID_LEN)) { snprintf( args->errmsg, args->errmsg_len, @@ -144,13 +149,15 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) case NB_EV_APPLY: area = nb_running_get_entry(args->dnode, NULL, true); addrr = args->resource->ptr; + assert(area); - if (isis->sysid_set == 0) { + if (area->isis->sysid_set == 0) { /* * First area address - get the SystemID for this router */ - memcpy(isis->sysid, GETSYSID(addrr), ISIS_SYS_ID_LEN); - isis->sysid_set = 1; + memcpy(area->isis->sysid, GETSYSID(addrr), + ISIS_SYS_ID_LEN); + area->isis->sysid_set = 1; } else { /* check that we don't already have this address */ for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node, @@ -200,6 +207,7 @@ int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args) addr.addr_len = dotformat2buff(buff, net_title); memcpy(addr.area_addr, buff, (int)addr.addr_len); area = nb_running_get_entry(args->dnode, NULL, true); + for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node, addrp)) { if ((addrp->addr_len + ISIS_SYS_ID_LEN + 1) == addr.addr_len && !memcmp(addrp->area_addr, addr.area_addr, addr.addr_len)) @@ -214,8 +222,8 @@ int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args) * Last area address - reset the SystemID for this router */ if (listcount(area->area_addrs) == 0) { - memset(isis->sysid, 0, ISIS_SYS_ID_LEN); - isis->sysid_set = 0; + memset(area->isis->sysid, 0, ISIS_SYS_ID_LEN); + area->isis->sysid_set = 0; if (IS_DEBUG_EVENTS) zlog_debug("Router has no SystemID"); } @@ -1822,10 +1830,12 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_mo */ int lib_interface_isis_create(struct nb_cb_create_args *args) { - struct isis_area *area; + struct isis_area *area = NULL; struct interface *ifp; - struct isis_circuit *circuit; + struct isis_circuit *circuit = NULL; + struct vrf *vrf; const char *area_tag = yang_dnode_get_string(args->dnode, "./area-tag"); + const char *vrf_name = yang_dnode_get_string(args->dnode, "./vrf"); uint32_t min_mtu, actual_mtu; switch (args->event) { @@ -1840,9 +1850,18 @@ int lib_interface_isis_create(struct nb_cb_create_args *args) /* zebra might not know yet about the MTU - nothing we can do */ if (!ifp || ifp->mtu == 0) break; + vrf = vrf_lookup_by_id(ifp->vrf_id); + if (ifp->vrf_id != VRF_DEFAULT && vrf + && strcmp(vrf->name, vrf_name) != 0) { + snprintf(args->errmsg, args->errmsg_len, + "interface %s not in vrf %s\n", ifp->name, + vrf_name); + return NB_ERR_VALIDATION; + } actual_mtu = if_is_broadcast(ifp) ? ifp->mtu - LLC_LEN : ifp->mtu; - area = isis_area_lookup(area_tag); + + area = isis_area_lookup(area_tag, ifp->vrf_id); if (area) min_mtu = area->lsp_mtu; else @@ -1860,7 +1879,7 @@ int lib_interface_isis_create(struct nb_cb_create_args *args) } break; case NB_EV_APPLY: - area = isis_area_lookup(area_tag); + area = isis_area_lookup_by_vrf(area_tag, vrf_name); /* The area should have already be created. We are * setting the priority of the global isis area creation * slightly lower, so it should be executed first, but I @@ -1873,7 +1892,6 @@ int lib_interface_isis_create(struct nb_cb_create_args *args) __func__, area_tag); abort(); } - ifp = nb_running_get_entry(args->dnode, NULL, true); circuit = isis_circuit_create(area, ifp); assert(circuit @@ -1915,6 +1933,7 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args) struct interface *ifp; struct vrf *vrf; const char *area_tag, *ifname, *vrfname; + struct isis *isis = NULL; if (args->event == NB_EV_VALIDATE) { /* libyang doesn't like relative paths across module boundaries @@ -1926,8 +1945,14 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args) vrf = vrf_lookup_by_name(vrfname); assert(vrf); ifp = if_lookup_by_name(ifname, vrf->vrf_id); + if (!ifp) return NB_OK; + + isis = isis_lookup_by_vrfid(ifp->vrf_id); + if (isis == NULL) + return NB_ERR_VALIDATION; + circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list); area_tag = yang_dnode_get_string(args->dnode, NULL); if (circuit && circuit->area && circuit->area->area_tag @@ -1943,6 +1968,44 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args) } /* + * XPath: /frr-interface:lib/interface/frr-isisd:isis/vrf + */ +int lib_interface_isis_vrf_modify(struct nb_cb_modify_args *args) +{ + struct interface *ifp; + struct vrf *vrf; + const char *ifname, *vrfname, *vrf_name; + struct isis_circuit *circuit; + + if (args->event == NB_EV_VALIDATE) { + /* libyang doesn't like relative paths across module boundaries + */ + ifname = yang_dnode_get_string(args->dnode->parent->parent, + "./name"); + vrfname = yang_dnode_get_string(args->dnode->parent->parent, + "./vrf"); + vrf = vrf_lookup_by_name(vrfname); + assert(vrf); + ifp = if_lookup_by_name(ifname, vrf->vrf_id); + + if (!ifp) + return NB_OK; + + vrf_name = yang_dnode_get_string(args->dnode, NULL); + circuit = circuit_scan_by_ifp(ifp); + if (circuit && circuit->area && circuit->area->isis + && strcmp(circuit->area->isis->name, vrf_name)) { + snprintf(args->errmsg, args->errmsg_len, + "ISIS circuit is already defined on vrf %s", + circuit->area->isis->name); + return NB_ERR_VALIDATION; + } + } + + return NB_OK; +} + +/* * XPath: /frr-interface:lib/interface/frr-isisd:isis/circuit-type */ int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args) @@ -1952,6 +2015,7 @@ int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args) struct interface *ifp; struct vrf *vrf; const char *ifname, *vrfname; + struct isis *isis = NULL; switch (args->event) { case NB_EV_VALIDATE: @@ -1966,6 +2030,11 @@ int lib_interface_isis_circuit_type_modify(struct nb_cb_modify_args *args) ifp = if_lookup_by_name(ifname, vrf->vrf_id); if (!ifp) break; + + isis = isis_lookup_by_vrfid(ifp->vrf_id); + if (isis == NULL) + return NB_ERR_VALIDATION; + circuit = circuit_lookup_by_ifp(ifp, isis->init_circ_list); if (circuit && circuit->state == C_STATE_UP && circuit->area->is_type != IS_LEVEL_1_AND_2 @@ -2019,7 +2088,7 @@ int lib_interface_isis_ipv6_routing_modify(struct nb_cb_modify_args *args) return NB_OK; circuit = nb_running_get_entry(args->dnode, NULL, true); - ipv4 = yang_dnode_exists(args->dnode, "../ipv4-routing"); + ipv4 = yang_dnode_get_bool(args->dnode, "../ipv4-routing"); ipv6 = yang_dnode_get_bool(args->dnode, NULL); isis_circuit_af_set(circuit, ipv4, ipv6); diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 72548e0425..43b9f6685e 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -74,8 +74,10 @@ static int ack_lsp(struct isis_lsp_hdr *hdr, struct isis_circuit *circuit, fill_fixed_hdr(pdu_type, circuit->snd_stream); lenp = stream_get_endp(circuit->snd_stream); + stream_putw(circuit->snd_stream, 0); /* PDU length */ - stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + stream_put(circuit->snd_stream, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); stream_putc(circuit->snd_stream, circuit->idx); stream_putc(circuit->snd_stream, 9); /* code */ stream_putc(circuit->snd_stream, 16); /* len */ @@ -128,6 +130,7 @@ struct iih_info { static int process_p2p_hello(struct iih_info *iih) { struct isis_threeway_adj *tw_adj = iih->tlvs->threeway_adj; + if (tw_adj) { if (tw_adj->state > ISIS_THREEWAY_DOWN) { if (IS_DEBUG_ADJ_PACKETS) { @@ -140,8 +143,10 @@ static int process_p2p_hello(struct iih_info *iih) } if (tw_adj->neighbor_set - && (memcmp(tw_adj->neighbor_id, isis->sysid, ISIS_SYS_ID_LEN) - || tw_adj->neighbor_circuit_id != (uint32_t) iih->circuit->idx)) { + && (memcmp(tw_adj->neighbor_id, + iih->circuit->area->isis->sysid, ISIS_SYS_ID_LEN) + || tw_adj->neighbor_circuit_id + != (uint32_t)iih->circuit->idx)) { if (IS_DEBUG_ADJ_PACKETS) { zlog_debug("ISIS-Adj (%s): Rcvd P2P IIH from (%s) which lists IS/Circuit different from us as neighbor.", @@ -561,7 +566,6 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, ? "P2P IIH" : (level == ISIS_LEVEL1) ? "L1 LAN IIH" : "L2 LAN IIH"; - stream_get_from(raw_pdu, circuit->rcv_stream, pdu_start, pdu_end - pdu_start); if (IS_DEBUG_ADJ_PACKETS) { @@ -724,7 +728,7 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit, goto out; } - if (!memcmp(iih.sys_id, isis->sysid, ISIS_SYS_ID_LEN)) { + if (!memcmp(iih.sys_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN)) { zlog_warn( "ISIS-Adj (%s): Received IIH with own sysid - discard", circuit->area->area_tag); @@ -1040,7 +1044,8 @@ dontcheckadj: ack_lsp(&hdr, circuit, level); goto out; /* FIXME: do we need a purge? */ } else { - if (memcmp(hdr.lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) { + if (memcmp(hdr.lsp_id, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN)) { /* LSP by some other system -> do 7.3.16.4 b) */ /* 7.3.16.4 b) 1) */ if (comp == LSP_NEWER) { @@ -1134,7 +1139,8 @@ dontcheckadj: } /* 7.3.15.1 c) - If this is our own lsp and we don't have it initiate a * purge */ - if (memcmp(hdr.lsp_id, isis->sysid, ISIS_SYS_ID_LEN) == 0) { + if (memcmp(hdr.lsp_id, circuit->area->isis->sysid, ISIS_SYS_ID_LEN) + == 0) { if (!lsp) { /* 7.3.16.4: initiate a purge */ lsp_purge_non_exist(level, &hdr, circuit->area); @@ -1370,6 +1376,7 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, struct isis_passwd *passwd = (level == IS_LEVEL_1) ? &circuit->area->area_passwd : &circuit->area->domain_passwd; + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) { int auth_code = isis_tlvs_auth_is_valid( tlvs, passwd, circuit->rcv_stream, false); @@ -1420,7 +1427,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, entry = entry->next) { struct isis_lsp *lsp = lsp_search(&circuit->area->lspdb[level - 1], entry->id); - bool own_lsp = !memcmp(entry->id, isis->sysid, ISIS_SYS_ID_LEN); + bool own_lsp = !memcmp(entry->id, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); if (lsp) { /* 7.3.15.2 b) 1) is this LSP newer */ int cmp = lsp_compare(circuit->area->area_tag, lsp, @@ -1459,8 +1467,9 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, * are not 0, * insert it and set SSN on it */ if (entry->rem_lifetime && entry->checksum - && entry->seqno && memcmp(entry->id, isis->sysid, - ISIS_SYS_ID_LEN)) { + && entry->seqno + && memcmp(entry->id, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN)) { struct isis_lsp *lsp0 = NULL; if (LSP_FRAGMENT(entry->id)) { @@ -1667,13 +1676,14 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) } /* either 3 or 0 */ - if (pdu_type != FS_LINK_STATE /* FS PDU doesn't contain max area addr field */ + if (pdu_type != FS_LINK_STATE /* FS PDU doesn't contain max area addr + field */ && max_area_addrs != 0 - && max_area_addrs != isis->max_area_addrs) { + && max_area_addrs != circuit->area->isis->max_area_addrs) { flog_err( EC_ISIS_PACKET, "maximumAreaAddressesMismatch: maximumAreaAdresses in a received PDU %hhu while the parameter for this IS is %u", - max_area_addrs, isis->max_area_addrs); + max_area_addrs, circuit->area->isis->max_area_addrs); circuit->max_area_addr_mismatches++; #ifndef FABRICD /* send northbound notification */ @@ -2052,8 +2062,10 @@ int send_csnp(struct isis_circuit *circuit, int level) fill_fixed_hdr(pdu_type, circuit->snd_stream); size_t len_pointer = stream_get_endp(circuit->snd_stream); + stream_putw(circuit->snd_stream, 0); - stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + stream_put(circuit->snd_stream, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); /* with zero circuit id - ref 9.10, 9.11 */ stream_putc(circuit->snd_stream, 0); @@ -2230,7 +2242,8 @@ static int send_psnp(int level, struct isis_circuit *circuit) size_t len_pointer = stream_get_endp(circuit->snd_stream); stream_putw(circuit->snd_stream, 0); /* length is filled in later */ - stream_put(circuit->snd_stream, isis->sysid, ISIS_SYS_ID_LEN); + stream_put(circuit->snd_stream, circuit->area->isis->sysid, + ISIS_SYS_ID_LEN); stream_putc(circuit->snd_stream, circuit->idx); struct isis_passwd *passwd = (level == ISIS_LEVEL1) diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index 45e79b46da..44422ff664 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -218,8 +218,9 @@ static void isis_redist_ensure_default(struct isis *isis, int family) } /* Handle notification about route being added */ -void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p, - uint8_t distance, uint32_t metric) +void isis_redist_add(struct isis *isis, int type, struct prefix *p, + struct prefix_ipv6 *src_p, uint8_t distance, + uint32_t metric) { int family = p->family; struct route_table *ei_table = get_ext_info(isis, family); @@ -270,7 +271,8 @@ void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p, } } -void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p) +void isis_redist_delete(struct isis *isis, int type, struct prefix *p, + struct prefix_ipv6 *src_p) { int family = p->family; struct route_table *ei_table = get_ext_info(isis, family); @@ -292,8 +294,8 @@ void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p) * by "default-information originate always". Areas without the * "always" setting will ignore routes with origin * DEFAULT_ROUTE. */ - isis_redist_add(DEFAULT_ROUTE, p, NULL, - 254, MAX_WIDE_PATH_METRIC); + isis_redist_add(isis, DEFAULT_ROUTE, p, NULL, 254, + MAX_WIDE_PATH_METRIC); return; } diff --git a/isisd/isis_redist.h b/isisd/isis_redist.h index 9c37c310ea..0d2dc6a803 100644 --- a/isisd/isis_redist.h +++ b/isisd/isis_redist.h @@ -40,6 +40,7 @@ struct isis_redist { struct route_map *map; }; +struct isis; struct isis_area; struct prefix; struct prefix_ipv6; @@ -47,9 +48,11 @@ struct vty; struct route_table *get_ext_reach(struct isis_area *area, int family, int level); -void isis_redist_add(int type, struct prefix *p, struct prefix_ipv6 *src_p, - uint8_t distance, uint32_t metric); -void isis_redist_delete(int type, struct prefix *p, struct prefix_ipv6 *src_p); +void isis_redist_add(struct isis *isis, int type, struct prefix *p, + struct prefix_ipv6 *src_p, uint8_t distance, + uint32_t metric); +void isis_redist_delete(struct isis *isis, int type, struct prefix *p, + struct prefix_ipv6 *src_p); int isis_redist_config_write(struct vty *vty, struct isis_area *area, int family); void isis_redist_init(void); diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 7b761f5cda..c83a7c04bb 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -46,6 +46,7 @@ #include "isis_pdu.h" #include "isis_lsp.h" #include "isis_spf.h" +#include "isis_spf_private.h" #include "isis_route.h" #include "isis_zebra.h" @@ -158,6 +159,17 @@ static void adjinfo2nexthop(int family, struct list *nexthops, } } +static void isis_route_add_dummy_nexthops(struct isis_route_info *rinfo, + const uint8_t *sysid) +{ + struct isis_nexthop *nh; + + nh = XCALLOC(MTYPE_ISIS_NEXTHOP, sizeof(struct isis_nexthop)); + memcpy(nh->sysid, sysid, sizeof(nh->sysid)); + isis_sr_nexthop_reset(&nh->sr); + listnode_add(rinfo->nexthops, nh); +} + static struct isis_route_info *isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p, uint32_t cost, @@ -165,13 +177,25 @@ static struct isis_route_info *isis_route_info_new(struct prefix *prefix, struct list *adjacencies) { struct isis_route_info *rinfo; - struct isis_adjacency *adj; + struct isis_vertex_adj *vadj; struct listnode *node; rinfo = XCALLOC(MTYPE_ISIS_ROUTE_INFO, sizeof(struct isis_route_info)); rinfo->nexthops = list_new(); - for (ALL_LIST_ELEMENTS_RO(adjacencies, node, adj)) { + for (ALL_LIST_ELEMENTS_RO(adjacencies, node, vadj)) { + struct isis_spf_adj *sadj = vadj->sadj; + struct isis_adjacency *adj = sadj->adj; + + /* + * Create dummy nexthops when running SPF on a testing + * environment. + */ + if (CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) { + isis_route_add_dummy_nexthops(rinfo, sadj->id); + continue; + } + /* check for force resync this route */ if (CHECK_FLAG(adj->circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index c092721ab4..dd0a6ec824 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -29,12 +29,14 @@ #include "vty.h" #include "log.h" #include "command.h" +#include "termtable.h" #include "memory.h" #include "prefix.h" #include "if.h" #include "table.h" #include "spf_backoff.h" #include "srcdest_table.h" +#include "vrf.h" #include "isis_constants.h" #include "isis_common.h" @@ -55,6 +57,13 @@ #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_VERTEX_ADJ, "ISIS SPF Vertex Adjacency"); + +static void spf_adj_list_parse_lsp(struct isis_spftree *spftree, + struct list *adj_list, struct isis_lsp *lsp, + const uint8_t *pseudo_nodeid, + uint32_t pseudo_metric); /* * supports the given af ? @@ -79,22 +88,29 @@ struct isis_spf_run { static void remove_excess_adjs(struct list *adjs) { struct listnode *node, *excess = NULL; - struct isis_adjacency *adj, *candidate = NULL; + struct isis_vertex_adj *vadj, *candidate = NULL; int comp; - for (ALL_LIST_ELEMENTS_RO(adjs, node, adj)) { + for (ALL_LIST_ELEMENTS_RO(adjs, node, vadj)) { + struct isis_adjacency *adj, *candidate_adj; + + adj = vadj->sadj->adj; + assert(adj); + if (excess == NULL) excess = node; candidate = listgetdata(excess); + candidate_adj = candidate->sadj->adj; - if (candidate->sys_type < adj->sys_type) { + if (candidate_adj->sys_type < adj->sys_type) { excess = node; continue; } - if (candidate->sys_type > adj->sys_type) + if (candidate_adj->sys_type > adj->sys_type) continue; - comp = memcmp(candidate->sysid, adj->sysid, ISIS_SYS_ID_LEN); + comp = memcmp(candidate_adj->sysid, adj->sysid, + ISIS_SYS_ID_LEN); if (comp > 0) { excess = node; continue; @@ -102,15 +118,15 @@ static void remove_excess_adjs(struct list *adjs) if (comp < 0) continue; - if (candidate->circuit->idx > adj->circuit->idx) { + if (candidate_adj->circuit->idx > adj->circuit->idx) { excess = node; continue; } - if (candidate->circuit->idx < adj->circuit->idx) + if (candidate_adj->circuit->idx < adj->circuit->idx) continue; - comp = memcmp(candidate->snpa, adj->snpa, ETH_ALEN); + comp = memcmp(candidate_adj->snpa, adj->snpa, ETH_ALEN); if (comp > 0) { excess = node; continue; @@ -154,7 +170,9 @@ static const char *vtype2string(enum vertextype vtype) const char *vid2string(struct isis_vertex *vertex, char *buff, int size) { if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) { - return print_sys_hostname(vertex->N.id); + const char *hostname = print_sys_hostname(vertex->N.id); + strlcpy(buff, hostname, size); + return buff; } if (VTYPE_IP(vertex->type)) { @@ -167,6 +185,13 @@ const char *vid2string(struct isis_vertex *vertex, char *buff, int size) return "UNKNOWN"; } +static void isis_vertex_adj_free(void *arg) +{ + struct isis_vertex_adj *vadj = arg; + + XFREE(MTYPE_ISIS_VERTEX_ADJ, vadj); +} + static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree, void *id, enum vertextype vtype) @@ -178,9 +203,10 @@ static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree, isis_vertex_id_init(vertex, id, vtype); vertex->Adj_N = list_new(); + vertex->Adj_N->del = isis_vertex_adj_free; vertex->parents = list_new(); - if (spftree->hopcount_metric) { + if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC)) { vertex->firsthops = hash_create(isis_vertex_queue_hash_key, isis_vertex_queue_hash_cmp, NULL); @@ -189,21 +215,69 @@ static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree, return vertex; } +static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_vertex *vertex, + struct isis_spf_adj *sadj) +{ + struct isis_vertex_adj *vadj; + + vadj = XCALLOC(MTYPE_ISIS_VERTEX_ADJ, sizeof(*vadj)); + vadj->sadj = sadj; + listnode_add(vertex->Adj_N, vadj); + + return vadj; +} + static void isis_vertex_adj_del(struct isis_vertex *vertex, struct isis_adjacency *adj) { + struct isis_vertex_adj *vadj; struct listnode *node, *nextnode; + if (!vertex) return; - for (node = listhead(vertex->Adj_N); node; node = nextnode) { - nextnode = listnextnode(node); - if (listgetdata(node) == adj) - list_delete_node(vertex->Adj_N, node); + + for (ALL_LIST_ELEMENTS(vertex->Adj_N, node, nextnode, vadj)) { + if (vadj->sadj->adj == adj) { + listnode_delete(vertex->Adj_N, vadj); + isis_vertex_adj_free(vadj); + } } return; } -struct isis_spftree *isis_spftree_new(struct isis_area *area) +bool isis_vertex_adj_exists(const struct isis_spftree *spftree, + const struct isis_vertex *vertex, + const struct isis_spf_adj *sadj) +{ + struct isis_vertex_adj *tmp; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(vertex->Adj_N, node, tmp)) { + if (CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) { + if (memcmp(sadj->id, tmp->sadj->id, sizeof(sadj->id)) + == 0) + return true; + } else { + if (sadj->adj == tmp->sadj->adj) + return true; + } + } + + return false; +} + +static void isis_spf_adj_free(void *arg) +{ + struct isis_spf_adj *sadj = arg; + + XFREE(MTYPE_ISIS_SPF_ADJ, sadj); +} + +struct isis_spftree *isis_spftree_new(struct isis_area *area, + struct lspdb_head *lspdb, + const uint8_t *sysid, int level, + enum spf_tree_id tree_id, + enum spf_type type, uint8_t flags) { struct isis_spftree *tree; @@ -213,15 +287,26 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area) isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths", false); tree->route_table = srcdest_table_init(); tree->area = area; + tree->lspdb = lspdb; + tree->sadj_list = list_new(); + tree->sadj_list->del = isis_spf_adj_free; tree->last_run_timestamp = 0; tree->last_run_monotime = 0; tree->last_run_duration = 0; tree->runcount = 0; + tree->type = type; + memcpy(tree->sysid, sysid, ISIS_SYS_ID_LEN); + tree->level = level; + tree->tree_id = tree_id; + tree->family = (tree->tree_id == SPFTREE_IPV4) ? AF_INET : AF_INET6; + tree->flags = flags; + return tree; } void isis_spftree_del(struct isis_spftree *spftree) { + list_delete(&spftree->sadj_list); isis_vertex_queue_free(&spftree->tents); isis_vertex_queue_free(&spftree->paths); route_table_finish(spftree->route_table); @@ -253,7 +338,10 @@ void spftree_area_init(struct isis_area *area) if (area->spftree[tree][level - 1]) continue; - area->spftree[tree][level - 1] = isis_spftree_new(area); + area->spftree[tree][level - 1] = + isis_spftree_new(area, &area->lspdb[level - 1], + area->isis->sysid, level, tree, + SPF_TYPE_FORWARD, 0); } } } @@ -272,8 +360,14 @@ void spftree_area_del(struct isis_area *area) } } -void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj) +static int spf_adj_state_change(struct isis_adjacency *adj) { + struct isis_area *area = adj->circuit->area; + + if (adj->adj_state == ISIS_ADJ_UP) + return 0; + + /* Remove adjacency from all SPF trees. */ for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { if (!(area->is_type & level)) @@ -287,13 +381,15 @@ void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj) if (fabricd_spftree(area) != NULL) isis_spftree_adj_del(fabricd_spftree(area), adj); + + return 0; } /* * Find the system LSP: returns the LSP in our LSP database * associated with the given system ID. */ -static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level, +static struct isis_lsp *isis_root_system_lsp(struct lspdb_head *lspdb, uint8_t *sysid) { struct isis_lsp *lsp; @@ -302,7 +398,7 @@ static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level, memcpy(lspid, sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID(lspid) = 0; LSP_FRAGMENT(lspid) = 0; - lsp = lsp_search(&area->lspdb[level - 1], lspid); + lsp = lsp_search(lspdb, lspid); if (lsp && lsp->hdr.rem_lifetime != 0) return lsp; return NULL; @@ -311,28 +407,21 @@ static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level, /* * Add this IS to the root of SPT */ -static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree, - uint8_t *sysid) +static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree) { struct isis_vertex *vertex; - struct isis_lsp *lsp; #ifdef EXTREME_DEBUG char buff[VID2STR_BUFFER]; #endif /* EXTREME_DEBUG */ - lsp = isis_root_system_lsp(spftree->area, spftree->level, sysid); - if (lsp == NULL) - zlog_warn("ISIS-Spf: could not find own l%d LSP!", - spftree->level); - - vertex = isis_vertex_new(spftree, sysid, + vertex = isis_vertex_new(spftree, spftree->sysid, spftree->area->oldmetric ? VTYPE_NONPSEUDO_IS : VTYPE_NONPSEUDO_TE_IS); isis_vertex_queue_append(&spftree->paths, vertex); #ifdef EXTREME_DEBUG - zlog_debug("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS", + zlog_debug("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS", vtype2string(vertex->type), vid2string(vertex, buff, sizeof(buff)), vertex->depth, vertex->d_N); @@ -367,12 +456,11 @@ static void vertex_update_firsthops(struct isis_vertex *vertex, static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id, uint32_t cost, int depth, - struct isis_adjacency *adj, + struct isis_spf_adj *sadj, struct isis_vertex *parent) { struct isis_vertex *vertex; struct listnode *node; - struct isis_adjacency *parent_adj; char buff[VID2STR_BUFFER]; vertex = isis_find_vertex(&spftree->paths, id, vtype); @@ -400,14 +488,16 @@ static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree, listnode_add(vertex->parents, parent); } - if (spftree->hopcount_metric) + if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC)) vertex_update_firsthops(vertex, parent); if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) { - for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_adj)) - listnode_add(vertex->Adj_N, parent_adj); - } else if (adj) { - listnode_add(vertex->Adj_N, adj); + struct isis_vertex_adj *parent_vadj; + + for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_vadj)) + isis_vertex_adj_add(vertex, parent_vadj->sadj); + } else if (sadj) { + isis_vertex_adj_add(vertex, sadj); } #ifdef EXTREME_DEBUG @@ -424,7 +514,7 @@ static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree, static void isis_spf_add_local(struct isis_spftree *spftree, enum vertextype vtype, void *id, - struct isis_adjacency *adj, uint32_t cost, + struct isis_spf_adj *sadj, uint32_t cost, struct isis_vertex *parent) { struct isis_vertex *vertex; @@ -434,10 +524,12 @@ static void isis_spf_add_local(struct isis_spftree *spftree, if (vertex) { /* C.2.5 c) */ if (vertex->d_N == cost) { - if (adj) - listnode_add(vertex->Adj_N, adj); + if (sadj) + isis_vertex_adj_add(vertex, sadj); /* d) */ - if (listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) + if (!CHECK_FLAG(spftree->flags, + F_SPFTREE_NO_ADJACENCIES) + && listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) remove_excess_adjs(vertex->Adj_N); if (parent && (listnode_lookup(vertex->parents, parent) == NULL)) @@ -453,7 +545,7 @@ static void isis_spf_add_local(struct isis_spftree *spftree, } } - isis_spf_add2tent(spftree, vtype, id, cost, 1, adj, parent); + isis_spf_add2tent(spftree, vtype, id, cost, 1, sadj, parent); return; } @@ -468,7 +560,7 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, assert(spftree && parent); - if (spftree->hopcount_metric + if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC) && !VTYPE_IS(vtype)) return; @@ -518,16 +610,20 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, #endif /* EXTREME_DEBUG */ if (vertex->d_N == dist) { struct listnode *node; - struct isis_adjacency *parent_adj; + struct isis_vertex_adj *parent_vadj; for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, - parent_adj)) - if (listnode_lookup(vertex->Adj_N, parent_adj) - == NULL) - listnode_add(vertex->Adj_N, parent_adj); - if (spftree->hopcount_metric) + parent_vadj)) + if (!isis_vertex_adj_exists(spftree, vertex, + parent_vadj->sadj)) + isis_vertex_adj_add(vertex, + parent_vadj->sadj); + if (CHECK_FLAG(spftree->flags, + F_SPFTREE_HOPCOUNT_METRIC)) vertex_update_firsthops(vertex, parent); /* 2) */ - if (listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) + if (!CHECK_FLAG(spftree->flags, + F_SPFTREE_NO_ADJACENCIES) + && listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) remove_excess_adjs(vertex->Adj_N); if (listnode_lookup(vertex->parents, parent) == NULL) listnode_add(vertex->parents, parent); @@ -610,7 +706,9 @@ lspfragloop: /* C.2.6 a) */ /* Two way connectivity */ - if (!memcmp(r->id, root_sysid, ISIS_SYS_ID_LEN)) + if (!LSP_PSEUDO_ID(r->id) + && !memcmp(r->id, root_sysid, + ISIS_SYS_ID_LEN)) continue; if (!pseudo_lsp && !memcmp(r->id, null_sysid, @@ -639,12 +737,19 @@ lspfragloop: te_neighs->head : NULL; er; er = er->next) { - if (!memcmp(er->id, root_sysid, ISIS_SYS_ID_LEN)) + /* C.2.6 a) */ + /* Two way connectivity */ + if (!LSP_PSEUDO_ID(er->id) + && !memcmp(er->id, root_sysid, ISIS_SYS_ID_LEN)) continue; if (!pseudo_lsp && !memcmp(er->id, null_sysid, ISIS_SYS_ID_LEN)) continue; - dist = cost + (spftree->hopcount_metric ? 1 : er->metric); + dist = cost + + (CHECK_FLAG(spftree->flags, + F_SPFTREE_HOPCOUNT_METRIC) + ? 1 + : er->metric); process_N(spftree, LSP_PSEUDO_ID(er->id) ? VTYPE_PSEUDO_TE_IS : VTYPE_NONPSEUDO_TE_IS, @@ -758,236 +863,311 @@ lspfragloop: return ISIS_OK; } -static int isis_spf_preload_tent(struct isis_spftree *spftree, - uint8_t *root_sysid, - struct isis_vertex *parent) +static struct isis_adjacency *adj_find(struct list *adj_list, const uint8_t *id, + int level, uint16_t mtid, int family) { - struct isis_circuit *circuit; - struct listnode *cnode, *anode, *ipnode; struct isis_adjacency *adj; - struct isis_lsp *lsp; - struct list *adj_list; - struct list *adjdb; - struct prefix_ipv4 *ipv4; - struct prefix_pair ip_info; - int retval = ISIS_OK; - uint8_t lsp_id[ISIS_SYS_ID_LEN + 2]; - static uint8_t null_lsp_id[ISIS_SYS_ID_LEN + 2]; - struct prefix_ipv6 *ipv6; - struct isis_circuit_mt_setting *circuit_mt; - - for (ALL_LIST_ELEMENTS_RO(spftree->area->circuit_list, cnode, - circuit)) { - circuit_mt = circuit_lookup_mt_setting(circuit, spftree->mtid); - if (circuit_mt && !circuit_mt->enabled) + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) { + if (!(adj->level & level)) continue; - if (circuit->state != C_STATE_UP) + if (memcmp(adj->sysid, id, ISIS_SYS_ID_LEN) != 0) continue; - if (!(circuit->is_type & spftree->level)) + if (adj->adj_state != ISIS_ADJ_UP) continue; - if (spftree->family == AF_INET && !circuit->ip_router) + if (!adj_has_mt(adj, mtid)) continue; - if (spftree->family == AF_INET6 && !circuit->ipv6_router) + if (mtid == ISIS_MT_IPV4_UNICAST + && !speaks(adj->nlpids.nlpids, adj->nlpids.count, family)) continue; - /* - * Add IP(v6) addresses of this circuit - */ - if (spftree->family == AF_INET && !spftree->hopcount_metric) { - memset(&ip_info, 0, sizeof(ip_info)); - ip_info.dest.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode, - ipv4)) { - ip_info.dest.u.prefix4 = ipv4->prefix; - ip_info.dest.prefixlen = ipv4->prefixlen; - apply_mask(&ip_info.dest); - isis_spf_add_local(spftree, - VTYPE_IPREACH_INTERNAL, - &ip_info, NULL, 0, parent); - } + return adj; + } + + return NULL; +} + +struct spf_preload_tent_ip_reach_args { + struct isis_spftree *spftree; + struct isis_vertex *parent; +}; + +static int isis_spf_preload_tent_ip_reach_cb(const struct prefix *prefix, + uint32_t metric, bool external, + struct isis_subtlvs *subtlvs, + void *arg) +{ + struct spf_preload_tent_ip_reach_args *args = arg; + struct isis_spftree *spftree = args->spftree; + struct isis_vertex *parent = args->parent; + struct prefix_pair ip_info; + enum vertextype vtype; + + if (external) + return LSP_ITER_CONTINUE; + + assert(spftree->family == prefix->family); + memset(&ip_info, 0, sizeof(ip_info)); + prefix_copy(&ip_info.dest, prefix); + apply_mask(&ip_info.dest); + + if (prefix->family == AF_INET) + vtype = VTYPE_IPREACH_INTERNAL; + else + vtype = VTYPE_IP6REACH_INTERNAL; + + isis_spf_add_local(spftree, vtype, &ip_info, NULL, 0, parent); + + return LSP_ITER_CONTINUE; +} + +static void isis_spf_preload_tent(struct isis_spftree *spftree, + uint8_t *root_sysid, + struct isis_lsp *root_lsp, + struct isis_vertex *parent) +{ + struct spf_preload_tent_ip_reach_args ip_reach_args; + struct isis_spf_adj *sadj; + struct listnode *node; + + if (!CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC)) { + ip_reach_args.spftree = spftree; + ip_reach_args.parent = parent; + isis_lsp_iterate_ip_reach( + root_lsp, spftree->family, spftree->mtid, + isis_spf_preload_tent_ip_reach_cb, &ip_reach_args); + } + + /* Iterate over adjacencies. */ + for (ALL_LIST_ELEMENTS_RO(spftree->sadj_list, node, sadj)) { + uint32_t metric; + + metric = CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC) + ? 1 + : sadj->metric; + if (!LSP_PSEUDO_ID(sadj->id)) { + isis_spf_add_local(spftree, + CHECK_FLAG(sadj->flags, + F_ISIS_SPF_ADJ_OLDMETRIC) + ? VTYPE_NONPSEUDO_IS + : VTYPE_NONPSEUDO_TE_IS, + sadj->id, sadj, metric, parent); + } else if (sadj->lan.lsp_pseudo) { + isis_spf_process_lsp(spftree, sadj->lan.lsp_pseudo, + metric, 0, spftree->sysid, parent); } - if (spftree->family == AF_INET6 && !spftree->hopcount_metric) { - memset(&ip_info, 0, sizeof(ip_info)); - ip_info.dest.family = AF_INET6; - for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, - ipnode, ipv6)) { - ip_info.dest.u.prefix6 = ipv6->prefix; - ip_info.dest.prefixlen = ipv6->prefixlen; - apply_mask(&ip_info.dest); - isis_spf_add_local(spftree, - VTYPE_IP6REACH_INTERNAL, - &ip_info, NULL, 0, parent); - } + } +} + +struct spf_adj_find_reverse_metric_args { + const uint8_t *id_self; + uint32_t reverse_metric; +}; + +static int spf_adj_find_reverse_metric_cb(const uint8_t *id, uint32_t metric, + bool oldmetric, + struct isis_ext_subtlvs *subtlvs, + void *arg) +{ + struct spf_adj_find_reverse_metric_args *args = arg; + + if (memcmp(id, args->id_self, ISIS_SYS_ID_LEN)) + return LSP_ITER_CONTINUE; + + args->reverse_metric = metric; + + return LSP_ITER_STOP; +} + +/* + * Change all SPF adjacencies to use the link cost in the direction from the + * next hop back towards root in place of the link cost in the direction away + * from root towards the next hop. + */ +static void spf_adj_get_reverse_metrics(struct isis_spftree *spftree) +{ + struct isis_spf_adj *sadj; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(spftree->sadj_list, node, nnode, sadj)) { + uint8_t lspid[ISIS_SYS_ID_LEN + 2]; + struct isis_lsp *lsp_adj; + const uint8_t *id_self; + struct spf_adj_find_reverse_metric_args args; + + /* Skip pseudonodes. */ + if (LSP_PSEUDO_ID(sadj->id)) + continue; + + /* Find LSP of the corresponding adjacency. */ + memcpy(lspid, sadj->id, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(lspid) = 0; + LSP_FRAGMENT(lspid) = 0; + lsp_adj = lsp_search(spftree->lspdb, lspid); + if (lsp_adj == NULL || lsp_adj->hdr.rem_lifetime == 0) { + /* Delete one-way adjacency. */ + listnode_delete(spftree->sadj_list, sadj); + continue; } - if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - /* - * Add the adjacencies - */ - adj_list = list_new(); - adjdb = circuit->u.bc.adjdb[spftree->level - 1]; - isis_adj_build_up_list(adjdb, adj_list); - if (listcount(adj_list) == 0) { - list_delete(&adj_list); - if (IS_DEBUG_SPF_EVENTS) - zlog_debug( - "ISIS-Spf: no L%d adjacencies on circuit %s", - spftree->level, - circuit->interface->name); - continue; - } - for (ALL_LIST_ELEMENTS_RO(adj_list, anode, adj)) { - if (!adj_has_mt(adj, spftree->mtid)) - continue; - if (spftree->mtid == ISIS_MT_IPV4_UNICAST - && !speaks(adj->nlpids.nlpids, - adj->nlpids.count, - spftree->family)) - continue; - switch (adj->sys_type) { - case ISIS_SYSTYPE_ES: - memcpy(lsp_id, adj->sysid, - ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID(lsp_id) = 0; - isis_spf_add_local( - spftree, VTYPE_ES, lsp_id, adj, - spftree->hopcount_metric ? 1 : - circuit->te_metric - [spftree->level - 1], - parent); - break; - case ISIS_SYSTYPE_IS: - case ISIS_SYSTYPE_L1_IS: - case ISIS_SYSTYPE_L2_IS: - memcpy(lsp_id, adj->sysid, - ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID(lsp_id) = 0; - LSP_FRAGMENT(lsp_id) = 0; - isis_spf_add_local( - spftree, - spftree->area->oldmetric - ? VTYPE_NONPSEUDO_IS - : VTYPE_NONPSEUDO_TE_IS, - lsp_id, adj, - spftree->hopcount_metric ? 1 : - circuit->te_metric - [spftree->level - 1], - parent); - lsp = lsp_search( - &spftree->area->lspdb[spftree->level- 1], - lsp_id); - if (lsp == NULL - || lsp->hdr.rem_lifetime == 0) - zlog_warn( - "ISIS-Spf: No LSP %s found for IS adjacency L%d on %s (ID %u)", - rawlspid_print(lsp_id), - spftree->level, - circuit->interface->name, - circuit->circuit_id); - break; - case ISIS_SYSTYPE_UNKNOWN: - default: - zlog_warn( - "isis_spf_preload_tent unknown adj type"); - } - } - list_delete(&adj_list); - /* - * Add the pseudonode - */ - if (spftree->level == 1) - memcpy(lsp_id, circuit->u.bc.l1_desig_is, - ISIS_SYS_ID_LEN + 1); - else - memcpy(lsp_id, circuit->u.bc.l2_desig_is, - ISIS_SYS_ID_LEN + 1); - /* can happen during DR reboot */ - if (memcmp(lsp_id, null_lsp_id, ISIS_SYS_ID_LEN + 1) - == 0) { - if (IS_DEBUG_SPF_EVENTS) - zlog_debug( - "ISIS-Spf: No L%d DR on %s (ID %d)", - spftree->level, - circuit->interface->name, - circuit->circuit_id); - continue; - } - adj = isis_adj_lookup(lsp_id, adjdb); - /* if no adj, we are the dis or error */ - if (!adj && !circuit->u.bc.is_dr[spftree->level - 1]) { - zlog_warn( - "ISIS-Spf: No adjacency found from root to L%d DR %s on %s (ID %d)", - spftree->level, rawlspid_print(lsp_id), - circuit->interface->name, - circuit->circuit_id); - continue; - } - lsp = lsp_search( - &spftree->area->lspdb[spftree->level - 1], - lsp_id); - if (lsp == NULL || lsp->hdr.rem_lifetime == 0) { - zlog_warn( - "ISIS-Spf: No lsp (%p) found from root to L%d DR %s on %s (ID %d)", - (void *)lsp, spftree->level, - rawlspid_print(lsp_id), - circuit->interface->name, - circuit->circuit_id); - continue; + + /* Find root node in the LSP of the adjacent router. */ + if (CHECK_FLAG(sadj->flags, F_ISIS_SPF_ADJ_BROADCAST)) + id_self = sadj->lan.desig_is_id; + else + id_self = spftree->sysid; + args.id_self = id_self; + args.reverse_metric = UINT32_MAX; + isis_lsp_iterate_is_reach(lsp_adj, spftree->mtid, + spf_adj_find_reverse_metric_cb, + &args); + if (args.reverse_metric == UINT32_MAX) { + /* Delete one-way adjacency. */ + listnode_delete(spftree->sadj_list, sadj); + continue; + } + sadj->metric = args.reverse_metric; + } +} + +static void spf_adj_list_parse_tlv(struct isis_spftree *spftree, + struct list *adj_list, const uint8_t *id, + const uint8_t *desig_is_id, + uint32_t pseudo_metric, uint32_t metric, + bool oldmetric, + struct isis_ext_subtlvs *subtlvs) +{ + struct isis_spf_adj *sadj; + uint8_t flags = 0; + + /* Skip self in the pseudonode. */ + if (desig_is_id && !memcmp(id, spftree->sysid, ISIS_SYS_ID_LEN)) + return; + + sadj = XCALLOC(MTYPE_ISIS_SPF_ADJ, sizeof(*sadj)); + memcpy(sadj->id, id, sizeof(sadj->id)); + if (desig_is_id) { + memcpy(sadj->lan.desig_is_id, desig_is_id, + sizeof(sadj->lan.desig_is_id)); + SET_FLAG(flags, F_ISIS_SPF_ADJ_BROADCAST); + sadj->metric = pseudo_metric; + } else + sadj->metric = metric; + if (oldmetric) + SET_FLAG(flags, F_ISIS_SPF_ADJ_OLDMETRIC); + sadj->subtlvs = subtlvs; + sadj->flags = flags; + + /* Set real adjacency. */ + if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES) + && !LSP_PSEUDO_ID(id)) { + struct isis_adjacency *adj; + + adj = adj_find(adj_list, id, spftree->level, spftree->mtid, + spftree->family); + if (!adj) { + XFREE(MTYPE_ISIS_SPF_ADJ, sadj); + return; + } + + listnode_delete(adj_list, adj); + sadj->adj = adj; + } + + /* Add adjacency to the list. */ + listnode_add(spftree->sadj_list, sadj); + + /* Parse pseudonode LSP too. */ + if (LSP_PSEUDO_ID(id)) { + uint8_t lspid[ISIS_SYS_ID_LEN + 2]; + struct isis_lsp *lsp_pseudo; + + memcpy(lspid, id, ISIS_SYS_ID_LEN + 1); + LSP_FRAGMENT(lspid) = 0; + lsp_pseudo = lsp_search(spftree->lspdb, lspid); + if (lsp_pseudo == NULL || lsp_pseudo->hdr.rem_lifetime == 0) { + zlog_warn( + "ISIS-Spf: No LSP found from root to L%d DR %s", + spftree->level, rawlspid_print(id)); + return; + } + + sadj->lan.lsp_pseudo = lsp_pseudo; + spf_adj_list_parse_lsp(spftree, adj_list, lsp_pseudo, id, + metric); + } +} + +static void spf_adj_list_parse_lsp(struct isis_spftree *spftree, + struct list *adj_list, struct isis_lsp *lsp, + const uint8_t *pseudo_nodeid, + uint32_t pseudo_metric) +{ + bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id); + struct isis_lsp *frag; + struct listnode *node; + struct isis_item *head; + struct isis_item_list *te_neighs; + + if (lsp->hdr.seqno == 0 || lsp->hdr.rem_lifetime == 0) + return; + + /* Parse main LSP. */ + if (lsp->tlvs) { + if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) { + head = lsp->tlvs->oldstyle_reach.head; + for (struct isis_oldstyle_reach *reach = + (struct isis_oldstyle_reach *)head; + reach; reach = reach->next) { + spf_adj_list_parse_tlv( + spftree, adj_list, reach->id, + pseudo_nodeid, pseudo_metric, + reach->metric, true, NULL); } - isis_spf_process_lsp(spftree, lsp, - spftree->hopcount_metric ? - 1 : circuit->te_metric[spftree->level - 1], - 0, root_sysid, parent); - } else if (circuit->circ_type == CIRCUIT_T_P2P) { - adj = circuit->u.p2p.neighbor; - if (!adj || adj->adj_state != ISIS_ADJ_UP) - continue; - if (!adj_has_mt(adj, spftree->mtid)) - continue; - switch (adj->sys_type) { - case ISIS_SYSTYPE_ES: - memcpy(lsp_id, adj->sysid, ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID(lsp_id) = 0; - isis_spf_add_local( - spftree, VTYPE_ES, lsp_id, adj, - spftree->hopcount_metric ? 1 : - circuit->te_metric[spftree->level - 1], - parent); - break; - case ISIS_SYSTYPE_IS: - case ISIS_SYSTYPE_L1_IS: - case ISIS_SYSTYPE_L2_IS: - memcpy(lsp_id, adj->sysid, ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID(lsp_id) = 0; - LSP_FRAGMENT(lsp_id) = 0; - if (spftree->mtid != ISIS_MT_IPV4_UNICAST - || speaks(adj->nlpids.nlpids, - adj->nlpids.count, - spftree->family)) - isis_spf_add_local( - spftree, - spftree->area->oldmetric - ? VTYPE_NONPSEUDO_IS - : VTYPE_NONPSEUDO_TE_IS, - lsp_id, adj, - spftree->hopcount_metric ? 1 : - circuit->te_metric - [spftree->level - 1], - parent); - break; - case ISIS_SYSTYPE_UNKNOWN: - default: - zlog_warn( - "isis_spf_preload_tent unknown adj type"); - break; + } + + if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) + te_neighs = &lsp->tlvs->extended_reach; + else + te_neighs = isis_get_mt_items(&lsp->tlvs->mt_reach, + spftree->mtid); + if (te_neighs) { + head = te_neighs->head; + for (struct isis_extended_reach *reach = + (struct isis_extended_reach *)head; + reach; reach = reach->next) { + spf_adj_list_parse_tlv( + spftree, adj_list, reach->id, + pseudo_nodeid, pseudo_metric, + reach->metric, false, reach->subtlvs); } - } else if (circuit->circ_type == CIRCUIT_T_LOOPBACK) { - continue; - } else { - zlog_warn("isis_spf_preload_tent unsupported media"); - retval = ISIS_WARNING; } } - return retval; + /* Parse LSP fragments. */ + for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) { + if (!frag->tlvs) + continue; + + spf_adj_list_parse_lsp(spftree, adj_list, frag, pseudo_nodeid, + pseudo_metric); + } +} + +static void isis_spf_build_adj_list(struct isis_spftree *spftree, + struct isis_lsp *lsp) +{ + struct list *adj_list = NULL; + + if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) + adj_list = list_dup(spftree->area->adjacency_list); + + spf_adj_list_parse_lsp(spftree, adj_list, lsp, NULL, 0); + + if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) + list_delete(&adj_list); + + if (spftree->type == SPF_TYPE_REVERSE) + spf_adj_get_reverse_metrics(spftree); } /* @@ -1010,10 +1190,10 @@ static void add_to_paths(struct isis_spftree *spftree, vertex->d_N); #endif /* EXTREME_DEBUG */ - if (VTYPE_IP(vertex->type)) { + if (VTYPE_IP(vertex->type) + && !CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ROUTES)) { if (listcount(vertex->Adj_N) > 0) - isis_route_create(&vertex->N.ip.dest, - &vertex->N.ip.src, + isis_route_create(&vertex->N.ip.dest, &vertex->N.ip.src, vertex->d_N, vertex->depth, vertex->Adj_N, spftree->area, spftree->route_table); @@ -1027,18 +1207,14 @@ static void add_to_paths(struct isis_spftree *spftree, return; } -static void init_spt(struct isis_spftree *spftree, int mtid, int level, - int family, enum spf_tree_id tree_id, - bool hopcount_metric) +static void init_spt(struct isis_spftree *spftree, int mtid) { + /* Clear data from previous run. */ + list_delete_all_node(spftree->sadj_list); isis_vertex_queue_clear(&spftree->tents); isis_vertex_queue_clear(&spftree->paths); spftree->mtid = mtid; - spftree->level = level; - spftree->family = family; - spftree->tree_id = tree_id; - spftree->hopcount_metric = hopcount_metric; } static void isis_spf_loop(struct isis_spftree *spftree, @@ -1079,18 +1255,31 @@ struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, struct isis_spftree *spftree) { if (!spftree) - spftree = isis_spftree_new(area); - - init_spt(spftree, ISIS_MT_IPV4_UNICAST, ISIS_LEVEL2, - AF_INET, SPFTREE_IPV4, true); - if (!memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN)) { - /* If we are running locally, initialize with information from adjacencies */ - struct isis_vertex *root = isis_spf_add_root(spftree, sysid); - isis_spf_preload_tent(spftree, sysid, root); + spftree = isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1], + sysid, ISIS_LEVEL2, SPFTREE_IPV4, + SPF_TYPE_FORWARD, + F_SPFTREE_HOPCOUNT_METRIC); + + init_spt(spftree, ISIS_MT_IPV4_UNICAST); + if (!memcmp(sysid, area->isis->sysid, ISIS_SYS_ID_LEN)) { + struct isis_lsp *root_lsp; + struct isis_vertex *root_vertex; + + root_lsp = isis_root_system_lsp(spftree->lspdb, spftree->sysid); + if (root_lsp) { + /* + * If we are running locally, initialize with + * information from adjacencies + */ + root_vertex = isis_spf_add_root(spftree); + + isis_spf_preload_tent(spftree, sysid, root_lsp, + root_vertex); + } } else { - isis_vertex_queue_insert(&spftree->tents, isis_vertex_new( - spftree, sysid, - VTYPE_NONPSEUDO_TE_IS)); + isis_vertex_queue_insert( + &spftree->tents, + isis_vertex_new(spftree, sysid, VTYPE_NONPSEUDO_TE_IS)); } isis_spf_loop(spftree, sysid); @@ -1098,56 +1287,56 @@ struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, return spftree; } -static int isis_run_spf(struct isis_area *area, int level, - enum spf_tree_id tree_id, - uint8_t *sysid, struct timeval *nowtv) +void isis_run_spf(struct isis_spftree *spftree) { - int retval = ISIS_OK; + struct isis_lsp *root_lsp; struct isis_vertex *root_vertex; - struct isis_spftree *spftree = area->spftree[tree_id][level - 1]; - struct timeval time_now; - unsigned long long start_time, end_time; + struct timeval time_start; + struct timeval time_end; + struct isis_mt_router_info *mt_router_info; uint16_t mtid = 0; /* Get time that can't roll backwards. */ - start_time = nowtv->tv_sec; - start_time = (start_time * 1000000) + nowtv->tv_usec; + monotime(&time_start); + + root_lsp = isis_root_system_lsp(spftree->lspdb, spftree->sysid); + if (root_lsp == NULL) { + zlog_err("ISIS-Spf: could not find own l%d LSP!", + spftree->level); + return; + } - int family = -1; - switch (tree_id) { + /* Get Multi-Topology ID. */ + switch (spftree->tree_id) { case SPFTREE_IPV4: - family = AF_INET; mtid = ISIS_MT_IPV4_UNICAST; break; case SPFTREE_IPV6: - family = AF_INET6; - mtid = isis_area_ipv6_topology(area); + mt_router_info = isis_tlvs_lookup_mt_router_info( + root_lsp->tlvs, ISIS_MT_IPV6_UNICAST); + if (mt_router_info) + mtid = ISIS_MT_IPV6_UNICAST; + else + mtid = ISIS_MT_IPV4_UNICAST; break; case SPFTREE_DSTSRC: - family = AF_INET6; mtid = ISIS_MT_IPV6_DSTSRC; break; case SPFTREE_COUNT: - assert(!"isis_run_spf should never be called with SPFTREE_COUNT as argument!"); - return ISIS_WARNING; + zlog_err( + "isis_run_spf should never be called with SPFTREE_COUNT as argument!"); + exit(1); } - assert(spftree); - assert(sysid); - /* * C.2.5 Step 0 */ - init_spt(spftree, mtid, level, family, tree_id, false); + init_spt(spftree, mtid); /* a) */ - root_vertex = isis_spf_add_root(spftree, sysid); + root_vertex = isis_spf_add_root(spftree); /* b) */ - retval = isis_spf_preload_tent(spftree, sysid, root_vertex); - if (retval != ISIS_OK) { - zlog_warn("ISIS-Spf: failed to load TENT SPF-root:%s", - print_sys_hostname(sysid)); - goto out; - } + isis_spf_build_adj_list(spftree, root_lsp); + isis_spf_preload_tent(spftree, spftree->sysid, root_lsp, root_vertex); /* * C.2.7 Step 2 @@ -1155,19 +1344,16 @@ static int isis_run_spf(struct isis_area *area, int level, if (!isis_vertex_queue_count(&spftree->tents) && (IS_DEBUG_SPF_EVENTS)) { zlog_warn("ISIS-Spf: TENT is empty SPF-root:%s", - print_sys_hostname(sysid)); + print_sys_hostname(spftree->sysid)); } - isis_spf_loop(spftree, sysid); -out: + isis_spf_loop(spftree, spftree->sysid); spftree->runcount++; spftree->last_run_timestamp = time(NULL); - spftree->last_run_monotime = monotime(&time_now); - end_time = time_now.tv_sec; - end_time = (end_time * 1000000) + time_now.tv_usec; - spftree->last_run_duration = end_time - start_time; - - return retval; + spftree->last_run_monotime = monotime(&time_end); + spftree->last_run_duration = + ((time_end.tv_sec - time_start.tv_sec) * 1000000) + + (time_end.tv_usec - time_start.tv_usec); } void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees) @@ -1191,8 +1377,8 @@ static int isis_run_spf_cb(struct thread *thread) { struct isis_spf_run *run = THREAD_ARG(thread); struct isis_area *area = run->area; + struct isis_spftree *spftree; int level = run->level; - int retval = ISIS_OK; XFREE(MTYPE_ISIS_SPF_RUN, run); area->spf_timer[level - 1] = NULL; @@ -1210,16 +1396,21 @@ static int isis_run_spf_cb(struct thread *thread) zlog_debug("ISIS-Spf (%s) L%d SPF needed, periodic SPF", area->area_tag, level); - if (area->ip_circuits) - retval = isis_run_spf(area, level, SPFTREE_IPV4, isis->sysid, - &thread->real); - if (area->ipv6_circuits) - retval = isis_run_spf(area, level, SPFTREE_IPV6, isis->sysid, - &thread->real); - if (area->ipv6_circuits - && isis_area_ipv6_dstsrc_enabled(area)) - retval = isis_run_spf(area, level, SPFTREE_DSTSRC, isis->sysid, - &thread->real); + if (area->ip_circuits) { + spftree = area->spftree[SPFTREE_IPV4][level - 1]; + memcpy(spftree->sysid, area->isis->sysid, ISIS_SYS_ID_LEN); + isis_run_spf(spftree); + } + if (area->ipv6_circuits) { + spftree = area->spftree[SPFTREE_IPV6][level - 1]; + memcpy(spftree->sysid, area->isis->sysid, ISIS_SYS_ID_LEN); + isis_run_spf(spftree); + } + if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area)) { + spftree = area->spftree[SPFTREE_DSTSRC][level - 1]; + memcpy(spftree->sysid, area->isis->sysid, ISIS_SYS_ID_LEN); + isis_run_spf(spftree); + } isis_area_verify_routes(area); @@ -1233,7 +1424,7 @@ static int isis_run_spf_cb(struct thread *thread) fabricd_run_spf(area); - return retval; + return 0; } static struct isis_spf_run *isis_run_spf_arg(struct isis_area *area, int level) @@ -1253,6 +1444,9 @@ int _isis_spf_schedule(struct isis_area *area, int level, time_t now = monotime(NULL); int diff = now - spftree->last_run_monotime; + if (CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) + return 0; + assert(diff >= 0); assert(area->is_type & level); @@ -1332,7 +1526,7 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue, int rows = 0; struct listnode *anode = listhead(vertex->Adj_N); struct listnode *pnode = listhead(vertex->parents); - struct isis_adjacency *adj; + struct isis_vertex_adj *vadj; struct isis_vertex *pvertex; vty_out(vty, "%-20s %-12s %-6u ", @@ -1343,10 +1537,10 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue, vertex->parents ? listcount(vertex->parents) : 0); i++) { if (anode) { - adj = listgetdata(anode); + vadj = listgetdata(anode); anode = anode->next; } else { - adj = NULL; + vadj = NULL; } if (pnode) { @@ -1361,14 +1555,18 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue, vty_out(vty, "%-20s %-12s %-6s ", "", "", ""); } - if (adj) { + if (vadj) { + struct isis_spf_adj *sadj = vadj->sadj; + vty_out(vty, "%-20s %-9s ", - print_sys_hostname(adj->sysid), - adj->circuit->interface->name); + print_sys_hostname(sadj->id), + sadj->adj ? sadj->adj->circuit + ->interface->name + : "-"); } if (pvertex) { - if (!adj) + if (!vadj) vty_out(vty, "%-20s %-9s ", "", ""); vty_out(vty, "%s(%d)", @@ -1382,13 +1580,14 @@ static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue, } } -static void isis_print_spftree(struct vty *vty, int level, - struct isis_area *area, - enum spf_tree_id tree_id) +void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree) { const char *tree_id_text = NULL; - switch (tree_id) { + if (!spftree || !isis_vertex_queue_count(&spftree->paths)) + return; + + switch (spftree->tree_id) { case SPFTREE_IPV4: tree_id_text = "that speak IP"; break; @@ -1403,46 +1602,20 @@ static void isis_print_spftree(struct vty *vty, int level, return; } - if (!area->spftree[tree_id][level - 1] - || !isis_vertex_queue_count( - &area->spftree[tree_id][level - 1]->paths)) - return; - - vty_out(vty, "IS-IS paths to level-%d routers %s\n", - level, tree_id_text); - isis_print_paths(vty, &area->spftree[tree_id][level - 1]->paths, - isis->sysid); + vty_out(vty, "IS-IS paths to level-%d routers %s\n", spftree->level, + tree_id_text); + isis_print_paths(vty, &spftree->paths, spftree->sysid); vty_out(vty, "\n"); } -DEFUN (show_isis_topology, - show_isis_topology_cmd, - "show " PROTO_NAME " topology" -#ifndef FABRICD - " [<level-1|level-2>]" -#endif - , SHOW_STR - PROTO_HELP - "IS-IS paths to Intermediate Systems\n" -#ifndef FABRICD - "Paths to all level-1 routers in the area\n" - "Paths to all level-2 routers in the domain\n" -#endif - ) +static void show_isis_topology_common(struct vty *vty, int levels, + struct isis *isis) { - int levels; struct listnode *node; struct isis_area *area; - if (argc < 4) - levels = ISIS_LEVEL1 | ISIS_LEVEL2; - else if (strmatch(argv[3]->text, "level-1")) - levels = ISIS_LEVEL1; - else - levels = ISIS_LEVEL2; - if (!isis->area_list || isis->area_list->count == 0) - return CMD_SUCCESS; + return; for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { vty_out(vty, "Area %s:\n", @@ -1453,16 +1626,19 @@ DEFUN (show_isis_topology, continue; if (area->ip_circuits > 0) { - isis_print_spftree(vty, level, area, - SPFTREE_IPV4); + isis_print_spftree( + vty, + area->spftree[SPFTREE_IPV4][level - 1]); } if (area->ipv6_circuits > 0) { - isis_print_spftree(vty, level, area, - SPFTREE_IPV6); + isis_print_spftree( + vty, + area->spftree[SPFTREE_IPV6][level - 1]); } if (isis_area_ipv6_dstsrc_enabled(area)) { - isis_print_spftree(vty, level, area, - SPFTREE_DSTSRC); + isis_print_spftree(vty, + area->spftree[SPFTREE_DSTSRC] + [level - 1]); } } @@ -1475,13 +1651,254 @@ DEFUN (show_isis_topology, vty_out(vty, "\n"); } +} + +DEFUN(show_isis_topology, show_isis_topology_cmd, + "show " PROTO_NAME + " [vrf <NAME|all>] topology" +#ifndef FABRICD + " [<level-1|level-2>]" +#endif + , + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS paths to Intermediate Systems\n" +#ifndef FABRICD + "Paths to all level-1 routers in the area\n" + "Paths to all level-2 routers in the domain\n" +#endif +) +{ + int levels = ISIS_LEVELS; + struct listnode *node; + struct isis *isis = NULL; + int idx = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + if (argv_find(argv, argc, "topology", &idx)) { + if (argc < idx + 2) + levels = ISIS_LEVEL1 | ISIS_LEVEL2; + else if (strmatch(argv[idx + 1]->arg, "level-1")) + levels = ISIS_LEVEL1; + else + levels = ISIS_LEVEL2; + } + + if (!im) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + show_isis_topology_common(vty, levels, isis); + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + show_isis_topology_common(vty, levels, isis); + } + + return CMD_SUCCESS; +} + +void isis_print_routes(struct vty *vty, struct isis_spftree *spftree) +{ + struct ttable *tt; + struct route_node *rn; + const char *tree_id_text = NULL; + + if (!spftree) + return; + + switch (spftree->tree_id) { + case SPFTREE_IPV4: + tree_id_text = "IPv4"; + break; + case SPFTREE_IPV6: + tree_id_text = "IPv6"; + break; + case SPFTREE_DSTSRC: + tree_id_text = "IPv6 (dst-src routing)"; + break; + case SPFTREE_COUNT: + assert(!"isis_print_routes shouldn't be called with SPFTREE_COUNT as type"); + return; + } + + vty_out(vty, "IS-IS %s %s routing table:\n\n", + circuit_t2string(spftree->level), tree_id_text); + + /* Prepare table. */ + tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); + ttable_add_row(tt, "Prefix|Metric|Interface|Nexthop|Label(s)"); + tt->style.cell.rpad = 2; + tt->style.corner = '+'; + ttable_restyle(tt); + ttable_rowseps(tt, 0, BOTTOM, true, '-'); + + for (rn = route_top(spftree->route_table); rn; rn = route_next(rn)) { + struct isis_route_info *rinfo; + struct isis_nexthop *nexthop; + struct listnode *node; + bool first = true; + char buf_prefix[BUFSIZ]; + + rinfo = rn->info; + if (!rinfo) + continue; + + (void)prefix2str(&rn->p, buf_prefix, sizeof(buf_prefix)); + for (ALL_LIST_ELEMENTS_RO(rinfo->nexthops, node, nexthop)) { + struct interface *ifp; + char buf_iface[BUFSIZ]; + char buf_nhop[BUFSIZ]; + char buf_labels[BUFSIZ] = {}; + + if (!CHECK_FLAG(spftree->flags, + F_SPFTREE_NO_ADJACENCIES)) { + inet_ntop(nexthop->family, &nexthop->ip, + buf_nhop, sizeof(buf_nhop)); + ifp = if_lookup_by_index(nexthop->ifindex, + VRF_DEFAULT); + if (ifp) + strlcpy(buf_iface, ifp->name, + sizeof(buf_iface)); + else + snprintf(buf_iface, sizeof(buf_iface), + "ifindex %u", + nexthop->ifindex); + } else { + strlcpy(buf_nhop, + print_sys_hostname(nexthop->sysid), + sizeof(buf_nhop)); + strlcpy(buf_iface, "-", sizeof(buf_iface)); + } + + if (nexthop->sr.label != MPLS_INVALID_LABEL) + label2str(nexthop->sr.label, buf_labels, + sizeof(buf_labels)); + else + strlcpy(buf_labels, "-", sizeof(buf_labels)); + + if (first) { + ttable_add_row(tt, "%s|%u|%s|%s|%s", buf_prefix, + rinfo->cost, buf_iface, buf_nhop, + buf_labels); + first = false; + } else + ttable_add_row(tt, "||%s|%s|%s", buf_iface, + buf_nhop, buf_labels); + } + } + + /* Dump the generated table. */ + if (tt->nrows > 1) { + char *table; + + table = ttable_dump(tt, "\n"); + vty_out(vty, "%s\n", table); + XFREE(MTYPE_TMP, table); + } + ttable_del(tt); +} + +static void show_isis_route_common(struct vty *vty, int levels, + struct isis *isis) +{ + struct listnode *node; + struct isis_area *area; + + if (!isis->area_list || isis->area_list->count == 0) + return; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag : "null"); + + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + if ((level & levels) == 0) + continue; + + if (area->ip_circuits > 0) { + isis_print_routes( + vty, + area->spftree[SPFTREE_IPV4][level - 1]); + } + if (area->ipv6_circuits > 0) { + isis_print_routes( + vty, + area->spftree[SPFTREE_IPV6][level - 1]); + } + if (isis_area_ipv6_dstsrc_enabled(area)) { + isis_print_routes(vty, + area->spftree[SPFTREE_DSTSRC] + [level - 1]); + } + } + } +} + +DEFUN(show_isis_route, show_isis_route_cmd, + "show " PROTO_NAME + " [vrf <NAME|all>] route" +#ifndef FABRICD + " [<level-1|level-2>]" +#endif + , + SHOW_STR PROTO_HELP VRF_FULL_CMD_HELP_STR + "IS-IS routing table\n" +#ifndef FABRICD + "level-1 routes\n" + "level-2 routes\n" +#endif +) +{ + int levels; + struct isis *isis; + struct listnode *node; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx = 0; + + if (argv_find(argv, argc, "level-1", &idx)) + levels = ISIS_LEVEL1; + else if (argv_find(argv, argc, "level-2", &idx)) + levels = ISIS_LEVEL2; + else + levels = ISIS_LEVEL1 | ISIS_LEVEL2; + + if (!im) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } + ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf); + + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + show_isis_route_common(vty, levels, isis); + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + show_isis_route_common(vty, levels, isis); + } return CMD_SUCCESS; } -void isis_spf_cmds_init(void) +void isis_spf_init(void) { install_element(VIEW_NODE, &show_isis_topology_cmd); + install_element(VIEW_NODE, &show_isis_route_cmd); + + /* Register hook(s). */ + hook_register(isis_adj_state_change_hook, spf_adj_state_change); } void isis_spf_print(struct isis_spftree *spftree, struct vty *vty) diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index 8bf9c9978a..b2dc23496f 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -26,21 +26,46 @@ struct isis_spftree; -struct isis_spftree *isis_spftree_new(struct isis_area *area); +enum spf_type { + SPF_TYPE_FORWARD = 1, + SPF_TYPE_REVERSE, +}; + +struct isis_spf_adj { + uint8_t id[ISIS_SYS_ID_LEN + 1]; + struct isis_adjacency *adj; + uint32_t metric; + struct isis_ext_subtlvs *subtlvs; + struct { + uint8_t desig_is_id[ISIS_SYS_ID_LEN + 1]; + struct isis_lsp *lsp_pseudo; + } lan; + uint8_t flags; +#define F_ISIS_SPF_ADJ_BROADCAST 0x01 +#define F_ISIS_SPF_ADJ_OLDMETRIC 0x02 +}; + +struct isis_spftree *isis_spftree_new(struct isis_area *area, + struct lspdb_head *lspdb, + const uint8_t *sysid, int level, + enum spf_tree_id tree_id, + enum spf_type type, uint8_t flags); void isis_spf_invalidate_routes(struct isis_spftree *tree); void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees); void isis_spftree_del(struct isis_spftree *spftree); void spftree_area_init(struct isis_area *area); void spftree_area_del(struct isis_area *area); -void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj); #define isis_spf_schedule(area, level) \ _isis_spf_schedule((area), (level), __func__, \ __FILE__, __LINE__) int _isis_spf_schedule(struct isis_area *area, int level, const char *func, const char *file, int line); -void isis_spf_cmds_init(void); +void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree); +void isis_print_routes(struct vty *vty, struct isis_spftree *spftree); +void isis_spf_init(void); void isis_spf_print(struct isis_spftree *spftree, struct vty *vty); +void isis_run_spf(struct isis_spftree *spftree); struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, uint8_t *sysid, struct isis_spftree *spftree); diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 05aae14b94..1e61bf0f48 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -50,6 +50,11 @@ struct prefix_pair { struct prefix_ipv6 src; }; +struct isis_vertex_adj { + struct isis_spf_adj *sadj; + struct mpls_label_stack *label_stack; +}; + /* * Triple <N, d(N), {Adj(N)}> */ @@ -180,6 +185,10 @@ static void isis_vertex_del(struct isis_vertex *vertex) XFREE(MTYPE_ISIS_VERTEX, vertex); } +bool isis_vertex_adj_exists(const struct isis_spftree *spftree, + const struct isis_vertex *vertex, + const struct isis_spf_adj *sadj); + __attribute__((__unused__)) static void isis_vertex_queue_clear(struct isis_vertex_queue *queue) { @@ -297,18 +306,26 @@ struct isis_spftree { struct isis_vertex_queue paths; /* the SPT */ struct isis_vertex_queue tents; /* TENT */ struct route_table *route_table; + struct lspdb_head *lspdb; /* link-state db */ + struct list *sadj_list; struct isis_area *area; /* back pointer to area */ unsigned int runcount; /* number of runs since uptime */ time_t last_run_timestamp; /* last run timestamp as wall time for display */ time_t last_run_monotime; /* last run as monotime for scheduling */ time_t last_run_duration; /* last run duration in msec */ + enum spf_type type; + uint8_t sysid[ISIS_SYS_ID_LEN]; uint16_t mtid; int family; int level; enum spf_tree_id tree_id; bool hopcount_metric; + uint8_t flags; }; +#define F_SPFTREE_HOPCOUNT_METRIC 0x01 +#define F_SPFTREE_NO_ROUTES 0x02 +#define F_SPFTREE_NO_ADJACENCIES 0x04 __attribute__((__unused__)) static void isis_vertex_id_init(struct isis_vertex *vertex, const void *id, @@ -347,8 +364,7 @@ static struct isis_lsp *lsp_for_vertex(struct isis_spftree *spftree, memcpy(lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT(lsp_id) = 0; - struct lspdb_head *lspdb = &spftree->area->lspdb[spftree->level - 1]; - struct isis_lsp *lsp = lsp_search(lspdb, lsp_id); + struct isis_lsp *lsp = lsp_search(spftree->lspdb, lsp_id); if (lsp && lsp->hdr.rem_lifetime != 0) return lsp; diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c index 59f00a73f5..d05afaa630 100644 --- a/isisd/isis_sr.c +++ b/isisd/isis_sr.c @@ -210,7 +210,7 @@ int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound, uint32_t upper_bound) { struct isis_sr_db *srdb = &area->srdb; - struct listnode *node, *nnode; + struct listnode *node; struct sr_adjacency *sra; sr_debug("ISIS-Sr (%s): Update SRLB with new range [%u/%u]", @@ -236,7 +236,7 @@ int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound, return -1; /* Reinstall local Adjacency-SIDs with new labels. */ - for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra)) + for (ALL_LIST_ELEMENTS_RO(area->srdb.adj_sids, node, sra)) sr_adj_sid_update(sra, &srdb->srlb); /* Update and Flood LSP */ @@ -1055,7 +1055,7 @@ static void parse_prefix_sid_subtlvs(struct sr_node *srn, || srp->sid.value != psid->value) { srp->sid = *psid; srp->state = SRDB_STATE_MODIFIED; - } else + } else if (srp->state == SRDB_STATE_VALIDATED) srp->state = SRDB_STATE_UNCHANGED; sr_debug(" |- Found %s Prefix-SID %pFX", srp->state == SRDB_STATE_MODIFIED @@ -1223,7 +1223,7 @@ static void process_node_changes(struct isis_area *area, int level, * If an neighbor router's SRGB was changed or created, then reinstall * all Prefix-SIDs from all nodes that use this neighbor as nexthop. */ - adjacent = isis_adj_exists(area, level, sysid); + adjacent = !!isis_adj_find(area, level, sysid); switch (srn->state) { case SRDB_STATE_NEW: case SRDB_STATE_MODIFIED: @@ -1521,13 +1521,13 @@ static void sr_adj_sid_add_single(struct isis_adjacency *adj, int family, /* Determine nexthop IP address */ switch (family) { case AF_INET: - if (!circuit->ip_router) + if (!circuit->ip_router || !adj->ipv4_address_count) return; nexthop.ipv4 = adj->ipv4_addresses[0]; break; case AF_INET6: - if (!circuit->ipv6_router) + if (!circuit->ipv6_router || !adj->ipv6_address_count) return; nexthop.ipv6 = adj->ipv6_addresses[0]; @@ -1980,20 +1980,48 @@ static void show_prefix_sids(struct vty *vty, struct isis_area *area, int level) * Declaration of new show commands. */ DEFUN(show_sr_prefix_sids, show_sr_prefix_sids_cmd, - "show isis segment-routing prefix-sids", - SHOW_STR PROTO_HELP + "show isis [vrf <NAME|all>] segment-routing prefix-sids", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" "Segment-Routing\n" "Segment-Routing Prefix-SIDs\n") { - struct listnode *node; + struct listnode *node, *inode; struct isis_area *area; - - for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { - vty_out(vty, "Area %s:\n", - area->area_tag ? area->area_tag : "null"); - - for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) - show_prefix_sids(vty, area, level); + struct isis *isis = NULL; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag + : "null"); + for (int level = ISIS_LEVEL1; + level <= ISIS_LEVELS; level++) + show_prefix_sids(vty, area, + level); + } + } + return 0; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, + area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag + : "null"); + for (int level = ISIS_LEVEL1; + level <= ISIS_LEVELS; level++) + show_prefix_sids(vty, area, level); + } + } } return CMD_SUCCESS; @@ -2056,15 +2084,19 @@ DEFUN(show_sr_node, show_sr_node_cmd, "Segment-Routing\n" "Segment-Routing node\n") { - struct listnode *node; + struct listnode *node, *inode; struct isis_area *area; + struct isis *isis; - for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { - vty_out(vty, "Area %s:\n", - area->area_tag ? area->area_tag : "null"); + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag : "null"); - for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) - show_node(vty, area, level); + for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; + level++) + show_node(vty, area, level); + } } return CMD_SUCCESS; @@ -2103,7 +2135,7 @@ static int sr_start_label_manager(struct thread *start) int isis_sr_start(struct isis_area *area) { struct isis_sr_db *srdb = &area->srdb; - struct isis_circuit *circuit; + struct isis_adjacency *adj; struct listnode *node; /* First start Label Manager if not ready */ @@ -2140,34 +2172,11 @@ int isis_sr_start(struct isis_area *area) area->area_tag); /* Create Adjacency-SIDs from existing IS-IS Adjacencies. */ - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { - struct isis_adjacency *adj; - struct listnode *anode; - - switch (circuit->circ_type) { - case CIRCUIT_T_BROADCAST: - for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; - level++) { - for (ALL_LIST_ELEMENTS_RO( - circuit->u.bc.adjdb[level - 1], - anode, adj)) { - if (adj->ipv4_address_count > 0) - sr_adj_sid_add(adj, AF_INET); - if (adj->ipv6_address_count > 0) - sr_adj_sid_add(adj, AF_INET6); - } - } - break; - case CIRCUIT_T_P2P: - adj = circuit->u.p2p.neighbor; - if (adj && adj->ipv4_address_count > 0) - sr_adj_sid_add(adj, AF_INET); - if (adj && adj->ipv6_address_count > 0) - sr_adj_sid_add(adj, AF_INET6); - break; - default: - break; - } + for (ALL_LIST_ELEMENTS_RO(area->adjacency_list, node, adj)) { + if (adj->ipv4_address_count > 0) + sr_adj_sid_add(adj, AF_INET); + if (adj->ipv6_address_count > 0) + sr_adj_sid_add(adj, AF_INET6); } area->srdb.enabled = true; diff --git a/isisd/isis_te.c b/isisd/isis_te.c index a599909eb8..87c4428155 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -302,34 +302,68 @@ int isis_mpls_te_update(struct interface *ifp) /* Followings are vty command functions */ #ifndef FABRICD -DEFUN (show_isis_mpls_te_router, - show_isis_mpls_te_router_cmd, - "show " PROTO_NAME " mpls-te router", - SHOW_STR - PROTO_HELP - MPLS_TE_STR - "Router information\n") +DEFUN(show_isis_mpls_te_router, + show_isis_mpls_te_router_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] mpls-te router", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR "All VRFs\n" + MPLS_TE_STR "Router information\n") { - struct listnode *anode; + struct listnode *anode, *inode; struct isis_area *area; + struct isis *isis = NULL; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; - if (!isis) { + if (!im) { vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } - - for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { - - if (!IS_MPLS_TE(area->mta)) - continue; - - vty_out(vty, "Area %s:\n", area->area_tag); - if (ntohs(area->mta->router_id.s_addr) != 0) - vty_out(vty, " MPLS-TE Router-Address: %s\n", - inet_ntoa(area->mta->router_id)); - else - vty_out(vty, " N/A\n"); + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, + anode, area)) { + if (!IS_MPLS_TE(area->mta)) + continue; + + vty_out(vty, "Area %s:\n", + area->area_tag); + if (ntohs(area->mta->router_id.s_addr) + != 0) + vty_out(vty, + " MPLS-TE Router-Address: %s\n", + inet_ntoa( + area->mta + ->router_id)); + else + vty_out(vty, " N/A\n"); + } + } + return 0; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, + area)) { + + if (!IS_MPLS_TE(area->mta)) + continue; + + vty_out(vty, "Area %s:\n", area->area_tag); + if (ntohs(area->mta->router_id.s_addr) != 0) + vty_out(vty, + " MPLS-TE Router-Address: %s\n", + inet_ntoa( + area->mta->router_id)); + else + vty_out(vty, " N/A\n"); + } + } } return CMD_SUCCESS; @@ -449,30 +483,35 @@ DEFUN (show_isis_mpls_te_interface, "Interface information\n" "Interface name\n") { - struct listnode *anode, *cnode; + struct listnode *anode, *cnode, *inode; struct isis_area *area; struct isis_circuit *circuit; struct interface *ifp; int idx_interface = 4; + struct isis *isis = NULL; - if (!isis) { + if (!im) { vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } if (argc == idx_interface) { /* Show All Interfaces. */ - for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, + area)) { - if (!IS_MPLS_TE(area->mta)) - continue; + if (!IS_MPLS_TE(area->mta)) + continue; - vty_out(vty, "Area %s:\n", area->area_tag); + vty_out(vty, "Area %s:\n", area->area_tag); - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, - circuit)) - show_ext_sub(vty, circuit->interface->name, - circuit->ext); + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, + cnode, circuit)) + show_ext_sub(vty, + circuit->interface->name, + circuit->ext); + } } } else { /* Interface name is specified. */ diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c index a574c5bd3f..d0a411a8db 100644 --- a/isisd/isis_vty_fabricd.c +++ b/isisd/isis_vty_fabricd.c @@ -112,12 +112,13 @@ DEFUN (no_triggered_csnp, return CMD_SUCCESS; } -static void lsp_print_flooding(struct vty *vty, struct isis_lsp *lsp) +static void lsp_print_flooding(struct vty *vty, struct isis_lsp *lsp, + struct isis *isis) { char lspid[255]; char buf[MONOTIME_STRLEN]; - lspid_print(lsp->hdr.lsp_id, lspid, true, true); + lspid_print(lsp->hdr.lsp_id, lspid, true, true, isis); vty_out(vty, "Flooding information for %s\n", lspid); if (!lsp->flooding_neighbors[TX_LSP_NORMAL]) { @@ -170,25 +171,29 @@ DEFUN (show_lsp_flooding, struct listnode *node; struct isis_area *area; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfid(VRF_DEFAULT); + + if (isis == NULL) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { struct lspdb_head *head = &area->lspdb[ISIS_LEVEL2 - 1]; struct isis_lsp *lsp; - vty_out(vty, "Area %s:\n", area->area_tag ? - area->area_tag : "null"); - + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag : "null"); if (lspid) { - lsp = lsp_for_arg(head, lspid); - + lsp = lsp_for_arg(head, lspid, isis); if (lsp) - lsp_print_flooding(vty, lsp); - + lsp_print_flooding(vty, lsp, isis); continue; } - frr_each (lspdb, head, lsp) { - lsp_print_flooding(vty, lsp); + lsp_print_flooding(vty, lsp, isis); vty_out(vty, "\n"); } } @@ -222,9 +227,9 @@ DEFUN (ip_router_isis, } } - area = isis_area_lookup(area_tag); + area = isis_area_lookup(area_tag, VRF_DEFAULT); if (!area) - area = isis_area_create(area_tag); + area = isis_area_create(area_tag, VRF_DEFAULT_NAME); if (!circuit || !circuit->area) { circuit = isis_circuit_create(area, ifp); @@ -276,7 +281,7 @@ DEFUN (no_ip_router_isis, const char *af = argv[idx_afi]->arg; const char *area_tag = argv[idx_word]->arg; - area = isis_area_lookup(area_tag); + area = isis_area_lookup(area_tag, VRF_DEFAULT); if (!area) { vty_out(vty, "Can't find ISIS instance %s\n", area_tag); diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index a80a18d887..a50eb607d9 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -62,6 +62,13 @@ static int isis_router_id_update_zebra(ZAPI_CALLBACK_ARGS) struct isis_area *area; struct listnode *node; struct prefix router_id; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfid(vrf_id); + + if (isis == NULL) { + return -1; + } zebra_router_id_update_read(zclient->ibuf, &router_id); if (isis->router_id == router_id.u.prefix4.s_addr) @@ -407,6 +414,12 @@ void isis_zebra_send_adjacency_sid(int cmd, const struct sr_adjacency *sra) static int isis_zebra_read(ZAPI_CALLBACK_ARGS) { struct zapi_route api; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfid(vrf_id); + + if (isis == NULL) + return -1; if (zapi_route_decode(zclient->ibuf, &api) < 0) return -1; @@ -428,10 +441,11 @@ static int isis_zebra_read(ZAPI_CALLBACK_ARGS) } if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) - isis_redist_add(api.type, &api.prefix, &api.src_prefix, + isis_redist_add(isis, api.type, &api.prefix, &api.src_prefix, api.distance, api.metric); else - isis_redist_delete(api.type, &api.prefix, &api.src_prefix); + isis_redist_delete(isis, api.type, &api.prefix, + &api.src_prefix); return 0; } @@ -563,6 +577,20 @@ int isis_zebra_label_manager_connect(void) return 0; } +void isis_zebra_vrf_register(struct isis *isis) +{ + if (!zclient || zclient->sock < 0 || !isis) + return; + + if (isis->vrf_id != VRF_UNKNOWN) { + if (IS_DEBUG_EVENTS) + zlog_debug("%s: Register VRF %s id %u", __func__, + isis->name, isis->vrf_id); + zclient_send_reg_requests(zclient, isis->vrf_id); + } +} + + static void isis_zebra_connected(struct zclient *zclient) { zclient_send_reg_requests(zclient, VRF_DEFAULT); diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h index 4449b63c2e..768919ff46 100644 --- a/isisd/isis_zebra.h +++ b/isisd/isis_zebra.h @@ -57,5 +57,6 @@ bool isis_zebra_label_manager_ready(void); int isis_zebra_label_manager_connect(void); int isis_zebra_request_label_range(uint32_t base, uint32_t chunk_size); int isis_zebra_release_label_range(uint32_t start, uint32_t end); +void isis_zebra_vrf_register(struct isis *isis); #endif /* _ZEBRA_ISIS_ZEBRA_H */ diff --git a/isisd/isisd.c b/isisd/isisd.c index 6f126b7faf..2a2c71b1fd 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -35,6 +35,8 @@ #include "prefix.h" #include "table.h" #include "qobj.h" +#include "zclient.h" +#include "vrf.h" #include "spf_backoff.h" #include "lib/northbound_cli.h" @@ -75,46 +77,177 @@ unsigned long debug_bfd; unsigned long debug_tx_queue; unsigned long debug_sr; -struct isis *isis = NULL; - -DEFINE_QOBJ_TYPE(isis) DEFINE_QOBJ_TYPE(isis_area) +/* ISIS process wide configuration. */ +static struct isis_master isis_master; + +/* ISIS process wide configuration pointer to export. */ +struct isis_master *im; + /* * Prototypes. */ int isis_area_get(struct vty *, const char *); int area_net_title(struct vty *, const char *); int area_clear_net_title(struct vty *, const char *); -int show_isis_interface_common(struct vty *, const char *ifname, char); -int show_isis_neighbor_common(struct vty *, const char *id, char); -int clear_isis_neighbor_common(struct vty *, const char *id); +int show_isis_interface_common(struct vty *, const char *ifname, char, + const char *vrf_name, bool all_vrf); +int show_isis_neighbor_common(struct vty *, const char *id, char, + const char *vrf_name, bool all_vrf); +int clear_isis_neighbor_common(struct vty *, const char *id, const char *vrf_name, + bool all_vrf); + +static void isis_add(struct isis *isis) +{ + listnode_add(im->isis, isis); +} +static void isis_delete(struct isis *isis) +{ + listnode_delete(im->isis, isis); +} + +/* Link ISIS instance to VRF. */ +void isis_vrf_link(struct isis *isis, struct vrf *vrf) +{ + isis->vrf_id = vrf->vrf_id; + if (vrf->info != (void *)isis) + vrf->info = (void *)isis; +} -void isis_new(unsigned long process_id, vrf_id_t vrf_id) +/* Unlink ISIS instance to VRF. */ +void isis_vrf_unlink(struct isis *isis, struct vrf *vrf) { + if (vrf->info == (void *)isis) + vrf->info = NULL; + isis->vrf_id = VRF_UNKNOWN; +} + +struct isis *isis_lookup_by_vrfid(vrf_id_t vrf_id) +{ + struct isis *isis; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + if (isis->vrf_id == vrf_id) + return isis; + + return NULL; +} + +struct isis *isis_lookup_by_vrfname(const char *vrfname) +{ + struct isis *isis; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + if (isis->name && vrfname && strcmp(isis->name, vrfname) == 0) + return isis; + + return NULL; +} + +struct isis *isis_lookup_by_sysid(const uint8_t *sysid) +{ + struct isis *isis; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + if (!memcmp(isis->sysid, sysid, ISIS_SYS_ID_LEN)) + return isis; + + return NULL; +} + +void isis_master_init(struct thread_master *master) +{ + memset(&isis_master, 0, sizeof(struct isis_master)); + im = &isis_master; + im->isis = list_new(); + im->master = master; +} + +void isis_global_instance_create(const char *vrf_name) +{ + struct isis *isis; + + isis = isis_lookup_by_vrfname(vrf_name); + if (isis == NULL) { + isis = isis_new(vrf_name); + isis_add(isis); + } +} + +struct isis *isis_new(const char *vrf_name) +{ + struct vrf *vrf; + struct isis *isis; + isis = XCALLOC(MTYPE_ISIS, sizeof(struct isis)); + vrf = vrf_lookup_by_name(vrf_name); + + if (vrf) { + isis->vrf_id = vrf->vrf_id; + isis_vrf_link(isis, vrf); + isis->name = XSTRDUP(MTYPE_ISIS, vrf->name); + } else { + isis->vrf_id = VRF_UNKNOWN; + isis->name = XSTRDUP(MTYPE_ISIS, vrf_name); + } + + if (IS_DEBUG_EVENTS) + zlog_debug( + "%s: Create new isis instance with vrf_name %s vrf_id %u", + __func__, isis->name, isis->vrf_id); + /* * Default values */ - isis->vrf_id = vrf_id; isis->max_area_addrs = 3; - isis->process_id = process_id; + isis->process_id = getpid(); isis->router_id = 0; isis->area_list = list_new(); isis->init_circ_list = list_new(); isis->uptime = time(NULL); - dyn_cache_init(); + dyn_cache_init(isis); - QOBJ_REG(isis, isis); + return isis; } -struct isis_area *isis_area_create(const char *area_tag) +struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name) { struct isis_area *area; - + struct isis *isis = NULL; + struct vrf *vrf = NULL; area = XCALLOC(MTYPE_ISIS_AREA, sizeof(struct isis_area)); + if (vrf_name) { + vrf = vrf_lookup_by_name(vrf_name); + if (vrf) { + isis = isis_lookup_by_vrfid(vrf->vrf_id); + if (isis == NULL) { + isis = isis_new(vrf_name); + isis_add(isis); + } + } else { + isis = isis_lookup_by_vrfid(VRF_UNKNOWN); + if (isis == NULL) { + isis = isis_new(vrf_name); + isis_add(isis); + } + } + } else { + isis = isis_lookup_by_vrfid(VRF_DEFAULT); + if (isis == NULL) { + isis = isis_new(VRF_DEFAULT_NAME); + isis_add(isis); + } + } + + listnode_add(isis->area_list, area); + area->isis = isis; + /* * Fabricd runs only as level-2. * For IS-IS, the default is level-1-2 @@ -136,8 +269,10 @@ struct isis_area *isis_area_create(const char *area_tag) spftree_area_init(area); area->circuit_list = list_new(); + area->adjacency_list = list_new(); area->area_addrs = list_new(); - thread_add_timer(master, lsp_tick, area, 1, &area->t_tick); + if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) + thread_add_timer(master, lsp_tick, area, 1, &area->t_tick); flags_initialize(&area->flags); isis_sr_area_init(area); @@ -192,8 +327,6 @@ struct isis_area *isis_area_create(const char *area_tag) area_mt_init(area); area->area_tag = strdup(area_tag); - listnode_add(isis->area_list, area); - area->isis = isis; if (fabricd) area->fabricd = fabricd_new(area); @@ -212,10 +345,33 @@ struct isis_area *isis_area_create(const char *area_tag) return area; } -struct isis_area *isis_area_lookup(const char *area_tag) +struct isis_area *isis_area_lookup_by_vrf(const char *area_tag, + const char *vrf_name) +{ + struct isis_area *area; + struct listnode *node; + struct isis *isis = NULL; + + isis = isis_lookup_by_vrfname(vrf_name); + if (isis == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) + if (strcmp(area->area_tag, area_tag) == 0) + return area; + + return NULL; +} + +struct isis_area *isis_area_lookup(const char *area_tag, vrf_id_t vrf_id) { struct isis_area *area; struct listnode *node; + struct isis *isis; + + isis = isis_lookup_by_vrfid(vrf_id); + if (isis == NULL) + return NULL; for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) if ((area->area_tag == NULL && area_tag == NULL) @@ -230,14 +386,14 @@ int isis_area_get(struct vty *vty, const char *area_tag) { struct isis_area *area; - area = isis_area_lookup(area_tag); + area = isis_area_lookup(area_tag, VRF_DEFAULT); if (area) { VTY_PUSH_CONTEXT(ROUTER_NODE, area); return CMD_SUCCESS; } - area = isis_area_create(area_tag); + area = isis_area_create(area_tag, VRF_DEFAULT_NAME); if (IS_DEBUG_EVENTS) zlog_debug("New IS-IS area instance %s", area->area_tag); @@ -247,21 +403,12 @@ int isis_area_get(struct vty *vty, const char *area_tag) return CMD_SUCCESS; } -int isis_area_destroy(const char *area_tag) +void isis_area_destroy(struct isis_area *area) { - struct isis_area *area; struct listnode *node, *nnode; struct isis_circuit *circuit; struct area_addr *addr; - area = isis_area_lookup(area_tag); - - if (area == NULL) { - zlog_warn("%s: could not find area with area-tag %s", - __func__, area_tag); - return CMD_ERR_NO_MATCH; - } - QOBJ_UNREG(area); if (fabricd) @@ -280,6 +427,7 @@ int isis_area_destroy(const char *area_tag) } list_delete(&area->circuit_list); } + list_delete(&area->adjacency_list); lsp_db_fini(&area->lspdb[0]); lsp_db_fini(&area->lspdb[1]); @@ -298,7 +446,8 @@ int isis_area_destroy(const char *area_tag) spf_backoff_free(area->spf_delay_ietf[0]); spf_backoff_free(area->spf_delay_ietf[1]); - isis_redist_area_finish(area); + if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) + isis_redist_area_finish(area); for (ALL_LIST_ELEMENTS(area->area_addrs, node, nnode, addr)) { list_delete_node(area->area_addrs, node); @@ -312,20 +461,139 @@ int isis_area_destroy(const char *area_tag) thread_cancel_event(master, area); - listnode_delete(isis->area_list, area); + listnode_delete(area->isis->area_list, area); free(area->area_tag); area_mt_finish(area); + if (listcount(area->isis->area_list) == 0) { + memset(area->isis->sysid, 0, ISIS_SYS_ID_LEN); + area->isis->sysid_set = 0; + } + XFREE(MTYPE_ISIS_AREA, area); - if (listcount(isis->area_list) == 0) { - memset(isis->sysid, 0, ISIS_SYS_ID_LEN); - isis->sysid_set = 0; +} + +/* This is hook function for vrf create called as part of vrf_init */ +static int isis_vrf_new(struct vrf *vrf) +{ + if (IS_DEBUG_EVENTS) + zlog_debug("%s: VRF Created: %s(%u)", __func__, vrf->name, + vrf->vrf_id); + + return 0; +} + +/* This is hook function for vrf delete call as part of vrf_init */ +static int isis_vrf_delete(struct vrf *vrf) +{ + if (IS_DEBUG_EVENTS) + zlog_debug("%s: VRF Deletion: %s(%u)", __func__, vrf->name, + vrf->vrf_id); + + return 0; +} + +static int isis_vrf_enable(struct vrf *vrf) +{ + struct isis *isis; + vrf_id_t old_vrf_id; + + if (IS_DEBUG_EVENTS) + zlog_debug("%s: VRF %s id %u enabled", __func__, vrf->name, + vrf->vrf_id); + + isis = isis_lookup_by_vrfname(vrf->name); + if (isis) { + if (isis->name && strmatch(vrf->name, VRF_DEFAULT_NAME)) { + XFREE(MTYPE_ISIS, isis->name); + isis->name = NULL; + } + old_vrf_id = isis->vrf_id; + /* We have instance configured, link to VRF and make it "up". */ + isis_vrf_link(isis, vrf); + if (IS_DEBUG_EVENTS) + zlog_debug( + "%s: isis linked to vrf %s vrf_id %u (old id %u)", + __func__, vrf->name, isis->vrf_id, old_vrf_id); + if (old_vrf_id != isis->vrf_id) { + frr_with_privs (&isisd_privs) { + /* stop zebra redist to us for old vrf */ + zclient_send_dereg_requests(zclient, + old_vrf_id); + /* start zebra redist to us for new vrf */ + isis_zebra_vrf_register(isis); + } + } } - return CMD_SUCCESS; + return 0; +} + +static int isis_vrf_disable(struct vrf *vrf) +{ + struct isis *isis; + vrf_id_t old_vrf_id = VRF_UNKNOWN; + + if (vrf->vrf_id == VRF_DEFAULT) + return 0; + + if (IS_DEBUG_EVENTS) + zlog_debug("%s: VRF %s id %d disabled.", __func__, vrf->name, + vrf->vrf_id); + isis = isis_lookup_by_vrfname(vrf->name); + if (isis) { + old_vrf_id = isis->vrf_id; + + /* We have instance configured, unlink + * from VRF and make it "down". + */ + isis_vrf_unlink(isis, vrf); + if (IS_DEBUG_EVENTS) + zlog_debug("%s: isis old_vrf_id %d unlinked", __func__, + old_vrf_id); + } + + return 0; +} + +void isis_vrf_init(void) +{ + vrf_init(isis_vrf_new, isis_vrf_enable, isis_vrf_disable, + isis_vrf_delete, isis_vrf_enable); +} + +void isis_finish(struct isis *isis) +{ + struct vrf *vrf = NULL; + + isis_delete(isis); + if (isis->name) { + vrf = vrf_lookup_by_name(isis->name); + if (vrf) + isis_vrf_unlink(isis, vrf); + XFREE(MTYPE_ISIS, isis->name); + } else { + vrf = vrf_lookup_by_id(VRF_DEFAULT); + if (vrf) + isis_vrf_unlink(isis, vrf); + } + + XFREE(MTYPE_ISIS, isis); +} + +void isis_terminate() +{ + struct isis *isis; + struct listnode *node, *nnode; + + if (listcount(im->isis) == 0) + return; + + for (ALL_LIST_ELEMENTS(im->isis, node, nnode, isis)) + isis_finish(isis); } #ifdef FABRICD @@ -366,10 +634,10 @@ int area_net_title(struct vty *vty, const char *net_title) uint8_t buff[255]; /* We check that we are not over the maximal number of addresses */ - if (listcount(area->area_addrs) >= isis->max_area_addrs) { + if (listcount(area->area_addrs) >= area->isis->max_area_addrs) { vty_out(vty, "Maximum of area addresses (%d) already reached \n", - isis->max_area_addrs); + area->isis->max_area_addrs); return CMD_ERR_NOTHING_TODO; } @@ -395,20 +663,21 @@ int area_net_title(struct vty *vty, const char *net_title) return CMD_WARNING_CONFIG_FAILED; } - if (isis->sysid_set == 0) { + if (area->isis->sysid_set == 0) { /* * First area address - get the SystemID for this router */ - memcpy(isis->sysid, GETSYSID(addr), ISIS_SYS_ID_LEN); - isis->sysid_set = 1; + memcpy(area->isis->sysid, GETSYSID(addr), ISIS_SYS_ID_LEN); + area->isis->sysid_set = 1; if (IS_DEBUG_EVENTS) zlog_debug("Router has SystemID %s", - sysid_print(isis->sysid)); + sysid_print(area->isis->sysid)); } else { /* * Check that the SystemID portions match */ - if (memcmp(isis->sysid, GETSYSID(addr), ISIS_SYS_ID_LEN)) { + if (memcmp(area->isis->sysid, GETSYSID(addr), + ISIS_SYS_ID_LEN)) { vty_out(vty, "System ID must not change when defining additional area addresses\n"); XFREE(MTYPE_ISIS_AREA_ADDR, addr); @@ -480,8 +749,8 @@ int area_clear_net_title(struct vty *vty, const char *net_title) * Last area address - reset the SystemID for this router */ if (listcount(area->area_addrs) == 0) { - memset(isis->sysid, 0, ISIS_SYS_ID_LEN); - isis->sysid_set = 0; + memset(area->isis->sysid, 0, ISIS_SYS_ID_LEN); + area->isis->sysid_set = 0; if (IS_DEBUG_EVENTS) zlog_debug("Router has no SystemID"); } @@ -493,100 +762,144 @@ int area_clear_net_title(struct vty *vty, const char *net_title) * 'show isis interface' command */ -int show_isis_interface_common(struct vty *vty, const char *ifname, char detail) +int show_isis_interface_common(struct vty *vty, const char *ifname, char detail, + const char *vrf_name, bool all_vrf) { - struct listnode *anode, *cnode; + struct listnode *anode, *cnode, *inode; struct isis_area *area; struct isis_circuit *circuit; + struct isis *isis; - if (!isis) { + if (!im) { vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, + anode, area)) { + vty_out(vty, "Area %s:\n", + area->area_tag); + + if (detail == ISIS_UI_LEVEL_BRIEF) + vty_out(vty, + " Interface CircId State Type Level\n"); + + for (ALL_LIST_ELEMENTS_RO( + area->circuit_list, cnode, + circuit)) + if (!ifname) + isis_circuit_print_vty( + circuit, vty, + detail); + else if (strcmp(circuit->interface->name, ifname) == 0) + isis_circuit_print_vty( + circuit, vty, + detail); + } + } + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) { + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, + area)) { + vty_out(vty, "Area %s:\n", area->area_tag); - for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { - vty_out(vty, "Area %s:\n", area->area_tag); - - if (detail == ISIS_UI_LEVEL_BRIEF) - vty_out(vty, - " Interface CircId State Type Level\n"); - - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) - if (!ifname) - isis_circuit_print_vty(circuit, vty, detail); - else if (strcmp(circuit->interface->name, ifname) == 0) - isis_circuit_print_vty(circuit, vty, detail); + if (detail == ISIS_UI_LEVEL_BRIEF) + vty_out(vty, + " Interface CircId State Type Level\n"); + + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, + cnode, circuit)) + if (!ifname) + isis_circuit_print_vty( + circuit, vty, detail); + else if ( + strcmp(circuit->interface->name, + ifname) + == 0) + isis_circuit_print_vty( + circuit, vty, detail); + } + } } return CMD_SUCCESS; } -DEFUN (show_isis_interface, - show_isis_interface_cmd, - "show " PROTO_NAME " interface", - SHOW_STR - PROTO_HELP - "ISIS interface\n") +DEFUN(show_isis_interface, + show_isis_interface_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] interface", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS interface\n") { - return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_BRIEF); + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_BRIEF, + vrf_name, all_vrf); } -DEFUN (show_isis_interface_detail, - show_isis_interface_detail_cmd, - "show " PROTO_NAME " interface detail", - SHOW_STR - PROTO_HELP - "ISIS interface\n" - "show detailed information\n") +DEFUN(show_isis_interface_detail, + show_isis_interface_detail_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] interface detail", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS interface\n" + "show detailed information\n") { - return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_DETAIL); + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); } -DEFUN (show_isis_interface_arg, - show_isis_interface_arg_cmd, - "show " PROTO_NAME " interface WORD", - SHOW_STR - PROTO_HELP - "ISIS interface\n" - "ISIS interface name\n") +DEFUN(show_isis_interface_arg, + show_isis_interface_arg_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] interface WORD", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS interface\n" + "IS-IS interface name\n") { - int idx_word = 3; - return show_isis_interface_common(vty, argv[idx_word]->arg, - ISIS_UI_LEVEL_DETAIL); -} + int idx_word = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; -/* - * 'show isis neighbor' command - */ + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + char *ifname = argv_find(argv, argc, "WORD", &idx_word) + ? argv[idx_word]->arg + : NULL; + return show_isis_interface_common(vty, ifname, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); +} -int show_isis_neighbor_common(struct vty *vty, const char *id, char detail) +static void isis_neighbor_common(struct vty *vty, const char *id, char detail, + struct isis *isis, uint8_t *sysid) { struct listnode *anode, *cnode, *node; struct isis_area *area; struct isis_circuit *circuit; struct list *adjdb; struct isis_adjacency *adj; - struct isis_dynhn *dynhn; - uint8_t sysid[ISIS_SYS_ID_LEN]; int i; - if (!isis) { - vty_out(vty, "IS-IS Routing Process not enabled\n"); - return CMD_SUCCESS; - } - - memset(sysid, 0, ISIS_SYS_ID_LEN); - if (id) { - if (sysid2buff(sysid, id) == 0) { - dynhn = dynhn_find_by_name(id); - if (dynhn == NULL) { - vty_out(vty, "Invalid system id %s\n", id); - return CMD_SUCCESS; - } - memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN); - } - } - for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { vty_out(vty, "Area %s:\n", area->area_tag); @@ -602,9 +915,10 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail) for (ALL_LIST_ELEMENTS_RO( adjdb, node, adj)) if (!id - || !memcmp(adj->sysid, - sysid, - ISIS_SYS_ID_LEN)) + || !memcmp( + adj->sysid, + sysid, + ISIS_SYS_ID_LEN)) isis_adj_print_vty( adj, vty, @@ -622,24 +936,20 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail) } } - return CMD_SUCCESS; } - /* - * 'clear isis neighbor' command + * 'show isis neighbor' command */ -int clear_isis_neighbor_common(struct vty *vty, const char *id) + +int show_isis_neighbor_common(struct vty *vty, const char *id, char detail, + const char *vrf_name, bool all_vrf) { - struct listnode *anode, *cnode, *cnextnode, *node, *nnode; - struct isis_area *area; - struct isis_circuit *circuit; - struct list *adjdb; - struct isis_adjacency *adj; + struct listnode *node; struct isis_dynhn *dynhn; uint8_t sysid[ISIS_SYS_ID_LEN]; - int i; + struct isis *isis; - if (!isis) { + if (!im) { vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } @@ -656,9 +966,34 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id) } } + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) { + isis_neighbor_common(vty, id, detail, isis, + sysid); + } + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + isis_neighbor_common(vty, id, detail, isis, sysid); + } + + return CMD_SUCCESS; +} + +static void isis_neighbor_common_clear(struct vty *vty, const char *id, + uint8_t *sysid, struct isis *isis) +{ + struct listnode *anode, *cnode, *node, *nnode; + struct isis_area *area; + struct isis_circuit *circuit; + struct list *adjdb; + struct isis_adjacency *adj; + int i; + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { - for (ALL_LIST_ELEMENTS(area->circuit_list, cnode, cnextnode, - circuit)) { + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) { if (circuit->circ_type == CIRCUIT_T_BROADCAST) { for (i = 0; i < 2; i++) { adjdb = circuit->u.bc.adjdb[i]; @@ -667,9 +1002,10 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id) adjdb, node, nnode, adj)) if (!id - || !memcmp(adj->sysid, - sysid, - ISIS_SYS_ID_LEN)) + || !memcmp( + adj->sysid, + sysid, + ISIS_SYS_ID_LEN)) isis_adj_state_change( &adj, ISIS_ADJ_DOWN, @@ -688,64 +1024,148 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id) } } } - - return CMD_SUCCESS; } - -DEFUN (show_isis_neighbor, - show_isis_neighbor_cmd, - "show " PROTO_NAME " neighbor", - SHOW_STR - PROTO_HELP - "ISIS neighbor adjacencies\n") +/* + * 'clear isis neighbor' command + */ +int clear_isis_neighbor_common(struct vty *vty, const char *id, const char *vrf_name, + bool all_vrf) { - return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_BRIEF); -} + struct listnode *node; + struct isis_dynhn *dynhn; + uint8_t sysid[ISIS_SYS_ID_LEN]; + struct isis *isis; -DEFUN (show_isis_neighbor_detail, - show_isis_neighbor_detail_cmd, - "show " PROTO_NAME " neighbor detail", - SHOW_STR - PROTO_HELP - "ISIS neighbor adjacencies\n" - "show detailed information\n") -{ - return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_DETAIL); -} + if (!im) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } -DEFUN (show_isis_neighbor_arg, - show_isis_neighbor_arg_cmd, - "show " PROTO_NAME " neighbor WORD", - SHOW_STR - PROTO_HELP - "ISIS neighbor adjacencies\n" - "System id\n") -{ - int idx_word = 3; - return show_isis_neighbor_common(vty, argv[idx_word]->arg, - ISIS_UI_LEVEL_DETAIL); -} + memset(sysid, 0, ISIS_SYS_ID_LEN); + if (id) { + if (sysid2buff(sysid, id) == 0) { + dynhn = dynhn_find_by_name(id); + if (dynhn == NULL) { + vty_out(vty, "Invalid system id %s\n", id); + return CMD_SUCCESS; + } + memcpy(sysid, dynhn->id, ISIS_SYS_ID_LEN); + } + } + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + isis_neighbor_common_clear(vty, id, sysid, + isis); + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + isis_neighbor_common_clear(vty, id, sysid, isis); + } -DEFUN (clear_isis_neighbor, - clear_isis_neighbor_cmd, - "clear " PROTO_NAME " neighbor", - CLEAR_STR - PROTO_HELP - "ISIS neighbor adjacencies\n") -{ - return clear_isis_neighbor_common(vty, NULL); + return CMD_SUCCESS; } -DEFUN (clear_isis_neighbor_arg, - clear_isis_neighbor_arg_cmd, - "clear " PROTO_NAME " neighbor WORD", - CLEAR_STR - PROTO_HELP - "ISIS neighbor adjacencies\n" - "System id\n") -{ - int idx_word = 3; - return clear_isis_neighbor_common(vty, argv[idx_word]->arg); +DEFUN(show_isis_neighbor, + show_isis_neighbor_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] neighbor", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All vrfs\n" + "IS-IS neighbor adjacencies\n") +{ + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_BRIEF, + vrf_name, all_vrf); +} + +DEFUN(show_isis_neighbor_detail, + show_isis_neighbor_detail_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] neighbor detail", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "all vrfs\n" + "IS-IS neighbor adjacencies\n" + "show detailed information\n") +{ + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); +} + +DEFUN(show_isis_neighbor_arg, + show_isis_neighbor_arg_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] neighbor WORD", + SHOW_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All vrfs\n" + "IS-IS neighbor adjacencies\n" + "System id\n") +{ + int idx_word = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + char *id = argv_find(argv, argc, "WORD", &idx_word) + ? argv[idx_word]->arg + : NULL; + + return show_isis_neighbor_common(vty, id, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); +} + +DEFUN(clear_isis_neighbor, + clear_isis_neighbor_cmd, + "clear " PROTO_NAME " [vrf <NAME|all>] neighbor", + CLEAR_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All vrfs\n" + "IS-IS neighbor adjacencies\n") +{ + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return clear_isis_neighbor_common(vty, NULL, vrf_name, all_vrf); +} + +DEFUN(clear_isis_neighbor_arg, + clear_isis_neighbor_arg_cmd, + "clear " PROTO_NAME " [vrf <NAME|all>] neighbor WORD", + CLEAR_STR + PROTO_HELP + VRF_CMD_HELP_STR + "All vrfs\n" + "IS-IS neighbor adjacencies\n" + "System id\n") +{ + int idx_word = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + + char *id = argv_find(argv, argc, "WORD", &idx_word) + ? argv[idx_word]->arg + : NULL; + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return clear_isis_neighbor_common(vty, id, vrf_name, all_vrf); } /* @@ -1248,34 +1668,41 @@ DEFUN (no_debug_isis_bfd, return CMD_SUCCESS; } -DEFUN (show_hostname, - show_hostname_cmd, - "show " PROTO_NAME " hostname", - SHOW_STR - PROTO_HELP - "IS-IS Dynamic hostname mapping\n") +DEFUN(show_hostname, show_hostname_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] hostname", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS Dynamic hostname mapping\n") { - dynhn_print_all(vty); + struct listnode *node; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx_vrf = 0; + struct isis *isis; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + dynhn_print_all(vty, isis); + + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + dynhn_print_all(vty, isis); + } return CMD_SUCCESS; } -DEFUN (show_isis_spf_ietf, - show_isis_spf_ietf_cmd, - "show " PROTO_NAME " spf-delay-ietf", - SHOW_STR - PROTO_HELP - "SPF delay IETF information\n") +static void isis_spf_ietf_common(struct vty *vty, struct isis *isis) { - if (!isis) { - vty_out(vty, "ISIS is not running\n"); - return CMD_SUCCESS; - } - struct listnode *node; struct isis_area *area; - for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + + vty_out(vty, "vrf : %s\n", isis->name); vty_out(vty, "Area %s:\n", area->area_tag ? area->area_tag : "null"); @@ -1306,23 +1733,49 @@ DEFUN (show_isis_spf_ietf, } } } +} + +DEFUN(show_isis_spf_ietf, show_isis_spf_ietf_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] spf-delay-ietf", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "SPF delay IETF information\n") +{ + struct listnode *node; + struct isis *isis; + int idx_vrf = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) + + if (!im) { + vty_out(vty, "ISIS is not running\n"); + return CMD_SUCCESS; + } + + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + isis_spf_ietf_common(vty, isis); + + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + isis_spf_ietf_common(vty, isis); + } + return CMD_SUCCESS; } -DEFUN (show_isis_summary, - show_isis_summary_cmd, - "show " PROTO_NAME " summary", - SHOW_STR PROTO_HELP "summary\n") +static void common_isis_summary(struct vty *vty, struct isis *isis) { struct listnode *node, *node2; struct isis_area *area; int level; - if (isis == NULL) { - vty_out(vty, PROTO_NAME " is not running\n"); - return CMD_SUCCESS; - } - + vty_out(vty, "vrf : %s\n", isis->name); vty_out(vty, "Process Id : %ld\n", isis->process_id); if (isis->sysid_set) vty_out(vty, "System Id : %s\n", @@ -1390,25 +1843,68 @@ DEFUN (show_isis_summary, " (not used, IETF SPF delay activated)"); vty_out(vty, "\n"); - vty_out(vty, " IPv4 route computation:\n"); - isis_spf_print(area->spftree[SPFTREE_IPV4][level - 1], - vty); + if (area->ip_circuits) { + vty_out(vty, " IPv4 route computation:\n"); + isis_spf_print( + area->spftree[SPFTREE_IPV4][level - 1], + vty); + } + + if (area->ipv6_circuits) { + vty_out(vty, " IPv6 route computation:\n"); + isis_spf_print( + area->spftree[SPFTREE_IPV6][level - 1], + vty); + } + + if (area->ipv6_circuits + && isis_area_ipv6_dstsrc_enabled(area)) { + vty_out(vty, + " IPv6 dst-src route computation:\n"); + isis_spf_print(area->spftree[SPFTREE_DSTSRC] + [level - 1], + vty); + } + } + } +} + +DEFUN(show_isis_summary, show_isis_summary_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] summary", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "summary\n") +{ + struct listnode *node; + int idx_vrf = 0; + struct isis *isis; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; - vty_out(vty, " IPv6 route computation:\n"); - isis_spf_print(area->spftree[SPFTREE_IPV6][level - 1], - vty); + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) + if (!im) { + vty_out(vty, PROTO_NAME " is not running\n"); + return CMD_SUCCESS; + } + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + common_isis_summary(vty, isis); - vty_out(vty, " IPv6 dst-src route computation:\n"); - isis_spf_print(area->spftree[SPFTREE_DSTSRC][level-1], - vty); + return CMD_SUCCESS; } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + common_isis_summary(vty, isis); } + vty_out(vty, "\n"); return CMD_SUCCESS; } -struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv) +struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv, + struct isis *isis) { char sysid[255] = {0}; uint8_t number[3]; @@ -1468,6 +1964,62 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv) return lsp; } +void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, + int level, struct lspdb_head *lspdb, + const char *argv, int ui_level) +{ + struct isis_lsp *lsp; + int lsp_count; + + if (lspdb_count(lspdb) > 0) { + lsp = lsp_for_arg(lspdb, argv, area->isis); + + if (lsp != NULL || argv == NULL) { + vty_out(vty, "IS-IS Level-%d link-state database:\n", + level + 1); + + /* print the title in all cases */ + vty_out(vty, + "LSP ID PduLen SeqNumber Chksum Holdtime ATT/P/OL\n"); + } + + if (lsp) { + if (ui_level == ISIS_UI_LEVEL_DETAIL) + lsp_print_detail(lsp, vty, area->dynhostname, + area->isis); + else + lsp_print(lsp, vty, area->dynhostname, + area->isis); + } else if (argv == NULL) { + lsp_count = + lsp_print_all(vty, lspdb, ui_level, + area->dynhostname, area->isis); + + vty_out(vty, " %u LSPs\n\n", lsp_count); + } + } +} + +static void show_isis_database_common(struct vty *vty, const char *argv, + int ui_level, struct isis *isis) +{ + struct listnode *node; + struct isis_area *area; + int level; + + if (isis->area_list->count == 0) + return; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + vty_out(vty, "Area %s:\n", + area->area_tag ? area->area_tag : "null"); + + for (level = 0; level < ISIS_LEVELS; level++) + show_isis_database_lspdb(vty, area, level, + &area->lspdb[level], argv, + ui_level); + } +} /* * This function supports following display options: * [ show isis database [detail] ] @@ -1484,72 +2036,46 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv) * [ show isis database detail <sysid>.<pseudo-id>-<fragment-number> ] * [ show isis database detail <hostname>.<pseudo-id>-<fragment-number> ] */ -static int show_isis_database(struct vty *vty, const char *argv, int ui_level) +static int show_isis_database(struct vty *vty, const char *argv, int ui_level, + const char *vrf_name, bool all_vrf) { struct listnode *node; - struct isis_area *area; - struct isis_lsp *lsp; - int level, lsp_count; - - if (isis->area_list->count == 0) - return CMD_SUCCESS; - - for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { - vty_out(vty, "Area %s:\n", - area->area_tag ? area->area_tag : "null"); + struct isis *isis; - for (level = 0; level < ISIS_LEVELS; level++) { - if (lspdb_count(&area->lspdb[level]) > 0) { - lsp = lsp_for_arg(&area->lspdb[level], argv); + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) + show_isis_database_common(vty, argv, ui_level, + isis); - if (lsp != NULL || argv == NULL) { - vty_out(vty, - "IS-IS Level-%d link-state database:\n", - level + 1); - - /* print the title in all cases */ - vty_out(vty, - "LSP ID PduLen SeqNumber Chksum Holdtime ATT/P/OL\n"); - } - - if (lsp) { - if (ui_level == ISIS_UI_LEVEL_DETAIL) - lsp_print_detail( - lsp, vty, - area->dynhostname); - else - lsp_print(lsp, vty, - area->dynhostname); - } else if (argv == NULL) { - lsp_count = lsp_print_all( - vty, &area->lspdb[level], - ui_level, area->dynhostname); - - vty_out(vty, " %u LSPs\n\n", - lsp_count); - } - } + return CMD_SUCCESS; } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis) + show_isis_database_common(vty, argv, ui_level, isis); } return CMD_SUCCESS; } -DEFUN (show_database, - show_database_cmd, - "show " PROTO_NAME " database [detail] [WORD]", - SHOW_STR - PROTO_HELP - "Link state database\n" - "Detailed information\n" - "LSP ID\n") +DEFUN(show_database, show_database_cmd, + "show " PROTO_NAME " [vrf <NAME|all>] database [detail] [WORD]", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "Link state database\n" + "Detailed information\n" + "LSP ID\n") { int idx = 0; + int idx_vrf = 0; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; int uilevel = argv_find(argv, argc, "detail", &idx) ? ISIS_UI_LEVEL_DETAIL : ISIS_UI_LEVEL_BRIEF; char *id = argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL; - return show_isis_database(vty, id, uilevel); + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + return show_isis_database(vty, id, uilevel, vrf_name, all_vrf); } #ifdef FABRICD @@ -1578,8 +2104,20 @@ DEFUN (no_router_openfabric, PROTO_HELP "ISO Routing area tag\n") { + struct isis_area *area; + const char *area_tag; int idx_word = 3; - return isis_area_destroy(argv[idx_word]->arg); + + area_tag = argv[idx_word]->arg; + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (area == NULL) { + zlog_warn("%s: could not find area with area-tag %s", + __func__, area_tag); + return CMD_ERR_NO_MATCH; + } + + isis_area_destroy(area); + return CMD_SUCCESS; } #endif /* ifdef FABRICD */ #ifdef FABRICD @@ -1934,11 +2472,16 @@ DEFUN (no_log_adj_changes, static int isis_config_write(struct vty *vty) { int write = 0; + struct isis_area *area; + struct listnode *node, *node2, *inode; + struct isis *isis; - if (isis != NULL) { - struct isis_area *area; - struct listnode *node, *node2; + if (!im) { + vty_out(vty, "IS-IS Routing Process not enabled\n"); + return CMD_SUCCESS; + } + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { /* ISIS - Area name */ vty_out(vty, "router " PROTO_NAME " %s\n", area->area_tag); diff --git a/isisd/isisd.h b/isisd/isisd.h index 57d9691cc7..c26a62dfac 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -55,6 +55,12 @@ static const bool fabricd = false; extern void isis_cli_init(void); #endif +#define ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) \ + if (argv_find(argv, argc, "vrf", &idx_vrf)) { \ + vrf_name = argv[idx_vrf + 1]->arg; \ + all_vrf = strmatch(vrf_name, "all"); \ + } + extern struct zebra_privs_t isisd_privs; /* uncomment if you are a developer in bug hunt */ @@ -62,8 +68,18 @@ extern struct zebra_privs_t isisd_privs; struct fabricd; +struct isis_master { + /* ISIS instance. */ + struct list *isis; + /* ISIS thread master. */ + struct thread_master *master; + uint8_t options; +}; +#define F_ISIS_UNIT_TEST 0x01 + struct isis { vrf_id_t vrf_id; + char *name; unsigned long process_id; int sysid_set; uint8_t sysid[ISIS_SYS_ID_LEN]; /* SystemID for this IS */ @@ -77,12 +93,9 @@ struct isis { uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */ struct route_table *ext_info[REDIST_PROTOCOL_COUNT]; - - QOBJ_FIELDS }; -extern struct isis *isis; -DECLARE_QOBJ_TYPE(isis_area) +extern struct isis_master *im; enum spf_tree_id { SPFTREE_IPV4 = 0, @@ -110,6 +123,7 @@ struct isis_area { #define DEFAULT_LSP_MTU 1497 unsigned int lsp_mtu; /* Size of LSPs to generate */ struct list *circuit_list; /* IS-IS circuits */ + struct list *adjacency_list; /* IS-IS adjacencies */ struct flags flags; struct thread *t_tick; /* LSP walker */ struct thread *t_lsp_refresh[ISIS_LEVELS]; @@ -193,14 +207,29 @@ struct isis_area { }; DECLARE_QOBJ_TYPE(isis_area) +void isis_terminate(void); +void isis_finish(struct isis *isis); +void isis_master_init(struct thread_master *master); +void isis_vrf_link(struct isis *isis, struct vrf *vrf); +void isis_vrf_unlink(struct isis *isis, struct vrf *vrf); +void isis_global_instance_create(const char *vrf_name); +struct isis *isis_lookup_by_vrfid(vrf_id_t vrf_id); +struct isis *isis_lookup_by_vrfname(const char *vrfname); +struct isis *isis_lookup_by_sysid(const uint8_t *sysid); + void isis_init(void); -void isis_new(unsigned long process_id, vrf_id_t vrf_id); -struct isis_area *isis_area_create(const char *); -struct isis_area *isis_area_lookup(const char *); +void isis_vrf_init(void); + +struct isis *isis_new(const char *vrf_name); +struct isis_area *isis_area_create(const char *, const char *); +struct isis_area *isis_area_lookup(const char *, vrf_id_t vrf_id); +struct isis_area *isis_area_lookup_by_vrf(const char *area_tag, + const char *vrf_name); int isis_area_get(struct vty *vty, const char *area_tag); -int isis_area_destroy(const char *area_tag); +void isis_area_destroy(struct isis_area *area); void print_debug(struct vty *, int, int); -struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv); +struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv, + struct isis *isis); void isis_area_invalidate_routes(struct isis_area *area, int levels); void isis_area_verify_routes(struct isis_area *area); @@ -222,6 +251,9 @@ int isis_area_passwd_cleartext_set(struct isis_area *area, int level, const char *passwd, uint8_t snp_auth); int isis_area_passwd_hmac_md5_set(struct isis_area *area, int level, const char *passwd, uint8_t snp_auth); +void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, + int level, struct lspdb_head *lspdb, + const char *argv, int ui_level); /* YANG paths */ #define ISIS_INSTANCE "/frr-isisd:isis/instance" diff --git a/ldpd/lde.c b/ldpd/lde.c index 734c1ea230..df64f908ea 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -468,6 +468,10 @@ lde_dispatch_parent(struct thread *thread) iface = if_lookup_name(ldeconf, kif->ifname); if (iface) { if_update_info(iface, kif); + + /* if up see if any labels need to be updated */ + if (kif->operative) + lde_route_update(iface, AF_UNSPEC); break; } @@ -786,7 +790,6 @@ lde_send_change_klabel(struct fec_node *fn, struct fec_nh *fnh) kr.remote_label = fnh->remote_label; kr.route_type = fnh->route_type; kr.route_instance = fnh->route_instance; - lde_imsg_compose_parent(IMSG_KLABEL_CHANGE, 0, &kr, sizeof(kr)); break; @@ -2271,3 +2274,156 @@ lde_check_filter_af(int af, struct ldpd_af_conf *af_conf, if (strcmp(af_conf->acl_label_expnull_for, filter_name) == 0) lde_change_expnull_for_filter(af); } + +void lde_route_update(struct iface *iface, int af) +{ + struct fec *f; + struct fec_node *fn; + struct fec_nh *fnh; + struct lde_nbr *ln; + + /* update label of non-connected routes */ + log_debug("update labels for interface %s", iface->name); + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + if (IS_MPLS_UNRESERVED_LABEL(fn->local_label)) + continue; + + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + break; + case AF_INET6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + break; + default: + /* unspecified so process both address families */ + break; + } + + LIST_FOREACH(fnh, &fn->nexthops, entry) { + /* + * If connected leave existing label. If LDP + * configured on interface or a static route + * may need new label. If no LDP configured + * treat fec as a connected route + */ + if (fnh->flags & F_FEC_NH_CONNECTED) + break; + + if (fnh->ifindex != iface->ifindex) + continue; + + fnh->flags &= ~F_FEC_NH_NO_LDP; + if (IS_MPLS_RESERVED_LABEL(fn->local_label)) { + fn->local_label = NO_LABEL; + fn->local_label = lde_update_label(fn); + if (fn->local_label != NO_LABEL) + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelmapping( + ln, fn, 0); + } + break; + } + } + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, + 0, NULL, 0); +} + +void lde_route_update_release(struct iface *iface, int af) +{ + struct lde_nbr *ln; + struct fec *f; + struct fec_node *fn; + struct fec_nh *fnh; + + /* update label of interfaces no longer running LDP */ + log_debug("release all labels for interface %s af %s", iface->name, + af == AF_INET ? "ipv4" : "ipv6"); + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + break; + case AF_INET6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + break; + default: + fatalx("lde_route_update_release: unknown af"); + } + + if (fn->local_label == NO_LABEL) + continue; + + LIST_FOREACH(fnh, &fn->nexthops, entry) { + /* + * If connected leave existing label. If LDP + * removed from interface may need new label + * and would be treated as a connected route + */ + if (fnh->flags & F_FEC_NH_CONNECTED) + break; + + if (fnh->ifindex != iface->ifindex) + continue; + + fnh->flags |= F_FEC_NH_NO_LDP; + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelwithdraw(ln, fn, NULL, NULL); + lde_free_label(fn->local_label); + fn->local_label = NO_LABEL; + fn->local_label = lde_update_label(fn); + if (fn->local_label != NO_LABEL) + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelmapping(ln, fn, 0); + break; + } + } + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_imsg_compose_ldpe(IMSG_MAPPING_ADD_END, ln->peerid, + 0, NULL, 0); +} + +void lde_route_update_release_all(int af) +{ + struct lde_nbr *ln; + struct fec *f; + struct fec_node *fn; + struct fec_nh *fnh; + + /* remove labels from all interfaces as LDP is no longer running for + * this address family + */ + log_debug("release all labels for address family %s", + af == AF_INET ? "ipv4" : "ipv6"); + RB_FOREACH(f, fec_tree, &ft) { + fn = (struct fec_node *)f; + switch (af) { + case AF_INET: + if (fn->fec.type != FEC_TYPE_IPV4) + continue; + break; + case AF_INET6: + if (fn->fec.type != FEC_TYPE_IPV6) + continue; + break; + default: + fatalx("lde_route_update_release: unknown af"); + } + + RB_FOREACH(ln, nbr_tree, &lde_nbrs) + lde_send_labelwithdraw(ln, fn, NULL, NULL); + + LIST_FOREACH(fnh, &fn->nexthops, entry) { + fnh->flags |= F_FEC_NH_NO_LDP; + lde_send_delete_klabel(fn, fnh); + } + } +} diff --git a/ldpd/lde.h b/ldpd/lde.h index 9e6db3a90b..660aeafb34 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -193,6 +193,9 @@ void lde_change_allocate_filter(int); void lde_change_advertise_filter(int); void lde_change_accept_filter(int); void lde_change_expnull_for_filter(int); +void lde_route_update(struct iface *, int); +void lde_route_update_release(struct iface *, int); +void lde_route_update_release_all(int); struct lde_addr *lde_address_find(struct lde_nbr *, int, union ldpd_addr *); diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index 11d85b7449..bed276c7b1 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -404,10 +404,15 @@ lde_kernel_update(struct fec *fec) * if LDP configured on interface or a static route * clear flag else treat fec as a connected route */ - iface = if_lookup(ldeconf,fnh->ifindex); - if (iface || fnh->route_type == ZEBRA_ROUTE_STATIC) - fnh->flags &=~F_FEC_NH_NO_LDP; - else + if (ldeconf->flags & F_LDPD_ENABLED) { + iface = if_lookup(ldeconf,fnh->ifindex); + if (fnh->flags & F_FEC_NH_CONNECTED || + iface || + fnh->route_type == ZEBRA_ROUTE_STATIC) + fnh->flags &=~F_FEC_NH_NO_LDP; + else + fnh->flags |= F_FEC_NH_NO_LDP; + } else fnh->flags |= F_FEC_NH_NO_LDP; } else { lde_send_delete_klabel(fn, fnh); @@ -437,6 +442,10 @@ lde_kernel_update(struct fec *fec) lde_send_labelmapping(ln, fn, 1); } + /* if no label created yet then don't try to program labeled route */ + if (fn->local_label == NO_LABEL) + return; + LIST_FOREACH(fnh, &fn->nexthops, entry) { lde_send_change_klabel(fn, fnh); @@ -567,7 +576,8 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping) fnh->flags &= ~F_FEC_NH_DEFER; } fnh->remote_label = map->label; - lde_send_change_klabel(fn, fnh); + if (fn->local_label != NO_LABEL) + lde_send_change_klabel(fn, fnh); break; case FEC_TYPE_PWID: pw = (struct l2vpn_pw *) fn->data; diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c index fc84c7f76b..53a384fe55 100644 --- a/ldpd/ldp_vty_cmds.c +++ b/ldpd/ldp_vty_cmds.c @@ -616,6 +616,8 @@ DEFPY (ldp_show_mpls_ldp_binding, "Show detailed information\n" JSON_STR) { + if (!(ldpd_conf->flags & F_LDPD_ENABLED)) + return CMD_SUCCESS; if (!local_label_str) local_label = NO_LABEL; if (!remote_label_str) diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 927c3a3c03..dca379e4eb 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -1329,6 +1329,7 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) int reset_nbrs_ipv4 = 0; int reset_nbrs = 0; int update_sockets = 0; + int change_ldp_disabled = 0; /* update timers */ if (af_conf->keepalive != xa->keepalive) { @@ -1362,6 +1363,11 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) != (xa->flags & F_LDPD_AF_ALLOCHOSTONLY)) change_host_label = 1; + /* disabling LDP for address family */ + if ((af_conf->flags & F_LDPD_AF_ENABLED) && + !(xa->flags & F_LDPD_AF_ENABLED)) + change_ldp_disabled = 1; + af_conf->flags = xa->flags; /* update the transport address */ @@ -1409,6 +1415,9 @@ merge_af(int af, struct ldpd_af_conf *af_conf, struct ldpd_af_conf *xa) lde_change_egress_label(af); if (change_host_label) lde_change_allocate_filter(af); + if (change_ldp_disabled) + lde_route_update_release_all(af); + break; case PROC_LDP_ENGINE: if (stop_init_backoff) @@ -1434,13 +1443,22 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) struct iface *iface, *itmp, *xi; RB_FOREACH_SAFE(iface, iface_head, &conf->iface_tree, itmp) { - /* find deleted interfaces */ + /* find deleted interfaces, which occurs when LDP is removed + * for all address families + */ if (if_lookup_name(xconf, iface->name) == NULL) { switch (ldpd_process) { case PROC_LDP_ENGINE: ldpe_if_exit(iface); break; case PROC_LDE_ENGINE: + if (iface->ipv4.enabled) + lde_route_update_release(iface, + AF_INET); + if (iface->ipv6.enabled) + lde_route_update_release(iface, + AF_INET6); + break; case PROC_MAIN: break; } @@ -1468,6 +1486,29 @@ merge_ifaces(struct ldpd_conf *conf, struct ldpd_conf *xconf) continue; } + /* update labels when adding or removing ldp on an + * interface + */ + if (ldpd_process == PROC_LDE_ENGINE) { + /* if we are removing lpd config for an address + * family on an interface then advertise routes + * learned over this interface as if they were + * connected routes + */ + if (iface->ipv4.enabled && !xi->ipv4.enabled) + lde_route_update_release(iface, AF_INET); + if (iface->ipv6.enabled && !xi->ipv6.enabled) + lde_route_update_release(iface, AF_INET6); + + /* if we are adding lpd config for an address + * family on an interface then add proper labels + */ + if (!iface->ipv4.enabled && xi->ipv4.enabled) + lde_route_update(iface, AF_INET); + if (!iface->ipv6.enabled && xi->ipv6.enabled) + lde_route_update(iface, AF_INET6); + } + /* update existing interfaces */ merge_iface_af(&iface->ipv4, &xi->ipv4); merge_iface_af(&iface->ipv6, &xi->ipv6); diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index 655313bf16..9078e711fb 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -614,10 +614,8 @@ ldpe_dispatch_lde(struct thread *thread) map = imsg.data; nbr = nbr_find_peerid(imsg.hdr.peerid); - if (nbr == NULL) { - log_debug("ldpe_dispatch_lde: cannot find neighbor"); + if (nbr == NULL) break; - } if (nbr->state != NBR_STA_OPER) break; @@ -641,10 +639,8 @@ ldpe_dispatch_lde(struct thread *thread) case IMSG_REQUEST_ADD_END: case IMSG_WITHDRAW_ADD_END: nbr = nbr_find_peerid(imsg.hdr.peerid); - if (nbr == NULL) { - log_debug("ldpe_dispatch_lde: cannot find neighbor"); + if (nbr == NULL) break; - } if (nbr->state != NBR_STA_OPER) break; diff --git a/lib/command.h b/lib/command.h index a7a2eaf868..e20bfe3318 100644 --- a/lib/command.h +++ b/lib/command.h @@ -367,6 +367,8 @@ struct cmd_node { #define SHOW_STR "Show running system information\n" #define IP_STR "IP information\n" #define IPV6_STR "IPv6 information\n" +#define SRTE_STR "SR-TE information\n" +#define SRTE_COLOR_STR "SR-TE Color information\n" #define NO_STR "Negate a command or set its defaults\n" #define REDIST_STR "Redistribute information from another routing protocol\n" #define CLEAR_STR "Reset functions\n" diff --git a/lib/filter.h b/lib/filter.h index d41f3b65cd..623fb94527 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -32,6 +32,16 @@ extern "C" { /* Maximum ACL name length */ #define ACL_NAMSIZ 128 +/** Cisco host wildcard mask. */ +#define CISCO_HOST_WILDCARD_MASK "0.0.0.0" +/** Cisco host wildcard binary mask. */ +#define CISCO_BIN_HOST_WILDCARD_MASK INADDR_ANY + +/** Cisco any wildcard mask. */ +#define CISCO_ANY_WILDCARD_MASK "255.255.255.255" +/** Cisco binary any wildcard mask. */ +#define CISCO_BIN_ANY_WILDCARD_MASK INADDR_NONE + /* Filter direction. */ #define FILTER_IN 0 #define FILTER_OUT 1 diff --git a/lib/filter_cli.c b/lib/filter_cli.c index 8c7a515dc5..09fc3289ce 100644 --- a/lib/filter_cli.c +++ b/lib/filter_cli.c @@ -37,14 +37,8 @@ #define ACCESS_LIST_STR "Access list entry\n" #define ACCESS_LIST_LEG_STR "IP standard access list\n" -#define ACCESS_LIST_LEG_EXT_STR "IP standard access list (expanded range)\n" #define ACCESS_LIST_ELEG_STR "IP extended access list\n" #define ACCESS_LIST_ELEG_EXT_STR "IP extended access list (expanded range)\n" -#define ACCESS_LIST_XLEG_STR \ - ACCESS_LIST_LEG_STR \ - ACCESS_LIST_LEG_EXT_STR \ - ACCESS_LIST_ELEG_STR \ - ACCESS_LIST_ELEG_EXT_STR #define ACCESS_LIST_ZEBRA_STR "Access list entry\n" #define ACCESS_LIST_SEQ_STR \ "Sequence number of an entry\n" \ @@ -68,7 +62,6 @@ static int64_t acl_cisco_get_seq(struct access_list *acl, const char *action, struct filter f, *fn; memset(&f, 0, sizeof(f)); - memset(&fc, 0, sizeof(fc)); f.cisco = 1; if (strcmp(action, "permit") == 0) f.type = FILTER_PERMIT; @@ -110,7 +103,8 @@ static int64_t acl_zebra_get_seq(struct access_list *acl, const char *action, f.type = FILTER_DENY; fz = &f.u.zfilter; - fz->prefix = *p; + if (p->family) + prefix_copy(&fz->prefix, p); fz->exact = exact; fn = filter_lookup_zebra(acl, &f); @@ -130,6 +124,7 @@ static void concat_addr_mask_v4(const char *addr, const char *mask, char *dst, int plen; assert(inet_pton(AF_INET, mask, &ia) == 1); + ia.s_addr = ~ia.s_addr; plen = ip_masklen(ia); snprintf(dst, dstlen, "%s/%d", addr, plen); } @@ -171,17 +166,15 @@ static long acl_get_seq(struct vty *vty, const char *xpath) */ DEFPY_YANG( access_list_std, access_list_std_cmd, - "access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>", + "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", ACCESS_LIST_STR ACCESS_LIST_LEG_STR - ACCESS_LIST_LEG_EXT_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "A single host address\n" "Address to match\n" "Address to match\n" - "Wildcard bits\n" - "Any source host\n") + "Wildcard bits\n") { int64_t sseq; char ipmask[64]; @@ -193,8 +186,7 @@ DEFPY_YANG( * none given (backward compatibility). */ snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", - number_str); + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); if (seq_str == NULL) { /* Use XPath to find the next sequence number. */ @@ -222,18 +214,16 @@ DEFPY_YANG( DEFPY_YANG( no_access_list_std, no_access_list_std_cmd, - "no access-list <(1-99)|(1300-1999)>$number [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask|any>", + "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action <[host] A.B.C.D$host|A.B.C.D$host A.B.C.D$mask>", NO_STR ACCESS_LIST_STR ACCESS_LIST_LEG_STR - ACCESS_LIST_LEG_EXT_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "A single host address\n" "Address to match\n" "Address to match\n" - "Wildcard bits\n" - "Any source host\n") + "Wildcard bits\n") { struct access_list *acl; struct lyd_node *dnode; @@ -246,15 +236,14 @@ DEFPY_YANG( snprintf( xpath, sizeof(xpath), "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", - number_str, seq_str); + name, seq_str); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } /* Otherwise, to keep compatibility, we need to figure it out. */ snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", - number_str); + "/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) @@ -263,13 +252,9 @@ DEFPY_YANG( /* 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 (host_str != NULL) - sseq = acl_cisco_get_seq(acl, action, host_str, - mask_str ? mask_str : "0.0.0.0", NULL, - NULL); - else - sseq = acl_cisco_get_seq(acl, action, "0.0.0.0", - "255.255.255.255", NULL, NULL); + sseq = acl_cisco_get_seq(acl, action, host_str, + mask_str ? mask_str : CISCO_HOST_WILDCARD_MASK, + NULL, NULL); if (sseq == -1) return CMD_WARNING; @@ -282,10 +267,9 @@ DEFPY_YANG( DEFPY_YANG( access_list_ext, access_list_ext_cmd, - "access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", + "access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", ACCESS_LIST_STR ACCESS_LIST_ELEG_STR - ACCESS_LIST_ELEG_EXT_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "IPv4 address\n" @@ -301,7 +285,7 @@ DEFPY_YANG( "Any destination host\n") { int64_t sseq; - char ipmask[64]; + char ipmask[64], ipmask_dst[64]; char xpath[XPATH_MAXLEN]; char xpath_entry[XPATH_MAXLEN + 128]; @@ -310,8 +294,7 @@ DEFPY_YANG( * none given (backward compatibility). */ snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", - number_str); + "/frr-filter:lib/access-list[type='ipv4'][name='%s']", name); nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); if (seq_str == NULL) { /* Use XPath to find the next sequence number. */ @@ -337,12 +320,12 @@ DEFPY_YANG( if (dst_str != NULL && dst_mask_str == NULL) { nb_cli_enqueue_change(vty, "./destination-host", NB_OP_MODIFY, - src_str); + dst_str); } else if (dst_str != NULL && dst_mask_str != NULL) { - concat_addr_mask_v4(dst_str, dst_mask_str, ipmask, - sizeof(ipmask)); + concat_addr_mask_v4(dst_str, dst_mask_str, ipmask_dst, + sizeof(ipmask_dst)); nb_cli_enqueue_change(vty, "./destination-network", - NB_OP_MODIFY, ipmask); + NB_OP_MODIFY, ipmask_dst); } else { nb_cli_enqueue_change(vty, "./destination-any", NB_OP_CREATE, NULL); @@ -353,11 +336,10 @@ DEFPY_YANG( DEFPY_YANG( no_access_list_ext, no_access_list_ext_cmd, - "no access-list <(100-199)|(2000-2699)>$number [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", + "no access-list WORD$name [seq (1-4294967295)$seq] <deny|permit>$action ip <A.B.C.D$src A.B.C.D$src_mask|host A.B.C.D$src|any> <A.B.C.D$dst A.B.C.D$dst_mask|host A.B.C.D$dst|any>", NO_STR ACCESS_LIST_STR ACCESS_LIST_ELEG_STR - ACCESS_LIST_ELEG_EXT_STR ACCESS_LIST_SEQ_STR ACCESS_LIST_ACTION_STR "Any Internet Protocol\n" @@ -383,15 +365,14 @@ DEFPY_YANG( snprintfrr( xpath, sizeof(xpath), "/frr-filter:lib/access-list[type='ipv4'][name='%s']/entry[sequence='%s']", - number_str, seq_str); + name, seq_str); nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); return nb_cli_apply_changes(vty, NULL); } /* Otherwise, to keep compatibility, we need to figure it out. */ snprintf(xpath, sizeof(xpath), - "/frr-filter:lib/access-list[type='ipv4'][name='%s']", - number_str); + "/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) @@ -404,24 +385,28 @@ DEFPY_YANG( if (dst_str != NULL) sseq = acl_cisco_get_seq( acl, action, src_str, - src_mask_str ? src_mask_str : "0.0.0.0", + src_mask_str ? src_mask_str + : CISCO_HOST_WILDCARD_MASK, dst_str, - dst_mask_str ? dst_mask_str : "0.0.0.0"); + 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 - : "0.0.0.0", - "0.0.0.0", "255.255.255.255"); + 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); } else { if (dst_str != NULL) - sseq = acl_cisco_get_seq(acl, action, "0.0.0.0", - "255.255.255.255", dst_str, - dst_mask_str ? dst_mask_str - : "0.0.0.0"); + 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", - "255.255.255.255", "0.0.0.0", - "255.255.255.255"); + sseq = acl_cisco_get_seq( + acl, action, "0.0.0.0", CISCO_ANY_WILDCARD_MASK, + "0.0.0.0", CISCO_ANY_WILDCARD_MASK); } if (sseq == -1) return CMD_WARNING; @@ -522,7 +507,7 @@ DEFPY_YANG( /* 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) { + if (prefix_str == NULL) { memset(&pany, 0, sizeof(pany)); pany.family = AF_INET; sseq = acl_zebra_get_seq(acl, action, &pany, exact); diff --git a/lib/filter_nb.c b/lib/filter_nb.c index 41bf3cf7f4..8838a48abd 100644 --- a/lib/filter_nb.c +++ b/lib/filter_nb.c @@ -24,10 +24,12 @@ #include "lib/northbound.h" #include "lib/prefix.h" +#include "lib/printfrr.h" #include "lib/filter.h" #include "lib/plist.h" #include "lib/plist_int.h" +#include "lib/routemap.h" /* Helper function. */ static in_addr_t @@ -39,20 +41,35 @@ ipv4_network_addr(in_addr_t hostaddr, int masklen) return hostaddr & mask.s_addr; } -static enum nb_error -prefix_list_length_validate(const struct lyd_node *dnode) +static void acl_notify_route_map(struct access_list *acl, int route_map_event) { - int type = yang_dnode_get_enum(dnode, "../../type"); + switch (route_map_event) { + case RMAP_EVENT_FILTER_ADDED: + if (acl->master->add_hook) + (*acl->master->add_hook)(acl); + break; + case RMAP_EVENT_FILTER_DELETED: + if (acl->master->delete_hook) + (*acl->master->delete_hook)(acl); + break; + } + + route_map_notify_dependencies(acl->name, route_map_event); +} + +static enum nb_error prefix_list_length_validate(struct nb_cb_modify_args *args) +{ + int type = yang_dnode_get_enum(args->dnode, "../../type"); const char *xpath_le = NULL, *xpath_ge = NULL; struct prefix p; uint8_t le, ge; if (type == YPLT_IPV4) { - yang_dnode_get_prefix(&p, dnode, "../ipv4-prefix"); + yang_dnode_get_prefix(&p, args->dnode, "../ipv4-prefix"); xpath_le = "../ipv4-prefix-length-lesser-or-equal"; xpath_ge = "../ipv4-prefix-length-greater-or-equal"; } else { - yang_dnode_get_prefix(&p, dnode, "../ipv6-prefix"); + yang_dnode_get_prefix(&p, args->dnode, "../ipv6-prefix"); xpath_le = "../ipv6-prefix-length-lesser-or-equal"; xpath_ge = "../ipv6-prefix-length-greater-or-equal"; } @@ -61,19 +78,18 @@ prefix_list_length_validate(const struct lyd_node *dnode) * Check rule: * prefix length <= le. */ - if (yang_dnode_exists(dnode, xpath_le)) { - le = yang_dnode_get_uint8(dnode, xpath_le); + if (yang_dnode_exists(args->dnode, xpath_le)) { + le = yang_dnode_get_uint8(args->dnode, xpath_le); if (p.prefixlen > le) goto log_and_fail; - } /* * Check rule: * prefix length < ge. */ - if (yang_dnode_exists(dnode, xpath_ge)) { - ge = yang_dnode_get_uint8(dnode, xpath_ge); + if (yang_dnode_exists(args->dnode, xpath_ge)) { + ge = yang_dnode_get_uint8(args->dnode, xpath_ge); if (p.prefixlen >= ge) goto log_and_fail; } @@ -82,18 +98,21 @@ prefix_list_length_validate(const struct lyd_node *dnode) * Check rule: * ge <= le. */ - if (yang_dnode_exists(dnode, xpath_le) && - yang_dnode_exists(dnode, xpath_ge)) { - le = yang_dnode_get_uint8(dnode, xpath_le); - ge = yang_dnode_get_uint8(dnode, xpath_ge); + if (yang_dnode_exists(args->dnode, xpath_le) + && yang_dnode_exists(args->dnode, xpath_ge)) { + le = yang_dnode_get_uint8(args->dnode, xpath_le); + ge = yang_dnode_get_uint8(args->dnode, xpath_ge); if (ge > le) goto log_and_fail; } return NB_OK; - log_and_fail: - zlog_info("prefix-list: invalid prefix range for %pFX: Make sure that mask length < ge <= le", &p); +log_and_fail: + snprintfrr( + args->errmsg, args->errmsg_len, + "Invalid prefix range for %pFX: Make sure that mask length < ge <= le", + &p); return NB_ERR_VALIDATION; } @@ -110,6 +129,19 @@ static void prefix_list_entry_set_empty(struct prefix_list_entry *ple) ple->le = 0; } +/** + * Unsets the cisco style rule for addresses so it becomes disabled (the + * equivalent of setting: `0.0.0.0/32`). + * + * \param addr address part. + * \param mask mask part. + */ +static void cisco_unset_addr_mask(struct in_addr *addr, struct in_addr *mask) +{ + addr->s_addr = INADDR_ANY; + mask->s_addr = CISCO_BIN_HOST_WILDCARD_MASK; +} + /* * XPath: /frr-filter:lib/access-list */ @@ -253,6 +285,8 @@ lib_access_list_entry_action_modify(struct nb_cb_modify_args *args) else f->type = FILTER_DENY; + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + return NB_OK; } @@ -273,6 +307,8 @@ lib_access_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) fz = &f->u.zfilter; yang_dnode_get_prefix(&fz->prefix, args->dnode, NULL); + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + return NB_OK; } @@ -289,6 +325,8 @@ lib_access_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) fz = &f->u.zfilter; memset(&fz->prefix, 0, sizeof(fz->prefix)); + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + return NB_OK; } @@ -308,6 +346,8 @@ lib_access_list_entry_ipv4_exact_match_modify(struct nb_cb_modify_args *args) fz = &f->u.zfilter; fz->exact = yang_dnode_get_bool(args->dnode, NULL); + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + return NB_OK; } @@ -324,6 +364,8 @@ lib_access_list_entry_ipv4_exact_match_destroy(struct nb_cb_destroy_args *args) fz = &f->u.zfilter; fz->exact = 0; + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + return NB_OK; } @@ -343,7 +385,9 @@ lib_access_list_entry_host_modify(struct nb_cb_modify_args *args) f->cisco = 1; fc = &f->u.cfilter; yang_dnode_get_ipv4(&fc->addr, args->dnode, NULL); - fc->addr_mask.s_addr = INADDR_ANY; + fc->addr_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -359,8 +403,9 @@ lib_access_list_entry_host_destroy(struct nb_cb_destroy_args *args) f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; - fc->addr.s_addr = INADDR_ANY; - fc->addr_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -384,6 +429,9 @@ lib_access_list_entry_network_modify(struct nb_cb_modify_args *args) yang_dnode_get_prefix(&p, args->dnode, NULL); fc->addr.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen); masklen2ip(p.prefixlen, &fc->addr_mask); + fc->addr_mask.s_addr = ~fc->addr_mask.s_addr; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -399,8 +447,9 @@ lib_access_list_entry_network_destroy(struct nb_cb_destroy_args *args) f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; - fc->addr.s_addr = INADDR_ANY; - fc->addr_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -421,7 +470,9 @@ lib_access_list_entry_source_any_create(struct nb_cb_create_args *args) f->cisco = 1; fc = &f->u.cfilter; fc->addr.s_addr = INADDR_ANY; - fc->addr_mask.s_addr = INADDR_NONE; + fc->addr_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -437,8 +488,9 @@ lib_access_list_entry_source_any_destroy(struct nb_cb_destroy_args *args) f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; - fc->addr.s_addr = INADDR_ANY; - fc->addr_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->addr, &fc->addr_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -459,7 +511,9 @@ static int lib_access_list_entry_destination_host_modify( fc = &f->u.cfilter; fc->extended = 1; yang_dnode_get_ipv4(&fc->mask, args->dnode, NULL); - fc->mask_mask.s_addr = INADDR_ANY; + fc->mask_mask.s_addr = CISCO_BIN_HOST_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -476,8 +530,9 @@ static int lib_access_list_entry_destination_host_destroy( f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; fc->extended = 0; - fc->mask.s_addr = INADDR_ANY; - fc->mask_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -501,6 +556,9 @@ static int lib_access_list_entry_destination_network_modify( yang_dnode_get_prefix(&p, args->dnode, NULL); fc->mask.s_addr = ipv4_network_addr(p.u.prefix4.s_addr, p.prefixlen); masklen2ip(p.prefixlen, &fc->mask_mask); + fc->mask_mask.s_addr = ~fc->mask_mask.s_addr; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -517,8 +575,9 @@ static int lib_access_list_entry_destination_network_destroy( f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; fc->extended = 0; - fc->mask.s_addr = INADDR_ANY; - fc->mask_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -539,7 +598,9 @@ static int lib_access_list_entry_destination_any_create( fc = &f->u.cfilter; fc->extended = 1; fc->mask.s_addr = INADDR_ANY; - fc->mask_mask.s_addr = INADDR_NONE; + fc->mask_mask.s_addr = CISCO_BIN_ANY_WILDCARD_MASK; + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); return NB_OK; } @@ -556,8 +617,9 @@ static int lib_access_list_entry_destination_any_destroy( f = nb_running_get_entry(args->dnode, NULL, true); fc = &f->u.cfilter; fc->extended = 0; - fc->mask.s_addr = INADDR_ANY; - fc->mask_mask.s_addr = INADDR_NONE; + cisco_unset_addr_mask(&fc->mask, &fc->mask_mask); + + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); return NB_OK; } @@ -592,6 +654,8 @@ static int lib_access_list_entry_any_create(struct nb_cb_create_args *args) break; } + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_ADDED); + return NB_OK; } @@ -607,6 +671,8 @@ static int lib_access_list_entry_any_destroy(struct nb_cb_destroy_args *args) fz = &f->u.zfilter; fz->prefix.family = 0; + acl_notify_route_map(f->acl, RMAP_EVENT_FILTER_DELETED); + return NB_OK; } @@ -829,7 +895,7 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify( struct prefix_list_entry *ple; if (args->event == NB_EV_VALIDATE && - prefix_list_length_validate(args->dnode) != NB_OK) + prefix_list_length_validate(args) != NB_OK) return NB_ERR_VALIDATION; if (args->event != NB_EV_APPLY) @@ -878,7 +944,7 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( struct prefix_list_entry *ple; if (args->event == NB_EV_VALIDATE && - prefix_list_length_validate(args->dnode) != NB_OK) + prefix_list_length_validate(args) != NB_OK) return NB_ERR_VALIDATION; if (args->event != NB_EV_APPLY) diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index fbb97d2dd5..2066e4c96d 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -55,7 +55,7 @@ int main(int argc, char **argv) vty_init(master, true); lib_cmd_init(); yang_init(true); - nb_init(master, NULL, 0); + nb_init(master, NULL, 0, false); vty_stdio(vty_do_exit); diff --git a/lib/hash.c b/lib/hash.c index 7f8a237047..85982774ac 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -77,9 +77,20 @@ void *hash_alloc_intern(void *arg) return arg; } +/* + * ssq = ssq + (new^2 - old^2) + * = ssq + ((new + old) * (new - old)) + */ #define hash_update_ssq(hz, old, new) \ - atomic_fetch_add_explicit(&hz->stats.ssq, (new + old) * (new - old), \ - memory_order_relaxed); + do { \ + int _adjust = (new + old) * (new - old); \ + if (_adjust < 0) \ + atomic_fetch_sub_explicit(&hz->stats.ssq, -_adjust, \ + memory_order_relaxed); \ + else \ + atomic_fetch_add_explicit(&hz->stats.ssq, _adjust, \ + memory_order_relaxed); \ + } while (0) /* Expand hash if the chain length exceeds the threshold. */ static void hash_expand(struct hash *hash) @@ -188,7 +188,9 @@ void if_destroy_via_zapi(struct interface *ifp) if (ifp_master.destroy_hook) (*ifp_master.destroy_hook)(ifp); + ifp->oldifindex = ifp->ifindex; if_set_index(ifp, IFINDEX_INTERNAL); + if (!ifp->configured) if_delete(&ifp); } @@ -224,6 +224,8 @@ struct interface { not work as expected. */ ifindex_t ifindex; + ifindex_t oldifindex; + /* * ifindex of parent interface, if any */ diff --git a/lib/ipaddr.h b/lib/ipaddr.h index f2b75c1306..730c7ce130 100644 --- a/lib/ipaddr.h +++ b/lib/ipaddr.h @@ -25,6 +25,8 @@ #include <zebra.h> +#include "lib/log.h" + #ifdef __cplusplus extern "C" { #endif @@ -59,6 +61,18 @@ struct ipaddr { #define IPADDRSZ(p) \ (IS_IPADDR_V4((p)) ? sizeof(struct in_addr) : sizeof(struct in6_addr)) +static inline int ipaddr_family(const struct ipaddr *ip) +{ + switch (ip->ipa_type) { + case IPADDR_V4: + return AF_INET; + case IPADDR_V6: + return AF_INET6; + default: + return AF_UNSPEC; + } +} + static inline int str2ipaddr(const char *str, struct ipaddr *ip) { int ret; @@ -131,6 +145,31 @@ static inline bool ipaddr_isset(struct ipaddr *ip) return (0 != memcmp(&a, ip, sizeof(struct ipaddr))); } +/* + * generic ordering comparison between IP addresses + */ +static inline int ipaddr_cmp(const struct ipaddr *a, const struct ipaddr *b) +{ + uint32_t va, vb; + va = a->ipa_type; + vb = b->ipa_type; + if (va != vb) + return (va < vb) ? -1 : 1; + switch (a->ipa_type) { + case IPADDR_V4: + va = ntohl(a->ipaddr_v4.s_addr); + vb = ntohl(b->ipaddr_v4.s_addr); + if (va != vb) + return (va < vb) ? -1 : 1; + return 0; + case IPADDR_V6: + return memcmp((void *)&a->ipaddr_v6, (void *)&b->ipaddr_v6, + sizeof(a->ipaddr_v6)); + default: + return 0; + } +} + #ifdef _FRR_ATTRIBUTE_PRINTFRR #pragma FRR printfrr_ext "%pIA" (struct ipaddr *) #endif diff --git a/lib/keychain.c b/lib/keychain.c index 251211734b..82fd6a65f2 100644 --- a/lib/keychain.c +++ b/lib/keychain.c @@ -1041,6 +1041,8 @@ static int keychain_config_write(struct vty *vty) } vty_out(vty, "\n"); } + + vty_out(vty, " exit\n"); } vty_out(vty, "!\n"); } diff --git a/lib/libfrr.c b/lib/libfrr.c index 2597eb61e2..500a02aacd 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -719,7 +719,7 @@ struct thread_master *frr_init(void) debug_init_cli(); - nb_init(master, di->yang_modules, di->n_yang_modules); + nb_init(master, di->yang_modules, di->n_yang_modules, true); if (nb_db_init() != NB_OK) flog_warn(EC_LIB_NB_DATABASE, "%s: failed to initialize northbound database", diff --git a/lib/linklist.c b/lib/linklist.c index 2936c5b502..84dc6e1419 100644 --- a/lib/linklist.c +++ b/lib/linklist.c @@ -150,6 +150,23 @@ bool listnode_add_sort_nodup(struct list *list, void *val) return true; } +struct list *list_dup(struct list *list) +{ + struct list *dup; + struct listnode *node; + void *data; + + assert(list); + + dup = list_new(); + dup->cmp = list->cmp; + dup->del = list->del; + for (ALL_LIST_ELEMENTS_RO(list, node, data)) + listnode_add(dup, data); + + return dup; +} + void listnode_add_sort(struct list *list, void *val) { struct listnode *n; diff --git a/lib/linklist.h b/lib/linklist.h index 94a1a1604a..d8820c924d 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -327,6 +327,19 @@ extern void list_filter_out_nodes(struct list *list, bool (*cond)(void *data)); */ extern bool listnode_add_sort_nodup(struct list *list, void *val); + +/* + * Duplicate the specified list, creating a shallow copy of each of its + * elements. + * + * list + * list to duplicate + * + * Returns: + * the duplicated list + */ +extern struct list *list_dup(struct list *list); + /* List iteration macro. * Usage: for (ALL_LIST_ELEMENTS (...) { ... } * It is safe to delete the listnode using this macro. @@ -384,6 +384,9 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_MPLS_LABELS_ADD), DESC_ENTRY(ZEBRA_MPLS_LABELS_DELETE), DESC_ENTRY(ZEBRA_MPLS_LABELS_REPLACE), + DESC_ENTRY(ZEBRA_SR_POLICY_SET), + DESC_ENTRY(ZEBRA_SR_POLICY_DELETE), + DESC_ENTRY(ZEBRA_SR_POLICY_NOTIFY_STATUS), DESC_ENTRY(ZEBRA_IPMR_ROUTE_STATS), DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT), DESC_ENTRY(ZEBRA_LABEL_MANAGER_CONNECT_ASYNC), @@ -448,7 +451,8 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_CLIENT_CAPABILITIES), DESC_ENTRY(ZEBRA_OPAQUE_MESSAGE), DESC_ENTRY(ZEBRA_OPAQUE_REGISTER), - DESC_ENTRY(ZEBRA_OPAQUE_UNREGISTER)}; + DESC_ENTRY(ZEBRA_OPAQUE_UNREGISTER), + DESC_ENTRY(ZEBRA_NEIGH_DISCOVER)}; #undef DESC_ENTRY static const struct zebra_desc_table unknown = {0, "unknown", '?'}; diff --git a/lib/mpls.h b/lib/mpls.h index 126dbf753d..74bd7aae3e 100644 --- a/lib/mpls.h +++ b/lib/mpls.h @@ -72,8 +72,7 @@ extern "C" { /* Maximum # labels that can be pushed. */ #define MPLS_MAX_LABELS 16 -#define IS_MPLS_RESERVED_LABEL(label) \ - (label >= MPLS_LABEL_RESERVED_MIN && label <= MPLS_LABEL_RESERVED_MAX) +#define IS_MPLS_RESERVED_LABEL(label) (label <= MPLS_LABEL_RESERVED_MAX) #define IS_MPLS_UNRESERVED_LABEL(label) \ (label >= MPLS_LABEL_UNRESERVED_MIN \ @@ -129,6 +128,7 @@ enum lsp_types_t { ZEBRA_LSP_OSPF_SR = 4,/* OSPF Segment Routing LSP. */ ZEBRA_LSP_ISIS_SR = 5,/* IS-IS Segment Routing LSP. */ ZEBRA_LSP_SHARP = 6, /* Identifier for test protocol */ + ZEBRA_LSP_SRTE = 7, /* SR-TE LSP */ }; /* Functions for basic label operations. */ diff --git a/lib/nexthop.c b/lib/nexthop.c index 28d96a539c..0ea72d03e1 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -152,6 +152,11 @@ static int _nexthop_cmp_no_labels(const struct nexthop *next1, break; } + if (next1->srte_color < next2->srte_color) + return -1; + if (next1->srte_color > next2->srte_color) + return 1; + ret = _nexthop_source_cmp(next1, next2); if (ret != 0) goto done; @@ -643,6 +648,7 @@ void nexthop_copy_no_recurse(struct nexthop *copy, if (copy->backup_num > 0) memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num); + copy->srte_color = nexthop->srte_color; memcpy(©->gate, &nexthop->gate, sizeof(nexthop->gate)); memcpy(©->src, &nexthop->src, sizeof(nexthop->src)); memcpy(©->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src)); diff --git a/lib/nexthop.h b/lib/nexthop.h index ed40cc7eed..cadcea1f41 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -98,6 +98,7 @@ struct nexthop { */ #define NEXTHOP_FLAG_RNH_FILTERED (1 << 5) /* rmap filtered, used by rnh */ #define NEXTHOP_FLAG_HAS_BACKUP (1 << 6) /* Backup nexthop index is set */ +#define NEXTHOP_FLAG_SRTE (1 << 7) /* SR-TE color used for BGP traffic */ #define NEXTHOP_IS_ACTIVE(flags) \ (CHECK_FLAG(flags, NEXTHOP_FLAG_ACTIVE) \ @@ -141,6 +142,9 @@ struct nexthop { union { vni_t vni; } nh_encap; + + /* SR-TE color used for matching SR-TE policies */ + uint32_t srte_color; }; /* Utility to append one nexthop to another. */ diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 97815ceeb9..687cac4062 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -940,6 +940,12 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, nhg_hooks.add_nexthop(nhgc, nh); } + if (intf) { + struct interface *ifp = if_lookup_by_name_all_vrf(intf); + + if (ifp) + ifp->configured = true; + } return CMD_SUCCESS; } @@ -952,24 +958,29 @@ static struct cmd_node nexthop_group_node = { .config_write = nexthop_group_write, }; -void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh) +void nexthop_group_write_nexthop_simple(struct vty *vty, + const struct nexthop *nh, + char *altifname) { char buf[100]; - struct vrf *vrf; - int i; + char *ifname; vty_out(vty, "nexthop "); + if (altifname) + ifname = altifname; + else + ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id); + switch (nh->type) { case NEXTHOP_TYPE_IFINDEX: - vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id)); + vty_out(vty, "%s", ifname); break; case NEXTHOP_TYPE_IPV4: vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4)); break; case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), - ifindex2ifname(nh->ifindex, nh->vrf_id)); + vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), ifname); break; case NEXTHOP_TYPE_IPV6: vty_out(vty, "%s", @@ -978,15 +989,23 @@ void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh) case NEXTHOP_TYPE_IPV6_IFINDEX: vty_out(vty, "%s %s", inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)), - ifindex2ifname(nh->ifindex, nh->vrf_id)); + ifname); break; case NEXTHOP_TYPE_BLACKHOLE: break; } +} + +void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh) +{ + struct vrf *vrf; + int i; + + nexthop_group_write_nexthop_simple(vty, nh, NULL); if (nh->vrf_id != VRF_DEFAULT) { vrf = vrf_lookup_by_id(nh->vrf_id); - vty_out(vty, " nexthop-vrf %s", vrf->name); + vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf)); } if (nh->nh_label && nh->nh_label->num_labels > 0) { @@ -1229,6 +1248,7 @@ void nexthop_group_interface_state_change(struct interface *ifp, if (ifp->ifindex != nhop.ifindex) continue; + ifp->configured = true; nh = nexthop_new(); memcpy(nh, &nhop, sizeof(nhop)); diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 0b5ac91bb2..5f7bde0def 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -135,6 +135,9 @@ extern bool nexthop_group_equal(const struct nexthop_group *nhg1, extern struct nexthop_group_cmd *nhgc_find(const char *name); +extern void nexthop_group_write_nexthop_simple(struct vty *vty, + const struct nexthop *nh, + char *altifname); extern void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh); diff --git a/lib/northbound.c b/lib/northbound.c index 48b8499bfc..18500a8bd2 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -57,6 +57,8 @@ static struct { const void *owner_user; } running_config_mgmt_lock; +/* Knob to record config transaction */ +static bool nb_db_enabled; /* * Global lock used to prevent multiple configuration transactions from * happening concurrently. @@ -680,8 +682,12 @@ int nb_candidate_commit_prepare(struct nb_context *context, RB_INIT(nb_config_cbs, &changes); nb_config_diff(running_config, candidate, &changes); - if (RB_EMPTY(nb_config_cbs, &changes)) + if (RB_EMPTY(nb_config_cbs, &changes)) { + snprintf( + errmsg, errmsg_len, + "No changes to apply were found during preparation phase"); return NB_ERR_NO_CHANGES; + } if (nb_candidate_validate_code(context, candidate, &changes, errmsg, errmsg_len) @@ -707,30 +713,28 @@ int nb_candidate_commit_prepare(struct nb_context *context, errmsg_len); } -void nb_candidate_commit_abort(struct nb_transaction *transaction) +void nb_candidate_commit_abort(struct nb_transaction *transaction, char *errmsg, + size_t errmsg_len) { - char errmsg[BUFSIZ] = {0}; - (void)nb_transaction_process(NB_EV_ABORT, transaction, errmsg, - sizeof(errmsg)); + errmsg_len); nb_transaction_free(transaction); } void nb_candidate_commit_apply(struct nb_transaction *transaction, - bool save_transaction, uint32_t *transaction_id) + bool save_transaction, uint32_t *transaction_id, + char *errmsg, size_t errmsg_len) { - char errmsg[BUFSIZ] = {0}; - (void)nb_transaction_process(NB_EV_APPLY, transaction, errmsg, - sizeof(errmsg)); - nb_transaction_apply_finish(transaction, errmsg, sizeof(errmsg)); + errmsg_len); + nb_transaction_apply_finish(transaction, errmsg, errmsg_len); /* Replace running by candidate. */ transaction->config->version++; nb_config_replace(running_config, transaction->config, true); /* Record transaction. */ - if (save_transaction + if (save_transaction && nb_db_enabled && nb_db_transaction_save(transaction, transaction_id) != NB_OK) flog_warn(EC_LIB_NB_TRANSACTION_RECORD_FAILED, "%s: failed to record transaction", __func__); @@ -754,9 +758,9 @@ int nb_candidate_commit(struct nb_context *context, struct nb_config *candidate, */ if (ret == NB_OK) nb_candidate_commit_apply(transaction, save_transaction, - transaction_id); + transaction_id, errmsg, errmsg_len); else if (transaction != NULL) - nb_candidate_commit_abort(transaction); + nb_candidate_commit_abort(transaction, errmsg, errmsg_len); return ret; } @@ -2046,18 +2050,21 @@ void *nb_running_unset_entry(const struct lyd_node *dnode) return entry; } -void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, - bool abort_if_not_found) +static void *nb_running_get_entry_worker(const struct lyd_node *dnode, + const char *xpath, + bool abort_if_not_found, + bool rec_search) { const struct lyd_node *orig_dnode = dnode; char xpath_buf[XPATH_MAXLEN]; + bool rec_flag = true; assert(dnode || xpath); if (!dnode) dnode = yang_dnode_get(running_config->dnode, xpath); - while (dnode) { + while (rec_flag && dnode) { struct nb_config_entry *config, s; yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath)); @@ -2065,6 +2072,8 @@ void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, if (config) return config->entry; + rec_flag = rec_search; + dnode = dnode->parent; } @@ -2078,6 +2087,20 @@ void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, abort(); } +void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, + bool abort_if_not_found) +{ + return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found, + true); +} + +void *nb_running_get_entry_non_rec(const struct lyd_node *dnode, + const char *xpath, bool abort_if_not_found) +{ + return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found, + false); +} + /* Logging functions. */ const char *nb_event_name(enum nb_event event) { @@ -2195,7 +2218,7 @@ static void nb_load_callbacks(const struct frr_yang_module_info *module) void nb_init(struct thread_master *tm, const struct frr_yang_module_info *const modules[], - size_t nmodules) + size_t nmodules, bool db_enabled) { unsigned int errors = 0; @@ -2220,6 +2243,8 @@ void nb_init(struct thread_master *tm, exit(1); } + nb_db_enabled = db_enabled; + /* Create an empty running configuration. */ running_config = nb_config_new(NULL); running_config_entries = hash_create(running_config_entry_key_make, diff --git a/lib/northbound.h b/lib/northbound.h index bd57013f59..7b481273cb 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -910,8 +910,15 @@ extern int nb_candidate_commit_prepare(struct nb_context *context, * * transaction * Candidate configuration to abort. It's consumed by this function. + * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. */ -extern void nb_candidate_commit_abort(struct nb_transaction *transaction); +extern void nb_candidate_commit_abort(struct nb_transaction *transaction, + char *errmsg, size_t errmsg_len); /* * Commit a previously created configuration transaction. @@ -925,10 +932,17 @@ extern void nb_candidate_commit_abort(struct nb_transaction *transaction); * * transaction_id * Optional output parameter providing the ID of the committed transaction. + * + * errmsg + * Buffer to store human-readable error message in case of error. + * + * errmsg_len + * Size of errmsg. */ extern void nb_candidate_commit_apply(struct nb_transaction *transaction, bool save_transaction, - uint32_t *transaction_id); + uint32_t *transaction_id, char *errmsg, + size_t errmsg_len); /* * Create a new transaction to commit a candidate configuration. This is a @@ -1165,6 +1179,14 @@ extern void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath, bool abort_if_not_found); /* + * Same as 'nb_running_get_entry', but doesn't search within parent nodes + * recursively if an user point is not found. + */ +extern void *nb_running_get_entry_non_rec(const struct lyd_node *dnode, + const char *xpath, + bool abort_if_not_found); + +/* * Return a human-readable string representing a northbound event. * * event @@ -1217,10 +1239,13 @@ extern const char *nb_client_name(enum nb_client client); * * nmodules * Size of the modules array. + * + * db_enabled + * Set this to record the transactions in the transaction log. */ extern void nb_init(struct thread_master *tm, const struct frr_yang_module_info *const modules[], - size_t nmodules); + size_t nmodules, bool db_enabled); /* * Finish the northbound layer gracefully. Should be called only when the daemon diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 534b5128ee..ee080bca3f 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -63,7 +63,15 @@ static int nb_cli_classic_commit(struct vty *vty) context.user = vty; ret = nb_candidate_commit(&context, vty->candidate_config, true, NULL, NULL, errmsg, sizeof(errmsg)); - if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { + switch (ret) { + case NB_OK: + /* Successful commit. Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); + break; + case NB_ERR_NO_CHANGES: + break; + default: vty_out(vty, "%% Configuration failed.\n\n"); vty_show_nb_errors(vty, ret, errmsg); if (vty->t_pending_commit) @@ -318,11 +326,14 @@ int nb_cli_confirmed_commit_rollback(struct vty *vty) &context, vty->confirmed_commit_rollback, true, "Rollback to previous configuration - confirmed commit has timed out", &transaction_id, errmsg, sizeof(errmsg)); - if (ret == NB_OK) + if (ret == NB_OK) { vty_out(vty, "Rollback performed successfully (Transaction ID #%u).\n", transaction_id); - else { + /* Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); + } else { vty_out(vty, "Failed to rollback to previous configuration.\n\n"); vty_show_nb_errors(vty, ret, errmsg); @@ -407,6 +418,9 @@ static int nb_cli_commit(struct vty *vty, bool force, vty_out(vty, "%% Configuration committed successfully (Transaction ID #%u).\n\n", transaction_id); + /* Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); return CMD_SUCCESS; case NB_ERR_NO_CHANGES: vty_out(vty, "%% No configuration changes to commit.\n\n"); @@ -1666,6 +1680,9 @@ static int nb_cli_rollback_configuration(struct vty *vty, case NB_OK: vty_out(vty, "%% Configuration was successfully rolled back.\n\n"); + /* Print warnings (if any). */ + if (strlen(errmsg) > 0) + vty_out(vty, "%s\n", errmsg); return CMD_SUCCESS; case NB_ERR_NO_CHANGES: vty_out(vty, diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index a3aaf02f08..1f480f3d02 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -375,8 +375,10 @@ static int frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen) /* Apply the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_apply(transaction, true, NULL); + nb_candidate_commit_apply(transaction, true, NULL, errmsg, + sizeof(errmsg)); nb_config_free(candidate); } @@ -400,8 +402,9 @@ static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen) /* Abort the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_abort(transaction); + nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg)); nb_config_free(candidate); } diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index 83d7e0ce95..f35b4bb31b 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -677,11 +677,13 @@ class NorthboundImpl switch (phase) { case frr::CommitRequest::VALIDATE: + zlog_debug("`-> Performing VALIDATE"); ret = nb_candidate_validate( &context, candidate->config, errmsg, sizeof(errmsg)); break; case frr::CommitRequest::PREPARE: + zlog_debug("`-> Performing PREPARE"); ret = nb_candidate_commit_prepare( &context, candidate->config, comment.c_str(), @@ -689,15 +691,20 @@ class NorthboundImpl sizeof(errmsg)); break; case frr::CommitRequest::ABORT: + zlog_debug("`-> Performing ABORT"); nb_candidate_commit_abort( - candidate->transaction); + candidate->transaction, errmsg, + sizeof(errmsg)); break; case frr::CommitRequest::APPLY: + zlog_debug("`-> Performing ABORT"); nb_candidate_commit_apply( candidate->transaction, true, - &transaction_id); + &transaction_id, errmsg, + sizeof(errmsg)); break; case frr::CommitRequest::ALL: + zlog_debug("`-> Performing ALL"); ret = nb_candidate_commit( &context, candidate->config, true, comment.c_str(), &transaction_id, @@ -735,12 +742,20 @@ class NorthboundImpl grpc::StatusCode::INTERNAL, errmsg); break; } + + if (nb_dbg_client_grpc) + zlog_debug("`-> Result: %s (message: '%s')", + nb_err_name((enum nb_error)ret), + errmsg); + if (ret == NB_OK) { // Response: uint32 transaction_id = 1; if (transaction_id) tag->response.set_transaction_id( transaction_id); } + if (strlen(errmsg) > 0) + tag->response.set_error_message(errmsg); tag->responder.Finish(tag->response, status, tag); tag->state = FINISH; @@ -1281,10 +1296,13 @@ class NorthboundImpl void delete_candidate(struct candidate *candidate) { + char errmsg[BUFSIZ] = {0}; + _candidates.erase(candidate->id); nb_config_free(candidate->config); if (candidate->transaction) - nb_candidate_commit_abort(candidate->transaction); + nb_candidate_commit_abort(candidate->transaction, + errmsg, sizeof(errmsg)); } struct candidate *get_candidate(uint32_t candidate_id) diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 500203173c..3dec685927 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -37,13 +37,11 @@ DEFINE_MTYPE_STATIC(LIB, SYSREPO, "Sysrepo module") static struct debug nb_dbg_client_sysrepo = {0, "Northbound client: Sysrepo"}; static struct thread_master *master; -static struct list *sysrepo_threads; static sr_session_ctx_t *session; static sr_conn_ctx_t *connection; static struct nb_transaction *transaction; static int frr_sr_read_cb(struct thread *thread); -static int frr_sr_write_cb(struct thread *thread); static int frr_sr_finish(void); /* Convert FRR YANG data value to sysrepo YANG data value. */ @@ -236,25 +234,23 @@ static int frr_sr_process_change(struct nb_config *candidate, return NB_OK; } -static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, - const char *module_name, - bool startup_config) +static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session, + const char *module_name, + bool startup_config) { sr_change_iter_t *it; int ret; sr_change_oper_t sr_op; sr_val_t *sr_old_val, *sr_new_val; - char xpath[XPATH_MAXLEN]; struct nb_context context = {}; struct nb_config *candidate; char errmsg[BUFSIZ] = {0}; - snprintf(xpath, sizeof(xpath), "/%s:*", module_name); - ret = sr_get_changes_iter(session, xpath, &it); + ret = sr_get_changes_iter(session, "//*", &it); if (ret != SR_ERR_OK) { flog_err(EC_LIB_LIBSYSREPO, - "%s: sr_get_changes_iter() failed for xpath %s", - __func__, xpath); + "%s: sr_get_changes_iter() failed for \"%s\"", + __func__, module_name); return ret; } @@ -307,12 +303,14 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session, __func__, nb_err_name(ret), errmsg); } + if (!transaction) + nb_config_free(candidate); + /* Map northbound return code to sysrepo return code. */ switch (ret) { case NB_OK: return SR_ERR_OK; case NB_ERR_NO_CHANGES: - nb_config_free(candidate); return SR_ERR_OK; case NB_ERR_LOCKED: return SR_ERR_LOCKED; @@ -329,8 +327,10 @@ static int frr_sr_config_change_cb_apply(sr_session_ctx_t *session, /* Apply the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_apply(transaction, true, NULL); + nb_candidate_commit_apply(transaction, true, NULL, errmsg, + sizeof(errmsg)); nb_config_free(candidate); } @@ -343,8 +343,9 @@ static int frr_sr_config_change_cb_abort(sr_session_ctx_t *session, /* Abort the transaction. */ if (transaction) { struct nb_config *candidate = transaction->config; + char errmsg[BUFSIZ] = {0}; - nb_candidate_commit_abort(transaction); + nb_candidate_commit_abort(transaction, errmsg, sizeof(errmsg)); nb_config_free(candidate); } @@ -353,22 +354,23 @@ static int frr_sr_config_change_cb_abort(sr_session_ctx_t *session, /* Callback for changes in the running configuration. */ static int frr_sr_config_change_cb(sr_session_ctx_t *session, - const char *module_name, - sr_notif_event_t sr_ev, void *private_ctx) + const char *module_name, const char *xpath, + sr_event_t sr_ev, uint32_t request_id, + void *private_data) { switch (sr_ev) { case SR_EV_ENABLED: - return frr_sr_config_change_cb_verify(session, module_name, - true); - case SR_EV_VERIFY: - return frr_sr_config_change_cb_verify(session, module_name, - false); - case SR_EV_APPLY: + return frr_sr_config_change_cb_prepare(session, module_name, + true); + case SR_EV_CHANGE: + return frr_sr_config_change_cb_prepare(session, module_name, + false); + case SR_EV_DONE: return frr_sr_config_change_cb_apply(session, module_name); case SR_EV_ABORT: return frr_sr_config_change_cb_abort(session, module_name); default: - flog_err(EC_LIB_LIBSYSREPO, "%s: unknown sysrepo event: %u", + flog_err(EC_LIB_LIBSYSREPO, "%s: unexpected sysrepo event: %u", __func__, sr_ev); return SR_ERR_INTERNAL; } @@ -378,70 +380,49 @@ static int frr_sr_state_data_iter_cb(const struct lys_node *snode, struct yang_translator *translator, struct yang_data *data, void *arg) { - struct list *elements = arg; - - listnode_add(elements, data); + struct lyd_node *dnode = arg; + + ly_errno = 0; + dnode = lyd_new_path(dnode, ly_native_ctx, data->xpath, data->value, 0, + LYD_PATH_OPT_UPDATE); + if (!dnode && ly_errno) { + flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", + __func__); + yang_data_free(data); + return NB_ERR; + } + yang_data_free(data); return NB_OK; } /* Callback for state retrieval. */ -static int frr_sr_state_cb(const char *xpath, sr_val_t **values, - size_t *values_cnt, uint64_t request_id, - const char *original_xpath, void *private_ctx) +static int frr_sr_state_cb(sr_session_ctx_t *session, const char *module_name, + const char *xpath, const char *request_xpath, + uint32_t request_id, struct lyd_node **parent, + void *private_ctx) { - struct list *elements; - struct yang_data *data; - struct listnode *node; - sr_val_t *v; - int ret, count, i = 0; + struct lyd_node *dnode; - elements = yang_data_list_new(); - if (nb_oper_data_iterate(xpath, NULL, NB_OPER_DATA_ITER_NORECURSE, - frr_sr_state_data_iter_cb, elements) + dnode = *parent; + if (nb_oper_data_iterate(request_xpath, NULL, 0, + frr_sr_state_data_iter_cb, dnode) != NB_OK) { flog_warn(EC_LIB_NB_OPERATIONAL_DATA, "%s: failed to obtain operational data [xpath %s]", __func__, xpath); - goto exit; - } - - if (list_isempty(elements)) - goto exit; - - count = listcount(elements); - ret = sr_new_values(count, &v); - if (ret != SR_ERR_OK) { - flog_err(EC_LIB_LIBSYSREPO, "%s: sr_new_values(): %s", __func__, - sr_strerror(ret)); - goto exit; - } - - for (ALL_LIST_ELEMENTS_RO(elements, node, data)) { - if (yang_data_frr2sr(data, &v[i++]) != 0) { - flog_err(EC_LIB_SYSREPO_DATA_CONVERT, - "%s: failed to convert data to sysrepo format", - __func__); - } + return SR_ERR_INTERNAL; } - *values = v; - *values_cnt = count; - - list_delete(&elements); - - return SR_ERR_OK; - -exit: - list_delete(&elements); - *values = NULL; - *values_cnt = 0; + *parent = dnode; return SR_ERR_OK; } -static int frr_sr_config_rpc_cb(const char *xpath, const sr_val_t *sr_input, - const size_t input_cnt, sr_val_t **sr_output, +static int frr_sr_config_rpc_cb(sr_session_ctx_t *session, const char *xpath, + const sr_val_t *sr_input, + const size_t input_cnt, sr_event_t sr_ev, + uint32_t request_id, sr_val_t **sr_output, size_t *sr_output_cnt, void *private_ctx) { struct nb_node *nb_node; @@ -548,8 +529,7 @@ static int frr_sr_notification_send(const char *xpath, struct list *arguments) } } - ret = sr_event_notif_send(session, xpath, values, values_cnt, - SR_EV_NOTIF_DEFAULT); + ret = sr_event_notif_send(session, xpath, values, values_cnt); if (ret != SR_ERR_OK) { flog_err(EC_LIB_LIBSYSREPO, "%s: sr_event_notif_send() failed for xpath %s", @@ -560,102 +540,13 @@ static int frr_sr_notification_send(const char *xpath, struct list *arguments) return NB_OK; } -/* Code to integrate the sysrepo client into FRR main event loop. */ -struct sysrepo_thread { - struct thread *thread; - sr_fd_event_t event; - int fd; -}; - -static struct sysrepo_thread *frr_sr_fd_lookup(sr_fd_event_t event, int fd) -{ - struct sysrepo_thread *sr_thread; - struct listnode *node; - - for (ALL_LIST_ELEMENTS_RO(sysrepo_threads, node, sr_thread)) { - if (sr_thread->event == event && sr_thread->fd == fd) - return sr_thread; - } - - return NULL; -} - -static void frr_sr_fd_add(int event, int fd) -{ - struct sysrepo_thread *sr_thread; - - if (frr_sr_fd_lookup(event, fd) != NULL) - return; - - sr_thread = XCALLOC(MTYPE_SYSREPO, sizeof(*sr_thread)); - sr_thread->event = event; - sr_thread->fd = fd; - listnode_add(sysrepo_threads, sr_thread); - - switch (event) { - case SR_FD_INPUT_READY: - thread_add_read(master, frr_sr_read_cb, NULL, fd, - &sr_thread->thread); - break; - case SR_FD_OUTPUT_READY: - thread_add_write(master, frr_sr_write_cb, NULL, fd, - &sr_thread->thread); - break; - default: - return; - } -} - -static void frr_sr_fd_free(struct sysrepo_thread *sr_thread) -{ - THREAD_OFF(sr_thread->thread); - XFREE(MTYPE_SYSREPO, sr_thread); -} - -static void frr_sr_fd_del(int event, int fd) -{ - struct sysrepo_thread *sr_thread; - - sr_thread = frr_sr_fd_lookup(event, fd); - if (!sr_thread) - return; - - listnode_delete(sysrepo_threads, sr_thread); - frr_sr_fd_free(sr_thread); -} - -static void frr_sr_fd_update(sr_fd_change_t *fd_change_set, - size_t fd_change_set_cnt) -{ - for (size_t i = 0; i < fd_change_set_cnt; i++) { - int fd = fd_change_set[i].fd; - int event = fd_change_set[i].events; - - if (event != SR_FD_INPUT_READY && event != SR_FD_OUTPUT_READY) - continue; - - switch (fd_change_set[i].action) { - case SR_FD_START_WATCHING: - frr_sr_fd_add(event, fd); - break; - case SR_FD_STOP_WATCHING: - frr_sr_fd_del(event, fd); - break; - default: - break; - } - } -} - static int frr_sr_read_cb(struct thread *thread) { + sr_subscription_ctx_t *sr_subscription = THREAD_ARG(thread); int fd = THREAD_FD(thread); - sr_fd_change_t *fd_change_set = NULL; - size_t fd_change_set_cnt = 0; int ret; - ret = sr_fd_event_process(fd, SR_FD_INPUT_READY, &fd_change_set, - &fd_change_set_cnt); + ret = sr_process_events(sr_subscription, session, NULL); if (ret != SR_ERR_OK) { flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s", __func__, sr_strerror(ret)); @@ -663,31 +554,7 @@ static int frr_sr_read_cb(struct thread *thread) } thread = NULL; - thread_add_read(master, frr_sr_read_cb, NULL, fd, &thread); - - frr_sr_fd_update(fd_change_set, fd_change_set_cnt); - free(fd_change_set); - - return 0; -} - -static int frr_sr_write_cb(struct thread *thread) -{ - int fd = THREAD_FD(thread); - sr_fd_change_t *fd_change_set = NULL; - size_t fd_change_set_cnt = 0; - int ret; - - ret = sr_fd_event_process(fd, SR_FD_OUTPUT_READY, &fd_change_set, - &fd_change_set_cnt); - if (ret != SR_ERR_OK) { - flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s", - __func__, sr_strerror(ret)); - return -1; - } - - frr_sr_fd_update(fd_change_set, fd_change_set_cnt); - free(fd_change_set); + thread_add_read(master, frr_sr_read_cb, sr_subscription, fd, &thread); return 0; } @@ -697,8 +564,8 @@ static void frr_sr_subscribe_config(struct yang_module *module) int ret; ret = sr_module_change_subscribe( - session, module->name, frr_sr_config_change_cb, NULL, 0, - SR_SUBSCR_DEFAULT | SR_SUBSCR_EV_ENABLED, + session, module->name, NULL, frr_sr_config_change_cb, NULL, 0, + SR_SUBSCR_DEFAULT | SR_SUBSCR_ENABLED | SR_SUBSCR_NO_THREAD, &module->sr_subscription); if (ret != SR_ERR_OK) flog_err(EC_LIB_LIBSYSREPO, "sr_module_change_subscribe(): %s", @@ -722,11 +589,11 @@ static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg) DEBUGD(&nb_dbg_client_sysrepo, "%s: providing data to '%s'", __func__, nb_node->xpath); - ret = sr_dp_get_items_subscribe( - session, nb_node->xpath, frr_sr_state_cb, NULL, - SR_SUBSCR_CTX_REUSE, &module->sr_subscription); + ret = sr_oper_get_items_subscribe( + session, snode->module->name, nb_node->xpath, frr_sr_state_cb, + NULL, SR_SUBSCR_CTX_REUSE, &module->sr_subscription); if (ret != SR_ERR_OK) - flog_err(EC_LIB_LIBSYSREPO, "sr_dp_get_items_subscribe(): %s", + flog_err(EC_LIB_LIBSYSREPO, "sr_oper_get_items_subscribe(): %s", sr_strerror(ret)); return YANG_ITER_CONTINUE; @@ -747,7 +614,7 @@ static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg) nb_node->xpath); ret = sr_rpc_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb, - NULL, SR_SUBSCR_CTX_REUSE, + NULL, 0, SR_SUBSCR_CTX_REUSE, &module->sr_subscription); if (ret != SR_ERR_OK) flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s", @@ -756,30 +623,6 @@ static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg) return YANG_ITER_CONTINUE; } -static int frr_sr_subscribe_action(const struct lys_node *snode, void *arg) -{ - struct yang_module *module = arg; - struct nb_node *nb_node; - int ret; - - if (snode->nodetype != LYS_ACTION) - return YANG_ITER_CONTINUE; - - nb_node = snode->priv; - - DEBUGD(&nb_dbg_client_sysrepo, "%s: providing action to '%s'", __func__, - nb_node->xpath); - - ret = sr_action_subscribe(session, nb_node->xpath, frr_sr_config_rpc_cb, - NULL, SR_SUBSCR_CTX_REUSE, - &module->sr_subscription); - if (ret != SR_ERR_OK) - flog_err(EC_LIB_LIBSYSREPO, "sr_action_subscribe(): %s", - sr_strerror(ret)); - - return YANG_ITER_CONTINUE; -} - /* CLI commands. */ DEFUN (debug_nb_sr, debug_nb_sr_cmd, @@ -827,22 +670,13 @@ static void frr_sr_cli_init(void) } /* FRR's Sysrepo initialization. */ -static int frr_sr_init(const char *program_name) +static int frr_sr_init(void) { struct yang_module *module; - int sysrepo_fd, ret; - - sysrepo_threads = list_new(); - - ret = sr_fd_watcher_init(&sysrepo_fd, NULL); - if (ret != SR_ERR_OK) { - flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_fd_watcher_init(): %s", - __func__, sr_strerror(ret)); - goto cleanup; - } + int ret; /* Connect to Sysrepo. */ - ret = sr_connect(program_name, SR_CONN_DEFAULT, &connection); + ret = sr_connect(SR_CONN_DEFAULT, &connection); if (ret != SR_ERR_OK) { flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_connect(): %s", __func__, sr_strerror(ret)); @@ -850,8 +684,7 @@ static int frr_sr_init(const char *program_name) } /* Start session. */ - ret = sr_session_start(connection, SR_DS_RUNNING, SR_SESS_DEFAULT, - &session); + ret = sr_session_start(connection, SR_DS_RUNNING, &session); if (ret != SR_ERR_OK) { flog_err(EC_LIB_SYSREPO_INIT, "%s: sr_session_start(): %s", __func__, sr_strerror(ret)); @@ -860,19 +693,28 @@ static int frr_sr_init(const char *program_name) /* Perform subscriptions. */ RB_FOREACH (module, yang_modules, &yang_modules) { + int event_pipe; + frr_sr_subscribe_config(module); yang_snodes_iterate_module(module->info, frr_sr_subscribe_state, 0, module); yang_snodes_iterate_module(module->info, frr_sr_subscribe_rpc, 0, module); - yang_snodes_iterate_module(module->info, - frr_sr_subscribe_action, 0, module); + + /* Watch subscriptions. */ + ret = sr_get_event_pipe(module->sr_subscription, &event_pipe); + if (ret != SR_ERR_OK) { + flog_err(EC_LIB_SYSREPO_INIT, + "%s: sr_get_event_pipe(): %s", __func__, + sr_strerror(ret)); + goto cleanup; + } + thread_add_read(master, frr_sr_read_cb, module->sr_subscription, + event_pipe, &module->sr_thread); } hook_register(nb_notification_send, frr_sr_notification_send); - frr_sr_fd_add(SR_FD_INPUT_READY, sysrepo_fd); - return 0; cleanup: @@ -888,7 +730,8 @@ static int frr_sr_finish(void) RB_FOREACH (module, yang_modules, &yang_modules) { if (!module->sr_subscription) continue; - sr_unsubscribe(session, module->sr_subscription); + sr_unsubscribe(module->sr_subscription); + THREAD_OFF(module->sr_thread); } if (session) @@ -896,10 +739,6 @@ static int frr_sr_finish(void) if (connection) sr_disconnect(connection); - sysrepo_threads->del = (void (*)(void *))frr_sr_fd_free; - list_delete(&sysrepo_threads); - sr_fd_watcher_cleanup(); - return 0; } @@ -907,7 +746,7 @@ static int frr_sr_module_late_init(struct thread_master *tm) { master = tm; - if (frr_sr_init(frr_get_progname()) < 0) { + if (frr_sr_init() < 0) { flog_err(EC_LIB_SYSREPO_INIT, "failed to initialize the Sysrepo module"); return -1; @@ -129,6 +129,8 @@ struct pbr_rule { #define MATCH_FRAGMENT_INVERSE_SET (1 << 9) #define MATCH_ICMP_SET (1 << 10) #define MATCH_PROTOCOL_SET (1 << 11) +#define MATCH_FLOW_LABEL_SET (1 << 12) +#define MATCH_FLOW_LABEL_INVERSE_SET (1 << 13) extern int zapi_pbr_rule_encode(uint8_t cmd, struct stream *s, struct pbr_rule *zrule); diff --git a/lib/prefix.c b/lib/prefix.c index 697e1a6239..0a88f9eca6 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -188,6 +188,10 @@ int prefix_match(const struct prefix *n, const struct prefix *p) if (n->family == AF_FLOWSPEC) { /* prefixlen is unused. look at fs prefix len */ + if (n->u.prefix_flowspec.family != + p->u.prefix_flowspec.family) + return 0; + if (n->u.prefix_flowspec.prefixlen > p->u.prefix_flowspec.prefixlen) return 0; @@ -325,6 +329,8 @@ void prefix_copy(union prefixptr udest, union prefixconstptr usrc) len = src->u.prefix_flowspec.prefixlen; dest->u.prefix_flowspec.prefixlen = src->u.prefix_flowspec.prefixlen; + dest->u.prefix_flowspec.family = + src->u.prefix_flowspec.family; dest->family = src->family; temp = XCALLOC(MTYPE_PREFIX_FLOWSPEC, len); dest->u.prefix_flowspec.ptr = (uintptr_t)temp; @@ -374,6 +380,9 @@ int prefix_same(union prefixconstptr up1, union prefixconstptr up2) sizeof(struct evpn_addr))) return 1; if (p1->family == AF_FLOWSPEC) { + if (p1->u.prefix_flowspec.family != + p2->u.prefix_flowspec.family) + return 0; if (p1->u.prefix_flowspec.prefixlen != p2->u.prefix_flowspec.prefixlen) return 0; @@ -415,6 +424,10 @@ int prefix_cmp(union prefixconstptr up1, union prefixconstptr up2) pp1 = (const uint8_t *)p1->u.prefix_flowspec.ptr; pp2 = (const uint8_t *)p2->u.prefix_flowspec.ptr; + if (p1->u.prefix_flowspec.family != + p2->u.prefix_flowspec.family) + return 1; + if (p1->u.prefix_flowspec.prefixlen != p2->u.prefix_flowspec.prefixlen) return numcmp(p1->u.prefix_flowspec.prefixlen, diff --git a/lib/prefix.h b/lib/prefix.h index 400f07386f..2a33d532c8 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -176,6 +176,7 @@ struct evpn_addr { #endif struct flowspec_prefix { + uint8_t family; uint16_t prefixlen; /* length in bytes */ uintptr_t ptr; }; diff --git a/lib/privs.c b/lib/privs.c index 5c7e1240e2..dc43b7279d 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -1020,11 +1020,11 @@ void zprivs_get_ids(struct zprivs_ids_t *ids) ids->uid_priv = getuid(); (zprivs_state.zuid) ? (ids->uid_normal = zprivs_state.zuid) - : (ids->uid_normal = -1); + : (ids->uid_normal = (uid_t)-1); (zprivs_state.zgid) ? (ids->gid_normal = zprivs_state.zgid) - : (ids->gid_normal = -1); + : (ids->gid_normal = (uid_t)-1); (zprivs_state.vtygrp) ? (ids->gid_vty = zprivs_state.vtygrp) - : (ids->gid_vty = -1); + : (ids->gid_vty = (uid_t)-1); return; } diff --git a/lib/privs.h b/lib/privs.h index db5707d675..18ba8e8888 100644 --- a/lib/privs.h +++ b/lib/privs.h @@ -24,6 +24,7 @@ #define _ZEBRA_PRIVS_H #include <pthread.h> +#include <stdint.h> #include "lib/queue.h" #ifdef __cplusplus diff --git a/lib/route_types.txt b/lib/route_types.txt index 71d0a46449..b549c11cfc 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -85,6 +85,7 @@ ZEBRA_ROUTE_BFD, bfd, bfdd, '-', 0, 0, 0, "BFD" ZEBRA_ROUTE_OPENFABRIC, openfabric, fabricd, 'f', 1, 1, 1, "OpenFabric" ZEBRA_ROUTE_VRRP, vrrp, vrrpd, '-', 0, 0, 0, "VRRP" ZEBRA_ROUTE_NHG, nhg, none, '-', 0, 0, 0, "Nexthop Group" +ZEBRA_ROUTE_SRTE, srte, none, '-', 0, 0, 0, "SR-TE" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, 0, "-" diff --git a/lib/routemap.c b/lib/routemap.c index df9a6a33ea..fb70860024 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -263,6 +263,24 @@ void route_map_no_match_tag_hook(int (*func)( 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, + const char *command, + const char *arg)) +{ + 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, + const char *command, + const char *arg)) +{ + 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, @@ -2613,6 +2631,47 @@ 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; @@ -3237,5 +3296,8 @@ void route_map_init(void) 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 62195b8349..64da4b87ef 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -424,6 +424,14 @@ extern void route_map_match_tag_hook(int (*func)( 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)); +/* 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)); +/* 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)); /* set ip nexthop */ extern void route_map_set_ip_nexthop_hook( int (*func)(struct vty *vty, struct route_map_index *index, @@ -606,6 +614,14 @@ struct route_map_match_set_hooks { const char *command, const char *arg, route_map_event_t type); + /* set sr-te color */ + int (*set_srte_color)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + + /* no set sr-te color */ + int (*no_set_srte_color)(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg); + /* set ip nexthop */ int (*set_ip_nexthop)(struct vty *vty, struct route_map_index *index, const char *command, const char *arg); diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 7fa759252b..836be38113 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -951,7 +951,8 @@ DEFPY_YANG( no_rmap_call, no_rmap_call_cmd, "no call [NAME]", NO_STR - "Jump to another Route-Map after match+set\n") + "Jump to another Route-Map after match+set\n" + "Target route-map name\n") { nb_cli_enqueue_change(vty, "./call", NB_OP_DESTROY, NULL); diff --git a/lib/srte.h b/lib/srte.h new file mode 100644 index 0000000000..d468c1cac9 --- /dev/null +++ b/lib/srte.h @@ -0,0 +1,56 @@ +/* + * SR-TE definitions + * Copyright 2020 NetDef Inc. + * Sascha Kattelmann + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_SRTE_H +#define _FRR_SRTE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define SRTE_POLICY_NAME_MAX_LENGTH 64 + +enum zebra_sr_policy_status { + ZEBRA_SR_POLICY_UP = 0, + ZEBRA_SR_POLICY_DOWN, +}; + +static inline int sr_policy_compare(const struct ipaddr *a_endpoint, + const struct ipaddr *b_endpoint, + uint32_t a_color, uint32_t b_color) +{ + int ret; + + ret = ipaddr_cmp(a_endpoint, b_endpoint); + if (ret < 0) + return -1; + if (ret > 0) + return 1; + + return a_color - b_color; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_SRTE_H */ diff --git a/lib/stream.c b/lib/stream.c index d3afebbf13..dc207c16a4 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -256,6 +256,42 @@ void stream_forward_getp(struct stream *s, size_t size) s->getp += size; } +bool stream_forward_getp2(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (!GETP_VALID(s, s->getp + size)) + return false; + + s->getp += size; + + return true; +} + +void stream_rewind_getp(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (size > s->getp || !GETP_VALID(s, s->getp - size)) { + STREAM_BOUND_WARN(s, "rewind getp"); + return; + } + + s->getp -= size; +} + +bool stream_rewind_getp2(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (size > s->getp || !GETP_VALID(s, s->getp - size)) + return false; + + s->getp -= size; + + return true; +} + void stream_forward_endp(struct stream *s, size_t size) { STREAM_VERIFY_SANE(s); @@ -268,6 +304,18 @@ void stream_forward_endp(struct stream *s, size_t size) s->endp += size; } +bool stream_forward_endp2(struct stream *s, size_t size) +{ + STREAM_VERIFY_SANE(s); + + if (!ENDP_VALID(s, s->endp + size)) + return false; + + s->endp += size; + + return true; +} + /* Copy from stream to destination. */ bool stream_get2(void *dst, struct stream *s, size_t size) { @@ -586,6 +634,43 @@ uint32_t stream_get_ipv4(struct stream *s) return l; } +bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip) +{ + uint16_t ipa_len; + + STREAM_VERIFY_SANE(s); + + /* Get address type. */ + if (STREAM_READABLE(s) < sizeof(uint16_t)) { + STREAM_BOUND_WARN2(s, "get ipaddr"); + return false; + } + ip->ipa_type = stream_getw(s); + + /* Get address value. */ + switch (ip->ipa_type) { + case IPADDR_V4: + ipa_len = IPV4_MAX_BYTELEN; + break; + case IPADDR_V6: + ipa_len = IPV6_MAX_BYTELEN; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown ip address-family: %u", __func__, + ip->ipa_type); + return false; + } + if (STREAM_READABLE(s) < ipa_len) { + STREAM_BOUND_WARN2(s, "get ipaddr"); + return false; + } + memcpy(&ip->ip, s->data + s->getp, ipa_len); + s->getp += ipa_len; + + return true; +} + float stream_getf(struct stream *s) { union { @@ -852,6 +937,27 @@ int stream_put_in_addr(struct stream *s, const struct in_addr *addr) return sizeof(uint32_t); } +bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip) +{ + stream_putw(s, ip->ipa_type); + + switch (ip->ipa_type) { + case IPADDR_V4: + stream_put_in_addr(s, &ip->ipaddr_v4); + break; + case IPADDR_V6: + stream_write(s, (uint8_t *)&ip->ipaddr_v6, 16); + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown ip address-family: %u", __func__, + ip->ipa_type); + return false; + } + + return true; +} + /* Put in_addr at location in the stream. */ int stream_put_in_addr_at(struct stream *s, size_t putp, const struct in_addr *addr) diff --git a/lib/stream.h b/lib/stream.h index 245f35db51..23f85d809b 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -160,7 +160,6 @@ extern size_t stream_resize_inplace(struct stream **sptr, size_t newsize); extern size_t stream_get_getp(const struct stream *s); extern size_t stream_get_endp(const struct stream *s); extern size_t stream_get_size(const struct stream *s); -extern uint8_t *stream_get_data(struct stream *s); /** * Create a new stream structure; copy offset bytes from s1 to the new @@ -173,7 +172,11 @@ extern struct stream *stream_dupcat(const struct stream *s1, extern void stream_set_getp(struct stream *, size_t); extern void stream_set_endp(struct stream *, size_t); extern void stream_forward_getp(struct stream *, size_t); +extern bool stream_forward_getp2(struct stream *, size_t); +extern void stream_rewind_getp(struct stream *s, size_t size); +extern bool stream_rewind_getp2(struct stream *s, size_t size); extern void stream_forward_endp(struct stream *, size_t); +extern bool stream_forward_endp2(struct stream *, size_t); /* steam_put: NULL source zeroes out size_t bytes of stream */ extern void stream_put(struct stream *, const void *, size_t); @@ -189,6 +192,7 @@ extern int stream_putq(struct stream *, uint64_t); extern int stream_putq_at(struct stream *, size_t, uint64_t); extern int stream_put_ipv4(struct stream *, uint32_t); extern int stream_put_in_addr(struct stream *s, const struct in_addr *addr); +extern bool stream_put_ipaddr(struct stream *s, struct ipaddr *ip); extern int stream_put_in_addr_at(struct stream *s, size_t putp, const struct in_addr *addr); extern int stream_put_in6_addr_at(struct stream *s, size_t putp, @@ -219,6 +223,7 @@ extern uint64_t stream_getq(struct stream *); extern uint64_t stream_getq_from(struct stream *, size_t); bool stream_getq2(struct stream *s, uint64_t *q); extern uint32_t stream_get_ipv4(struct stream *); +extern bool stream_get_ipaddr(struct stream *s, struct ipaddr *ip); /* IEEE-754 floats */ extern float stream_getf(struct stream *); @@ -439,12 +444,36 @@ static inline const uint8_t *ptr_get_be32(const uint8_t *ptr, uint32_t *out) (P) = _pval; \ } while (0) +#define STREAM_GET_IPADDR(S, P) \ + do { \ + if (!stream_get_ipaddr((S), (P))) \ + goto stream_failure; \ + } while (0) + #define STREAM_GET(P, STR, SIZE) \ do { \ if (!stream_get2((P), (STR), (SIZE))) \ goto stream_failure; \ } while (0) +#define STREAM_FORWARD_GETP(STR, SIZE) \ + do { \ + if (!stream_forward_getp2((STR), (SIZE))) \ + goto stream_failure; \ + } while (0) + +#define STREAM_REWIND_GETP(STR, SIZE) \ + do { \ + if (!stream_rewind_getp2((STR), (SIZE))) \ + goto stream_failure; \ + } while (0) + +#define STREAM_FORWARD_ENDP(STR, SIZE) \ + do { \ + if (!stream_forward_endp2((STR), (SIZE))) \ + goto stream_failure; \ + } while (0) + #ifdef __cplusplus } #endif diff --git a/lib/subdir.am b/lib/subdir.am index 34ad30f968..1feaa56d13 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -242,6 +242,7 @@ pkginclude_HEADERS += \ lib/sockunion.h \ lib/spf_backoff.h \ lib/srcdest_table.h \ + lib/srte.h \ lib/stream.h \ lib/systemd.h \ lib/table.h \ diff --git a/lib/thread.c b/lib/thread.c index 1df4eee25c..19e4827283 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -726,17 +726,16 @@ static void thread_free(struct thread_master *master, struct thread *thread) static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize, nfds_t count, const struct timeval *timer_wait) { - /* If timer_wait is null here, that means poll() should block - * indefinitely, - * unless the thread_master has overridden it by setting + /* + * If timer_wait is null here, that means poll() should block + * indefinitely, unless the thread_master has overridden it by setting * ->selectpoll_timeout. + * * If the value is positive, it specifies the maximum number of - * milliseconds - * to wait. If the timeout is -1, it specifies that we should never wait - * and - * always return immediately even if no event is detected. If the value - * is - * zero, the behavior is default. */ + * milliseconds to wait. If the timeout is -1, it specifies that + * we should never wait and always return immediately even if no + * event is detected. If the value is zero, the behavior is default. + */ int timeout = -1; /* number of file descriptors with events */ @@ -860,7 +859,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m, frr_with_mutex(&m->mtx) { if (t_ptr && *t_ptr) - // thread is already scheduled; don't reschedule + /* thread is already scheduled; don't reschedule */ return NULL; thread = thread_get(m, type, func, arg, debugargpass); @@ -940,7 +939,7 @@ struct thread *funcname_thread_add_event(struct thread_master *m, frr_with_mutex(&m->mtx) { if (t_ptr && *t_ptr) - // thread is already scheduled; don't reschedule + /* thread is already scheduled; don't reschedule */ break; thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass); @@ -1047,11 +1046,12 @@ static void do_thread_cancel(struct thread_master *master) struct cancel_req *cr; struct listnode *ln; for (ALL_LIST_ELEMENTS_RO(master->cancel_req, ln, cr)) { - /* If this is an event object cancellation, linear search - * through event - * list deleting any events which have the specified argument. - * We also - * need to check every thread in the ready queue. */ + /* + * If this is an event object cancellation, linear search + * through event list deleting any events which have the + * specified argument. We also need to check every thread + * in the ready queue. + */ if (cr->eventobj) { struct thread *t; @@ -1075,11 +1075,12 @@ static void do_thread_cancel(struct thread_master *master) continue; } - /* The pointer varies depending on whether the cancellation - * request was - * made asynchronously or not. If it was, we need to check - * whether the - * thread even exists anymore before cancelling it. */ + /* + * The pointer varies depending on whether the cancellation + * request was made asynchronously or not. If it was, we + * need to check whether the thread even exists anymore + * before cancelling it. + */ thread = (cr->thread) ? cr->thread : *cr->threadref; if (!thread) @@ -1304,18 +1305,21 @@ static void thread_process_io(struct thread_master *m, unsigned int num) ready++; - /* Unless someone has called thread_cancel from another pthread, - * the only - * thing that could have changed in m->handler.pfds while we - * were - * asleep is the .events field in a given pollfd. Barring - * thread_cancel() - * that value should be a superset of the values we have in our - * copy, so - * there's no need to update it. Similarily, barring deletion, - * the fd - * should still be a valid index into the master's pfds. */ - if (pfds[i].revents & (POLLIN | POLLHUP)) { + /* + * Unless someone has called thread_cancel from another + * pthread, the only thing that could have changed in + * m->handler.pfds while we were asleep is the .events + * field in a given pollfd. Barring thread_cancel() that + * value should be a superset of the values we have in our + * copy, so there's no need to update it. Similarily, + * barring deletion, the fd should still be a valid index + * into the master's pfds. + * + * We are including POLLERR here to do a READ event + * this is because the read should fail and the + * read function should handle it appropriately + */ + if (pfds[i].revents & (POLLIN | POLLHUP | POLLERR)) { thread_process_io_helper(m, m->read[pfds[i].fd], POLLIN, pfds[i].revents, i); } @@ -1427,11 +1431,10 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * * - If there are events pending, set the poll() timeout to zero * - If there are no events pending, but there are timers - * pending, set the - * timeout to the smallest remaining time on any timer + * pending, set the timeout to the smallest remaining time on + * any timer. * - If there are neither timers nor events pending, but there - * are file - * descriptors pending, block indefinitely in poll() + * are file descriptors pending, block indefinitely in poll() * - If nothing is pending, it's time for the application to die * * In every case except the last, we need to hit poll() at least diff --git a/lib/yang.c b/lib/yang.c index 6ab9492d52..9bfdcb858c 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -84,6 +84,9 @@ static const char *yang_module_imp_clb(const char *mod_name, static const char *const frr_native_modules[] = { "frr-interface", "frr-vrf", + "frr-routing", + "frr-route-map", + "frr-nexthop", "frr-ripd", "frr-ripngd", "frr-isisd", diff --git a/lib/yang.h b/lib/yang.h index cc048c44e8..94bbed233d 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -63,6 +63,7 @@ struct yang_module { #endif #ifdef HAVE_SYSREPO sr_subscription_ctx_t *sr_subscription; + struct thread *sr_thread; #endif }; RB_HEAD(yang_modules, yang_module); diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index 4f743096ee..4c658c1bfb 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -792,6 +792,29 @@ struct yang_data *yang_data_new_empty(const char *xpath) return yang_data_new(xpath, NULL); } +bool yang_dnode_get_empty(const struct lyd_node *dnode, const char *xpath_fmt, + ...) +{ + va_list ap; + char xpath[XPATH_MAXLEN]; + const struct lyd_node_leaf_list *dleaf; + + assert(dnode); + + va_start(ap, xpath_fmt); + vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap); + va_end(ap); + + dnode = yang_dnode_get(dnode, xpath); + if (dnode) { + dleaf = (const struct lyd_node_leaf_list *)dnode; + if (dleaf->value_type == LY_TYPE_EMPTY) + return true; + } + + return false; +} + /* * Derived type: IP prefix. */ diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h index 335ff319d5..d781dfb1e4 100644 --- a/lib/yang_wrappers.h +++ b/lib/yang_wrappers.h @@ -120,6 +120,8 @@ extern void yang_get_default_string_buf(char *buf, size_t size, /* empty */ extern struct yang_data *yang_data_new_empty(const char *xpath); +extern bool yang_dnode_get_empty(const struct lyd_node *dnode, + const char *xpath_fmt, ...); /* ip prefix */ extern void yang_str2prefix(const char *value, union prefixptr prefix); diff --git a/lib/zclient.c b/lib/zclient.c index eb62350f4f..b842e7c31b 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -39,6 +39,7 @@ #include "pbr.h" #include "nexthop_group.h" #include "lib_errors.h" +#include "srte.h" DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient") DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs") @@ -436,7 +437,8 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) vrf_id); /* We need router-id information. */ - zebra_message_send(zclient, ZEBRA_ROUTER_ID_ADD, vrf_id); + zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_ADD, AFI_IP, + vrf_id); /* We need interface information. */ zebra_message_send(zclient, ZEBRA_INTERFACE_ADD, vrf_id); @@ -503,7 +505,8 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) vrf_id); /* We need router-id information. */ - zebra_message_send(zclient, ZEBRA_ROUTER_ID_DELETE, vrf_id); + zclient_send_router_id_update(zclient, ZEBRA_ROUTER_ID_DELETE, AFI_IP, + vrf_id); zebra_message_send(zclient, ZEBRA_INTERFACE_DELETE, vrf_id); @@ -554,6 +557,18 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) } } +int zclient_send_router_id_update(struct zclient *zclient, + zebra_message_types_t type, afi_t afi, + vrf_id_t vrf_id) +{ + struct stream *s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, type, vrf_id); + stream_putw(s, afi); + stream_putw_at(s, 0, stream_get_endp(s)); + return zclient_send_message(zclient); +} + /* Send request to zebra daemon to start or stop RA. */ void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, struct interface *ifp, int enable, @@ -856,6 +871,37 @@ static int zapi_nexthop_cmp_no_labels(const struct zapi_nexthop *next1, break; } + if (next1->srte_color < next2->srte_color) + return -1; + if (next1->srte_color > next2->srte_color) + return 1; + + if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) || + CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + + if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return -1; + + if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) && + !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP)) + return 1; + + if (next1->backup_num > 0 || next2->backup_num > 0) { + + if (next1->backup_num < next2->backup_num) + return -1; + + if (next1->backup_num > next2->backup_num) + return 1; + + ret = memcmp(next1->backup_idx, + next2->backup_idx, next1->backup_num); + if (ret != 0) + return ret; + } + } + return 0; } @@ -886,7 +932,7 @@ static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp, * Encode a single zapi nexthop */ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, - uint32_t api_flags) + uint32_t api_flags, uint32_t api_message) { int i, ret = 0; int nh_flags = api_nh->flags; @@ -950,6 +996,10 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, stream_put(s, &(api_nh->rmac), sizeof(struct ethaddr)); + /* Color for Segment Routing TE. */ + if (CHECK_FLAG(api_message, ZAPI_MESSAGE_SRTE)) + stream_putl(s, api_nh->srte_color); + /* Index of backup nexthop */ if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { /* Validate backup count */ @@ -986,7 +1036,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) stream_putw(s, api->instance); stream_putl(s, api->flags); - stream_putc(s, api->message); + stream_putl(s, api->message); if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, @@ -1047,7 +1097,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return -1; } - if (zapi_nexthop_encode(s, api_nh, api->flags) != 0) + if (zapi_nexthop_encode(s, api_nh, api->flags, + api->message) + != 0) return -1; } } @@ -1091,7 +1143,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return -1; } - if (zapi_nexthop_encode(s, api_nh, api->flags) != 0) + if (zapi_nexthop_encode(s, api_nh, api->flags, + api->message) + != 0) return -1; } } @@ -1118,7 +1172,7 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) * Decode a single zapi nexthop object */ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, - uint32_t api_flags) + uint32_t api_flags, uint32_t api_message) { int i, ret = -1; @@ -1171,6 +1225,10 @@ static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, STREAM_GET(&(api_nh->rmac), s, sizeof(struct ethaddr)); + /* Color for Segment Routing TE. */ + if (CHECK_FLAG(api_message, ZAPI_MESSAGE_SRTE)) + STREAM_GETL(s, api_nh->srte_color); + /* Backup nexthop index */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { STREAM_GETC(s, api_nh->backup_num); @@ -1208,7 +1266,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) STREAM_GETW(s, api->instance); STREAM_GETL(s, api->flags); - STREAM_GETC(s, api->message); + STREAM_GETL(s, api->message); STREAM_GETC(s, api->safi); if (api->safi < SAFI_UNICAST || api->safi >= SAFI_MAX) { flog_err(EC_LIB_ZAPI_ENCODE, @@ -1283,7 +1341,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) for (i = 0; i < api->nexthop_num; i++) { api_nh = &api->nexthops[i]; - if (zapi_nexthop_decode(s, api_nh, api->flags) != 0) + if (zapi_nexthop_decode(s, api_nh, api->flags, + api->message) + != 0) return -1; } } @@ -1301,7 +1361,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api) for (i = 0; i < api->backup_nexthop_num; i++) { api_nh = &api->backup_nexthops[i]; - if (zapi_nexthop_decode(s, api_nh, api->flags) != 0) + if (zapi_nexthop_decode(s, api_nh, api->flags, + api->message) + != 0) return -1; } } @@ -1488,6 +1550,7 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) n->vrf_id = znh->vrf_id; n->ifindex = znh->ifindex; n->gate = znh->gate; + n->srte_color = znh->srte_color; /* * This function currently handles labels @@ -1605,6 +1668,7 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) memset(nhr, 0, sizeof(*nhr)); + STREAM_GETL(s, nhr->message); STREAM_GETW(s, nhr->prefix.family); STREAM_GETC(s, nhr->prefix.prefixlen); switch (nhr->prefix.family) { @@ -1617,6 +1681,8 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) default: break; } + if (CHECK_FLAG(nhr->message, ZAPI_MESSAGE_SRTE)) + STREAM_GETL(s, nhr->srte_color); STREAM_GETC(s, nhr->type); STREAM_GETW(s, nhr->instance); @@ -1625,7 +1691,7 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) STREAM_GETC(s, nhr->nexthop_num); for (i = 0; i < nhr->nexthop_num; i++) { - if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0) != 0) + if (zapi_nexthop_decode(s, &(nhr->nexthops[i]), 0, 0) != 0) return -1; } @@ -2002,7 +2068,7 @@ static void zebra_interface_if_set_value(struct stream *s, uint8_t link_params_status = 0; ifindex_t old_ifindex, new_ifindex; - old_ifindex = ifp->ifindex; + old_ifindex = ifp->oldifindex; /* Read interface's index. */ STREAM_GETL(s, new_ifindex); if_set_index(ifp, new_ifindex); @@ -2821,6 +2887,92 @@ int tm_release_table_chunk(struct zclient *zclient, uint32_t start, return zclient_send_message(zclient); } +int zebra_send_sr_policy(struct zclient *zclient, int cmd, + struct zapi_sr_policy *zp) +{ + if (zapi_sr_policy_encode(zclient->obuf, cmd, zp) < 0) + return -1; + return zclient_send_message(zclient); +} + +int zapi_sr_policy_encode(struct stream *s, int cmd, struct zapi_sr_policy *zp) +{ + struct zapi_srte_tunnel *zt = &zp->segment_list; + + stream_reset(s); + + zclient_create_header(s, cmd, VRF_DEFAULT); + stream_putl(s, zp->color); + stream_put_ipaddr(s, &zp->endpoint); + stream_write(s, &zp->name, SRTE_POLICY_NAME_MAX_LENGTH); + + stream_putc(s, zt->type); + stream_putl(s, zt->local_label); + + if (zt->label_num > MPLS_MAX_LABELS) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: label %u: can't encode %u labels (maximum is %u)", + __func__, zt->local_label, zt->label_num, + MPLS_MAX_LABELS); + return -1; + } + stream_putw(s, zt->label_num); + + for (int i = 0; i < zt->label_num; i++) + stream_putl(s, zt->labels[i]); + + /* Put length at the first point of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return 0; +} + +int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp) +{ + memset(zp, 0, sizeof(*zp)); + + struct zapi_srte_tunnel *zt = &zp->segment_list; + + STREAM_GETL(s, zp->color); + STREAM_GET_IPADDR(s, &zp->endpoint); + STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH); + + /* segment list of active candidate path */ + STREAM_GETC(s, zt->type); + STREAM_GETL(s, zt->local_label); + STREAM_GETW(s, zt->label_num); + if (zt->label_num > MPLS_MAX_LABELS) { + flog_err(EC_LIB_ZAPI_ENCODE, + "%s: label %u: can't decode %u labels (maximum is %u)", + __func__, zt->local_label, zt->label_num, + MPLS_MAX_LABELS); + return -1; + } + for (int i = 0; i < zt->label_num; i++) + STREAM_GETL(s, zt->labels[i]); + + return 0; + +stream_failure: + return -1; +} + +int zapi_sr_policy_notify_status_decode(struct stream *s, + struct zapi_sr_policy *zp) +{ + memset(zp, 0, sizeof(*zp)); + + STREAM_GETL(s, zp->color); + STREAM_GET_IPADDR(s, &zp->endpoint); + STREAM_GET(&zp->name, s, SRTE_POLICY_NAME_MAX_LENGTH); + STREAM_GETL(s, zp->status); + + return 0; + +stream_failure: + return -1; +} + int zebra_send_mpls_labels(struct zclient *zclient, int cmd, struct zapi_labels *zl) { @@ -2860,7 +3012,7 @@ int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl) for (int i = 0; i < zl->nexthop_num; i++) { znh = &zl->nexthops[i]; - if (zapi_nexthop_encode(s, znh, 0) < 0) + if (zapi_nexthop_encode(s, znh, 0, 0) < 0) return -1; } @@ -2879,7 +3031,7 @@ int zapi_labels_encode(struct stream *s, int cmd, struct zapi_labels *zl) for (int i = 0; i < zl->backup_nexthop_num; i++) { znh = &zl->backup_nexthops[i]; - if (zapi_nexthop_encode(s, znh, 0) < 0) + if (zapi_nexthop_encode(s, znh, 0, 0) < 0) return -1; } @@ -2955,7 +3107,7 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl) for (int i = 0; i < zl->nexthop_num; i++) { znh = &zl->nexthops[i]; - if (zapi_nexthop_decode(s, znh, 0) < 0) + if (zapi_nexthop_decode(s, znh, 0, 0) < 0) return -1; } @@ -2976,7 +3128,7 @@ int zapi_labels_decode(struct stream *s, struct zapi_labels *zl) for (int i = 0; i < zl->backup_nexthop_num; i++) { znh = &zl->backup_nexthops[i]; - if (zapi_nexthop_decode(s, znh, 0) < 0) + if (zapi_nexthop_decode(s, znh, 0, 0) < 0) return -1; } } @@ -3643,6 +3795,10 @@ static int zclient_read(struct thread *thread) (*zclient->opaque_unregister_handler)(command, zclient, length, vrf_id); break; + case ZEBRA_SR_POLICY_NOTIFY_STATUS: + if (zclient->sr_policy_notify_status) + (*zclient->sr_policy_notify_status)(command, zclient, + length, vrf_id); default: break; } @@ -3828,3 +3984,23 @@ int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api) stream_failure: return 0; } + +int zclient_send_neigh_discovery_req(struct zclient *zclient, + const struct interface *ifp, + const struct prefix *p) +{ + struct stream *s; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_NEIGH_DISCOVER, ifp->vrf_id); + stream_putl(s, ifp->ifindex); + + stream_putc(s, p->family); + stream_putc(s, p->prefixlen); + stream_put(s, &p->u.prefix, prefix_blen(p)); + + stream_putw_at(s, 0, stream_get_endp(s)); + return zclient_send_message(zclient); +} diff --git a/lib/zclient.h b/lib/zclient.h index da06239d01..c6a67790a1 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -37,6 +37,7 @@ #include "pw.h" #include "mlag.h" +#include "srte.h" #ifdef __cplusplus extern "C" { @@ -89,6 +90,8 @@ enum zserv_client_capabilities { /* Macro to check if there GR enabled. */ #define ZEBRA_CLIENT_GR_ENABLED(X) (X == ZEBRA_CLIENT_GR_CAPABILITIES) +#define ZEBRA_SR_POLICY_NAME_MAX_LENGTH 100 + extern struct sockaddr_storage zclient_addr; extern socklen_t zclient_addr_len; @@ -143,6 +146,9 @@ typedef enum { ZEBRA_MPLS_LABELS_ADD, ZEBRA_MPLS_LABELS_DELETE, ZEBRA_MPLS_LABELS_REPLACE, + ZEBRA_SR_POLICY_SET, + ZEBRA_SR_POLICY_DELETE, + ZEBRA_SR_POLICY_NOTIFY_STATUS, ZEBRA_IPMR_ROUTE_STATS, ZEBRA_LABEL_MANAGER_CONNECT, ZEBRA_LABEL_MANAGER_CONNECT_ASYNC, @@ -208,6 +214,7 @@ typedef enum { ZEBRA_OPAQUE_MESSAGE, ZEBRA_OPAQUE_REGISTER, ZEBRA_OPAQUE_UNREGISTER, + ZEBRA_NEIGH_DISCOVER, } zebra_message_types_t; enum zebra_error_types { @@ -351,6 +358,7 @@ struct zclient { int (*opaque_msg_handler)(ZAPI_CALLBACK_ARGS); int (*opaque_register_handler)(ZAPI_CALLBACK_ARGS); int (*opaque_unregister_handler)(ZAPI_CALLBACK_ARGS); + int (*sr_policy_notify_status)(ZAPI_CALLBACK_ARGS); }; /* Zebra API message flag. */ @@ -368,7 +376,8 @@ struct zclient { * the table being used is not in the VRF. You must pass the * default vrf, else this will be ignored. */ -#define ZAPI_MESSAGE_TABLEID 0x80 +#define ZAPI_MESSAGE_TABLEID 0x0080 +#define ZAPI_MESSAGE_SRTE 0x0100 #define ZSERV_VERSION 6 /* Zserv protocol message header */ @@ -382,6 +391,11 @@ struct zmsghdr { } __attribute__((packed)); #define ZAPI_HEADER_CMD_LOCATION offsetof(struct zmsghdr, command) +/* + * ZAPI nexthop. Note that these are sorted when associated with ZAPI routes, + * and that sorting must be aligned with the sorting of nexthops in + * lib/nexthop.c. Any new fields must be accounted for in zapi_nexthop_cmp(). + */ struct zapi_nexthop { enum nexthop_types_t type; vrf_id_t vrf_id; @@ -403,6 +417,9 @@ struct zapi_nexthop { /* Backup nexthops, for IP-FRR, TI-LFA, etc */ uint8_t backup_num; uint8_t backup_idx[NEXTHOP_MAX_BACKUPS]; + + /* SR-TE color. */ + uint32_t srte_color; }; /* @@ -465,7 +482,7 @@ struct zapi_route { #define ZEBRA_FLAG_RR_USE_DISTANCE 0x40 /* The older XXX_MESSAGE flags live here */ - uint8_t message; + uint32_t message; /* * This is an enum but we are going to treat it as a uint8_t @@ -494,6 +511,9 @@ struct zapi_route { vrf_id_t vrf_id; uint32_t tableid; + + /* SR-TE color (used for nexthop updates only). */ + uint32_t srte_color; }; struct zapi_labels { @@ -516,6 +536,21 @@ struct zapi_labels { struct zapi_nexthop backup_nexthops[MULTIPATH_NUM]; }; +struct zapi_srte_tunnel { + enum lsp_types_t type; + mpls_label_t local_label; + uint8_t label_num; + mpls_label_t labels[MPLS_MAX_LABELS]; +}; + +struct zapi_sr_policy { + uint32_t color; + struct ipaddr endpoint; + char name[SRTE_POLICY_NAME_MAX_LENGTH]; + struct zapi_srte_tunnel segment_list; + int status; +}; + struct zapi_pw { char ifname[IF_NAMESIZE]; ifindex_t ifindex; @@ -659,6 +694,9 @@ extern void zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, extern void zclient_send_reg_requests(struct zclient *, vrf_id_t); extern void zclient_send_dereg_requests(struct zclient *, vrf_id_t); +extern int zclient_send_router_id_update(struct zclient *zclient, + zebra_message_types_t type, afi_t afi, + vrf_id_t vrf_id); extern void zclient_send_interface_radv_req(struct zclient *zclient, vrf_id_t vrf_id, @@ -775,6 +813,14 @@ extern int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size, extern int tm_release_table_chunk(struct zclient *zclient, uint32_t start, uint32_t end); +extern int zebra_send_sr_policy(struct zclient *zclient, int cmd, + struct zapi_sr_policy *zp); +extern int zapi_sr_policy_encode(struct stream *s, int cmd, + struct zapi_sr_policy *zp); +extern int zapi_sr_policy_decode(struct stream *s, struct zapi_sr_policy *zp); +extern int zapi_sr_policy_notify_status_decode(struct stream *s, + struct zapi_sr_policy *zp); + extern int zebra_send_mpls_labels(struct zclient *zclient, int cmd, struct zapi_labels *zl); extern int zapi_labels_encode(struct stream *s, int cmd, @@ -791,7 +837,7 @@ extern int zclient_send_rnh(struct zclient *zclient, int command, const struct prefix *p, bool exact_match, vrf_id_t vrf_id); int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, - uint32_t api_flags); + uint32_t api_flags, uint32_t api_message); extern int zapi_route_encode(uint8_t, struct stream *, struct zapi_route *); extern int zapi_route_decode(struct stream *, struct zapi_route *); bool zapi_route_notify_decode(struct stream *s, struct prefix *p, @@ -916,6 +962,10 @@ enum zapi_opaque_registry { */ extern int zclient_send_hello(struct zclient *client); +extern int zclient_send_neigh_discovery_req(struct zclient *zclient, + const struct interface *ifp, + const struct prefix *p); + #ifdef __cplusplus } #endif diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index b339790492..6fe3a289ce 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -72,7 +72,7 @@ static int ospf6_abr_nexthops_belong_to_area(struct ospf6_route *route, struct ospf6_interface *oi; oi = ospf6_interface_lookup_by_ifindex( - ospf6_route_get_first_nh_index(route)); + ospf6_route_get_first_nh_index(route), area->ospf6->vrf_id); if (oi && oi->area && oi->area == area) return 1; else diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 5562529ea8..71ca5afcd2 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -47,8 +47,8 @@ #include "ospf6_flood.h" #include "ospf6d.h" -static void ospf6_asbr_redistribute_set(int type); -static void ospf6_asbr_redistribute_unset(int type); +static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id); +static void ospf6_asbr_redistribute_unset(int type, vrf_id_t vrf_id); unsigned char conf_debug_ospf6_asbr = 0; @@ -881,8 +881,8 @@ static int ospf6_asbr_routemap_update_timer(struct thread *thread) __func__, ospf6->rmap[arg_type].name, ZROUTE_NAME(arg_type)); - ospf6_zebra_no_redistribute(arg_type); - ospf6_zebra_redistribute(arg_type); + ospf6_zebra_no_redistribute(arg_type, ospf6->vrf_id); + ospf6_zebra_redistribute(arg_type, ospf6->vrf_id); } XFREE(MTYPE_OSPF6_DIST_ARGS, arg); @@ -948,9 +948,11 @@ static void ospf6_asbr_routemap_update(const char *mapname) "%s: route-map %s deleted, reset redist %s", __func__, mapname, ZROUTE_NAME(type)); - ospf6_asbr_redistribute_unset(type); + ospf6_asbr_redistribute_unset( + type, ospf6->vrf_id); ospf6_asbr_routemap_set(type, mapname); - ospf6_asbr_redistribute_set(type); + ospf6_asbr_redistribute_set( + type, ospf6->vrf_id); } } } else @@ -977,17 +979,17 @@ int ospf6_asbr_is_asbr(struct ospf6 *o) return o->external_table->count; } -static void ospf6_asbr_redistribute_set(int type) +static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id) { - ospf6_zebra_redistribute(type); + ospf6_zebra_redistribute(type, vrf_id); } -static void ospf6_asbr_redistribute_unset(int type) +static void ospf6_asbr_redistribute_unset(int type, vrf_id_t vrf_id) { struct ospf6_route *route; struct ospf6_external_info *info; - ospf6_zebra_no_redistribute(type); + ospf6_zebra_no_redistribute(type, vrf_id); for (route = ospf6_route_head(ospf6->external_table); route; route = ospf6_route_next(route)) { @@ -1031,7 +1033,7 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, struct listnode *lnode, *lnnode; struct ospf6_area *oa; - if (!ospf6_zebra_is_redistribute(type)) + if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) return; memset(&troute, 0, sizeof(troute)); @@ -1243,13 +1245,15 @@ DEFUN (ospf6_redistribute, { int type; + OSPF6_CMD_CHECK_RUNNING(); + char *proto = argv[argc - 1]->text; type = proto_redistnum(AFI_IP6, proto); if (type < 0) return CMD_WARNING_CONFIG_FAILED; - ospf6_asbr_redistribute_unset(type); - ospf6_asbr_redistribute_set(type); + ospf6_asbr_redistribute_unset(type, ospf6->vrf_id); + ospf6_asbr_redistribute_set(type, ospf6->vrf_id); return CMD_SUCCESS; } @@ -1265,14 +1269,16 @@ DEFUN (ospf6_redistribute_routemap, int idx_word = 3; int type; + OSPF6_CMD_CHECK_RUNNING(); + char *proto = argv[idx_protocol]->text; type = proto_redistnum(AFI_IP6, proto); if (type < 0) return CMD_WARNING_CONFIG_FAILED; - ospf6_asbr_redistribute_unset(type); + ospf6_asbr_redistribute_unset(type, ospf6->vrf_id); ospf6_asbr_routemap_set(type, argv[idx_word]->arg); - ospf6_asbr_redistribute_set(type); + ospf6_asbr_redistribute_set(type, ospf6->vrf_id); return CMD_SUCCESS; } @@ -1288,12 +1294,14 @@ DEFUN (no_ospf6_redistribute, int idx_protocol = 2; int type; + OSPF6_CMD_CHECK_RUNNING(); + char *proto = argv[idx_protocol]->text; type = proto_redistnum(AFI_IP6, proto); if (type < 0) return CMD_WARNING_CONFIG_FAILED; - ospf6_asbr_redistribute_unset(type); + ospf6_asbr_redistribute_unset(type, ospf6->vrf_id); return CMD_SUCCESS; } @@ -1305,7 +1313,7 @@ int ospf6_redistribute_config_write(struct vty *vty) for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { if (type == ZEBRA_ROUTE_OSPF6) continue; - if (!ospf6_zebra_is_redistribute(type)) + if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) continue; if (ospf6->rmap[type].name) @@ -1340,7 +1348,7 @@ static void ospf6_redistribute_show_config(struct vty *vty) for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { if (type == ZEBRA_ROUTE_OSPF6) continue; - if (!ospf6_zebra_is_redistribute(type)) + if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) continue; if (ospf6->rmap[type].name) @@ -1408,7 +1416,7 @@ ospf6_routemap_rule_match_interface(void *rule, const struct prefix *prefix, if (type == RMAP_OSPF6) { ei = ((struct ospf6_route *)object)->route_option; - ifp = if_lookup_by_name((char *)rule, VRF_DEFAULT); + ifp = if_lookup_by_name_all_vrf((char *)rule); if (ifp != NULL && ei->ifindex == ifp->ifindex) return RMAP_MATCH; @@ -1880,15 +1888,15 @@ void ospf6_asbr_init(void) install_element(OSPF6_NODE, &no_ospf6_redistribute_cmd); } -void ospf6_asbr_redistribute_reset(void) +void ospf6_asbr_redistribute_reset(vrf_id_t vrf_id) { int type; for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { if (type == ZEBRA_ROUTE_OSPF6) continue; - if (ospf6_zebra_is_redistribute(type)) - ospf6_asbr_redistribute_unset(type); + if (ospf6_zebra_is_redistribute(type, vrf_id)) + ospf6_asbr_redistribute_unset(type, vrf_id); } } diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 9890ef0619..41b1ac70e9 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -88,7 +88,7 @@ extern void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, extern int ospf6_redistribute_config_write(struct vty *vty); extern void ospf6_asbr_init(void); -extern void ospf6_asbr_redistribute_reset(void); +extern void ospf6_asbr_redistribute_reset(vrf_id_t vrf_id); extern void ospf6_asbr_terminate(void); extern void ospf6_asbr_send_externals_to_area(struct ospf6_area *); diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c index 916e59baf0..1b58cd14f6 100644 --- a/ospf6d/ospf6_bfd.c +++ b/ospf6d/ospf6_bfd.c @@ -89,8 +89,8 @@ void ospf6_bfd_reg_dereg_nbr(struct ospf6_neighbor *on, int command) cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON); bfd_peer_sendmsg(zclient, bfd_info, AF_INET6, &on->linklocal_addr, - on->ospf6_if->linklocal_addr, ifp->name, 0, 0, - cbit, command, 0, VRF_DEFAULT); + on->ospf6_if->linklocal_addr, ifp->name, 0, 0, cbit, + command, 0, ifp->vrf_id); if (command == ZEBRA_BFD_DEST_DEREGISTER) bfd_info_free((struct bfd_info **)&on->bfd_info); @@ -143,7 +143,7 @@ static void ospf6_bfd_reg_dereg_all_nbr(struct ospf6_interface *oi, int command) */ static int ospf6_bfd_nbr_replay(ZAPI_CALLBACK_ARGS) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf = vrf_lookup_by_id(vrf_id); struct listnode *node; struct interface *ifp; struct ospf6_interface *oi; diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index d10329a93b..fabcc426ea 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -56,12 +56,13 @@ const char *const ospf6_interface_state_str[] = { "None", "Down", "Loopback", "Waiting", "PointToPoint", "DROther", "BDR", "DR", NULL}; -struct ospf6_interface *ospf6_interface_lookup_by_ifindex(ifindex_t ifindex) +struct ospf6_interface *ospf6_interface_lookup_by_ifindex(ifindex_t ifindex, + vrf_id_t vrf_id) { struct ospf6_interface *oi; struct interface *ifp; - ifp = if_lookup_by_index(ifindex, VRF_DEFAULT); + ifp = if_lookup_by_index(ifindex, vrf_id); if (ifp == NULL) return (struct ospf6_interface *)NULL; @@ -1022,7 +1023,7 @@ DEFUN (show_ipv6_ospf6_interface, return CMD_SUCCESS; } -static int ospf6_interface_show_traffic(struct vty *vty, uint32_t vrf_id, +static int ospf6_interface_show_traffic(struct vty *vty, struct interface *intf_ifp, int display_once) { @@ -1030,7 +1031,10 @@ static int ospf6_interface_show_traffic(struct vty *vty, uint32_t vrf_id, struct vrf *vrf = NULL; struct ospf6_interface *oi = NULL; - vrf = vrf_lookup_by_id(vrf_id); + if (intf_ifp) + vrf = vrf_lookup_by_id(intf_ifp->vrf_id); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); if (!display_once) { vty_out(vty, "\n"); @@ -1105,7 +1109,7 @@ DEFUN (show_ipv6_ospf6_interface_traffic, } } - ospf6_interface_show_traffic(vty, VRF_DEFAULT, ifp, display_once); + ospf6_interface_show_traffic(vty, ifp, display_once); return CMD_SUCCESS; diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 6cbfe04c44..dd7f4d1b1e 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -170,7 +170,8 @@ extern const char *const ospf6_interface_state_str[]; /* Function Prototypes */ -extern struct ospf6_interface *ospf6_interface_lookup_by_ifindex(ifindex_t); +extern struct ospf6_interface * +ospf6_interface_lookup_by_ifindex(ifindex_t, vrf_id_t vrf_id); extern struct ospf6_interface *ospf6_interface_create(struct interface *); extern void ospf6_interface_delete(struct ospf6_interface *); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index ef5d1d0583..6eda9f750c 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -1569,8 +1569,8 @@ void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, if (intra_prefix_lsa->ref_adv_router == oa->ospf6->router_id) { ifp = if_lookup_prefix( - &old_route->prefix, - VRF_DEFAULT); + &old_route->prefix, + oa->ospf6->vrf_id); if (ifp) ospf6_route_add_nexthop( old_route, @@ -1714,7 +1714,8 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) memcpy(&route->path.ls_prefix, &ls_prefix, sizeof(struct prefix)); if (direct_connect) { - ifp = if_lookup_prefix(&route->prefix, VRF_DEFAULT); + ifp = if_lookup_prefix(&route->prefix, + oa->ospf6->vrf_id); if (ifp) ospf6_route_add_nexthop(route, ifp->ifindex, NULL); diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index 8ae5fdcf06..182faf0038 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -79,15 +79,17 @@ struct thread_master *master; static void __attribute__((noreturn)) ospf6_exit(int status) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf; struct interface *ifp; frr_early_fini(); if (ospf6) { + vrf = vrf_lookup_by_id(ospf6->vrf_id); ospf6_delete(ospf6); ospf6 = NULL; - } + } else + vrf = vrf_lookup_by_id(VRF_DEFAULT); bfd_gbl_exit(); diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index f891f548ae..4830b38a66 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1554,7 +1554,7 @@ int ospf6_receive(struct thread *thread) return 0; } - oi = ospf6_interface_lookup_by_ifindex(ifindex); + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); if (oi == NULL || oi->area == NULL || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 723746c471..a443e4c3ba 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -307,14 +307,14 @@ void ospf6_route_zebra_copy_nexthops(struct ospf6_route *route, inet_ntop(AF_INET6, &nh->address, buf, sizeof(buf)); ifname = ifindex2ifname(nh->ifindex, - VRF_DEFAULT); + ospf6->vrf_id); zlog_debug(" nexthop: %s%%%.*s(%d)", buf, IFNAMSIZ, ifname, nh->ifindex); } if (i >= entries) return; - nexthops[i].vrf_id = VRF_DEFAULT; + nexthops[i].vrf_id = ospf6->vrf_id; nexthops[i].ifindex = nh->ifindex; if (!IN6_IS_ADDR_UNSPECIFIED(&nh->address)) { nexthops[i].gate.ipv6 = nh->address; @@ -1042,6 +1042,11 @@ void ospf6_route_show(struct vty *vty, struct ospf6_route *route) struct listnode *node; struct ospf6_nexthop *nh; + if (ospf6 == NULL) { + vty_out(vty, "OSPFv3 is not running\n"); + return; + } + monotime(&now); timersub(&now, &route->changed, &res); timerstring(&res, duration, sizeof(duration)); @@ -1060,7 +1065,7 @@ void ospf6_route_show(struct vty *vty, struct ospf6_route *route) for (ALL_LIST_ELEMENTS_RO(route->nh_list, node, nh)) { /* nexthop */ inet_ntop(AF_INET6, &nh->address, nexthop, sizeof(nexthop)); - ifname = ifindex2ifname(nh->ifindex, VRF_DEFAULT); + ifname = ifindex2ifname(nh->ifindex, ospf6->vrf_id); if (!i) { vty_out(vty, "%c%1s %2s %-30s %-25s %6.*s %s\n", @@ -1086,6 +1091,11 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route) struct listnode *node; struct ospf6_nexthop *nh; + if (ospf6 == NULL) { + vty_out(vty, "OSPFv3 is not running\n"); + return; + } + monotime(&now); /* destination */ @@ -1160,7 +1170,7 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route) for (ALL_LIST_ELEMENTS_RO(route->nh_list, node, nh)) { /* nexthop */ inet_ntop(AF_INET6, &nh->address, nexthop, sizeof(nexthop)); - ifname = ifindex2ifname(nh->ifindex, VRF_DEFAULT); + ifname = ifindex2ifname(nh->ifindex, ospf6->vrf_id); vty_out(vty, " %s %.*s\n", nexthop, IFNAMSIZ, ifname); } vty_out(vty, "\n"); diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index 9a1141f631..57cc055296 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -837,7 +837,7 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf; struct ospf6_lsa *lsa = NULL; ifindex_t ifindex; uint32_t area_id, id, instid, adv_router; @@ -861,6 +861,7 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, if (ospf6 == NULL) return NULL; + vrf = vrf_lookup_by_id(ospf6->vrf_id); /* Get variable length. */ offset = name + v->namelen; offsetlen = *length - v->namelen; @@ -926,7 +927,8 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, return NULL; lsa = ospf6_lsdb_lookup(type, id, adv_router, oa->lsdb); } else if (v->magic & OSPFv3WWLINKTABLE) { - oi = ospf6_interface_lookup_by_ifindex(ifindex); + oi = ospf6_interface_lookup_by_ifindex(ifindex, + ospf6->vrf_id); if (!oi || oi->instance_id != instid) return NULL; lsa = ospf6_lsdb_lookup(type, id, adv_router, oi->lsdb); @@ -963,7 +965,7 @@ static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, if (!iif->ifindex) continue; oi = ospf6_interface_lookup_by_ifindex( - iif->ifindex); + iif->ifindex, iif->vrf_id); if (!oi) continue; if (iif->ifindex < ifindex) @@ -1038,7 +1040,7 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf; ifindex_t ifindex = 0; unsigned int instid = 0; struct ospf6_interface *oi = NULL; @@ -1058,6 +1060,7 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, if (ospf6 == NULL) return NULL; + vrf = vrf_lookup_by_id(ospf6->vrf_id); /* Get variable length. */ offset = name + v->namelen; offsetlen = *length - v->namelen; @@ -1080,7 +1083,7 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, // offsetlen -= len; if (exact) { - oi = ospf6_interface_lookup_by_ifindex(ifindex); + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); if (!oi || oi->instance_id != instid) return NULL; } else { @@ -1095,7 +1098,8 @@ static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, for (ALL_LIST_ELEMENTS_RO(ifslist, i, iif)) { if (!iif->ifindex) continue; - oi = ospf6_interface_lookup_by_ifindex(iif->ifindex); + oi = ospf6_interface_lookup_by_ifindex(iif->ifindex, + iif->vrf_id); if (!oi) continue; if (iif->ifindex > ifindex @@ -1191,7 +1195,7 @@ static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct vrf *vrf; ifindex_t ifindex = 0; unsigned int instid, rtrid; struct ospf6_interface *oi = NULL; @@ -1212,6 +1216,7 @@ static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, if (ospf6 == NULL) return NULL; + vrf = vrf_lookup_by_id(ospf6->vrf_id); /* Get variable length. */ offset = name + v->namelen; offsetlen = *length - v->namelen; @@ -1241,7 +1246,7 @@ static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, // offsetlen -= len; if (exact) { - oi = ospf6_interface_lookup_by_ifindex(ifindex); + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); if (!oi || oi->instance_id != instid) return NULL; on = ospf6_neighbor_lookup(rtrid, oi); @@ -1257,7 +1262,8 @@ static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, for (ALL_LIST_ELEMENTS_RO(ifslist, i, iif)) { if (!iif->ifindex) continue; - oi = ospf6_interface_lookup_by_ifindex(iif->ifindex); + oi = ospf6_interface_lookup_by_ifindex(iif->ifindex, + iif->vrf_id); if (!oi) continue; for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on)) { diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index e75c132956..e5eb8d74eb 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -278,7 +278,7 @@ static void ospf6_nexthop_calc(struct ospf6_vertex *w, struct ospf6_vertex *v, return; } - oi = ospf6_interface_lookup_by_ifindex(ifindex); + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); if (oi == NULL) { if (IS_OSPF6_DEBUG_SPF(PROCESS)) zlog_debug("Can't find interface in SPF: ifindex %d", diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 50687a7290..6f23051dc3 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -232,7 +232,7 @@ static void ospf6_disable(struct ospf6 *o) ospf6_area_disable(oa); /* XXX: This also changes persistent settings */ - ospf6_asbr_redistribute_reset(); + ospf6_asbr_redistribute_reset(o->vrf_id); ospf6_lsdb_remove_all(o->lsdb); ospf6_route_remove_all(o->route_table); diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 2773a666a3..62e0e149b8 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -75,25 +75,25 @@ static int ospf6_router_id_update_zebra(ZAPI_CALLBACK_ARGS) } /* redistribute function */ -void ospf6_zebra_redistribute(int type) +void ospf6_zebra_redistribute(int type, vrf_id_t vrf_id) { - if (vrf_bitmap_check(zclient->redist[AFI_IP6][type], VRF_DEFAULT)) + if (vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id)) return; - vrf_bitmap_set(zclient->redist[AFI_IP6][type], VRF_DEFAULT); + vrf_bitmap_set(zclient->redist[AFI_IP6][type], vrf_id); if (zclient->sock > 0) zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, - AFI_IP6, type, 0, VRF_DEFAULT); + AFI_IP6, type, 0, vrf_id); } -void ospf6_zebra_no_redistribute(int type) +void ospf6_zebra_no_redistribute(int type, vrf_id_t vrf_id) { - if (!vrf_bitmap_check(zclient->redist[AFI_IP6][type], VRF_DEFAULT)) + if (!vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id)) return; - vrf_bitmap_unset(zclient->redist[AFI_IP6][type], VRF_DEFAULT); + vrf_bitmap_unset(zclient->redist[AFI_IP6][type], vrf_id); if (zclient->sock > 0) zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient, - AFI_IP6, type, 0, VRF_DEFAULT); + AFI_IP6, type, 0, vrf_id); } static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS) @@ -279,7 +279,7 @@ static void ospf6_zebra_route_update(int type, struct ospf6_route *request) dest = &request->prefix; memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; + api.vrf_id = ospf6->vrf_id; api.type = ZEBRA_ROUTE_OSPF6; api.safi = SAFI_UNICAST; api.prefix = *dest; @@ -330,7 +330,7 @@ void ospf6_zebra_add_discard(struct ospf6_route *request) if (!CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; + api.vrf_id = ospf6->vrf_id; api.type = ZEBRA_ROUTE_OSPF6; api.safi = SAFI_UNICAST; api.prefix = *dest; @@ -363,7 +363,7 @@ void ospf6_zebra_delete_discard(struct ospf6_route *request) if (CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { memset(&api, 0, sizeof(api)); - api.vrf_id = VRF_DEFAULT; + api.vrf_id = ospf6->vrf_id; api.type = ZEBRA_ROUTE_OSPF6; api.safi = SAFI_UNICAST; api.prefix = *dest; diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h index e2f778fa72..d23268303a 100644 --- a/ospf6d/ospf6_zebra.h +++ b/ospf6d/ospf6_zebra.h @@ -45,10 +45,10 @@ extern struct zclient *zclient; extern void ospf6_zebra_route_update_add(struct ospf6_route *request); extern void ospf6_zebra_route_update_remove(struct ospf6_route *request); -extern void ospf6_zebra_redistribute(int); -extern void ospf6_zebra_no_redistribute(int); -#define ospf6_zebra_is_redistribute(type) \ - vrf_bitmap_check(zclient->redist[AFI_IP6][type], VRF_DEFAULT) +extern void ospf6_zebra_redistribute(int, vrf_id_t vrf_id); +extern void ospf6_zebra_no_redistribute(int, vrf_id_t vrf_id); +#define ospf6_zebra_is_redistribute(type, vrf_id) \ + vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id) extern void ospf6_zebra_init(struct thread_master *); extern void ospf6_zebra_add_discard(struct ospf6_route *request); extern void ospf6_zebra_delete_discard(struct ospf6_route *request); diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index 3dbc476172..f6c0504999 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -1802,6 +1802,7 @@ static int ospf_abr_task_timer(struct thread *thread) ospf_abr_task(ospf); ospf_abr_nssa_task(ospf); /* if nssa-abr, then scan Type-7 LSDB */ + ospf_asbr_nssa_redist_task(ospf); return 0; } diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index 1480b0e391..8fb6402c7e 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -275,6 +275,30 @@ void ospf_asbr_status_update(struct ospf *ospf, uint8_t status) ospf_router_lsa_update(ospf); } +/* If there's redistribution configured, we need to refresh external + * LSAs in order to install Type-7 and flood to all NSSA Areas + */ +void ospf_asbr_nssa_redist_task(struct ospf *ospf) +{ + int type; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + struct list *red_list; + struct listnode *node; + struct ospf_redist *red; + + red_list = ospf->redist[type]; + if (!red_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) + ospf_external_lsa_refresh_type( + ospf, type, red->instance, LSA_REFRESH_FORCE); + } + + ospf_external_lsa_refresh_default(ospf); +} + void ospf_redistribute_withdraw(struct ospf *ospf, uint8_t type, unsigned short instance) { diff --git a/ospfd/ospf_asbr.h b/ospfd/ospf_asbr.h index 1bcc32e3d8..ede6c47906 100644 --- a/ospfd/ospf_asbr.h +++ b/ospfd/ospf_asbr.h @@ -72,6 +72,7 @@ extern struct external_info *ospf_external_info_lookup(struct ospf *, uint8_t, unsigned short, struct prefix_ipv4 *); extern void ospf_asbr_status_update(struct ospf *, uint8_t); +extern void ospf_asbr_nssa_redist_task(struct ospf *ospf); extern void ospf_redistribute_withdraw(struct ospf *, uint8_t, unsigned short); extern void ospf_asbr_check(void); diff --git a/ospfd/ospf_errors.c b/ospfd/ospf_errors.c index a985efc668..2de77a43f6 100644 --- a/ospfd/ospf_errors.c +++ b/ospfd/ospf_errors.c @@ -158,6 +158,12 @@ static struct log_ref ferr_ospf_err[] = { .suggestion = "Restart OSPF instance, If the problem persists, report the problem for troubleshooting" }, { + .code = EC_OSPF_SR_SID_OVERFLOW, + .title = "OSPF SR Segment-ID overflow", + .description = "OSPF Segment Routing ID index or label exceed Global or Local Block Range", + .suggestion = "Restart OSPF instance, If the problem persists, report the problem for troubleshooting" + }, + { .code = EC_OSPF_INVALID_ALGORITHM, .title = "OSPF SR Invalid Algorithm", .description = "OSPF Segment Routing invalid Algorithm", diff --git a/ospfd/ospf_errors.h b/ospfd/ospf_errors.h index 726f7d9c8b..df5bdaa491 100644 --- a/ospfd/ospf_errors.h +++ b/ospfd/ospf_errors.h @@ -31,6 +31,7 @@ enum ospf_log_refs { EC_OSPF_SR_INVALID_DB, EC_OSPF_SR_NODE_CREATE, EC_OSPF_SR_INVALID_LSA_ID, + EC_OSPF_SR_SID_OVERFLOW, EC_OSPF_INVALID_ALGORITHM, EC_OSPF_FSM_INVALID_STATE, EC_OSPF_SET_METRIC_PLUS, diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c index 6dd5d78bd0..90dc108c0e 100644 --- a/ospfd/ospf_ext.c +++ b/ospfd/ospf_ext.c @@ -98,6 +98,7 @@ static void ospf_ext_link_lsa_schedule(struct ext_itf *exti, static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op); static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa); static int ospf_ext_pref_lsa_update(struct ospf_lsa *lsa); +static void ospf_ext_link_delete_adj_sid(struct ext_itf *exti); static void del_ext_info(void *val); /* @@ -434,6 +435,20 @@ static void set_lan_adj_sid(struct ext_itf *exti, bool backup, uint32_t value, exti->lan_sid[index].neighbor_id = neighbor_id; } +static void unset_adjacency_sid(struct ext_itf *exti) +{ + /* Reset Adjacency TLV */ + if (exti->type == ADJ_SID) { + TLV_TYPE(exti->adj_sid[0]) = 0; + TLV_TYPE(exti->adj_sid[1]) = 0; + } + /* or Lan-Adjacency TLV */ + if (exti->type == LAN_ADJ_SID) { + TLV_TYPE(exti->lan_sid[0]) = 0; + TLV_TYPE(exti->lan_sid[1]) = 0; + } +} + /* Experimental SubTLV from Cisco */ static void set_rmt_itf_addr(struct ext_itf *exti, struct in_addr rmtif) { @@ -452,7 +467,7 @@ static void ospf_extended_lsa_delete(struct ext_itf *exti) return; osr_debug("EXT (%s): Disable %s%s%s-SID on interface %s", __func__, - exti->stype == PREF_SID ? "Prefix" : "", + exti->stype == LOCAL_SID ? "Prefix" : "", exti->stype == ADJ_SID ? "Adjacency" : "", exti->stype == LAN_ADJ_SID ? "LAN-Adjacency" : "", exti->ifp->name); @@ -465,8 +480,10 @@ static void ospf_extended_lsa_delete(struct ext_itf *exti) /* De-activate this Extended Prefix/Link and remove corresponding * Segment-Routing Prefix-SID or (LAN)-ADJ-SID */ - exti->flags = EXT_LPFLG_LSA_INACTIVE; - ospf_sr_ext_itf_delete(exti); + if (exti->stype == ADJ_SID || exti->stype == LAN_ADJ_SID) + ospf_ext_link_delete_adj_sid(exti); + else + ospf_sr_ext_itf_delete(exti); } /* @@ -499,7 +516,6 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index, set_prefix_sid(exti, SR_ALGORITHM_SPF, index, SID_INDEX, flags); /* Try to Schedule LSA */ - // SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE); if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) { if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) ospf_ext_pref_lsa_schedule(exti, @@ -512,15 +528,104 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index, osr_debug("EXT (%s): Remove prefix for interface %s", __func__, ifp->name); - if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) { + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) ospf_ext_pref_lsa_schedule(exti, FLUSH_THIS_LSA); - exti->flags = EXT_LPFLG_LSA_INACTIVE; - } } return SET_OPAQUE_LSID(exti->type, exti->instance); } +/** + * Update Adjacecny-SID for Extended Link LSA + * + * @param exti Extended Link information + */ +static void ospf_ext_link_update_adj_sid(struct ext_itf *exti) +{ + mpls_label_t label; + mpls_label_t bck_label; + + /* Process only (LAN)Adjacency-SID Type */ + if (exti->stype != ADJ_SID && exti->stype != LAN_ADJ_SID) + return; + + /* Request Primary & Backup Labels from Label Manager */ + bck_label = ospf_sr_local_block_request_label(); + label = ospf_sr_local_block_request_label(); + if (bck_label == MPLS_INVALID_LABEL || label == MPLS_INVALID_LABEL) { + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA); + return; + } + + /* Set Adjacency-SID, backup first */ + if (exti->stype == ADJ_SID) { + set_adj_sid(exti, true, bck_label, SID_LABEL); + set_adj_sid(exti, false, label, SID_LABEL); + } else { + set_lan_adj_sid(exti, true, bck_label, SID_LABEL, + exti->lan_sid[0].neighbor_id); + set_lan_adj_sid(exti, false, label, SID_LABEL, + exti->lan_sid[1].neighbor_id); + } + + /* Finally, add corresponding SR Link in SRDB & MPLS LFIB */ + SET_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET); + ospf_sr_ext_itf_add(exti); +} + +/** + * Delete Adjacecny-SID for Extended Link LSA + * + * @param exti Extended Link information + */ +static void ospf_ext_link_delete_adj_sid(struct ext_itf *exti) +{ + /* Process only (LAN)Adjacency-SID Type */ + if (exti->stype != ADJ_SID && exti->stype != LAN_ADJ_SID) + return; + + /* Release Primary & Backup Labels from Label Manager */ + if (exti->stype == ADJ_SID) { + ospf_sr_local_block_release_label(exti->adj_sid[0].value); + ospf_sr_local_block_release_label(exti->adj_sid[1].value); + } else { + ospf_sr_local_block_release_label(exti->lan_sid[0].value); + ospf_sr_local_block_release_label(exti->lan_sid[1].value); + } + /* And reset corresponding TLV */ + unset_adjacency_sid(exti); + + /* Finally, remove corresponding SR Link in SRDB & MPLS LFIB */ + UNSET_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET); + ospf_sr_ext_itf_delete(exti); +} + +/** + * Update Extended Link LSA once Segment Routing Label Block has been changed. + */ +void ospf_ext_link_srlb_update(void) +{ + struct listnode *node; + struct ext_itf *exti; + + + osr_debug("EXT (%s): Update Extended Links with new SRLB", __func__); + + /* Update all Extended Link Adjaceny-SID */ + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { + /* Skip Extended Prefix */ + if (exti->stype == PREF_SID || exti->stype == LOCAL_SID) + continue; + + /* Skip inactive Extended Link */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + continue; + + ospf_ext_link_update_adj_sid(exti); + } +} + /* * Used by Segment Routing to activate/deactivate Extended Link/Prefix flooding * @@ -541,10 +646,15 @@ void ospf_ext_update_sr(bool enable) /* Refresh LSAs if already engaged or originate */ for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { - /* Skip inactive Extended Link */ + /* Skip Inactive Extended Link */ if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) continue; + /* Update Extended Link (LAN)Adj-SID if not set */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET)) + ospf_ext_link_update_adj_sid(exti); + + /* Finally, flood the extended Link */ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) ospf_ext_lsa_schedule(exti, REFRESH_THIS_LSA); else @@ -684,7 +794,6 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) { struct ospf_interface *oi = nbr->oi; struct ext_itf *exti; - uint32_t label; /* Process Link only when neighbor old or new state is NSM Full */ if (nbr->state != NSM_Full && old_status != NSM_Full) @@ -709,7 +818,10 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) /* Remove Extended Link if Neighbor State goes Down or Deleted */ if (nbr->state == NSM_Down || nbr->state == NSM_Deleted) { - ospf_extended_lsa_delete(exti); + ospf_ext_link_delete_adj_sid(exti); + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_link_lsa_schedule(exti, FLUSH_THIS_LSA); + exti->flags = EXT_LPFLG_LSA_INACTIVE; return; } @@ -729,11 +841,6 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) set_ext_link(exti, OSPF_IFTYPE_POINTOPOINT, nbr->router_id, oi->address->u.prefix4); - /* Set Extended Link Adjacency SubTLVs, backup first */ - label = get_ext_link_label_value(); - set_adj_sid(exti, true, label, SID_LABEL); - label = get_ext_link_label_value(); - set_adj_sid(exti, false, label, SID_LABEL); /* And Remote Interface address */ set_rmt_itf_addr(exti, nbr->address.u.prefix4); @@ -747,11 +854,9 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi), oi->address->u.prefix4); - /* Set Extended Link Adjacency SubTLVs, backup first */ - label = get_ext_link_label_value(); - set_lan_adj_sid(exti, true, label, SID_LABEL, nbr->router_id); - label = get_ext_link_label_value(); - set_lan_adj_sid(exti, false, label, SID_LABEL, nbr->router_id); + /* Set Neighbor ID */ + exti->lan_sid[0].neighbor_id = nbr->router_id; + exti->lan_sid[1].neighbor_id = nbr->router_id; break; @@ -764,36 +869,33 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi), oi->address->u.prefix4); - /* Set Extended Link Adjacency SubTLVs, backup first */ - label = get_ext_link_label_value(); - set_adj_sid(exti, true, label, SID_LABEL); - label = get_ext_link_label_value(); - set_adj_sid(exti, false, label, SID_LABEL); - break; default: + if (CHECK_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET)) + ospf_ext_link_delete_adj_sid(exti); if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) ospf_ext_link_lsa_schedule(exti, FLUSH_THIS_LSA); exti->flags = EXT_LPFLG_LSA_INACTIVE; return; } - osr_debug("EXT (%s): Complete %sAdjacency SID for interface %s ", - __func__, exti->stype == ADJ_SID ? "" : "LAN-", - oi->ifp->name); - - /* flood this links params if everything is ok */ SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE); + if (OspfEXT.enabled) { + osr_debug("EXT (%s): Set %sAdjacency SID for interface %s ", + __func__, exti->stype == ADJ_SID ? "" : "LAN-", + oi->ifp->name); + + /* Update (LAN)Adjacency SID */ + ospf_ext_link_update_adj_sid(exti); + + /* flood this links params if everything is ok */ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) ospf_ext_link_lsa_schedule(exti, REFRESH_THIS_LSA); else ospf_ext_link_lsa_schedule(exti, REORIGINATE_THIS_LSA); } - - /* Finally install (LAN)Adjacency-SID in the SRDB */ - ospf_sr_ext_itf_add(exti); } /* Callbacks to handle Extended Link Segment Routing LSA information */ @@ -1272,10 +1374,6 @@ static int ospf_ext_link_lsa_originate(void *arg) || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id))) continue; - /* Skip Extended Link which are not Active */ - if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) - continue; - /* Check if LSA not already engaged */ if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) { if (CHECK_FLAG(exti->flags, diff --git a/ospfd/ospf_ext.h b/ospfd/ospf_ext.h index 0071584e26..b1e4feb6df 100644 --- a/ospfd/ospf_ext.h +++ b/ospfd/ospf_ext.h @@ -189,6 +189,7 @@ extern int ospf_ext_init(void); extern void ospf_ext_term(void); extern void ospf_ext_finish(void); extern void ospf_ext_update_sr(bool enable); +extern void ospf_ext_link_srlb_update(void); extern uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index, struct prefix_ipv4 *p, diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 7977a2a9f4..af801da8d7 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -995,7 +995,8 @@ void ospf_vl_delete(struct ospf *ospf, struct ospf_vl_data *vl_data) ospf_vl_data_free(vl_data); } -static int ospf_vl_set_params(struct ospf_vl_data *vl_data, struct vertex *v) +static int ospf_vl_set_params(struct ospf_area *area, + struct ospf_vl_data *vl_data, struct vertex *v) { int changed = 0; struct ospf_interface *voi; @@ -1003,6 +1004,7 @@ static int ospf_vl_set_params(struct ospf_vl_data *vl_data, struct vertex *v) struct vertex_parent *vp = NULL; unsigned int i; struct router_lsa *rl; + struct ospf_interface *oi; voi = vl_data->vl_oi; @@ -1013,17 +1015,24 @@ static int ospf_vl_set_params(struct ospf_vl_data *vl_data, struct vertex *v) } for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) { - vl_data->nexthop.oi = vp->nexthop->oi; + vl_data->nexthop.lsa_pos = vp->nexthop->lsa_pos; vl_data->nexthop.router = vp->nexthop->router; - if (!IPV4_ADDR_SAME(&voi->address->u.prefix4, - &vl_data->nexthop.oi->address->u.prefix4)) - changed = 1; + /* + * Only deal with interface data when the local + * (calculating) node is the SPF root node + */ + if (!area->spf_dry_run) { + oi = ospf_if_lookup_by_lsa_pos( + area, vl_data->nexthop.lsa_pos); - voi->address->u.prefix4 = - vl_data->nexthop.oi->address->u.prefix4; - voi->address->prefixlen = - vl_data->nexthop.oi->address->prefixlen; + if (!IPV4_ADDR_SAME(&voi->address->u.prefix4, + &oi->address->u.prefix4)) + changed = 1; + + voi->address->u.prefix4 = oi->address->u.prefix4; + voi->address->prefixlen = oi->address->prefixlen; + } break; /* We take the first interface. */ } @@ -1114,7 +1123,7 @@ void ospf_vl_up_check(struct ospf_area *area, struct in_addr rid, OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); } - if (ospf_vl_set_params(vl_data, v)) { + if (ospf_vl_set_params(area, vl_data, v)) { if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) zlog_debug( "ospf_vl_up_check: VL cost change, scheduling router lsa refresh"); diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index b0dd5c6fc2..61aae695b3 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -53,6 +53,7 @@ #include "ospfd/ospf_flood.h" #include "ospfd/ospf_dump.h" #include "ospfd/ospf_errors.h" +#include "ospfd/ospf_zebra.h" /* * OSPF Fragmentation / fragmented writes @@ -4319,21 +4320,10 @@ void ospf_ls_ack_send_delayed(struct ospf_interface *oi) * punt-to-CPU set on them. This may overload the CPU control path that * can be avoided if the MAC was known apriori. */ -#define OSPF_PING_NBR_STR_MAX (BUFSIZ) void ospf_proactively_arp(struct ospf_neighbor *nbr) { - char ping_nbr[OSPF_PING_NBR_STR_MAX]; - int ret; - - if (!nbr) + if (!nbr || !nbr->oi->ospf->proactive_arp) return; - snprintf(ping_nbr, sizeof(ping_nbr), - "ping -c 1 -I %s %s > /dev/null 2>&1 &", nbr->oi->ifp->name, - inet_ntoa(nbr->address.u.prefix4)); - - ret = system(ping_nbr); - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("Executed %s %s", ping_nbr, - ((ret == 0) ? "successfully" : "but failed")); + ospf_zebra_send_arp(nbr->oi->ifp, &nbr->address); } diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c index ececed0643..fc9c8f6be6 100644 --- a/ospfd/ospf_ri.c +++ b/ospfd/ospf_ri.c @@ -442,29 +442,52 @@ static void unset_sr_algorithm(uint8_t algo) TLV_LEN(OspfRI.sr_info.algo) = htons(0); } -/* Segment Routing Global Block SubTLV - section 3.2 */ -static void set_sr_sid_label_range(struct sr_srgb srgb) +/* Set Segment Routing Global Block SubTLV - section 3.2 */ +static void set_sr_global_label_range(struct sr_block srgb) { /* Set Header */ - TLV_TYPE(OspfRI.sr_info.range) = htons(RI_SR_TLV_SID_LABEL_RANGE); - TLV_LEN(OspfRI.sr_info.range) = + TLV_TYPE(OspfRI.sr_info.srgb) = htons(RI_SR_TLV_SRGB_LABEL_RANGE); + TLV_LEN(OspfRI.sr_info.srgb) = htons(SUBTLV_SID_LABEL_SIZE + sizeof(uint32_t)); /* Set Range Size */ - OspfRI.sr_info.range.size = htonl(SET_RANGE_SIZE(srgb.range_size)); + OspfRI.sr_info.srgb.size = htonl(SET_RANGE_SIZE(srgb.range_size)); /* Set Lower bound label SubTLV */ - TLV_TYPE(OspfRI.sr_info.range.lower) = htons(SUBTLV_SID_LABEL); - TLV_LEN(OspfRI.sr_info.range.lower) = htons(SID_RANGE_LABEL_LENGTH); - OspfRI.sr_info.range.lower.value = htonl(SET_LABEL(srgb.lower_bound)); + TLV_TYPE(OspfRI.sr_info.srgb.lower) = htons(SUBTLV_SID_LABEL); + TLV_LEN(OspfRI.sr_info.srgb.lower) = htons(SID_RANGE_LABEL_LENGTH); + OspfRI.sr_info.srgb.lower.value = htonl(SET_LABEL(srgb.lower_bound)); } -/* Unset this SRGB SubTLV */ -static void unset_sr_sid_label_range(void) +/* Unset Segment Routing Global Block SubTLV */ +static void unset_sr_global_label_range(void) { + TLV_TYPE(OspfRI.sr_info.srgb) = htons(0); + TLV_LEN(OspfRI.sr_info.srgb) = htons(0); + TLV_TYPE(OspfRI.sr_info.srgb.lower) = htons(0); + TLV_LEN(OspfRI.sr_info.srgb.lower) = htons(0); +} - TLV_TYPE(OspfRI.sr_info.range) = htons(0); - TLV_LEN(OspfRI.sr_info.range) = htons(0); - TLV_TYPE(OspfRI.sr_info.range.lower) = htons(0); - TLV_LEN(OspfRI.sr_info.range.lower) = htons(0); +/* Set Segment Routing Local Block SubTLV - section 3.2 */ +static void set_sr_local_label_range(struct sr_block srlb) +{ + /* Set Header */ + TLV_TYPE(OspfRI.sr_info.srlb) = htons(RI_SR_TLV_SRLB_LABEL_RANGE); + TLV_LEN(OspfRI.sr_info.srlb) = + htons(SUBTLV_SID_LABEL_SIZE + sizeof(uint32_t)); + /* Set Range Size */ + OspfRI.sr_info.srlb.size = htonl(SET_RANGE_SIZE(srlb.range_size)); + /* Set Lower bound label SubTLV */ + TLV_TYPE(OspfRI.sr_info.srlb.lower) = htons(SUBTLV_SID_LABEL); + TLV_LEN(OspfRI.sr_info.srlb.lower) = htons(SID_RANGE_LABEL_LENGTH); + OspfRI.sr_info.srlb.lower.value = htonl(SET_LABEL(srlb.lower_bound)); +} + +/* Unset Segment Routing Local Block SubTLV */ +static void unset_sr_local_label_range(void) +{ + TLV_TYPE(OspfRI.sr_info.srlb) = htons(0); + TLV_LEN(OspfRI.sr_info.srlb) = htons(0); + TLV_TYPE(OspfRI.sr_info.srlb.lower) = htons(0); + TLV_LEN(OspfRI.sr_info.srlb.lower) = htons(0); } /* Set Maximum Stack Depth for this router */ @@ -550,20 +573,23 @@ static void initialize_params(struct ospf_router_info *ori) return; } -static int is_mandated_params_set(struct ospf_router_info ori) +static int is_mandated_params_set(struct ospf_router_info *ori) { int rc = 0; - if (ntohs(ori.router_cap.header.type) == 0) + if (ori == NULL) + return rc; + + if (ntohs(ori->router_cap.header.type) == 0) return rc; - if ((ntohs(ori.pce_info.pce_header.header.type) == RI_TLV_PCE) - && (ntohs(ori.pce_info.pce_address.header.type) == 0) - && (ntohs(ori.pce_info.pce_cap_flag.header.type) == 0)) + if ((ntohs(ori->pce_info.pce_header.header.type) == RI_TLV_PCE) + && (ntohs(ori->pce_info.pce_address.header.type) == 0) + && (ntohs(ori->pce_info.pce_cap_flag.header.type) == 0)) return rc; - if ((ori.sr_info.enabled) && (ntohs(TLV_TYPE(ori.sr_info.algo)) == 0) - && (ntohs(TLV_TYPE(ori.sr_info.range)) == 0)) + if ((ori->sr_info.enabled) && (ntohs(TLV_TYPE(ori->sr_info.algo)) == 0) + && (ntohs(TLV_TYPE(ori->sr_info.srgb)) == 0)) return rc; rc = 1; @@ -575,13 +601,11 @@ static int is_mandated_params_set(struct ospf_router_info ori) * Used by Segment Routing to set new TLVs and Sub-TLVs values * * @param enable To activate or not Segment Routing router Information flooding - * @param size Size of Label Range i.e. SRGB size - * @param lower Lower bound of the Label Range i.e. SRGB first label - * @param msd Maximum label Stack Depth supported by the router + * @param srn Self Segment Routing node * * @return none */ -void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, uint8_t msd) +void ospf_router_info_update_sr(bool enable, struct sr_node *srn) { struct listnode *node, *nnode; struct ospf_ri_area_info *ai; @@ -605,6 +629,10 @@ void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, uint8_t msd) initialize_params(&OspfRI); } + /* Check that SR node is valid */ + if (srn == NULL) + return; + if (IS_DEBUG_OSPF_SR) zlog_debug("RI (%s): %s Routing Information for Segment Routing", __func__, enable ? "Enable" : "Disable"); @@ -612,15 +640,17 @@ void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, uint8_t msd) /* Unset or Set SR parameters */ if (!enable) { unset_sr_algorithm(SR_ALGORITHM_SPF); - unset_sr_sid_label_range(); + unset_sr_global_label_range(); + unset_sr_local_label_range(); unset_sr_node_msd(); OspfRI.sr_info.enabled = false; } else { // Only SR_ALGORITHM_SPF is supported set_sr_algorithm(SR_ALGORITHM_SPF); - set_sr_sid_label_range(srgb); - if (msd != 0) - set_sr_node_msd(msd); + set_sr_global_label_range(srn->srgb); + set_sr_local_label_range(srn->srlb); + if (srn->msd != 0) + set_sr_node_msd(srn->msd); else unset_sr_node_msd(); OspfRI.sr_info.enabled = true; @@ -698,7 +728,9 @@ static void ospf_router_info_lsa_body_set(struct stream *s) /* Build Algorithm TLV */ build_tlv(s, &TLV_HDR(OspfRI.sr_info.algo)); /* Build SRGB TLV */ - build_tlv(s, &TLV_HDR(OspfRI.sr_info.range)); + build_tlv(s, &TLV_HDR(OspfRI.sr_info.srgb)); + /* Build SRLB TLV */ + build_tlv(s, &TLV_HDR(OspfRI.sr_info.srlb)); /* Build MSD TLV */ build_tlv(s, &TLV_HDR(OspfRI.sr_info.msd)); } @@ -961,7 +993,7 @@ static int ospf_router_info_lsa_originate(void *arg) } /* Router Information is not yet Engaged, check parameters */ - if (!is_mandated_params_set(OspfRI)) + if (!is_mandated_params_set(&OspfRI)) flog_warn( EC_OSPF_LSA, "RI (%s): lacks mandated ROUTER INFORMATION parameters", @@ -1409,16 +1441,22 @@ static uint16_t show_vty_sr_range(struct vty *vty, struct tlv_header *tlvh) if (vty != NULL) { vty_out(vty, - " Segment Routing Range TLV:\n" + " Segment Routing %s Range TLV:\n" " Range Size = %d\n" " SID Label = %d\n\n", + ntohs(range->header.type) == RI_SR_TLV_SRGB_LABEL_RANGE + ? "Global" + : "Local", GET_RANGE_SIZE(ntohl(range->size)), GET_LABEL(ntohl(range->lower.value))); } else { zlog_debug( - " Segment Routing Range TLV:\n" + " Segment Routing %s Range TLV:\n" " Range Size = %d\n" " SID Label = %d\n\n", + ntohs(range->header.type) == RI_SR_TLV_SRGB_LABEL_RANGE + ? "Global" + : "Local", GET_RANGE_SIZE(ntohl(range->size)), GET_LABEL(ntohl(range->lower.value))); } @@ -1469,7 +1507,8 @@ static void ospf_router_info_show_info(struct vty *vty, struct ospf_lsa *lsa) case RI_SR_TLV_SR_ALGORITHM: sum += show_vty_sr_algorithm(vty, tlvh); break; - case RI_SR_TLV_SID_LABEL_RANGE: + case RI_SR_TLV_SRGB_LABEL_RANGE: + case RI_SR_TLV_SRLB_LABEL_RANGE: sum += show_vty_sr_range(vty, tlvh); break; case RI_SR_TLV_NODE_MSD: diff --git a/ospfd/ospf_ri.h b/ospfd/ospf_ri.h index 84511ac5e7..4729677bca 100644 --- a/ospfd/ospf_ri.h +++ b/ospfd/ospf_ri.h @@ -201,7 +201,12 @@ struct ospf_ri_sr_info { * Segment Routing Global Block i.e. label range * Only one range supported in this code */ - struct ri_sr_tlv_sid_label_range range; + struct ri_sr_tlv_sid_label_range srgb; + /* + * Segment Routing Local Block. + * Only one block is authorized - see section 3.3 + */ + struct ri_sr_tlv_sid_label_range srlb; /* Maximum SID Depth supported by the node */ struct ri_sr_tlv_node_msd msd; }; @@ -242,7 +247,6 @@ extern int ospf_router_info_init(void); extern void ospf_router_info_term(void); extern void ospf_router_info_finish(void); extern int ospf_router_info_enable(void); -extern void ospf_router_info_update_sr(bool enable, struct sr_srgb srgb, - uint8_t msd); +extern void ospf_router_info_update_sr(bool enable, struct sr_node *self); extern struct scope_info ospf_router_info_get_flooding_scope(void); #endif /* _ZEBRA_OSPF_ROUTER_INFO_H */ diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index 776f50b33a..3b049555ba 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -375,7 +375,7 @@ void ospf_intra_add_router(struct route_table *rt, struct vertex *v, else route_unlock_node(rn); - ospf_route_copy_nexthops_from_vertex(or, v); + ospf_route_copy_nexthops_from_vertex(area, or, v); listnode_add(rn->info, or); @@ -438,7 +438,7 @@ void ospf_intra_add_transit(struct route_table *rt, struct vertex *v, or->type = OSPF_DESTINATION_NETWORK; or->u.std.origin = (struct lsa_header *)lsa; - ospf_route_copy_nexthops_from_vertex(or, v); + ospf_route_copy_nexthops_from_vertex(area, or, v); rn->info = or ; } @@ -453,7 +453,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, struct ospf_route * or ; struct prefix_ipv4 p; struct router_lsa *lsa; - struct ospf_interface *oi; + struct ospf_interface *oi = NULL; struct ospf_path *path; if (IS_DEBUG_OSPF_EVENT) @@ -538,7 +538,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, zlog_debug( "ospf_intra_add_stub(): routes are equal, merge"); - ospf_route_copy_nexthops_from_vertex(cur_or, v); + ospf_route_copy_nexthops_from_vertex(area, cur_or, v); if (IPV4_ADDR_CMP(&cur_or->u.std.origin->id, &lsa->header.id) @@ -563,7 +563,7 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, list_delete_all_node(cur_or->paths); - ospf_route_copy_nexthops_from_vertex(cur_or, v); + ospf_route_copy_nexthops_from_vertex(area, cur_or, v); cur_or->u.std.origin = (struct lsa_header *)lsa; return; @@ -588,24 +588,35 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, if (IS_DEBUG_OSPF_EVENT) zlog_debug( "ospf_intra_add_stub(): this network is on remote router"); - ospf_route_copy_nexthops_from_vertex(or, v); + ospf_route_copy_nexthops_from_vertex(area, or, v); } else { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "ospf_intra_add_stub(): this network is on this router"); - if ((oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos))) { + /* + * Only deal with interface data when we + * don't do a dry run + */ + if (!area->spf_dry_run) + oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos); + + if (oi || area->spf_dry_run) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_intra_add_stub(): the interface is %s", - IF_NAME(oi)); + "ospf_intra_add_stub(): the lsa pos is %d", + lsa_pos); path = ospf_path_new(); path->nexthop.s_addr = INADDR_ANY; - path->ifindex = oi->ifp->ifindex; - if (CHECK_FLAG(oi->connected->flags, - ZEBRA_IFA_UNNUMBERED)) - path->unnumbered = 1; + + if (oi) { + path->ifindex = oi->ifp->ifindex; + if (CHECK_FLAG(oi->connected->flags, + ZEBRA_IFA_UNNUMBERED)) + path->unnumbered = 1; + } + listnode_add(or->paths, path); } else { if (IS_DEBUG_OSPF_EVENT) @@ -654,6 +665,37 @@ void ospf_route_table_dump(struct route_table *rt) zlog_debug("========================================"); } +void ospf_route_table_print(struct vty *vty, struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route * or ; + struct listnode *pnode; + struct ospf_path *path; + + vty_out(vty, "========== OSPF routing table ==========\n"); + for (rn = route_top(rt); rn; rn = route_next(rn)) + if ((or = rn->info) != NULL) { + if (or->type == OSPF_DESTINATION_NETWORK) { + vty_out(vty, "N %-18pFX %-15pI4 %s %d\n", + &rn->p, & or->u.std.area_id, + ospf_path_type_str[or->path_type], + or->cost); + for (ALL_LIST_ELEMENTS_RO(or->paths, pnode, + path)) + vty_out(vty, " -> %s\n", + path->nexthop.s_addr != 0 + ? inet_ntoa( + path->nexthop) + : "directly connected"); + } else + vty_out(vty, "R %-18pI4 %-15pI4 %s %d\n", + &rn->p.u.prefix4, & or->u.std.area_id, + ospf_path_type_str[or->path_type], + or->cost); + } + vty_out(vty, "========================================\n"); +} + /* This is 16.4.1 implementation. o Intra-area paths using non-backbone areas are always the most preferred. o The other paths, intra-area backbone paths and inter-area paths, @@ -734,30 +776,41 @@ static int ospf_path_exist(struct list *plist, struct in_addr nexthop, return 0; } -void ospf_route_copy_nexthops_from_vertex(struct ospf_route *to, +void ospf_route_copy_nexthops_from_vertex(struct ospf_area *area, + struct ospf_route *to, struct vertex *v) { struct listnode *node; struct ospf_path *path; struct vertex_nexthop *nexthop; struct vertex_parent *vp; + struct ospf_interface *oi = NULL; assert(to->paths); for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) { nexthop = vp->nexthop; - if (nexthop->oi != NULL) { - if (!ospf_path_exist(to->paths, nexthop->router, - nexthop->oi)) { - path = ospf_path_new(); - path->nexthop = nexthop->router; - path->ifindex = nexthop->oi->ifp->ifindex; - if (CHECK_FLAG(nexthop->oi->connected->flags, + /* + * Only deal with interface data when we + * don't do a dry run + */ + if (!area->spf_dry_run) + oi = ospf_if_lookup_by_lsa_pos(area, nexthop->lsa_pos); + + if ((oi && !ospf_path_exist(to->paths, nexthop->router, oi)) + || area->spf_dry_run) { + path = ospf_path_new(); + path->nexthop = nexthop->router; + + if (oi) { + path->ifindex = oi->ifp->ifindex; + if (CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) path->unnumbered = 1; - listnode_add(to->paths, path); } + + listnode_add(to->paths, path); } } } diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h index 20cdc75fe8..c3fa5954d5 100644 --- a/ospfd/ospf_route.h +++ b/ospfd/ospf_route.h @@ -132,6 +132,7 @@ extern void ospf_route_table_free(struct route_table *); extern void ospf_route_install(struct ospf *, struct route_table *); extern void ospf_route_table_dump(struct route_table *); +extern void ospf_route_table_print(struct vty *vty, struct route_table *rt); extern void ospf_intra_add_router(struct route_table *, struct vertex *, struct ospf_area *); @@ -146,7 +147,8 @@ extern void ospf_intra_add_stub(struct route_table *, struct router_lsa_link *, extern int ospf_route_cmp(struct ospf *, struct ospf_route *, struct ospf_route *); extern void ospf_route_copy_nexthops(struct ospf_route *, struct list *); -extern void ospf_route_copy_nexthops_from_vertex(struct ospf_route *, +extern void ospf_route_copy_nexthops_from_vertex(struct ospf_area *area, + struct ospf_route *, struct vertex *); extern void ospf_route_subst(struct route_node *, struct ospf_route *, diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 91fc20475d..f5e393a13c 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -68,14 +68,11 @@ static void ospf_spf_set_reason(ospf_spf_reason_t reason) } static void ospf_vertex_free(void *); -/* List of allocated vertices, to simplify cleanup of SPF. - * Not thread-safe obviously. If it ever needs to be, it'd have to be - * dynamically allocated at begin of ospf_spf_calculate - */ -static struct list vertex_list = {.del = ospf_vertex_free}; -/* Heap related functions, for the managment of the candidates, to - * be used with pqueue. */ +/* + * Heap related functions, for the managment of the candidates, to + * be used with pqueue. + */ static int vertex_cmp(const struct vertex *v1, const struct vertex *v2) { if (v1->distance != v2->distance) @@ -118,7 +115,8 @@ static void vertex_nexthop_free(struct vertex_nexthop *nh) XFREE(MTYPE_OSPF_NEXTHOP, nh); } -/* Free the canonical nexthop objects for an area, ie the nexthop objects +/* + * Free the canonical nexthop objects for an area, ie the nexthop objects * attached to the first-hop router vertices, and any intervening network * vertices. */ @@ -131,7 +129,8 @@ static void ospf_canonical_nexthops_free(struct vertex *root) struct listnode *n2, *nn2; struct vertex_parent *vp; - /* router vertices through an attached network each + /* + * router vertices through an attached network each * have a distinct (canonical / not inherited) nexthop * which must be freed. * @@ -150,7 +149,8 @@ static void ospf_canonical_nexthops_free(struct vertex *root) } } -/* TODO: Parent list should be excised, in favour of maintaining only +/* + * TODO: Parent list should be excised, in favour of maintaining only * vertex_nexthop, with refcounts. */ static struct vertex_parent *vertex_parent_new(struct vertex *v, int backlink, @@ -163,6 +163,7 @@ static struct vertex_parent *vertex_parent_new(struct vertex *v, int backlink, new->parent = v; new->backlink = backlink; new->nexthop = hop; + return new; } @@ -177,7 +178,8 @@ static int vertex_parent_cmp(void *aa, void *bb) return IPV4_ADDR_CMP(&a->nexthop->router, &b->nexthop->router); } -static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa) +static struct vertex *ospf_vertex_new(struct ospf_area *area, + struct ospf_lsa *lsa) { struct vertex *new; @@ -195,13 +197,14 @@ static struct vertex *ospf_vertex_new(struct ospf_lsa *lsa) lsa->stat = new; - listnode_add(&vertex_list, new); + listnode_add(area->spf_vertex_list, new); if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: Created %s vertex %s", __func__, new->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", inet_ntoa(new->lsa->id)); + return new; } @@ -214,14 +217,6 @@ static void ospf_vertex_free(void *data) v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", inet_ntoa(v->lsa->id)); - /* There should be no parents potentially holding references to this - * vertex - * Children however may still be there, but presumably referenced by - * other - * vertices - */ - // assert (listcount (v->parents) == 0); - if (v->children) list_delete(&v->children); @@ -252,14 +247,12 @@ static void ospf_vertex_dump(const char *msg, struct vertex *v, if (vp) { zlog_debug( - "parent %s backlink %d nexthop %s interface %s", + "parent %s backlink %d nexthop %s lsa pos %d", inet_ntoa(vp->parent->lsa->id), vp->backlink, inet_ntop(AF_INET, &vp->nexthop->router, buf1, BUFSIZ), - vp->nexthop->oi - ? IF_NAME(vp->nexthop->oi) - : "NULL"); + vp->nexthop->lsa_pos); } } } @@ -291,15 +284,24 @@ static void ospf_vertex_add_parent(struct vertex *v) } } -static void ospf_spf_init(struct ospf_area *area) +static void ospf_spf_init(struct ospf_area *area, struct ospf_lsa *root_lsa, + bool is_dry_run, bool is_root_node) { + struct list *vertex_list; struct vertex *v; - /* Create root node. */ - v = ospf_vertex_new(area->router_lsa_self); + /* Create vertex list */ + vertex_list = list_new(); + vertex_list->del = ospf_vertex_free; + area->spf_vertex_list = vertex_list; + /* Create root node. */ + v = ospf_vertex_new(area, root_lsa); area->spf = v; + area->spf_dry_run = is_dry_run; + area->spf_root_node = is_root_node; + /* Reset ABR and ASBR router counts. */ area->abr_count = 0; area->asbr_count = 0; @@ -364,7 +366,8 @@ static int ospf_lsa_has_link(struct lsa_header *w, struct lsa_header *v) return -1; } -/* Find the next link after prev_link from v to w. If prev_link is +/* + * Find the next link after prev_link from v to w. If prev_link is * NULL, return the first link from v to w. Ignore stub and virtual links; * these link types will never be returned. */ @@ -433,10 +436,11 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w, assert(v && w && newhop); assert(distance); - /* IFF w has already been assigned a distance, then we shouldn't get - * here - * unless callers have determined V(l)->W is shortest / equal-shortest - * path (0 is a special case distance (no distance yet assigned)). + /* + * IFF w has already been assigned a distance, then we shouldn't get + * here unless callers have determined V(l)->W is shortest / + * equal-shortest path (0 is a special case distance (no distance yet + * assigned)). */ if (w->distance) assert(distance <= w->distance); @@ -452,7 +456,8 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w, sizeof(buf[1]))); } - /* Adding parent for a new, better path: flush existing parents from W. + /* + * Adding parent for a new, better path: flush existing parents from W. */ if (distance < w->distance) { if (IS_DEBUG_OSPF_EVENT) @@ -463,7 +468,8 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w, w->distance = distance; } - /* new parent is <= existing parents, add it to parent list (if nexthop + /* + * new parent is <= existing parents, add it to parent list (if nexthop * not on parent list) */ for (ALL_LIST_ELEMENTS_RO(w->parents, node, wp)) { @@ -482,7 +488,43 @@ static void ospf_spf_add_parent(struct vertex *v, struct vertex *w, return; } -/* 16.1.1. Calculate nexthop from root through V (parent) to +static int match_stub_prefix(struct lsa_header *lsa, struct in_addr v_link_addr, + struct in_addr w_link_addr) +{ + uint8_t *p, *lim; + struct router_lsa_link *l = NULL; + struct in_addr masked_lsa_addr; + + if (lsa->type != OSPF_ROUTER_LSA) + return 0; + + p = ((uint8_t *)lsa) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((uint8_t *)lsa) + ntohs(lsa->length); + + while (p < lim) { + l = (struct router_lsa_link *)p; + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + if (l->m[0].type != LSA_LINK_TYPE_STUB) + continue; + + masked_lsa_addr.s_addr = + (l->link_id.s_addr & l->link_data.s_addr); + + /* check that both links belong to the same stub subnet */ + if ((masked_lsa_addr.s_addr + == (v_link_addr.s_addr & l->link_data.s_addr)) + && (masked_lsa_addr.s_addr + == (w_link_addr.s_addr & l->link_data.s_addr))) + return 1; + } + + return 0; +} + +/* + * 16.1.1. Calculate nexthop from root through V (parent) to * vertex W (destination), with given distance from root->W. * * The link must be supplied if V is the root vertex. In all other cases @@ -501,7 +543,6 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, struct listnode *node, *nnode; struct vertex_nexthop *nh; struct vertex_parent *vp; - struct ospf_interface *oi = NULL; unsigned int added = 0; char buf1[BUFSIZ]; char buf2[BUFSIZ]; @@ -514,154 +555,140 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, } if (v == area->spf) { - /* 16.1.1 para 4. In the first case, the parent vertex (V) is - the - root (the calculating router itself). This means that the - destination is either a directly connected network or - directly - connected router. The outgoing interface in this case is - simply - the OSPF interface connecting to the destination - network/router. - */ + /* + * 16.1.1 para 4. In the first case, the parent vertex (V) is + * the root (the calculating router itself). This means that + * the destination is either a directly connected network or + * directly connected router. The outgoing interface in this + * case is simply the OSPF interface connecting to the + * destination network/router. + */ /* we *must* be supplied with the link data */ assert(l != NULL); - oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos); - if (!oi) { - zlog_debug( - "%s: OI not found in LSA: lsa_pos:%d link_id:%s link_data:%s", - __func__, lsa_pos, - inet_ntop(AF_INET, &l->link_id, buf1, BUFSIZ), - inet_ntop(AF_INET, &l->link_data, buf2, - BUFSIZ)); - return 0; - } if (IS_DEBUG_OSPF_EVENT) { zlog_debug( - "%s: considering link:%s type:%d link_id:%s link_data:%s", - __func__, oi->ifp->name, l->m[0].type, + "%s: considering link type:%d link_id:%s link_data:%s", + __func__, l->m[0].type, inet_ntop(AF_INET, &l->link_id, buf1, BUFSIZ), inet_ntop(AF_INET, &l->link_data, buf2, BUFSIZ)); } if (w->type == OSPF_VERTEX_ROUTER) { - /* l is a link from v to w - * l2 will be link from w to v + /* + * l is a link from v to w l2 will be link from w to v */ struct router_lsa_link *l2 = NULL; if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT) { - struct in_addr nexthop = {.s_addr = 0}; - /* If the destination is a router which connects - to - the calculating router via a - Point-to-MultiPoint - network, the destination's next hop IP - address(es) - can be determined by examining the - destination's - router-LSA: each link pointing back to the - calculating router and having a Link Data - field - belonging to the Point-to-MultiPoint network - provides an IP address of the next hop - router. - - At this point l is a link from V to W, and V - is the - root ("us"). If it is a point-to-multipoint - interface, - then look through the links in the opposite - direction (W to V). - If any of them have an address that lands - within the - subnet declared by the PtMP link, then that - link - is a constituent of the PtMP link, and its - address is - a nexthop address for V. - */ - if (oi->type == OSPF_IFTYPE_POINTOPOINT) { - /* Having nexthop = 0 is tempting, but - NOT acceptable. - It breaks AS-External routes with a - forwarding address, - since - ospf_ase_complete_direct_routes() - will mistakenly - assume we've reached the last hop and - should place the - forwarding address as nexthop. - Also, users may configure - multi-access links in p2p mode, - so we need the IP to ARP the nexthop. - */ - struct ospf_neighbor *nbr_w; - - nbr_w = ospf_nbr_lookup_by_routerid( - oi->nbrs, &l->link_id); - if (nbr_w != NULL) { - added = 1; - nexthop = nbr_w->src; - } - } else if (oi->type - == OSPF_IFTYPE_POINTOMULTIPOINT) { - struct prefix_ipv4 la; + /* + * If the destination is a router which connects + * to the calculating router via a + * Point-to-MultiPoint network, the + * destination's next hop IP address(es) can be + * determined by examining the destination's + * router-LSA: each link pointing back to the + * calculating router and having a Link Data + * field belonging to the Point-to-MultiPoint + * network provides an IP address of the next + * hop router. + * + * At this point l is a link from V to W, and V + * is the root ("us"). If it is a point-to- + * multipoint interface, then look through the + * links in the opposite direction (W to V). + * If any of them have an address that lands + * within the subnet declared by the PtMP link, + * then that link is a constituent of the PtMP + * link, and its address is a nexthop address + * for V. + * + * Note for point-to-point interfaces: + * + * Having nexthop = 0 (as proposed in the RFC) + * is tempting, but NOT acceptable. It breaks + * AS-External routes with a forwarding address, + * since ospf_ase_complete_direct_routes() will + * mistakenly assume we've reached the last hop + * and should place the forwarding address as + * nexthop. Also, users may configure multi- + * access links in p2p mode, so we need the IP + * to ARP the nexthop. + * + * If the calculating router is the SPF root + * node and the link is P2P then access the + * interface information directly. This can be + * crucial when e.g. IP unnumbered is used + * where 'correct' nexthop information are not + * available via Router LSAs. + * + * Otherwise handle P2P and P2MP the same way + * as described above using a reverse lookup to + * figure out the nexthop. + */ - la.family = AF_INET; - la.prefixlen = oi->address->prefixlen; + struct in_addr nexthop = {.s_addr = 0}; + struct ospf_interface *oi = NULL; + struct ospf_neighbor *nbr_w = NULL; + + /* Calculating node is root node, link is P2P */ + if (area->spf_root_node) { + oi = ospf_if_lookup_by_lsa_pos(area, + lsa_pos); + if (oi->type + == OSPF_IFTYPE_POINTOPOINT) { + nbr_w = ospf_nbr_lookup_by_routerid( + oi->nbrs, &l->link_id); + if (nbr_w) { + added = 1; + nexthop = nbr_w->src; + } + } + } - /* V links to W on PtMP interface - - find the interface address on W */ + /* Reverse lookup */ + if (!added) { while ((l2 = ospf_get_next_link(w, v, l2))) { - la.prefix = l2->link_data; - - if (prefix_cmp((struct prefix - *)&la, - oi->address) - != 0) - continue; - /* link_data is on our PtMP - * network */ - added = 1; - nexthop = l2->link_data; - break; + if (match_stub_prefix( + v->lsa, + l->link_data, + l2->link_data)) { + added = 1; + nexthop = l2->link_data; + break; + } } } if (added) { - /* found all necessary info to build - * nexthop */ nh = vertex_nexthop_new(); - nh->oi = oi; nh->router = nexthop; + nh->lsa_pos = lsa_pos; ospf_spf_add_parent(v, w, nh, distance); return 1; } else zlog_info( - "%s: could not determine nexthop for link %s", - __func__, oi->ifp->name); + "%s: could not determine nexthop for link", + __func__); } /* end point-to-point link from V to W */ else if (l->m[0].type == LSA_LINK_TYPE_VIRTUALLINK) { - struct ospf_vl_data *vl_data; - - /* VLink implementation limitations: - * a) vl_data can only reference one nexthop, so - * no ECMP - * to backbone through VLinks. Though - * transit-area - * summaries may be considered, and those can - * be ECMP. + /* + * VLink implementation limitations: + * a) vl_data can only reference one nexthop, + * so no ECMP to backbone through VLinks. + * Though transit-area summaries may be + * considered, and those can be ECMP. * b) We can only use /one/ VLink, even if - * multiple ones - * exist this router through multiple - * transit-areas. + * multiple ones exist this router through + * multiple transit-areas. */ + + struct ospf_vl_data *vl_data; + vl_data = ospf_vl_lookup(area->ospf, NULL, l->link_id); @@ -669,8 +696,8 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, && CHECK_FLAG(vl_data->flags, OSPF_VL_FLAG_APPROVED)) { nh = vertex_nexthop_new(); - nh->oi = vl_data->nexthop.oi; nh->router = vl_data->nexthop.router; + nh->lsa_pos = vl_data->nexthop.lsa_pos; ospf_spf_add_parent(v, w, nh, distance); return 1; } else @@ -683,8 +710,8 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, assert(w->type == OSPF_VERTEX_NETWORK); nh = vertex_nexthop_new(); - nh->oi = oi; nh->router.s_addr = 0; /* Nexthop not required */ + nh->lsa_pos = lsa_pos; ospf_spf_add_parent(v, w, nh, distance); return 1; } @@ -693,82 +720,70 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, else if (v->type == OSPF_VERTEX_NETWORK) { /* See if any of V's parents are the root. */ for (ALL_LIST_ELEMENTS(v->parents, node, nnode, vp)) { - if (vp->parent == area->spf) /* connects to root? */ - { - /* 16.1.1 para 5. ...the parent vertex is a - * network that - * directly connects the calculating router to - * the destination - * router. The list of next hops is then - * determined by - * examining the destination's router-LSA... + if (vp->parent == area->spf) { + /* + * 16.1.1 para 5. ...the parent vertex is a + * network that directly connects the + * calculating router to the destination + * router. The list of next hops is then + * determined by examining the destination's + * router-LSA ... */ assert(w->type == OSPF_VERTEX_ROUTER); while ((l = ospf_get_next_link(w, v, l))) { - /* ...For each link in the router-LSA - * that points back to the - * parent network, the link's Link Data - * field provides the IP - * address of a next hop router. The - * outgoing interface to - * use can then be derived from the next - * hop IP address (or - * it can be inherited from the parent - * network). + /* + * ... For each link in the router-LSA + * that points back to the parent + * network, the link's Link Data field + * provides the IP address of a next hop + * router. The outgoing interface to use + * can then be derived from the next + * hop IP address (or it can be + * inherited from the parent network). */ nh = vertex_nexthop_new(); - nh->oi = vp->nexthop->oi; nh->router = l->link_data; + nh->lsa_pos = vp->nexthop->lsa_pos; added = 1; ospf_spf_add_parent(v, w, nh, distance); } - /* Note lack of return is deliberate. See next - * comment. */ + /* + * Note lack of return is deliberate. See next + * comment. + */ } } - /* NB: This code is non-trivial. + /* + * NB: This code is non-trivial. * * E.g. it is not enough to know that V connects to the root. It - * is - * also important that the while above, looping through all - * links from - * W->V found at least one link, so that we know there is - * bi-directional connectivity between V and W (which need not - * be the - * case, e.g. when OSPF has not yet converged fully). - * Otherwise, if - * we /always/ return here, without having checked that - * root->V->-W - * actually resulted in a valid nexthop being created, then we - * we will - * prevent SPF from finding/using higher cost paths. + * is also important that the while above, looping through all + * links from W->V found at least one link, so that we know + * there is bi-directional connectivity between V and W (which + * need not be the case, e.g. when OSPF has not yet converged + * fully). Otherwise, if we /always/ return here, without having + * checked that root->V->-W actually resulted in a valid nexthop + * being created, then we we will prevent SPF from finding/using + * higher cost paths. * * It is important, if root->V->W has not been added, that we - * continue - * through to the intervening-router nexthop code below. So as - * to - * ensure other paths to V may be used. This avoids unnecessary - * blackholes while OSPF is convergening. + * continue through to the intervening-router nexthop code + * below. So as to ensure other paths to V may be used. This + * avoids unnecessary blackholes while OSPF is converging. * * I.e. we may have arrived at this function, examining V -> W, - * via - * workable paths other than root -> V, and it's important to - * avoid - * getting "confused" by non-working root->V->W path - it's - * important - * to *not* lose the working non-root paths, just because of a - * non-viable root->V->W. - * - * See also bug #330 (required reading!), and: - * - * http://blogs.oracle.com/paulj/entry/the_difference_a_line_makes + * via workable paths other than root -> V, and it's important + * to avoid getting "confused" by non-working root->V->W path + * - it's important to *not* lose the working non-root paths, + * just because of a non-viable root->V->W. */ if (added) return added; } - /* 16.1.1 para 4. If there is at least one intervening router in the + /* + * 16.1.1 para 4. If there is at least one intervening router in the * current shortest path between the destination and the root, the * destination simply inherits the set of next hops from the * parent. @@ -785,13 +800,13 @@ static unsigned int ospf_nexthop_calculation(struct ospf_area *area, return added; } -/* RFC2328 Section 16.1 (2). - * v is on the SPF tree. Examine the links in v's LSA. Update the list - * of candidates with any vertices not already on the list. If a lower-cost - * path is found to a vertex already on the candidate list, store the new cost. +/* + * RFC2328 16.1 (2). + * v is on the SPF tree. Examine the links in v's LSA. Update the list of + * candidates with any vertices not already on the list. If a lower-cost path + * is found to a vertex already on the candidate list, store the new cost. */ -static void ospf_spf_next(struct vertex *v, struct ospf *ospf, - struct ospf_area *area, +static void ospf_spf_next(struct vertex *v, struct ospf_area *area, struct vertex_pqueue_head *candidate) { struct ospf_lsa *w_lsa = NULL; @@ -801,8 +816,10 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, struct in_addr *r; int type = 0, lsa_pos = -1, lsa_pos_next = 0; - /* If this is a router-LSA, and bit V of the router-LSA (see Section - A.4.2:RFC2328) is set, set Area A's TransitCapability to true. */ + /* + * If this is a router-LSA, and bit V of the router-LSA (see Section + * A.4.2:RFC2328) is set, set Area A's TransitCapability to true. + */ if (v->type == OSPF_VERTEX_ROUTER) { if (IS_ROUTER_LSA_VIRTUAL((struct router_lsa *)v->lsa)) area->transit = OSPF_TRANSIT_TRUE; @@ -826,40 +843,39 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, lsa_pos = lsa_pos_next; /* LSA link position */ lsa_pos_next++; + p += (OSPF_ROUTER_LSA_LINK_SIZE + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); - /* (a) If this is a link to a stub network, examine the - next - link in V's LSA. Links to stub networks will be - considered in the second stage of the shortest path - calculation. */ + /* + * (a) If this is a link to a stub network, examine the + * next link in V's LSA. Links to stub networks will + * be considered in the second stage of the shortest + * path calculation. + */ if ((type = l->m[0].type) == LSA_LINK_TYPE_STUB) continue; - /* (b) Otherwise, W is a transit vertex (router or - transit - network). Look up the vertex W's LSA (router-LSA or - network-LSA) in Area A's link state database. */ + /* + * (b) Otherwise, W is a transit vertex (router or + * transit network). Look up the vertex W's LSA + * (router-LSA or network-LSA) in Area A's link state + * database. + */ switch (type) { case LSA_LINK_TYPE_POINTOPOINT: case LSA_LINK_TYPE_VIRTUALLINK: - if (type == LSA_LINK_TYPE_VIRTUALLINK) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "looking up LSA through VL: %s", - inet_ntoa(l->link_id)); - } - - w_lsa = ospf_lsa_lookup(ospf, area, + if (type == LSA_LINK_TYPE_VIRTUALLINK + && IS_DEBUG_OSPF_EVENT) + zlog_debug( + "looking up LSA through VL: %s", + inet_ntoa(l->link_id)); + w_lsa = ospf_lsa_lookup(area->ospf, area, OSPF_ROUTER_LSA, l->link_id, l->link_id); - if (w_lsa) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug( - "found Router LSA %s", - inet_ntoa(l->link_id)); - } + if (w_lsa && IS_DEBUG_OSPF_EVENT) + zlog_debug("found Router LSA %s", + inet_ntoa(l->link_id)); break; case LSA_LINK_TYPE_TRANSIT: if (IS_DEBUG_OSPF_EVENT) @@ -868,9 +884,8 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, inet_ntoa(l->link_id)); w_lsa = ospf_lsa_lookup_by_id( area, OSPF_NETWORK_LSA, l->link_id); - if (w_lsa) - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("found the LSA"); + if (w_lsa && IS_DEBUG_OSPF_EVENT) + zlog_debug("found the LSA"); break; default: flog_warn(EC_OSPF_LSA, @@ -888,19 +903,19 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, /* Lookup the vertex W's LSA. */ w_lsa = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, *r); - if (w_lsa) { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("found Router LSA %s", - inet_ntoa(w_lsa->data->id)); - } + if (w_lsa && IS_DEBUG_OSPF_EVENT) + zlog_debug("found Router LSA %s", + inet_ntoa(w_lsa->data->id)); /* step (d) below */ distance = v->distance; } - /* (b cont.) If the LSA does not exist, or its LS age is equal - to MaxAge, or it does not have a link back to vertex V, - examine the next link in V's LSA.[23] */ + /* + * (b cont.) If the LSA does not exist, or its LS age is equal + * to MaxAge, or it does not have a link back to vertex V, + * examine the next link in V's LSA.[23] + */ if (w_lsa == NULL) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("No LSA found"); @@ -919,26 +934,30 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, continue; } - /* (c) If vertex W is already on the shortest-path tree, examine - the next link in the LSA. */ + /* + * (c) If vertex W is already on the shortest-path tree, examine + * the next link in the LSA. + */ if (w_lsa->stat == LSA_SPF_IN_SPFTREE) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("The LSA is already in SPF"); continue; } - /* (d) Calculate the link state cost D of the resulting path - from the root to vertex W. D is equal to the sum of the link - state cost of the (already calculated) shortest path to - vertex V and the advertised cost of the link between vertices - V and W. If D is: */ + /* + * (d) Calculate the link state cost D of the resulting path + * from the root to vertex W. D is equal to the sum of the link + * state cost of the (already calculated) shortest path to + * vertex V and the advertised cost of the link between vertices + * V and W. If D is: + */ /* calculate link cost D -- moved above */ /* Is there already vertex W in candidate list? */ if (w_lsa->stat == LSA_SPF_NOT_EXPLORED) { /* prepare vertex W. */ - w = ospf_vertex_new(w_lsa); + w = ospf_vertex_new(area, w_lsa); /* Calculate nexthop to W. */ if (ospf_nexthop_calculation(area, v, w, l, distance, @@ -948,29 +967,28 @@ static void ospf_spf_next(struct vertex *v, struct ospf *ospf, zlog_debug("Nexthop Calc failed"); } else if (w_lsa->stat != LSA_SPF_IN_SPFTREE) { w = w_lsa->stat; - /* if D is greater than. */ if (w->distance < distance) { continue; } - /* equal to. */ else if (w->distance == distance) { - /* Found an equal-cost path to W. - * Calculate nexthop of to W from V. */ + /* + * Found an equal-cost path to W. + * Calculate nexthop of to W from V. + */ ospf_nexthop_calculation(area, v, w, l, distance, lsa_pos); } - /* less than. */ else { - /* Found a lower-cost path to W. + /* + * Found a lower-cost path to W. * nexthop_calculation is conditional, if it - * finds - * valid nexthop it will call spf_add_parents, - * which - * will flush the old parents + * finds valid nexthop it will call + * spf_add_parents, which will flush the old + * parents. */ vertex_pqueue_del(candidate, w); ospf_nexthop_calculation(area, v, w, l, - distance, lsa_pos); + distance, lsa_pos); vertex_pqueue_add(candidate, w); } } /* end W is already on the candidate list */ @@ -997,11 +1015,9 @@ static void ospf_spf_dump(struct vertex *v, int i) if (IS_DEBUG_OSPF_EVENT) for (ALL_LIST_ELEMENTS_RO(v->parents, nnode, parent)) { - zlog_debug(" nexthop %p %s %s", (void *)parent->nexthop, + zlog_debug(" nexthop %p %s %d", (void *)parent->nexthop, inet_ntoa(parent->nexthop->router), - parent->nexthop->oi - ? IF_NAME(parent->nexthop->oi) - : "NULL"); + parent->nexthop->lsa_pos); } i++; @@ -1010,6 +1026,33 @@ static void ospf_spf_dump(struct vertex *v, int i) ospf_spf_dump(v, i); } +void ospf_spf_print(struct vty *vty, struct vertex *v, int i) +{ + struct listnode *cnode; + struct listnode *nnode; + struct vertex_parent *parent; + + if (v->type == OSPF_VERTEX_ROUTER) { + vty_out(vty, "SPF Result: depth %d [R] %s\n", i, + inet_ntoa(v->lsa->id)); + } else { + struct network_lsa *lsa = (struct network_lsa *)v->lsa; + vty_out(vty, "SPF Result: depth %d [N] %s/%d\n", i, + inet_ntoa(v->lsa->id), ip_masklen(lsa->mask)); + } + + for (ALL_LIST_ELEMENTS_RO(v->parents, nnode, parent)) { + vty_out(vty, " nexthop %s lsa pos %d\n", + inet_ntoa(parent->nexthop->router), + parent->nexthop->lsa_pos); + } + + i++; + + for (ALL_LIST_ELEMENTS_RO(v->children, cnode, v)) + ospf_spf_print(vty, v, i); +} + /* Second stage of SPF calculation. */ static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v, struct route_table *rt, int parent_is_root) @@ -1020,24 +1063,26 @@ static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v, if (IS_DEBUG_OSPF_EVENT) zlog_debug("ospf_process_stub():processing stubs for area %s", inet_ntoa(area->area_id)); + if (v->type == OSPF_VERTEX_ROUTER) { uint8_t *p; uint8_t *lim; struct router_lsa_link *l; - struct router_lsa *rlsa; + struct router_lsa *router_lsa; int lsa_pos = 0; if (IS_DEBUG_OSPF_EVENT) zlog_debug( "ospf_process_stubs():processing router LSA, id: %s", inet_ntoa(v->lsa->id)); - rlsa = (struct router_lsa *)v->lsa; + router_lsa = (struct router_lsa *)v->lsa; if (IS_DEBUG_OSPF_EVENT) zlog_debug( "ospf_process_stubs(): we have %d links to process", - ntohs(rlsa->links)); + ntohs(router_lsa->links)); + p = ((uint8_t *)v->lsa) + OSPF_LSA_HEADER_SIZE + 4; lim = ((uint8_t *)v->lsa) + ntohs(v->lsa->length); @@ -1061,7 +1106,8 @@ static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v, if (CHECK_FLAG(child->flags, OSPF_VERTEX_PROCESSED)) continue; - /* the first level of routers connected to the root + /* + * The first level of routers connected to the root * should have 'parent_is_root' set, including those * connected via a network vertex. */ @@ -1097,9 +1143,23 @@ void ospf_rtrs_free(struct route_table *rtrs) rn->info = NULL; route_unlock_node(rn); } + route_table_finish(rtrs); } +void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list) +{ + /* + * Free nexthop information, canonical versions of which are + * attached the first level of router vertices attached to the + * root vertex, see ospf_nexthop_calculation. + */ + ospf_canonical_nexthops_free(spf); + + /* Free SPF vertices list with deconstructor ospf_vertex_free. */ + list_delete(&vertex_list); +} + #if 0 static void ospf_rtrs_print (struct route_table *rtrs) @@ -1162,10 +1222,11 @@ ospf_rtrs_print (struct route_table *rtrs) } #endif -/* Calculating the shortest-path tree for an area. */ -static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, - struct route_table *new_table, - struct route_table *new_rtrs) +/* Calculating the shortest-path tree for an area, see RFC2328 16.1. */ +void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa, + struct route_table *new_table, + struct route_table *new_rtrs, bool is_dry_run, + bool is_root_node) { struct vertex_pqueue_head candidate; struct vertex *v; @@ -1176,55 +1237,57 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, inet_ntoa(area->area_id)); } - /* Check router-lsa-self. If self-router-lsa is not yet allocated, - return this area's calculation. */ - if (!area->router_lsa_self) { + /* + * If the router LSA of the root is not yet allocated, return this + * area's calculation. In the 'usual' case the root_lsa is the + * self-originated router LSA of the node itself. + */ + if (!root_lsa) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( - "ospf_spf_calculate: Skip area %s's calculation due to empty router_lsa_self", + "ospf_spf_calculate: Skip area %s's calculation due to empty root LSA", inet_ntoa(area->area_id)); return; } - /* RFC2328 16.1. (1). */ - /* Initialize the algorithm's data structures. */ + /* Initialize the algorithm's data structures, see RFC2328 16.1. (1). */ - /* This function scans all the LSA database and set the stat field to - * LSA_SPF_NOT_EXPLORED. */ + /* + * This function scans all the LSA database and set the stat field to + * LSA_SPF_NOT_EXPLORED. + */ lsdb_clean_stat(area->lsdb); + /* Create a new heap for the candidates. */ vertex_pqueue_init(&candidate); - /* Initialize the shortest-path tree to only the root (which is the - router doing the calculation). */ - ospf_spf_init(area); - v = area->spf; - /* Set LSA position to LSA_SPF_IN_SPFTREE. This vertex is the root of - * the - * spanning tree. */ - v->lsa_p->stat = LSA_SPF_IN_SPFTREE; + /* + * Initialize the shortest-path tree to only the root (which is usually + * the router doing the calculation). + */ + ospf_spf_init(area, root_lsa, is_dry_run, is_root_node); /* Set Area A's TransitCapability to false. */ area->transit = OSPF_TRANSIT_FALSE; area->shortcut_capability = 1; + /* + * Use the root vertex for the start of the SPF algorithm and make it + * part of the tree. + */ + v = area->spf; + v->lsa_p->stat = LSA_SPF_IN_SPFTREE; + for (;;) { /* RFC2328 16.1. (2). */ - ospf_spf_next(v, ospf, area, &candidate); + ospf_spf_next(v, area, &candidate); /* RFC2328 16.1. (3). */ - /* If at this step the candidate list is empty, the shortest- - path tree (of transit vertices) has been completely built and - this stage of the procedure terminates. */ - /* Otherwise, choose the vertex belonging to the candidate list - that is closest to the root, and add it to the shortest-path - tree (removing it from the candidate list in the - process). */ - /* Extract from the candidates the node with the lower key. */ v = vertex_pqueue_pop(&candidate); if (!v) + /* No more vertices left. */ break; - /* Update stat field in vertex. */ + v->lsa_p->stat = LSA_SPF_IN_SPFTREE; ospf_vertex_add_parent(v); @@ -1235,28 +1298,21 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, else ospf_intra_add_transit(new_table, v, area); - /* RFC2328 16.1. (5). */ - /* Iterate the algorithm by returning to Step 2. */ - - } /* end loop until no more candidate vertices */ + /* Iterate back to (2), see RFC2328 16.1. (5). */ + } if (IS_DEBUG_OSPF_EVENT) { ospf_spf_dump(area->spf, 0); ospf_route_table_dump(new_table); } - /* Second stage of SPF calculation procedure's */ + /* + * Second stage of SPF calculation procedure's, add leaves to the tree + * for stub networks. + */ ospf_spf_process_stubs(area, area->spf, new_table, 0); - /* Free candidate queue. */ - //vertex_pqueue_fini(&candidate); - ospf_vertex_dump(__func__, area->spf, 0, 1); - /* Free nexthop information, canonical versions of which are attached - * the first level of router vertices attached to the root vertex, see - * ospf_nexthop_calculation. - */ - ospf_canonical_nexthops_free(area->spf); /* Increment SPF Calculation Counter. */ area->spf_calculation++; @@ -1268,75 +1324,89 @@ static void ospf_spf_calculate(struct ospf *ospf, struct ospf_area *area, zlog_debug("ospf_spf_calculate: Stop. %zd vertices", mtype_stats_alloc(MTYPE_OSPF_VERTEX)); - /* Free SPF vertices, but not the list. List has ospf_vertex_free - * as deconstructor. - */ - list_delete_all_node(&vertex_list); + /* If this is a dry run then keep the SPF data in place */ + if (!area->spf_dry_run) + ospf_spf_cleanup(area->spf, area->spf_vertex_list); } -/* Timer for SPF calculation. */ -static int ospf_spf_calculate_timer(struct thread *thread) +int ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table, + struct route_table *new_rtrs, bool is_dry_run, + bool is_root_node) { - struct ospf *ospf = THREAD_ARG(thread); - struct route_table *new_table, *new_rtrs; struct ospf_area *area; struct listnode *node, *nnode; - struct timeval start_time, spf_start_time; int areas_processed = 0; - unsigned long ia_time, prune_time, rt_time; - unsigned long abr_time, total_spf_time, spf_time; - char rbuf[32]; /* reason_buf */ - - if (IS_DEBUG_OSPF_EVENT) - zlog_debug("SPF: Timer (SPF calculation expire)"); - - ospf->t_spf_calc = NULL; - - monotime(&spf_start_time); - /* Allocate new table tree. */ - new_table = route_table_init(); - new_rtrs = route_table_init(); - - ospf_vl_unapprove(ospf); /* Calculate SPF for each area. */ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { /* Do backbone last, so as to first discover intra-area paths - * for any back-bone virtual-links - */ + * for any back-bone virtual-links */ if (ospf->backbone && ospf->backbone == area) continue; - ospf_spf_calculate(ospf, area, new_table, new_rtrs); + ospf_spf_calculate(area, area->router_lsa_self, new_table, + new_rtrs, is_dry_run, is_root_node); areas_processed++; } /* SPF for backbone, if required */ if (ospf->backbone) { - ospf_spf_calculate(ospf, ospf->backbone, new_table, new_rtrs); + area = ospf->backbone; + ospf_spf_calculate(area, area->router_lsa_self, new_table, + new_rtrs, is_dry_run, is_root_node); areas_processed++; } + return areas_processed; +} + +/* Worker for SPF calculation scheduler. */ +static int ospf_spf_calculate_schedule_worker(struct thread *thread) +{ + struct ospf *ospf = THREAD_ARG(thread); + struct route_table *new_table, *new_rtrs; + struct timeval start_time, spf_start_time; + int areas_processed; + unsigned long ia_time, prune_time, rt_time; + unsigned long abr_time, total_spf_time, spf_time; + char rbuf[32]; /* reason_buf */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("SPF: Timer (SPF calculation expire)"); + + ospf->t_spf_calc = NULL; + + ospf_vl_unapprove(ospf); + + /* Execute SPF for each area including backbone, see RFC 2328 16.1. */ + monotime(&spf_start_time); + new_table = route_table_init(); /* routing table */ + new_rtrs = route_table_init(); /* ABR/ASBR routing table */ + areas_processed = ospf_spf_calculate_areas(ospf, new_table, new_rtrs, + false, true); spf_time = monotime_since(&spf_start_time, NULL); ospf_vl_shut_unapproved(ospf); + /* Calculate inter-area routes, see RFC 2328 16.2. */ monotime(&start_time); ospf_ia_routing(ospf, new_table, new_rtrs); ia_time = monotime_since(&start_time, NULL); + /* Get rid of transit networks and routers we cannot reach anyway. */ monotime(&start_time); ospf_prune_unreachable_networks(new_table); ospf_prune_unreachable_routers(new_rtrs); prune_time = monotime_since(&start_time, NULL); - /* AS-external-LSA calculation should not be performed here. */ - - /* If new Router Route is installed, - then schedule re-calculate External routes. */ - if (1) - ospf_ase_calculate_schedule(ospf); + /* Note: RFC 2328 16.3. is apparently missing. */ + /* + * Calculate AS external routes, see RFC 2328 16.4. + * There is a dedicated routing table for external routes which is not + * handled here directly + */ + ospf_ase_calculate_schedule(ospf); ospf_ase_calculate_timer_add(ospf); if (IS_DEBUG_OSPF_EVENT) @@ -1344,22 +1414,22 @@ static int ospf_spf_calculate_timer(struct thread *thread) "%s: ospf install new route, vrf %s id %u new_table count %lu", __func__, ospf_vrf_id_to_name(ospf->vrf_id), ospf->vrf_id, new_table->count); + /* Update routing table. */ monotime(&start_time); ospf_route_install(ospf, new_table); rt_time = monotime_since(&start_time, NULL); - /* Update ABR/ASBR routing table */ - if (ospf->old_rtrs) { - /* old_rtrs's node holds linked list of ospf_route. --kunihiro. - */ + /* Free old ABR/ASBR routing table */ + if (ospf->old_rtrs) /* ospf_route_delete (ospf->old_rtrs); */ ospf_rtrs_free(ospf->old_rtrs); - } + /* Update ABR/ASBR routing table */ ospf->old_rtrs = ospf->new_rtrs; ospf->new_rtrs = new_rtrs; + /* ABRs may require additional changes, see RFC 2328 16.7. */ monotime(&start_time); if (IS_OSPF_ABR(ospf)) ospf_abr_task(ospf); @@ -1413,8 +1483,10 @@ static int ospf_spf_calculate_timer(struct thread *thread) return 0; } -/* Add schedule for SPF calculation. To avoid frequenst SPF calc, we - set timer for SPF calc. */ +/* + * Add schedule for SPF calculation. To avoid frequenst SPF calc, we set timer + * for SPF calc. + */ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason) { unsigned long delay, elapsed, ht; @@ -1446,9 +1518,10 @@ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason) /* Get SPF calculation delay time. */ if (elapsed < ht) { - /* Got an event within the hold time of last SPF. We need to + /* + * Got an event within the hold time of last SPF. We need to * increase the hold_multiplier, if it's not already at/past - * maximum value, and wasn't already increased.. + * maximum value, and wasn't already increased. */ if (ht < ospf->spf_max_holdtime) ospf->spf_hold_multiplier++; @@ -1468,6 +1541,6 @@ void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason) zlog_debug("SPF: calculation timer delay = %ld msec", delay); ospf->t_spf_calc = NULL; - thread_add_timer_msec(master, ospf_spf_calculate_timer, ospf, delay, - &ospf->t_spf_calc); + thread_add_timer_msec(master, ospf_spf_calculate_schedule_worker, ospf, + delay, &ospf->t_spf_calc); } diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h index 09a0b6f1b7..2dc0f8b886 100644 --- a/ospfd/ospf_spf.h +++ b/ospfd/ospf_spf.h @@ -49,15 +49,14 @@ struct vertex { /* A nexthop taken on the root node to get to this (parent) vertex */ struct vertex_nexthop { - struct ospf_interface *oi; /* output intf on root node */ struct in_addr router; /* router address to send to */ + int lsa_pos; /* LSA position for resolving the interface */ }; struct vertex_parent { - struct vertex_nexthop - *nexthop; /* link to nexthop info for this parent */ - struct vertex *parent; /* parent vertex */ - int backlink; /* index back to parent for router-lsa's */ + struct vertex_nexthop *nexthop; /* nexthop address for this parent */ + struct vertex *parent; /* parent vertex */ + int backlink; /* index back to parent for router-lsa's */ }; /* What triggered the SPF ? */ @@ -73,7 +72,19 @@ typedef enum { } ospf_spf_reason_t; extern void ospf_spf_calculate_schedule(struct ospf *, ospf_spf_reason_t); +extern void ospf_spf_calculate(struct ospf_area *area, + struct ospf_lsa *root_lsa, + struct route_table *new_table, + struct route_table *new_rtrs, bool is_dry_run, + bool is_root_node); +extern int ospf_spf_calculate_areas(struct ospf *ospf, + struct route_table *new_table, + struct route_table *new_rtrs, + bool is_dry_run, bool is_root_node); extern void ospf_rtrs_free(struct route_table *); +extern void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list); + +extern void ospf_spf_print(struct vty *vty, struct vertex *v, int i); /* void ospf_spf_calculate_timer_add (); */ #endif /* _QUAGGA_OSPF_SPF_H */ diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index 8110bc2d02..eb882c5d0e 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -1,13 +1,14 @@ /* * This is an implementation of Segment Routing - * as per draft draft-ietf-ospf-segment-routing-extensions-24 + * as per RFC 8665 - OSPF Extensions for Segment Routing + * and RFC 8476 - Signaling Maximum SID Depth (MSD) Using OSPF * * Module name: Segment Routing * * Author: Olivier Dugeon <olivier.dugeon@orange.com> * Author: Anselme Sawadogo <anselmesawadogo@gmail.com> * - * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com + * Copyright (C) 2016 - 2020 Orange Labs http://www.orange.com * * 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 @@ -82,6 +83,7 @@ static struct ospf_sr_db OspfSR; static void ospf_sr_register_vty(void); static inline void del_adj_sid(struct sr_nhlfe nhlfe); +static int ospf_sr_start(struct ospf *ospf); /* * Segment Routing Data Base functions @@ -218,9 +220,205 @@ static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf, } /* + * Segment Routing Local Block management functions + */ + +/** + * It is necessary to known which label is already allocated to manage the range + * of SRLB. This is particular useful when an interface flap (goes up / down + * frequently). Here, SR will release and then allocate label for the Adjacency + * for each concerned interface. If we don't care, there is a risk to run out of + * label. + * + * For that purpose, a similar principle as already provided to manage chunk of + * label is proposed. But, here, the label chunk has not a fix range of 64 + * labels that could be easily manage with a single variable of 64 bits size. + * So, used_mark is used as a bit wise to mark label reserved (bit set) or not + * (bit unset). Its size is equal to the number of label of the SRLB range round + * up to 64 bits. + * + * - sr__local_block_init() computes the number of 64 bits variables that are + * needed to manage the SRLB range and allocates this number. + * - ospf_sr_local_block_request_label() pick up the first available label and + * set corresponding bit + * - ospf_sr_local_block_release_label() release label by reseting the + * corresponding bit and set the next label to the first free position + */ + +/** + * Initialize Segment Routing Local Block from SRDB configuration and reserve + * block of bits to manage label allocation. + * + * @param lower_bound The lower bound of the SRLB range + * @param upper_bound The upper bound of the SRLB range + * + * @return 0 on success, -1 otherwise + */ +static int sr_local_block_init(uint32_t lower_bound, uint32_t upper_bound) +{ + struct sr_local_block *srlb = &OspfSR.srlb; + uint32_t size; + + /* Check if SRLB is not already configured */ + if (srlb->reserved) + return 0; + + /* + * Request SRLB to the label manager. If the allocation fails, return + * an error to disable SR until a new SRLB is successfully allocated. + */ + size = upper_bound - lower_bound + 1; + if (ospf_zebra_request_label_range(lower_bound, size)) { + srlb->reserved = false; + return -1; + } + + osr_debug("SR (%s): Got new SRLB [%u/%u]", __func__, lower_bound, + upper_bound); + + /* Initialize the SRLB */ + srlb->start = lower_bound; + srlb->end = upper_bound; + srlb->current = 0; + /* Compute the needed Used Mark number and allocate them */ + srlb->max_block = size / SRLB_BLOCK_SIZE; + if ((size % SRLB_BLOCK_SIZE) != 0) + srlb->max_block++; + srlb->used_mark = XCALLOC(MTYPE_OSPF_SR_PARAMS, + srlb->max_block * SRLB_BLOCK_SIZE); + srlb->reserved = true; + + return 0; +} + +/** + * Remove Segment Routing Local Block. + * + */ +static void sr_local_block_delete() +{ + struct sr_local_block *srlb = &OspfSR.srlb; + + /* Check if SRLB is not already delete */ + if (!srlb->reserved) + return; + + osr_debug("SR (%s): Remove SRLB [%u/%u]", __func__, srlb->start, + srlb->end); + + /* First release the label block */ + ospf_zebra_release_label_range(srlb->start, srlb->end); + + /* Then reset SRLB structure */ + if (srlb->used_mark != NULL) + XFREE(MTYPE_OSPF_SR_PARAMS, srlb->used_mark); + srlb->reserved = false; +} + +/** + * Request a label from the Segment Routing Local Block. + * + * @return First available label on success or MPLS_INVALID_LABEL if the + * block of labels is full + */ +mpls_label_t ospf_sr_local_block_request_label(void) +{ + struct sr_local_block *srlb = &OspfSR.srlb; + mpls_label_t label; + uint32_t index; + uint32_t pos; + + /* Check if we ran out of available labels */ + if (srlb->current >= srlb->end) + return MPLS_INVALID_LABEL; + + /* Get first available label and mark it used */ + label = srlb->current + srlb->start; + index = srlb->current / SRLB_BLOCK_SIZE; + pos = 1ULL << (srlb->current % SRLB_BLOCK_SIZE); + srlb->used_mark[index] |= pos; + + /* Jump to the next free position */ + srlb->current++; + pos = srlb->current % SRLB_BLOCK_SIZE; + while (srlb->current < srlb->end) { + if (pos == 0) + index++; + if (!((1ULL << pos) & srlb->used_mark[index])) + break; + else { + srlb->current++; + pos = srlb->current % SRLB_BLOCK_SIZE; + } + } + + return label; +} + +/** + * Release label in the Segment Routing Local Block. + * + * @param label Label to be release + * + * @return 0 on success or -1 if label falls outside SRLB + */ +int ospf_sr_local_block_release_label(mpls_label_t label) +{ + struct sr_local_block *srlb = &OspfSR.srlb; + uint32_t index; + uint32_t pos; + + /* Check that label falls inside the SRLB */ + if ((label < srlb->start) || (label > srlb->end)) { + flog_warn(EC_OSPF_SR_SID_OVERFLOW, + "%s: Returning label %u is outside SRLB [%u/%u]", + __func__, label, srlb->start, srlb->end); + return -1; + } + + index = (label - srlb->start) / SRLB_BLOCK_SIZE; + pos = 1ULL << ((label - srlb->start) % SRLB_BLOCK_SIZE); + srlb->used_mark[index] &= ~pos; + /* Reset current to the first available position */ + for (index = 0; index < srlb->max_block; index++) { + if (srlb->used_mark[index] != 0xFFFFFFFFFFFFFFFF) { + for (pos = 0; pos < SRLB_BLOCK_SIZE; pos++) + if (!((1ULL << pos) & srlb->used_mark[index])) { + srlb->current = + index * SRLB_BLOCK_SIZE + pos; + break; + } + break; + } + } + + return 0; +} + +/* * Segment Routing Initialization functions */ +/** + * Thread function to re-attempt connection to the Label Manager and thus be + * able to start Segment Routing. + * + * @param start Thread structure that contains area as argument + * + * @return 1 on success + */ +static int sr_start_label_manager(struct thread *start) +{ + struct ospf *ospf; + + ospf = THREAD_ARG(start); + + /* re-attempt to start SR & Label Manager connection */ + ospf_sr_start(ospf); + + return 1; +} + /* Segment Routing starter function */ static int ospf_sr_start(struct ospf *ospf) { @@ -231,16 +429,59 @@ static int ospf_sr_start(struct ospf *ospf) osr_debug("SR (%s): Start Segment Routing", __func__); - /* Initialize self SR Node */ - srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id), - (void *)sr_node_new); + /* Initialize self SR Node if not already done */ + if (OspfSR.self == NULL) { + srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id), + (void *)sr_node_new); + + /* Complete & Store self SR Node */ + srn->srgb.range_size = OspfSR.srgb.size; + srn->srgb.lower_bound = OspfSR.srgb.start; + srn->srlb.lower_bound = OspfSR.srlb.start; + srn->srlb.range_size = OspfSR.srlb.end - OspfSR.srlb.start + 1; + srn->algo[0] = OspfSR.algo[0]; + srn->msd = OspfSR.msd; + OspfSR.self = srn; + } + + /* Then, start Label Manager if not ready */ + if (!ospf_zebra_label_manager_ready()) + if (ospf_zebra_label_manager_connect() < 0) { + /* Re-attempt to connect to Label Manager in 1 sec. */ + thread_add_timer(master, sr_start_label_manager, ospf, + 1, &OspfSR.t_start_lm); + osr_debug(" |- Failed to start the Label Manager"); + return -1; + } + + /* + * Request SRLB & SGRB to the label manager if not already reserved. + * If the allocation fails, return an error to disable SR until a new + * SRLB and/or SRGB are successfully allocated. + */ + sr_local_block_init(OspfSR.srlb.start, OspfSR.srlb.end); + if (!OspfSR.srgb.reserved) { + if (ospf_zebra_request_label_range(OspfSR.srgb.start, + OspfSR.srgb.size) + < 0) { + OspfSR.srgb.reserved = false; + return -1; + } else + OspfSR.srgb.reserved = true; + } + + /* SR is UP and ready to flood LSA */ + OspfSR.status = SR_UP; + + /* Set Router Information SR parameters */ + osr_debug("SR: Activate SR for Router Information LSA"); + + ospf_router_info_update_sr(true, OspfSR.self); + + /* Update Ext LSA */ + osr_debug("SR: Activate SR for Extended Link/Prefix LSA"); - /* Complete & Store self SR Node */ - srn->srgb.range_size = OspfSR.srgb.range_size; - srn->srgb.lower_bound = OspfSR.srgb.lower_bound; - srn->algo[0] = OspfSR.algo[0]; - srn->msd = OspfSR.msd; - OspfSR.self = srn; + ospf_ext_update_sr(true); osr_debug("SR (%s): Update SR-DB from LSDB", __func__); @@ -277,13 +518,33 @@ static void ospf_sr_stop(void) osr_debug("SR (%s): Stop Segment Routing", __func__); + /* Disable any re-attempt to connect to Label Manager */ + THREAD_TIMER_OFF(OspfSR.t_start_lm); + + /* Release SRGB & SRLB if active. */ + if (OspfSR.srgb.reserved) + ospf_zebra_release_label_range( + OspfSR.srgb.start, + OspfSR.srgb.start + OspfSR.srgb.size - 1); + sr_local_block_delete(); + + /* Revert SRGB, SRLB and MSD to default values */ + OspfSR.srgb.size = DEFAULT_SRGB_SIZE; + OspfSR.srgb.start = DEFAULT_SRGB_LABEL; + OspfSR.srgb.reserved = false; + + OspfSR.srlb.start = DEFAULT_SRLB_LABEL; + OspfSR.srlb.end = DEFAULT_SRLB_LABEL + DEFAULT_SRLB_SIZE - 1; + OspfSR.srlb.reserved = false; + OspfSR.msd = 0; + /* * Remove all SR Nodes from the Hash table. Prefix and Link SID will * be remove though list_delete() call. See sr_node_del() */ hash_clean(OspfSR.neighbors, (void *)sr_node_del); OspfSR.self = NULL; - OspfSR.enabled = false; + OspfSR.status = SR_OFF; } /* @@ -300,18 +561,23 @@ int ospf_sr_init(void) osr_debug("SR (%s): Initialize SR Data Base", __func__); memset(&OspfSR, 0, sizeof(struct ospf_sr_db)); - OspfSR.enabled = false; + OspfSR.status = SR_OFF; /* Only AREA flooding is supported in this release */ OspfSR.scope = OSPF_OPAQUE_AREA_LSA; - /* Initialize SRGB, Algorithms and MSD TLVs */ + /* Initialize Algorithms, SRGB, SRLB and MSD TLVs */ /* Only Algorithm SPF is supported */ OspfSR.algo[0] = SR_ALGORITHM_SPF; for (int i = 1; i < ALGORITHM_COUNT; i++) OspfSR.algo[i] = SR_ALGORITHM_UNSET; - OspfSR.srgb.range_size = MPLS_DEFAULT_MAX_SRGB_SIZE; - OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL; + OspfSR.srgb.size = DEFAULT_SRGB_SIZE; + OspfSR.srgb.start = DEFAULT_SRGB_LABEL; + OspfSR.srgb.reserved = false; + + OspfSR.srlb.start = DEFAULT_SRLB_LABEL; + OspfSR.srlb.end = DEFAULT_SRLB_LABEL + DEFAULT_SRLB_SIZE - 1; + OspfSR.srlb.reserved = false; OspfSR.msd = 0; /* Initialize Hash table for neighbor SR nodes */ @@ -370,14 +636,17 @@ void ospf_sr_finish(void) */ /* Compute label from index */ -static mpls_label_t index2label(uint32_t index, struct sr_srgb srgb) +static mpls_label_t index2label(uint32_t index, struct sr_block srgb) { mpls_label_t label; label = srgb.lower_bound + index; - if (label > (srgb.lower_bound + srgb.range_size)) + if (label > (srgb.lower_bound + srgb.range_size)) { + flog_warn(EC_OSPF_SR_SID_OVERFLOW, + "%s: SID index %u falls outside SRGB range", + __func__, index); return MPLS_INVALID_LABEL; - else + } else return label; } @@ -484,6 +753,45 @@ static int compute_link_nhlfe(struct sr_link *srl) return rc; } +/** + * Compute output label for the given Prefix-SID. + * + * @param srp Segment Routing Prefix + * @param srnext Segment Routing nexthop node + * + * @return MPLS label or MPLS_INVALID_LABEL in case of error + */ +static mpls_label_t sr_prefix_out_label(const struct sr_prefix *srp, + const struct sr_node *srnext) +{ + /* Check if the nexthop SR Node is the last hop? */ + if (srnext == srp->srn) { + /* SR-Node doesn't request NO-PHP. Return Implicit NULL label */ + if (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) + return MPLS_LABEL_IMPLICIT_NULL; + + /* SR-Node requests Explicit NULL Label */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + return MPLS_LABEL_IPV4_EXPLICIT_NULL; + /* Fallthrough */ + } + + /* Return SID value as MPLS label if it is an Absolute SID */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG + | EXT_SUBTLV_PREFIX_SID_LFLG)) { + /* + * V/L SIDs have local significance, so only adjacent routers + * can use them (RFC8665 section #5) + */ + if (srp->srn != srnext) + return MPLS_INVALID_LABEL; + return srp->sid; + } + + /* Return MPLS label as SRGB lower bound + SID index as per RFC 8665 */ + return (index2label(srp->sid, srnext->srgb)); +} + /* * Compute NHLFE entry for Extended Prefix * @@ -515,7 +823,7 @@ static int compute_prefix_nhlfe(struct sr_prefix *srp) return rc; /* Compute Input Label with self SRGB */ - srp->label_in = index2label(srp->sid, OspfSR.srgb); + srp->label_in = index2label(srp->sid, OspfSR.self->srgb); rc = 0; for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) { @@ -534,10 +842,7 @@ static int compute_prefix_nhlfe(struct sr_prefix *srp) /* And store this information for later update */ srnext->neighbor = OspfSR.self; - if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router)) - path->srni.nexthop = NULL; - else - path->srni.nexthop = srnext; + path->srni.nexthop = srnext; /* * SR Node could be known, but SRGB could be not initialize @@ -552,18 +857,8 @@ static int compute_prefix_nhlfe(struct sr_prefix *srp) srnext->srgb.range_size, srnext->srgb.lower_bound, &srnext->adv_router); - /* - * Compute Output Label with Nexthop SR Node SRGB or Implicit - * Null label if next hop is the destination and request PHP - */ - if ((path->srni.nexthop == NULL) - && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))) - path->srni.label_out = MPLS_LABEL_IMPLICIT_NULL; - else if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) - path->srni.label_out = srp->sid; - else - path->srni.label_out = - index2label(srp->sid, srnext->srgb); + /* Compute Output Label with Nexthop SR Node SRGB */ + path->srni.label_out = sr_prefix_out_label(srp, srnext); osr_debug(" |- Computed new labels in: %u out: %u", srp->label_in, path->srni.label_out); @@ -900,9 +1195,11 @@ static void update_in_nhlfe(struct hash_bucket *bucket, void *args) if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) continue; - /* OK. Compute new input label ... */ - srp->label_in = index2label(srp->sid, OspfSR.srgb); - /* ... and update MPLS LFIB */ + /* First, remove old MPLS table entries ... */ + ospf_zebra_delete_prefix_sid(srp); + /* ... then compute new input label ... */ + srp->label_in = index2label(srp->sid, OspfSR.self->srgb); + /* ... and install new MPLS LFIB */ ospf_zebra_update_prefix_sid(srp); } } @@ -926,7 +1223,7 @@ static void update_out_nhlfe(struct hash_bucket *bucket, void *args) for (ALL_LIST_ELEMENTS_RO(srp->route->paths, pnode, path)) { /* Process only SID Index for next hop without PHP */ - if ((path->srni.nexthop == NULL) + if ((path->srni.nexthop == srp->srn) && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))) continue; @@ -951,8 +1248,9 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) struct tlv_header *tlvh; struct lsa_header *lsah = lsa->data; struct ri_sr_tlv_sid_label_range *ri_srgb = NULL; + struct ri_sr_tlv_sid_label_range *ri_srlb = NULL; struct ri_sr_tlv_sr_algorithm *algo = NULL; - struct sr_srgb srgb; + struct sr_block srgb; uint16_t length = 0, sum = 0; uint8_t msd = 0; @@ -988,10 +1286,14 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) algo = (struct ri_sr_tlv_sr_algorithm *)tlvh; sum += TLV_SIZE(tlvh); break; - case RI_SR_TLV_SID_LABEL_RANGE: + case RI_SR_TLV_SRGB_LABEL_RANGE: ri_srgb = (struct ri_sr_tlv_sid_label_range *)tlvh; sum += TLV_SIZE(tlvh); break; + case RI_SR_TLV_SRLB_LABEL_RANGE: + ri_srlb = (struct ri_sr_tlv_sid_label_range *)tlvh; + sum += TLV_SIZE(tlvh); + break; case RI_SR_TLV_NODE_MSD: msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value; sum += TLV_SIZE(tlvh); @@ -1051,29 +1353,38 @@ void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) /* Copy SRGB */ srn->srgb.range_size = srgb.range_size; srn->srgb.lower_bound = srgb.lower_bound; - /* Set Algorithm */ - if (algo != NULL) { - int i; - for (i = 0; i < ntohs(algo->header.length); i++) - srn->algo[i] = algo->value[0]; - for (; i < ALGORITHM_COUNT; i++) - srn->algo[i] = SR_ALGORITHM_UNSET; - } else { - srn->algo[0] = SR_ALGORITHM_SPF; - } - /* set MSD */ - srn->msd = msd; - return; } + /* Update Algorithm, SRLB and MSD if present */ + if (algo != NULL) { + int i; + for (i = 0; i < ntohs(algo->header.length); i++) + srn->algo[i] = algo->value[0]; + for (; i < ALGORITHM_COUNT; i++) + srn->algo[i] = SR_ALGORITHM_UNSET; + } else { + srn->algo[0] = SR_ALGORITHM_SPF; + } + srn->msd = msd; + if (ri_srlb != NULL) { + srn->srlb.range_size = GET_RANGE_SIZE(ntohl(ri_srlb->size)); + srn->srlb.lower_bound = GET_LABEL(ntohl(ri_srlb->lower.value)); + } + + osr_debug(" |- Update SR-Node[%pI4], SRGB[%u/%u], SRLB[%u/%u], Algo[%u], MSD[%u]", + &srn->adv_router, srn->srgb.lower_bound, srn->srgb.range_size, + srn->srlb.lower_bound, srn->srlb.range_size, srn->algo[0], + srn->msd); + /* Check if SRGB has changed */ if ((srn->srgb.range_size == srgb.range_size) && (srn->srgb.lower_bound == srgb.lower_bound)) return; - /* Update SRGB ... */ + /* Copy SRGB */ srn->srgb.range_size = srgb.range_size; srn->srgb.lower_bound = srgb.lower_bound; + /* ... and NHLFE if it is a neighbor SR node */ if (srn->neighbor == OspfSR.self) hash_iterate(OspfSR.neighbors, update_out_nhlfe, srn); @@ -1460,18 +1771,6 @@ void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa) } } -/* Get Label for Extended Link SID */ -/* TODO: To be replace by Zebra Label Manager */ -uint32_t get_ext_link_label_value(void) -{ - static uint32_t label = ADJ_SID_MIN - 1; - - if (label < ADJ_SID_MAX) - label += 1; - - return label; -} - /* * Update Prefix SID. Call by ospf_ext_pref_ism_change to * complete initial CLI command at startup. @@ -1514,9 +1813,10 @@ void ospf_sr_update_local_prefix(struct interface *ifp, struct prefix *p) " |- Update Node SID %pFX - %u for self SR Node", (struct prefix *)&srp->prefv4, srp->sid); - /* Install NHLFE if NO-PHP is requested */ - if (CHECK_FLAG(srp->flags, - EXT_SUBTLV_PREFIX_SID_NPFLG)) { + /* Install SID if NO-PHP is set and not EXPLICIT-NULL */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(srp->flags, + EXT_SUBTLV_PREFIX_SID_EFLG)) { srp->label_in = index2label(srp->sid, OspfSR.self->srgb); srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; @@ -1618,17 +1918,23 @@ void ospf_sr_config_write_router(struct vty *vty) { struct listnode *node; struct sr_prefix *srp; + uint32_t upper; - if (OspfSR.enabled) { + if (OspfSR.status == SR_UP) { vty_out(vty, " segment-routing on\n"); - if ((OspfSR.srgb.lower_bound != MPLS_DEFAULT_MIN_SRGB_LABEL) - || (OspfSR.srgb.range_size != MPLS_DEFAULT_MAX_SRGB_SIZE)) { + upper = OspfSR.srgb.start + OspfSR.srgb.size - 1; + if ((OspfSR.srgb.start != DEFAULT_SRGB_LABEL) + || (OspfSR.srgb.size != DEFAULT_SRGB_SIZE)) vty_out(vty, " segment-routing global-block %u %u\n", - OspfSR.srgb.lower_bound, - OspfSR.srgb.lower_bound + OspfSR.srgb.range_size - - 1); - } + OspfSR.srgb.start, upper); + + upper = DEFAULT_SRLB_LABEL + DEFAULT_SRLB_SIZE - 1; + if ((OspfSR.srlb.start != DEFAULT_SRLB_LABEL) + || (OspfSR.srlb.end != upper)) + vty_out(vty, " segment-routing local-block %u %u\n", + OspfSR.srlb.start, OspfSR.srlb.end); + if (OspfSR.msd != 0) vty_out(vty, " segment-routing node-msd %u\n", OspfSR.msd); @@ -1637,13 +1943,19 @@ void ospf_sr_config_write_router(struct vty *vty) for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { vty_out(vty, - " segment-routing prefix %s/%u index %u%s\n", + " segment-routing prefix %s/%u " + "index %u", inet_ntoa(srp->prefv4.prefix), - srp->prefv4.prefixlen, srp->sid, - CHECK_FLAG(srp->flags, - EXT_SUBTLV_PREFIX_SID_NPFLG) - ? " no-php-flag" - : ""); + srp->prefv4.prefixlen, srp->sid); + if (CHECK_FLAG(srp->flags, + EXT_SUBTLV_PREFIX_SID_EFLG)) + vty_out(vty, " explicit-null\n"); + else if (CHECK_FLAG( + srp->flags, + EXT_SUBTLV_PREFIX_SID_NPFLG)) + vty_out(vty, " no-php-flag\n"); + else + vty_out(vty, "\n"); } } } @@ -1658,7 +1970,7 @@ DEFUN(ospf_sr_enable, VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - if (OspfSR.enabled) + if (OspfSR.status != SR_OFF) return CMD_SUCCESS; if (ospf->vrf_id != VRF_DEFAULT) { @@ -1670,19 +1982,9 @@ DEFUN(ospf_sr_enable, osr_debug("SR: Segment Routing: OFF -> ON"); /* Start Segment Routing */ - OspfSR.enabled = true; + OspfSR.status = SR_ON; ospf_sr_start(ospf); - /* Set Router Information SR parameters */ - osr_debug("SR: Activate SR for Router Information LSA"); - - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); - - /* Update Ext LSA */ - osr_debug("SR: Activate SR for Extended Link/Prefix LSA"); - - ospf_ext_update_sr(true); - return CMD_SUCCESS; } @@ -1694,7 +1996,7 @@ DEFUN (no_ospf_sr_enable, "Disable Segment Routing\n") { - if (!OspfSR.enabled) + if (OspfSR.status == SR_OFF) return CMD_SUCCESS; osr_debug("SR: Segment Routing: ON -> OFF"); @@ -1703,7 +2005,7 @@ DEFUN (no_ospf_sr_enable, ospf_ext_update_sr(false); /* then, disable Router Information SR parameters */ - ospf_router_info_update_sr(false, OspfSR.srgb, OspfSR.msd); + ospf_router_info_update_sr(false, OspfSR.self); /* Finally, stop Segment Routing */ ospf_sr_stop(); @@ -1713,7 +2015,7 @@ DEFUN (no_ospf_sr_enable, static int ospf_sr_enabled(struct vty *vty) { - if (OspfSR.enabled) + if (OspfSR.status != SR_OFF) return 1; if (vty) @@ -1722,13 +2024,78 @@ static int ospf_sr_enabled(struct vty *vty) return 0; } -DEFUN (sr_sid_label_range, - sr_sid_label_range_cmd, - "segment-routing global-block (0-1048575) (0-1048575)", +/** + * Update SRGB following new CLI value. + * + * @param lower Lower bound of the SRGB + * @param size Size of the SRGB + * + * @return 0 on success, -1 otherwise + */ +static int update_srgb(uint32_t lower, uint32_t size) +{ + + /* Check if values have changed */ + if ((OspfSR.srgb.size == size) && (OspfSR.srgb.start == lower)) + return 0; + + /* Release old SRGB if active. */ + if (OspfSR.srgb.reserved) { + ospf_zebra_release_label_range( + OspfSR.srgb.start, + OspfSR.srgb.start + OspfSR.srgb.size - 1); + OspfSR.srgb.reserved = false; + } + + /* Set new SRGB values */ + OspfSR.srgb.size = size; + OspfSR.srgb.start = lower; + if (OspfSR.self != NULL) { + OspfSR.self->srgb.range_size = size; + OspfSR.self->srgb.lower_bound = lower; + } + + /* Check if SR is correctly started i.e. Label Manager connected */ + if (OspfSR.status != SR_UP) + return 0; + + /* + * Try to reserve the new block from the Label Manger. If the allocation + * fails, disable SR until a new SRGB is successfully allocated. + */ + if (ospf_zebra_request_label_range(OspfSR.srgb.start, + OspfSR.srgb.size) < 0) { + OspfSR.srgb.reserved = false; + ospf_sr_stop(); + return -1; + } else + OspfSR.srgb.reserved = true; + + osr_debug("SR(%s): Got new SRGB [%u/%u]", __func__, OspfSR.srgb.start, + OspfSR.srgb.start + OspfSR.srgb.size - 1); + + /* Update Self SR-Node */ + if (OspfSR.self != NULL) { + /* SRGB is reserved, set Router Information parameters */ + ospf_router_info_update_sr(true, OspfSR.self); + + /* and update NHLFE entries */ + hash_iterate( + OspfSR.neighbors, + (void (*)(struct hash_bucket *, void *))update_in_nhlfe, + NULL); + } + + return 0; +} + +DEFUN (sr_global_label_range, + sr_global_label_range_cmd, + "segment-routing global-block (16-1048575) (16-1048575)", SR_STR "Segment Routing Global Block label range\n" - "Lower-bound range in decimal (0-1048575)\n" - "Upper-bound range in decimal (0-1048575)\n") + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") { uint32_t upper; uint32_t lower; @@ -1744,77 +2111,156 @@ DEFUN (sr_sid_label_range, upper = strtoul(argv[idx_up]->arg, NULL, 10); size = upper - lower + 1; - if (size > MPLS_DEFAULT_MAX_SRGB_SIZE || size <= 0) { + /* Validate SRGB against SRLB */ + if (!((upper < OspfSR.srlb.start) || (lower > OspfSR.srlb.end))) { vty_out(vty, - "Range size cannot be less than 0 or more than %u\n", - MPLS_DEFAULT_MAX_SRGB_SIZE); + "New SR Global Block (%u/%u) conflict with Local Block (%u/%u)\n", + lower, upper, OspfSR.srlb.end, OspfSR.srlb.start); return CMD_WARNING_CONFIG_FAILED; } - if (upper > MPLS_DEFAULT_MAX_SRGB_LABEL) { - vty_out(vty, "Upper-bound cannot exceed %u\n", - MPLS_DEFAULT_MAX_SRGB_LABEL); + if (update_srgb(lower, size) < 0) + return CMD_WARNING_CONFIG_FAILED; + else + return CMD_SUCCESS; +} + +DEFUN (no_sr_global_label_range, + no_sr_global_label_range_cmd, + "no segment-routing global-block [(16-1048575) (16-1048575)]", + NO_STR + SR_STR + "Segment Routing Global Block label range\n" + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") +{ + + if (!ospf_sr_enabled(vty)) return CMD_WARNING_CONFIG_FAILED; - } - if (upper < MPLS_DEFAULT_MIN_SRGB_LABEL) { - vty_out(vty, "Upper-bound cannot be lower than %u\n", - MPLS_DEFAULT_MIN_SRGB_LABEL); + /* Validate SRGB against SRLB */ + uint32_t upper = DEFAULT_SRGB_LABEL + DEFAULT_SRGB_SIZE - 1; + if (!((upper < OspfSR.srlb.start) + || (DEFAULT_SRGB_LABEL > OspfSR.srlb.end))) { + vty_out(vty, + "New SR Global Block (%u/%u) conflict with Local Block (%u/%u)\n", + DEFAULT_SRGB_LABEL, upper, OspfSR.srlb.end, + OspfSR.srlb.start); return CMD_WARNING_CONFIG_FAILED; } + if (update_srgb(DEFAULT_SRGB_LABEL, DEFAULT_SRGB_SIZE) < 0) + return CMD_WARNING_CONFIG_FAILED; + else + return CMD_SUCCESS; +} + +DEFUN (sr_local_label_range, + sr_local_label_range_cmd, + "segment-routing local-block (16-1048575) (16-1048575)", + SR_STR + "Segment Routing Local Block label range\n" + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") +{ + uint32_t upper; + uint32_t lower; + uint32_t srgb_upper; + int idx_low = 2; + int idx_up = 3; + + if (!ospf_sr_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + /* Get lower and upper bound */ + lower = strtoul(argv[idx_low]->arg, NULL, 10); + upper = strtoul(argv[idx_up]->arg, NULL, 10); + /* Check if values have changed */ - if ((OspfSR.srgb.range_size == size) - && (OspfSR.srgb.lower_bound == lower)) + if ((OspfSR.srlb.start == lower) + && (OspfSR.srlb.end == upper)) return CMD_SUCCESS; - /* Set SID/Label range SRGB */ - OspfSR.srgb.range_size = size; - OspfSR.srgb.lower_bound = lower; - if (OspfSR.self != NULL) { - OspfSR.self->srgb.range_size = size; - OspfSR.self->srgb.lower_bound = lower; + /* Validate SRLB against SRGB */ + srgb_upper = OspfSR.srgb.start + OspfSR.srgb.size - 1; + if (!((upper < OspfSR.srgb.start) || (lower > srgb_upper))) { + vty_out(vty, + "New SR Local Block (%u/%u) conflict with Global Block (%u/%u)\n", + lower, upper, OspfSR.srgb.start, srgb_upper); + return CMD_WARNING_CONFIG_FAILED; } - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); + /* Remove old SRLB */ + sr_local_block_delete(); - /* Update NHLFE entries */ - hash_iterate(OspfSR.neighbors, - (void (*)(struct hash_bucket *, void *))update_in_nhlfe, - NULL); + /* Try to reserve the new block from the Label Manger. If the allocation + * fails, disable SR until a new SRLB is successfully allocated. + */ + if (sr_local_block_init(lower, upper) != 0) { + ospf_sr_stop(); + return CMD_WARNING_CONFIG_FAILED; + } + + /* SRLB is reserved, Update Self SR-Node and Router Information LSA */ + if (OspfSR.self != NULL) { + OspfSR.self->srlb.lower_bound = lower; + OspfSR.self->srlb.range_size = upper - lower + 1; + ospf_router_info_update_sr(true, OspfSR.self); + } + + /* and update (LAN)-Adjacency SID */ + ospf_ext_link_srlb_update(); return CMD_SUCCESS; } -DEFUN (no_sr_sid_label_range, - no_sr_sid_label_range_cmd, - "no segment-routing global-block [(0-1048575) (0-1048575)]", +DEFUN (no_sr_local_label_range, + no_sr_local_label_range_cmd, + "no segment-routing local-block [(16-1048575) (16-1048575)]", NO_STR SR_STR - "Segment Routing Global Block label range\n" - "Lower-bound range in decimal (0-1048575)\n" - "Upper-bound range in decimal (0-1048575)\n") + "Segment Routing Local Block label range\n" + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") { + uint32_t upper; + uint32_t srgb_end; + if (!ospf_sr_enabled(vty)) return CMD_WARNING_CONFIG_FAILED; - /* Revert to default SRGB value */ - OspfSR.srgb.range_size = MPLS_DEFAULT_MIN_SRGB_SIZE; - OspfSR.srgb.lower_bound = MPLS_DEFAULT_MIN_SRGB_LABEL; - if (OspfSR.self != NULL) { - OspfSR.self->srgb.range_size = OspfSR.srgb.range_size; - OspfSR.self->srgb.lower_bound = OspfSR.srgb.lower_bound; + /* First, remove old SRLB */ + sr_local_block_delete(); + + /* Validate SRLB against SRGB */ + srgb_end = OspfSR.srgb.start + OspfSR.srgb.size - 1; + upper = DEFAULT_SRLB_LABEL + DEFAULT_SRLB_SIZE - 1; + if (!((upper < OspfSR.srgb.start) || (DEFAULT_SRLB_LABEL > srgb_end))) { + vty_out(vty, + "New SR Local Block (%u/%u) conflict with Global Block (%u/%u)\n", + DEFAULT_SRLB_LABEL, upper, OspfSR.srgb.start, srgb_end); + return CMD_WARNING_CONFIG_FAILED; } - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); + /* Then, initialize SRLB with default value and try to reserve the new + * block from the Label Manger. If the allocation fails, disable SR + * until a new SRLB is successfully allocated. + */ + if (sr_local_block_init(DEFAULT_SRLB_LABEL, upper) != 0) { + ospf_sr_stop(); + return CMD_WARNING_CONFIG_FAILED; + } - /* Update NHLFE entries */ - hash_iterate(OspfSR.neighbors, - (void (*)(struct hash_bucket *, void *))update_in_nhlfe, - NULL); + /* SRLB is reserved, Update Self SR-Node and Router Information LSA */ + if (OspfSR.self != NULL) { + OspfSR.self->srlb.lower_bound = DEFAULT_SRLB_LABEL; + OspfSR.self->srlb.range_size = DEFAULT_SRLB_SIZE; + ospf_router_info_update_sr(true, OspfSR.self); + } + + /* and update (LAN)-Adjacency SID */ + ospf_ext_link_srlb_update(); return CMD_SUCCESS; } @@ -1847,11 +2293,13 @@ DEFUN (sr_node_msd, /* Set this router MSD */ OspfSR.msd = msd; - if (OspfSR.self != NULL) + if (OspfSR.self != NULL) { OspfSR.self->msd = msd; - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, OspfSR.msd); + /* Set Router Information parameters if SR is UP */ + if (OspfSR.status == SR_UP) + ospf_router_info_update_sr(true, OspfSR.self); + } return CMD_SUCCESS; } @@ -1870,30 +2318,33 @@ DEFUN (no_sr_node_msd, /* unset this router MSD */ OspfSR.msd = 0; - if (OspfSR.self != NULL) + if (OspfSR.self != NULL) { OspfSR.self->msd = 0; - /* Set Router Information SR parameters */ - ospf_router_info_update_sr(true, OspfSR.srgb, 0); + /* Set Router Information parameters if SR is UP */ + if (OspfSR.status == SR_UP) + ospf_router_info_update_sr(true, OspfSR.self); + } return CMD_SUCCESS; } DEFUN (sr_prefix_sid, sr_prefix_sid_cmd, - "segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag]", + "segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag|explicit-null]", SR_STR "Prefix SID\n" "IPv4 Prefix as A.B.C.D/M\n" "SID index for this prefix in decimal (0-65535)\n" "Index value inside SRGB (lower_bound < index < upper_bound)\n" - "Don't request Penultimate Hop Popping (PHP)\n") + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n") { int idx = 0; struct prefix p; uint32_t index; struct listnode *node; - struct sr_prefix *srp, *new; + struct sr_prefix *srp, *new = NULL; struct interface *ifp; if (!ospf_sr_enabled(vty)) @@ -1909,33 +2360,48 @@ DEFUN (sr_prefix_sid, /* Get & verify index value */ argv_find(argv, argc, "(0-65535)", &idx); index = strtoul(argv[idx]->arg, NULL, 10); - if (index > OspfSR.srgb.range_size - 1) { + if (index > OspfSR.srgb.size - 1) { vty_out(vty, "Index %u must be lower than range size %u\n", - index, OspfSR.srgb.range_size); + index, OspfSR.srgb.size); return CMD_WARNING_CONFIG_FAILED; } - /* check that the index is not already used */ + /* Search for an existing Prefix-SID */ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { if (srp->sid == index) { - vty_out(vty, "Index %u is already used\n", index); - return CMD_WARNING_CONFIG_FAILED; + if (prefix_same((struct prefix *)&srp->prefv4, &p)) { + new = srp; + break; + } else { + vty_out(vty, "Index %u is already used\n", + index); + return CMD_WARNING_CONFIG_FAILED; + } } } /* Create new Extended Prefix to SRDB if not found */ - new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); - IPV4_ADDR_COPY(&new->prefv4.prefix, &p.u.prefix4); - new->prefv4.prefixlen = p.prefixlen; - new->prefv4.family = p.family; - new->sid = index; - new->type = LOCAL_SID; + if (new == NULL) { + new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); + IPV4_ADDR_COPY(&new->prefv4.prefix, &p.u.prefix4); + new->prefv4.prefixlen = p.prefixlen; + new->prefv4.family = p.family; + new->sid = index; + new->type = LOCAL_SID; + } + /* Set NO PHP flag if present and compute NHLFE */ if (argv_find(argv, argc, "no-php-flag", &idx)) { SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG); + UNSET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_EFLG); new->label_in = index2label(new->sid, OspfSR.self->srgb); new->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; } + /* Set EXPLICIT NULL flag is present */ + if (argv_find(argv, argc, "explicit-null", &idx)) { + SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG); + SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_EFLG); + } osr_debug("SR (%s): Add new index %u to Prefix %pFX", __func__, index, (struct prefix *)&new->prefv4); @@ -1963,25 +2429,19 @@ DEFUN (sr_prefix_sid, } new->nhlfe.ifindex = ifp->ifindex; - /* Search if this prefix already exist */ - for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { - if ((IPV4_ADDR_SAME(&srp->prefv4.prefix, &p.u.prefix4) - && srp->prefv4.prefixlen == p.prefixlen)) - break; - else - srp = NULL; - } - - /* Update or Add this new SR Prefix */ - if (srp) { - listnode_delete(OspfSR.self->ext_prefix, srp); - listnode_add(OspfSR.self->ext_prefix, new); - } else { + /* Add this new SR Prefix if not already found */ + if (srp != new) listnode_add(OspfSR.self->ext_prefix, new); - } - ospf_zebra_update_prefix_sid(new); - /* Finally, update Extended Prefix LSA */ + /* Install Prefix SID if SR is UP and a valid input label set */ + if (OspfSR.status == SR_UP) { + if (CHECK_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + ospf_zebra_update_prefix_sid(new); + } else + return CMD_SUCCESS; + + /* Finally, update Extended Prefix LSA id SR is UP */ new->instance = ospf_ext_schedule_prefix_index( ifp, new->sid, &new->prefv4, new->flags); if (new->instance == 0) { @@ -1995,14 +2455,15 @@ DEFUN (sr_prefix_sid, DEFUN (no_sr_prefix_sid, no_sr_prefix_sid_cmd, - "no segment-routing prefix A.B.C.D/M [index (0-65535) no-php-flag]", + "no segment-routing prefix A.B.C.D/M [index (0-65535)|no-php-flag|explicit-null]", NO_STR SR_STR "Prefix SID\n" "IPv4 Prefix as A.B.C.D/M\n" "SID index for this prefix in decimal (0-65535)\n" "Index value inside SRGB (lower_bound < index < upper_bound)\n" - "Don't request Penultimate Hop Popping (PHP)\n") + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n") { int idx = 0; struct prefix p; @@ -2015,6 +2476,9 @@ DEFUN (no_sr_prefix_sid, if (!ospf_sr_enabled(vty)) return CMD_WARNING_CONFIG_FAILED; + if (OspfSR.status != SR_UP) + return CMD_SUCCESS; + /* Get network prefix */ argv_find(argv, argc, "A.B.C.D/M", &idx); rc = str2prefix(argv[idx]->arg, &p); @@ -2054,8 +2518,9 @@ DEFUN (no_sr_prefix_sid, osr_debug("SR (%s): Remove Prefix %pFX with index %u", __func__, (struct prefix *)&srp->prefv4, srp->sid); - /* Delete NHLFE if NO-PHP is set */ - if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) + /* Delete NHLFE if NO-PHP is set and EXPLICIT NULL not set */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) ospf_zebra_delete_prefix_sid(srp); /* OK, all is clean, remove SRP from SRDB */ @@ -2077,7 +2542,10 @@ static char *sr_op2str(char *buf, size_t size, mpls_label_t label_in, snprintf(buf, size, "Pop(%u)", label_in); break; case MPLS_LABEL_IPV4_EXPLICIT_NULL: - snprintf(buf, size, "Swap(%u, null)", label_in); + if (label_in == MPLS_LABEL_IPV4_EXPLICIT_NULL) + snprintf(buf, size, "no-op."); + else + snprintf(buf, size, "Swap(%u, null)", label_in); break; case MPLS_INVALID_LABEL: snprintf(buf, size, "no-op."); @@ -2188,6 +2656,7 @@ static void show_sr_node(struct vty *vty, struct json_object *json, char pref[19]; char sid[22]; char op[32]; + uint32_t upper; json_object *json_node = NULL, *json_algo, *json_obj; json_object *json_prefix = NULL, *json_link = NULL; @@ -2205,6 +2674,10 @@ static void show_sr_node(struct vty *vty, struct json_object *json, srn->srgb.range_size); json_object_int_add(json_node, "srgbLabel", srn->srgb.lower_bound); + json_object_int_add(json_node, "srlbSize", + srn->srlb.range_size); + json_object_int_add(json_node, "srlbLabel", + srn->srlb.lower_bound); json_algo = json_object_new_array(); json_object_object_add(json_node, "algorithms", json_algo); for (int i = 0; i < ALGORITHM_COUNT; i++) { @@ -2224,9 +2697,13 @@ static void show_sr_node(struct vty *vty, struct json_object *json, json_object_int_add(json_node, "nodeMsd", srn->msd); } else { sbuf_push(&sbuf, 0, "SR-Node: %s", inet_ntoa(srn->adv_router)); - sbuf_push(&sbuf, 0, "\tSRGB (Size/Label): %u/%u", - srn->srgb.range_size, srn->srgb.lower_bound); - sbuf_push(&sbuf, 0, "\tAlgorithm(s): %s", + upper = srn->srgb.lower_bound + srn->srgb.range_size - 1; + sbuf_push(&sbuf, 0, "\tSRGB: [%u/%u]", + srn->srgb.lower_bound, upper); + upper = srn->srlb.lower_bound + srn->srlb.range_size - 1; + sbuf_push(&sbuf, 0, "\tSRLB: [%u/%u]", + srn->srlb.lower_bound, upper); + sbuf_push(&sbuf, 0, "\tAlgo.(s): %s", srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF"); for (int i = 1; i < ALGORITHM_COUNT; i++) { if (srn->algo[i] == SR_ALGORITHM_UNSET) @@ -2359,7 +2836,7 @@ DEFUN (show_ip_opsf_srdb, bool uj = use_json(argc, argv); json_object *json = NULL, *json_node_array = NULL; - if (!OspfSR.enabled) { + if (OspfSR.status == SR_OFF) { vty_out(vty, "Segment Routing is disabled on this router\n"); return CMD_WARNING; } @@ -2430,8 +2907,10 @@ void ospf_sr_register_vty(void) install_element(OSPF_NODE, &ospf_sr_enable_cmd); install_element(OSPF_NODE, &no_ospf_sr_enable_cmd); - install_element(OSPF_NODE, &sr_sid_label_range_cmd); - install_element(OSPF_NODE, &no_sr_sid_label_range_cmd); + install_element(OSPF_NODE, &sr_global_label_range_cmd); + install_element(OSPF_NODE, &no_sr_global_label_range_cmd); + install_element(OSPF_NODE, &sr_local_label_range_cmd); + install_element(OSPF_NODE, &no_sr_local_label_range_cmd); install_element(OSPF_NODE, &sr_node_msd_cmd); install_element(OSPF_NODE, &no_sr_node_msd_cmd); install_element(OSPF_NODE, &sr_prefix_sid_cmd); diff --git a/ospfd/ospf_sr.h b/ospfd/ospf_sr.h index 3621ea53de..c54d2dcc3c 100644 --- a/ospfd/ospf_sr.h +++ b/ospfd/ospf_sr.h @@ -1,13 +1,14 @@ /* * This is an implementation of Segment Routing - * as per draft draft-ietf-ospf-segment-routing-extensions-24 + * as per RFC 8665 - OSPF Extensions for Segment Routing + * and RFC 8476 - Signaling Maximum SID Depth (MSD) Using OSPF * * Module name: Segment Routing header definitions * * Author: Olivier Dugeon <olivier.dugeon@orange.com> * Author: Anselme Sawadogo <anselmesawadogo@gmail.com> * - * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com + * Copyright (C) 2016 - 2020 Orange Labs http://www.orange.com * * 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 @@ -37,13 +38,9 @@ #define SET_LABEL(label) ((label << 8) & SET_LABEL_MASK) #define GET_LABEL(label) ((label >> 8) & GET_LABEL_MASK) -/* Label range for Adj-SID attribution purpose. Start just right after SRGB */ -#define ADJ_SID_MIN MPLS_DEFAULT_MAX_SRGB_LABEL -#define ADJ_SID_MAX (MPLS_DEFAULT_MAX_SRGB_LABEL + 1000) - #define OSPF_SR_DEFAULT_METRIC 1 -/* Segment Routing TLVs as per draft-ietf-ospf-segment-routing-extensions-19 */ +/* Segment Routing TLVs as per RFC 8665 */ /* Segment ID could be a Label (3 bytes) or an Index (4 bytes) */ #define SID_LABEL 3 @@ -88,8 +85,9 @@ struct ri_sr_tlv_sr_algorithm { uint8_t value[ALGORITHM_COUNT]; }; -/* RI SID/Label Range TLV - section 3.2 */ -#define RI_SR_TLV_SID_LABEL_RANGE 9 +/* RI SID/Label Range TLV used for SRGB & SRLB - section 3.2 & 3.3 */ +#define RI_SR_TLV_SRGB_LABEL_RANGE 9 +#define RI_SR_TLV_SRLB_LABEL_RANGE 14 struct ri_sr_tlv_sid_label_range { struct tlv_header header; /* Only 24 upper most bits are significant */ @@ -99,7 +97,7 @@ struct ri_sr_tlv_sid_label_range { struct subtlv_sid_label lower; }; -/* RI Node/MSD TLV as per draft-ietf-ospf-segment-routing-msd-05 */ +/* RI Node/MSD TLV as per RFC 8476 */ #define RI_SR_TLV_NODE_MSD 12 struct ri_sr_tlv_node_msd { struct tlv_header header; @@ -183,20 +181,48 @@ struct ext_subtlv_lan_adj_sid { * Following section define structure used to manage Segment Routing * information and TLVs / SubTLVs */ +/* Default min and size of SR Global Block label range */ +#define DEFAULT_SRGB_LABEL 16000 +#define DEFAULT_SRGB_SIZE 8000 + +/* Default min and size of SR Local Block label range */ +#define DEFAULT_SRLB_LABEL 15000 +#define DEFAULT_SRLB_SIZE 1000 -/* Structure aggregating SRGB info retrieved from an lsa */ -struct sr_srgb { +/* Structure aggregating SR Range Block info retrieved from an lsa */ +struct sr_block { uint32_t range_size; uint32_t lower_bound; }; +/* Segment Routing Global Block allocation */ +struct sr_global_block { + bool reserved; + uint32_t start; + uint32_t size; +}; + +/* Segment Routing Local Block allocation */ +struct sr_local_block { + bool reserved; + uint32_t start; + uint32_t end; + uint32_t current; + uint32_t max_block; + uint64_t *used_mark; +}; +#define SRLB_BLOCK_SIZE 64 + /* SID type to make difference between loopback interfaces and others */ enum sid_type { PREF_SID, LOCAL_SID, ADJ_SID, LAN_ADJ_SID }; +/* Status of Segment Routing: Off (Disable), On (Enable), (Up) Started */ +enum sr_status { SR_OFF, SR_ON, SR_UP, SR_DOWN }; + /* Structure aggregating all OSPF Segment Routing information for the node */ struct ospf_sr_db { - /* Status of Segment Routing: enable or disable */ - bool enabled; + /* Status of Segment Routing */ + enum sr_status status; /* Flooding Scope: Area = 10 or AS = 11 */ uint8_t scope; @@ -218,9 +244,16 @@ struct ospf_sr_db { * Segment Routing Global Block i.e. label range * Only one range supported in this code */ - struct sr_srgb srgb; + struct sr_global_block srgb; + + /* Segment Routing Local Block */ + struct sr_local_block srlb; + /* Maximum SID Depth supported by the node */ uint8_t msd; + + /* Thread timer to start Label Manager */ + struct thread *t_start_lm; }; /* Structure aggregating all received SR info from LSAs by node */ @@ -230,9 +263,9 @@ struct sr_node { uint32_t instance; uint8_t algo[ALGORITHM_COUNT]; /* Algorithms supported by the node */ - /* Segment Routing Global Block i.e. label range */ - struct sr_srgb srgb; - uint8_t msd; /* Maximum SID Depth */ + struct sr_block srgb; /* Segment Routing Global Block */ + struct sr_block srlb; /* Segment Routing Local Block */ + uint8_t msd; /* Maximum SID Depth */ /* List of Prefix & Link advertise by this node */ struct list *ext_prefix; /* For Node SID */ @@ -242,7 +275,6 @@ struct sr_node { struct sr_node *neighbor; }; - /* Segment Routing - NHLFE info: support IPv4 Only */ struct sr_nhlfe { struct in_addr nexthop; @@ -309,6 +341,9 @@ struct sr_prefix { extern int ospf_sr_init(void); extern void ospf_sr_term(void); extern void ospf_sr_finish(void); +/* Segment Routing label allocation functions */ +extern mpls_label_t ospf_sr_local_block_request_label(void); +extern int ospf_sr_local_block_release_label(mpls_label_t label); /* Segment Routing LSA update & delete functions */ extern void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa); extern void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa); @@ -321,7 +356,6 @@ struct ext_itf; extern void ospf_sr_ext_itf_add(struct ext_itf *exti); extern void ospf_sr_ext_itf_delete(struct ext_itf *exti); /* Segment Routing configuration functions */ -extern uint32_t get_ext_link_label_value(void); extern void ospf_sr_config_write_router(struct vty *vty); extern void ospf_sr_update_local_prefix(struct interface *ifp, struct prefix *p); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index e8cc50c8d0..8be7748c87 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -8925,6 +8925,31 @@ DEFUN (no_ospf_max_metric_router_lsa_shutdown, return CMD_SUCCESS; } +DEFUN (ospf_proactive_arp, + ospf_proactive_arp_cmd, + "proactive-arp", + "Allow sending ARP requests proactively\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->proactive_arp = true; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_proactive_arp, + no_ospf_proactive_arp_cmd, + "no proactive-arp", + NO_STR + "Disallow sending ARP requests proactively\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->proactive_arp = false; + + return CMD_SUCCESS; +} + static void config_write_stub_router(struct vty *vty, struct ospf *ospf) { struct listnode *ln; @@ -10415,6 +10440,14 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) if (ospf->passive_interface_default == OSPF_IF_PASSIVE) vty_out(vty, " passive-interface default\n"); + /* proactive-arp print. */ + if (ospf->proactive_arp != OSPF_PROACTIVE_ARP_DEFAULT) { + if (ospf->proactive_arp) + vty_out(vty, " proactive-arp\n"); + else + vty_out(vty, " no proactive-arp\n"); + } + FOR_ALL_INTERFACES (vrf, ifp) if (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), passive_interface) @@ -10871,6 +10904,10 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &no_ospf_write_multiplier_cmd); install_element(OSPF_NODE, &no_write_multiplier_cmd); + /* "proactive-arp" commands. */ + install_element(OSPF_NODE, &ospf_proactive_arp_cmd); + install_element(OSPF_NODE, &no_ospf_proactive_arp_cmd); + /* Init interface related vty commands. */ ospf_vty_if_init(); diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 84bdb9ec5b..8cf8430247 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -59,6 +59,8 @@ DEFINE_MTYPE_STATIC(OSPFD, OSPF_DIST_ARGS, "OSPF Distribute arguments") /* Zebra structure to hold current status. */ struct zclient *zclient = NULL; +/* and for the Synchronous connection to the Label Manager */ +static struct zclient *zclient_sync; /* For registering threads. */ extern struct thread_master *master; @@ -1351,11 +1353,18 @@ static int ospf_distribute_list_update_timer(struct thread *thread) default_refresh = 1; else if ( (lsa = ospf_external_info_find_lsa( - ospf, &ei->p))) - ospf_external_lsa_refresh( - ospf, lsa, ei, - LSA_REFRESH_IF_CHANGED); - else + ospf, &ei->p))) { + if (!CHECK_FLAG( + lsa->flags, + OSPF_LSA_IN_MAXAGE)) + ospf_external_lsa_refresh( + ospf, lsa, ei, + LSA_REFRESH_IF_CHANGED); + else + ospf_external_lsa_refresh( + ospf, lsa, ei, + LSA_REFRESH_FORCE); + } else ospf_external_lsa_originate( ospf, ei); } @@ -1713,6 +1722,109 @@ void ospf_zebra_vrf_deregister(struct ospf *ospf) } } +/* Label Manager Functions */ + +/** + * Check if Label Manager is Ready or not. + * + * @return True if Label Manager is ready, False otherwise + */ +bool ospf_zebra_label_manager_ready(void) +{ + return (zclient_sync->sock > 0); +} + +/** + * Request Label Range to the Label Manager. + * + * @param base base label of the label range to request + * @param chunk_size size of the label range to request + * + * @return 0 on success, -1 on failure + */ +int ospf_zebra_request_label_range(uint32_t base, uint32_t chunk_size) +{ + int ret; + uint32_t start, end; + + if (zclient_sync->sock < 0) + return -1; + + ret = lm_get_label_chunk(zclient_sync, 0, base, chunk_size, &start, + &end); + if (ret < 0) { + zlog_warn("%s: error getting label range!", __func__); + return -1; + } + + return 0; +} + +/** + * Release Label Range to the Label Manager. + * + * @param start start of label range to release + * @param end end of label range to release + * + * @return 0 on success, -1 otherwise + */ +int ospf_zebra_release_label_range(uint32_t start, uint32_t end) +{ + int ret; + + if (zclient_sync->sock < 0) + return -1; + + ret = lm_release_label_chunk(zclient_sync, start, end); + if (ret < 0) { + zlog_warn("%s: error releasing label range!", __func__); + return -1; + } + + return 0; +} + +/** + * Connect to the Label Manager. + * + * @return 0 on success, -1 otherwise + */ +int ospf_zebra_label_manager_connect(void) +{ + /* Connect to label manager. */ + if (zclient_socket_connect(zclient_sync) < 0) { + zlog_warn("%s: failed connecting synchronous zclient!", + __func__); + return -1; + } + /* make socket non-blocking */ + set_nonblocking(zclient_sync->sock); + + /* Send hello to notify zebra this is a synchronous client */ + if (zclient_send_hello(zclient_sync) < 0) { + zlog_warn("%s: failed sending hello for synchronous zclient!", + __func__); + close(zclient_sync->sock); + zclient_sync->sock = -1; + return -1; + } + + /* Connect to label manager */ + if (lm_label_manager_connect(zclient_sync, 0) != 0) { + zlog_warn("%s: failed connecting to label manager!", __func__); + if (zclient_sync->sock > 0) { + close(zclient_sync->sock); + zclient_sync->sock = -1; + } + return -1; + } + + osr_debug("SR (%s): Successfully connected to the Label Manager", + __func__); + + return 0; +} + static void ospf_zebra_connected(struct zclient *zclient) { /* Send the client registration */ @@ -1736,8 +1848,27 @@ void ospf_zebra_init(struct thread_master *master, unsigned short instance) zclient->redistribute_route_add = ospf_zebra_read_route; zclient->redistribute_route_del = ospf_zebra_read_route; + /* Initialize special zclient for synchronous message exchanges. */ + struct zclient_options options = zclient_options_default; + options.synchronous = true; + zclient_sync = zclient_new(master, &options); + zclient_sync->sock = -1; + zclient_sync->redist_default = ZEBRA_ROUTE_OSPF; + zclient_sync->instance = instance; + /* + * session_id must be different from default value (0) to distinguish + * the asynchronous socket from the synchronous one + */ + zclient_sync->session_id = 1; + zclient_sync->privs = &ospfd_privs; + access_list_add_hook(ospf_filter_update); access_list_delete_hook(ospf_filter_update); prefix_list_add_hook(ospf_prefix_list_update); prefix_list_delete_hook(ospf_prefix_list_update); } + +void ospf_zebra_send_arp(const struct interface *ifp, const struct prefix *p) +{ + zclient_send_neigh_discovery_req(zclient, ifp, p); +} diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h index 80abf62369..bdc8af0402 100644 --- a/ospfd/ospf_zebra.h +++ b/ospfd/ospf_zebra.h @@ -41,6 +41,7 @@ struct ospf_distance { }; /* Prototypes */ +struct ospf_route; extern void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *, struct ospf_route *); extern void ospf_zebra_delete(struct ospf *ospf, struct prefix_ipv4 *, @@ -98,4 +99,11 @@ bool ospf_external_default_routemap_apply_walk( int ospf_external_info_apply_default_routemap(struct ospf *ospf, struct external_info *ei, struct external_info *default_ei); + +extern void ospf_zebra_send_arp(const struct interface *ifp, + const struct prefix *p); +bool ospf_zebra_label_manager_ready(void); +int ospf_zebra_label_manager_connect(void); +int ospf_zebra_request_label_range(uint32_t base, uint32_t chunk_size); +int ospf_zebra_release_label_range(uint32_t start, uint32_t end); #endif /* _ZEBRA_OSPF_ZEBRA_H */ diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index f9cc474d5c..31d8417eb6 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -305,6 +305,8 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) new->oi_write_q = list_new(); new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT; + new->proactive_arp = OSPF_PROACTIVE_ARP_DEFAULT; + QOBJ_REG(new, ospf); new->fd = -1; diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index cdeaa38dc0..e5e07875e8 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -312,6 +312,10 @@ struct ospf { * update to neighbors immediatly */ uint8_t inst_shutdown; + /* Enable or disable sending proactive ARP requests. */ + bool proactive_arp; +#define OSPF_PROACTIVE_ARP_DEFAULT true + /* Redistributed external information. */ struct list *external[ZEBRA_ROUTE_MAX + 1]; #define EXTERNAL_INFO(E) (E->external_info) @@ -410,6 +414,12 @@ struct ospf_area { /* Shortest Path Tree. */ struct vertex *spf; + struct list *spf_vertex_list; + + bool spf_dry_run; /* flag for checking if the SPF calculation is + intended for the local RIB */ + bool spf_root_node; /* flag for checking if the calculating node is the + root node of the SPF tree */ /* Threads. */ struct thread *t_stub_router; /* Stub-router timer */ diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 10a75a9f54..058881cbfc 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -771,6 +771,67 @@ void pbr_map_check_nh_group_change(const char *nh_group) } } +void pbr_map_check_vrf_nh_group_change(const char *nh_group, + struct pbr_vrf *pbr_vrf, + uint32_t old_vrf_id) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node; + + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (pbrms->nhgrp_name) + continue; + + if (pbrms->nhg == NULL) + continue; + + if (strcmp(nh_group, pbrms->internal_nhg_name)) + continue; + + if (pbrms->nhg->nexthop == NULL) + continue; + + if (pbrms->nhg->nexthop->vrf_id != old_vrf_id) + continue; + + pbrms->nhg->nexthop->vrf_id = pbr_vrf_id(pbr_vrf); + } + } +} + +void pbr_map_check_interface_nh_group_change(const char *nh_group, + struct interface *ifp, + ifindex_t oldifindex) +{ + struct pbr_map *pbrm; + struct pbr_map_sequence *pbrms; + struct listnode *node; + + RB_FOREACH (pbrm, pbr_map_entry_head, &pbr_maps) { + for (ALL_LIST_ELEMENTS_RO(pbrm->seqnumbers, node, pbrms)) { + if (pbrms->nhgrp_name) + continue; + + if (pbrms->nhg == NULL) + continue; + + if (strcmp(nh_group, pbrms->internal_nhg_name)) + continue; + + if (pbrms->nhg->nexthop == NULL) + continue; + + if (pbrms->nhg->nexthop->ifindex != oldifindex) + continue; + + pbrms->nhg->nexthop->ifindex = ifp->ifindex; + } + } +} + void pbr_map_check(struct pbr_map_sequence *pbrms, bool changed) { struct pbr_map *pbrm; diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 64c090d2e8..43266f21e9 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -205,4 +205,11 @@ extern void pbr_map_install(struct pbr_map *pbrm); extern void pbr_map_policy_install(const char *name); extern void pbr_map_policy_delete(struct pbr_map *pbrm, struct pbr_map_interface *pmi); + +extern void pbr_map_check_vrf_nh_group_change(const char *nh_group, + struct pbr_vrf *pbr_vrf, + uint32_t old_vrf_id); +extern void pbr_map_check_interface_nh_group_change(const char *nh_group, + struct interface *ifp, + ifindex_t oldifindex); #endif diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 31da656793..3fb3759049 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -37,7 +37,7 @@ DEFINE_MTYPE_STATIC(PBRD, PBR_NHG, "PBR Nexthop Groups") -static struct hash *pbr_nhg_hash; +struct hash *pbr_nhg_hash; static struct hash *pbr_nhrc_hash; static uint32_t pbr_nhg_low_table; @@ -91,15 +91,15 @@ static void *pbr_nh_alloc(void *p) struct nhrc *nhrc; new = XCALLOC(MTYPE_PBR_NHG, sizeof(*new)); - nhrc = hash_get(pbr_nhrc_hash, pnhc->nexthop, pbr_nhrc_hash_alloc); - new->nexthop = &nhrc->nexthop; + nhrc = hash_get(pbr_nhrc_hash, &pnhc->nexthop, pbr_nhrc_hash_alloc); + new->nexthop = nhrc->nexthop; /* Decremented again in pbr_nh_delete */ ++nhrc->refcount; DEBUGD(&pbr_dbg_nht, "%s: Sending nexthop to Zebra", __func__); - pbr_send_rnh(new->nexthop, true); + pbr_send_rnh(&new->nexthop, true); new->valid = false; return new; @@ -109,14 +109,14 @@ static void pbr_nh_delete(struct pbr_nexthop_cache **pnhc) { struct nhrc *nhrc; - nhrc = hash_lookup(pbr_nhrc_hash, (*pnhc)->nexthop); + nhrc = hash_lookup(pbr_nhrc_hash, &((*pnhc)->nexthop)); if (nhrc) --nhrc->refcount; if (!nhrc || nhrc->refcount == 0) { DEBUGD(&pbr_dbg_nht, "%s: Removing nexthop from Zebra", __func__); - pbr_send_rnh((*pnhc)->nexthop, false); + pbr_send_rnh(&((*pnhc)->nexthop), false); } if (nhrc && nhrc->refcount == 0) { hash_release(pbr_nhrc_hash, nhrc); @@ -136,7 +136,7 @@ static uint32_t pbr_nh_hash_key(const void *arg) uint32_t key; const struct pbr_nexthop_cache *pbrnc = arg; - key = nexthop_hash(pbrnc->nexthop); + key = nexthop_hash(&pbrnc->nexthop); return key; } @@ -148,28 +148,28 @@ static bool pbr_nh_hash_equal(const void *arg1, const void *arg2) const struct pbr_nexthop_cache *pbrnc2 = (const struct pbr_nexthop_cache *)arg2; - if (pbrnc1->nexthop->vrf_id != pbrnc2->nexthop->vrf_id) + if (pbrnc1->nexthop.vrf_id != pbrnc2->nexthop.vrf_id) return false; - if (pbrnc1->nexthop->ifindex != pbrnc2->nexthop->ifindex) + if (pbrnc1->nexthop.ifindex != pbrnc2->nexthop.ifindex) return false; - if (pbrnc1->nexthop->type != pbrnc2->nexthop->type) + if (pbrnc1->nexthop.type != pbrnc2->nexthop.type) return false; - switch (pbrnc1->nexthop->type) { + switch (pbrnc1->nexthop.type) { case NEXTHOP_TYPE_IFINDEX: - return pbrnc1->nexthop->ifindex == pbrnc2->nexthop->ifindex; + return pbrnc1->nexthop.ifindex == pbrnc2->nexthop.ifindex; case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4: - return pbrnc1->nexthop->gate.ipv4.s_addr - == pbrnc2->nexthop->gate.ipv4.s_addr; + return pbrnc1->nexthop.gate.ipv4.s_addr + == pbrnc2->nexthop.gate.ipv4.s_addr; case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_IPV6: - return !memcmp(&pbrnc1->nexthop->gate.ipv6, - &pbrnc2->nexthop->gate.ipv6, 16); + return !memcmp(&pbrnc1->nexthop.gate.ipv6, + &pbrnc2->nexthop.gate.ipv6, 16); case NEXTHOP_TYPE_BLACKHOLE: - return pbrnc1->nexthop->bh_type == pbrnc2->nexthop->bh_type; + return pbrnc1->nexthop.bh_type == pbrnc2->nexthop.bh_type; } /* @@ -249,9 +249,8 @@ void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc, pnhgc = hash_get(pbr_nhg_hash, &pnhgc_find, pbr_nhgc_alloc); /* create & insert new pnhc into pnhgc->nhh */ - pnhc_find.nexthop = (struct nexthop *)nhop; + pnhc_find.nexthop = *nhop; pnhc = hash_get(pnhgc->nhh, &pnhc_find, pbr_nh_alloc); - pnhc_find.nexthop = NULL; /* set parent pnhgc */ pnhc->parent = pnhgc; @@ -291,7 +290,7 @@ void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, pnhgc = hash_lookup(pbr_nhg_hash, &pnhgc_find); /* delete pnhc from pnhgc->nhh */ - pnhc_find.nexthop = (struct nexthop *)nhop; + pnhc_find.nexthop = *nhop; pnhc = hash_release(pnhgc->nhh, &pnhc_find); /* delete pnhc */ @@ -499,7 +498,7 @@ void pbr_nht_change_group(const char *name) struct pbr_nexthop_cache lookup; struct pbr_nexthop_cache *pnhc; - lookup.nexthop = nhop; + lookup.nexthop = *nhop; pnhc = hash_lookup(pnhgc->nhh, &lookup); if (!pnhc) { pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc); @@ -553,9 +552,25 @@ void pbr_nht_add_individual_nexthop(struct pbr_map_sequence *pbrms, pnhgc = hash_get(pbr_nhg_hash, &find, pbr_nhgc_alloc); - lookup.nexthop = pbrms->nhg->nexthop; + lookup.nexthop = *pbrms->nhg->nexthop; pnhc = hash_get(pnhgc->nhh, &lookup, pbr_nh_alloc); pnhc->parent = pnhgc; + if (nhop->vrf_id != VRF_DEFAULT) { + struct vrf *vrf = vrf_lookup_by_id(nhop->vrf_id); + + if (vrf) + strlcpy(pnhc->vrf_name, vrf->name, + sizeof(pnhc->vrf_name)); + } + + if (nhop->ifindex != 0) { + struct interface *ifp = + if_lookup_by_index(nhop->ifindex, nhop->vrf_id); + + if (ifp) + strlcpy(pnhc->intf_name, ifp->name, + sizeof(pnhc->intf_name)); + } pbr_nht_install_nexthop_group(pnhgc, *pbrms->nhg); } @@ -574,7 +589,7 @@ static void pbr_nht_release_individual_nexthop(struct pbr_map_sequence *pbrms) nh = pbrms->nhg->nexthop; nh_type = nh->type; - lup.nexthop = nh; + lup.nexthop = *nh; pnhc = hash_lookup(pnhgc->nhh, &lup); pnhc->parent = NULL; hash_release(pnhgc->nhh, pnhc); @@ -625,7 +640,7 @@ struct pbr_nexthop_group_cache *pbr_nht_add_group(const char *name) struct pbr_nexthop_cache lookupc; struct pbr_nexthop_cache *pnhc; - lookupc.nexthop = nhop; + lookupc.nexthop = *nhop; pnhc = hash_lookup(pnhgc->nhh, &lookupc); if (!pnhc) { pnhc = hash_get(pnhgc->nhh, &lookupc, pbr_nh_alloc); @@ -689,13 +704,18 @@ bool pbr_nht_nexthop_group_valid(const char *name) struct pbr_nht_individual { struct zapi_route *nhr; struct interface *ifp; + struct pbr_vrf *pbr_vrf; + struct pbr_nexthop_cache *pnhc; + vrf_id_t old_vrf_id; - uint32_t valid; + bool valid; + + bool nhr_matched; }; static bool pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc, - const struct pbr_nht_individual *pnhi) + struct pbr_nht_individual *pnhi) { bool is_valid = pnhc->valid; @@ -704,25 +724,26 @@ pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc, switch (pnhi->nhr->prefix.family) { case AF_INET: - if (pnhc->nexthop->gate.ipv4.s_addr + if (pnhc->nexthop.gate.ipv4.s_addr != pnhi->nhr->prefix.u.prefix4.s_addr) goto done; /* Unrelated change */ break; case AF_INET6: - if (memcmp(&pnhc->nexthop->gate.ipv6, + if (memcmp(&pnhc->nexthop.gate.ipv6, &pnhi->nhr->prefix.u.prefix6, 16) != 0) goto done; /* Unrelated change */ break; } + pnhi->nhr_matched = true; if (!pnhi->nhr->nexthop_num) { is_valid = false; goto done; } - if (pnhc->nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX - || pnhc->nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { + if (pnhc->nexthop.type == NEXTHOP_TYPE_IPV4_IFINDEX + || pnhc->nexthop.type == NEXTHOP_TYPE_IPV6_IFINDEX) { /* GATEWAY_IFINDEX type shouldn't resolve to group */ if (pnhi->nhr->nexthop_num > 1) { @@ -733,7 +754,7 @@ pbr_nht_individual_nexthop_gw_update(struct pbr_nexthop_cache *pnhc, /* If whatever we resolved to wasn't on the interface we * specified. (i.e. not a connected route), its invalid. */ - if (pnhi->nhr->nexthops[0].ifindex != pnhc->nexthop->ifindex) { + if (pnhi->nhr->nexthops[0].ifindex != pnhc->nexthop.ifindex) { is_valid = false; goto done; } @@ -747,18 +768,20 @@ done: return pnhc->valid; } -static bool pbr_nht_individual_nexthop_interface_update( - struct pbr_nexthop_cache *pnhc, const struct pbr_nht_individual *pnhi) +static bool +pbr_nht_individual_nexthop_interface_update(struct pbr_nexthop_cache *pnhc, + struct pbr_nht_individual *pnhi) { bool is_valid = pnhc->valid; if (!pnhi->ifp) /* It doesn't care about non-interface updates */ goto done; - if (pnhc->nexthop->ifindex + if (pnhc->nexthop.ifindex != pnhi->ifp->ifindex) /* Un-related interface */ goto done; + pnhi->nhr_matched = true; is_valid = !!if_is_up(pnhi->ifp); done: @@ -773,18 +796,17 @@ done: * If the update is un-related, the subroutines shoud just return their cached * valid state. */ -static void -pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc, - const struct pbr_nht_individual *pnhi) +static void pbr_nht_individual_nexthop_update(struct pbr_nexthop_cache *pnhc, + struct pbr_nht_individual *pnhi) { assert(pnhi->nhr || pnhi->ifp); /* Either nexthop or interface update */ - switch (pnhc->nexthop->type) { + switch (pnhc->nexthop.type) { case NEXTHOP_TYPE_IFINDEX: pbr_nht_individual_nexthop_interface_update(pnhc, pnhi); break; case NEXTHOP_TYPE_IPV6_IFINDEX: - if (IN6_IS_ADDR_LINKLOCAL(&pnhc->nexthop->gate.ipv6)) { + if (IN6_IS_ADDR_LINKLOCAL(&pnhc->nexthop.gate.ipv6)) { pbr_nht_individual_nexthop_interface_update(pnhc, pnhi); break; } @@ -817,7 +839,7 @@ static void pbr_nht_individual_nexthop_update_lookup(struct hash_bucket *b, pnhc->valid); if (pnhc->valid) - pnhi->valid += 1; + pnhi->valid = true; } static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket *b, @@ -827,7 +849,7 @@ static void pbr_nexthop_group_cache_iterate_to_group(struct hash_bucket *b, struct nexthop_group *nhg = data; struct nexthop *nh = NULL; - copy_nexthops(&nh, pnhc->nexthop, NULL); + copy_nexthops(&nh, &pnhc->nexthop, NULL); _nexthop_add(&nhg->nexthop, nh); } @@ -849,10 +871,14 @@ static void pbr_nht_nexthop_update_lookup(struct hash_bucket *b, void *data) old_valid = pnhgc->valid; pnhi.nhr = (struct zapi_route *)data; - pnhi.valid = 0; + pnhi.valid = false; + pnhi.nhr_matched = false; hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_update_lookup, &pnhi); + if (!pnhi.nhr_matched) + return; + /* * If any of the specified nexthops are valid we are valid */ @@ -877,6 +903,178 @@ void pbr_nht_nexthop_update(struct zapi_route *nhr) hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_update_lookup, nhr); } +struct nhrc_vrf_info { + struct pbr_vrf *pbr_vrf; + uint32_t old_vrf_id; + struct nhrc *nhrc; +}; + +static int pbr_nht_nhrc_vrf_change(struct hash_bucket *b, void *data) +{ + struct nhrc *nhrc = b->data; + struct nhrc_vrf_info *nhrcvi = data; + + if (nhrc->nexthop.vrf_id == nhrcvi->old_vrf_id) { + nhrcvi->nhrc = nhrc; + return HASHWALK_ABORT; + } + + return HASHWALK_CONTINUE; +} + +static int pbr_nht_individual_nexthop_vrf_handle(struct hash_bucket *b, + void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + struct pbr_nht_individual *pnhi = data; + + if (pnhc->looked_at == true) + return HASHWALK_CONTINUE; + + if (pnhc->nexthop.vrf_id == VRF_DEFAULT) + return HASHWALK_CONTINUE; + + if (strncmp(pnhc->vrf_name, pbr_vrf_name(pnhi->pbr_vrf), + sizeof(pnhc->vrf_name)) + == 0) { + pnhi->pnhc = pnhc; + + if (pnhc->nexthop.vrf_id != pbr_vrf_id(pnhi->pbr_vrf)) { + struct nhrc_vrf_info nhrcvi; + + memset(&nhrcvi, 0, sizeof(nhrcvi)); + nhrcvi.pbr_vrf = pnhi->pbr_vrf; + nhrcvi.old_vrf_id = pnhc->nexthop.vrf_id; + + pnhi->nhr_matched = true; + pnhi->old_vrf_id = pnhc->nexthop.vrf_id; + + do { + nhrcvi.nhrc = NULL; + hash_walk(pbr_nhrc_hash, + pbr_nht_nhrc_vrf_change, &nhrcvi); + if (nhrcvi.nhrc) { + hash_release(pbr_nhrc_hash, + nhrcvi.nhrc); + nhrcvi.nhrc->nexthop.vrf_id = + pbr_vrf_id(pnhi->pbr_vrf); + hash_get(pbr_nhrc_hash, nhrcvi.nhrc, + hash_alloc_intern); + pbr_send_rnh(&nhrcvi.nhrc->nexthop, true); + } + } while (nhrcvi.nhrc); + } + + pnhc->looked_at = true; + return HASHWALK_ABORT; + } + + return HASHWALK_CONTINUE; +} + +static void pbr_nht_clear_looked_at(struct hash_bucket *b, void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + + pnhc->looked_at = false; +} + +static void pbr_nht_nexthop_vrf_handle(struct hash_bucket *b, void *data) +{ + struct pbr_nexthop_group_cache *pnhgc = b->data; + struct pbr_vrf *pbr_vrf = data; + struct pbr_nht_individual pnhi = {}; + + zlog_debug("pnhgc iterating"); + hash_iterate(pnhgc->nhh, pbr_nht_clear_looked_at, NULL); + memset(&pnhi, 0, sizeof(pnhi)); + pnhi.pbr_vrf = pbr_vrf; + do { + struct pbr_nexthop_cache *pnhc; + + pnhi.pnhc = NULL; + hash_walk(pnhgc->nhh, pbr_nht_individual_nexthop_vrf_handle, + &pnhi); + + if (!pnhi.pnhc) + continue; + + pnhc = pnhi.pnhc; + pnhc->nexthop.vrf_id = pnhi.old_vrf_id; + pnhi.pnhc = hash_release(pnhgc->nhh, pnhi.pnhc); + if (pnhi.pnhc) { + pnhi.pnhc->nexthop.vrf_id = pbr_vrf_id(pbr_vrf); + + hash_get(pnhgc->nhh, pnhi.pnhc, hash_alloc_intern); + } else + pnhc->nexthop.vrf_id = pbr_vrf_id(pbr_vrf); + + pbr_map_check_vrf_nh_group_change(pnhgc->name, pbr_vrf, + pnhi.old_vrf_id); + } while (pnhi.pnhc); +} + +void pbr_nht_vrf_update(struct pbr_vrf *pbr_vrf) +{ + hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_vrf_handle, pbr_vrf); +} + +static void pbr_nht_individual_nexthop_interface_handle(struct hash_bucket *b, + void *data) +{ + struct pbr_nexthop_cache *pnhc = b->data; + struct pbr_nht_individual *pnhi = data; + + if (pnhc->nexthop.ifindex == 0) + return; + + if ((strncmp(pnhc->intf_name, pnhi->ifp->name, sizeof(pnhc->intf_name)) + == 0) + && pnhc->nexthop.ifindex != pnhi->ifp->ifindex) + pnhi->pnhc = pnhc; +} + +static void pbr_nht_nexthop_interface_handle(struct hash_bucket *b, void *data) +{ + struct pbr_nexthop_group_cache *pnhgc = b->data; + struct interface *ifp = data; + struct pbr_nht_individual pnhi = {}; + struct nhrc *nhrc; + uint32_t old_ifindex; + + do { + memset(&pnhi, 0, sizeof(pnhi)); + pnhi.ifp = ifp; + hash_iterate(pnhgc->nhh, + pbr_nht_individual_nexthop_interface_handle, + &pnhi); + + if (!pnhi.pnhc) + continue; + + pnhi.pnhc = hash_release(pnhgc->nhh, pnhi.pnhc); + old_ifindex = pnhi.pnhc->nexthop.ifindex; + + nhrc = hash_lookup(pbr_nhrc_hash, &pnhi.pnhc->nexthop); + if (nhrc) { + hash_release(pbr_nhrc_hash, nhrc); + nhrc->nexthop.ifindex = ifp->ifindex; + hash_get(pbr_nhrc_hash, nhrc, hash_alloc_intern); + } + pnhi.pnhc->nexthop.ifindex = ifp->ifindex; + + hash_get(pnhgc->nhh, pnhi.pnhc, hash_alloc_intern); + + pbr_map_check_interface_nh_group_change(pnhgc->name, ifp, + old_ifindex); + } while (pnhi.pnhc); +} + +void pbr_nht_interface_update(struct interface *ifp) +{ + hash_iterate(pbr_nhg_hash, pbr_nht_nexthop_interface_handle, ifp); +} + static void pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket *b, void *data) @@ -893,7 +1091,7 @@ pbr_nht_individual_nexthop_interface_update_lookup(struct hash_bucket *b, old_valid, pnhc->valid); if (pnhc->valid) - pnhi->valid += 1; + pnhi->valid = true; } static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket *b, @@ -906,14 +1104,14 @@ static void pbr_nht_nexthop_interface_update_lookup(struct hash_bucket *b, old_valid = pnhgc->valid; pnhi.ifp = data; - pnhi.valid = 0; + pnhi.valid = false; hash_iterate(pnhgc->nhh, pbr_nht_individual_nexthop_interface_update_lookup, &pnhi); /* * If any of the specified nexthops are valid we are valid */ - pnhgc->valid = !!pnhi.valid; + pnhgc->valid = pnhi.valid; if (old_valid != pnhgc->valid) pbr_map_check_nh_group_change(pnhgc->name); @@ -1036,7 +1234,7 @@ static void pbr_nht_show_nhg_nexthops(struct hash_bucket *b, void *data) struct vty *vty = data; vty_out(vty, "\tValid: %d ", pnhc->valid); - nexthop_group_write_nexthop(vty, pnhc->nexthop); + nexthop_group_write_nexthop(vty, &pnhc->nexthop); } static void pbr_nht_json_nhg_nexthops(struct hash_bucket *b, void *data) @@ -1046,7 +1244,7 @@ static void pbr_nht_json_nhg_nexthops(struct hash_bucket *b, void *data) json_object *this_hop; this_hop = json_object_new_object(); - nexthop_group_json_nexthop(this_hop, pnhc->nexthop); + nexthop_group_json_nexthop(this_hop, &pnhc->nexthop); json_object_boolean_add(this_hop, "valid", pnhc->valid); json_object_array_add(all_hops, this_hop); diff --git a/pbrd/pbr_nht.h b/pbrd/pbr_nht.h index cbcf71d2f5..6346795215 100644 --- a/pbrd/pbr_nht.h +++ b/pbrd/pbr_nht.h @@ -28,6 +28,8 @@ #define PBR_NHC_NAMELEN PBR_MAP_NAMELEN + 10 +extern struct hash *pbr_nhg_hash; + struct pbr_nexthop_group_cache { char name[PBR_NHC_NAMELEN]; @@ -46,9 +48,14 @@ struct pbr_nexthop_group_cache { struct pbr_nexthop_cache { struct pbr_nexthop_group_cache *parent; - struct nexthop *nexthop; + char vrf_name[VRF_NAMSIZ + 1]; + char intf_name[INTERFACE_NAMSIZ + 1]; + + struct nexthop nexthop; + bool looked_at; bool valid; + bool nhr_matched; }; extern void pbr_nht_write_table_range(struct vty *vty); @@ -126,4 +133,7 @@ extern void pbr_nht_nexthop_update(struct zapi_route *nhr); extern void pbr_nht_nexthop_interface_update(struct interface *ifp); extern void pbr_nht_init(void); + +extern void pbr_nht_vrf_update(struct pbr_vrf *pbr_vrf); +extern void pbr_nht_interface_update(struct interface *ifp); #endif diff --git a/pbrd/pbr_vrf.c b/pbrd/pbr_vrf.c index d5a2bd0fef..389e5e8be0 100644 --- a/pbrd/pbr_vrf.c +++ b/pbrd/pbr_vrf.c @@ -25,6 +25,7 @@ #include "pbr_memory.h" #include "pbr_map.h" #include "pbr_debug.h" +#include "pbr_nht.h" DEFINE_MTYPE_STATIC(PBRD, PBR_MAP_VRF, "PBR Map VRF") @@ -59,6 +60,7 @@ static int pbr_vrf_enable(struct vrf *vrf) { DEBUGD(&pbr_dbg_event, "%s: %u (%s)", __func__, vrf->vrf_id, vrf->name); + pbr_nht_vrf_update(vrf->info); pbr_map_vrf_update(vrf->info); return 0; diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index a73d885ea6..9f966f617f 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -618,6 +618,33 @@ DEFPY (show_pbr, return CMD_SUCCESS; } +static void +pbrms_nexthop_group_write_individual_nexthop( + struct vty *vty, const struct pbr_map_sequence *pbrms) +{ + struct pbr_nexthop_group_cache find; + struct pbr_nexthop_group_cache *pnhgc; + struct pbr_nexthop_cache lookup; + struct pbr_nexthop_cache *pnhc; + + memset(&find, 0, sizeof(find)); + strlcpy(find.name, pbrms->internal_nhg_name, sizeof(find.name)); + + pnhgc = hash_lookup(pbr_nhg_hash, &find); + assert(pnhgc); + + lookup.nexthop = *pbrms->nhg->nexthop; + pnhc = hash_lookup(pnhgc->nhh, &lookup); + + nexthop_group_write_nexthop_simple( + vty, pbrms->nhg->nexthop, + pnhc->nexthop.ifindex != 0 ? pnhc->intf_name : NULL); + if (pnhc->nexthop.vrf_id != VRF_DEFAULT) + vty_out(vty, " nexthop-vrf %s", pnhc->vrf_name); + + vty_out(vty, "\n"); +} + static void vty_show_pbrms(struct vty *vty, const struct pbr_map_sequence *pbrms, bool detail) { @@ -670,7 +697,7 @@ static void vty_show_pbrms(struct vty *vty, } else if (pbrms->nhg) { vty_out(vty, " "); - nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop); + pbrms_nexthop_group_write_individual_nexthop(vty, pbrms); if (detail) vty_out(vty, " Installed: %u(%d) Tableid: %d\n", @@ -1065,7 +1092,7 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty, if (pbrms->nhg) { vty_out(vty, " set "); - nexthop_group_write_nexthop(vty, pbrms->nhg->nexthop); + pbrms_nexthop_group_write_individual_nexthop(vty, pbrms); } vty_out(vty, "!\n"); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index d0099a46e3..a7420974a9 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -67,6 +67,7 @@ int pbr_ifp_create(struct interface *ifp) if (!ifp->info) pbr_if_new(ifp); + pbr_nht_interface_update(ifp); /* Update nexthops tracked from a `set nexthop` command */ pbr_nht_nexthop_interface_update(ifp); diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index b79fb689dc..0caa360df9 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -341,7 +341,7 @@ pim_sec_addr_find(struct pim_interface *pim_ifp, struct prefix *addr) struct listnode *node; for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { - if (prefix_cmp(&sec_addr->addr, addr)) { + if (prefix_cmp(&sec_addr->addr, addr) == 0) { return sec_addr; } } @@ -1504,14 +1504,14 @@ void pim_if_create_pimreg(struct pim_instance *pim) } } -int pim_if_connected_to_source(struct interface *ifp, struct in_addr src) +struct prefix *pim_if_connected_to_source(struct interface *ifp, struct in_addr src) { struct listnode *cnode; struct connected *c; struct prefix p; if (!ifp) - return 0; + return NULL; p.family = AF_INET; p.u.prefix4 = src; @@ -1520,11 +1520,11 @@ int pim_if_connected_to_source(struct interface *ifp, struct in_addr src) for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { if ((c->address->family == AF_INET) && prefix_match(CONNECTED_PREFIX(c), &p)) { - return 1; + return CONNECTED_PREFIX(c); } } - return 0; + return NULL; } bool pim_if_is_vrf_device(struct interface *ifp) diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 13aaf8d3b2..8decfef74d 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -223,7 +223,7 @@ void pim_if_update_assert_tracking_desired(struct interface *ifp); void pim_if_create_pimreg(struct pim_instance *pim); -int pim_if_connected_to_source(struct interface *ifp, struct in_addr src); +struct prefix *pim_if_connected_to_source(struct interface *ifp, struct in_addr src); int pim_update_source_set(struct interface *ifp, struct in_addr source); bool pim_if_is_vrf_device(struct interface *ifp); diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index a8612f91fa..20f339d43f 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -310,7 +310,7 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version, return 0; } - if (if_lookup_address(&from, AF_INET, ifp->vrf_id)) { + if (if_lookup_exact_address(&from, AF_INET, ifp->vrf_id)) { if (PIM_DEBUG_IGMP_PACKETS) zlog_debug("Recv IGMP query on interface: %s from ourself %s", ifp->name, from_str); @@ -995,6 +995,7 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, { struct pim_interface *pim_ifp; struct igmp_sock *igmp; + struct sockaddr_in sin; int fd; pim_ifp = ifp->info; @@ -1006,6 +1007,15 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list, return 0; } + sin.sin_family = AF_INET; + sin.sin_addr = ifaddr; + sin.sin_port = 0; + if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) != 0) { + zlog_warn("Could not bind IGMP socket for %s on %s", + inet_ntoa(ifaddr), ifp->name); + return 0; + } + igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only); igmp_read_on(igmp); diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 9060b6a95a..0bccba397b 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -592,12 +592,9 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, struct pim_interface *pim_ifp; const struct ip *ip_hdr; const struct igmpmsg *msg; - char ip_src_str[INET_ADDRSTRLEN] = ""; - char ip_dst_str[INET_ADDRSTRLEN] = ""; - char src_str[INET_ADDRSTRLEN] = "<src?>"; - char grp_str[INET_ADDRSTRLEN] = "<grp?>"; struct in_addr ifaddr; struct igmp_sock *igmp; + const struct prefix *connected_src; if (buf_size < (int)sizeof(struct ip)) return 0; @@ -617,34 +614,37 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, if (!ifp || !ifp->info) return 0; + connected_src = pim_if_connected_to_source(ifp, ip_hdr->ip_src); + + if (!connected_src) { + if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("Recv IGMP packet on interface: %s from a non-connected source: %pI4", + ifp->name, &ip_hdr->ip_src); + } + return 0; + } + pim_ifp = ifp->info; - ifaddr = pim_find_primary_addr(ifp); - igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, - ifaddr); + ifaddr = connected_src->u.prefix4; + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr); if (PIM_DEBUG_MROUTE) { - pim_inet4_dump("<src?>", ip_hdr->ip_src, ip_src_str, - sizeof(ip_src_str)); - pim_inet4_dump("<dst?>", ip_hdr->ip_dst, ip_dst_str, - sizeof(ip_dst_str)); - zlog_debug( - "%s(%s): igmp kernel upcall on %s(%p) for %s -> %s", + "%s(%s): igmp kernel upcall on %s(%p) for %pI4 -> %pI4", __func__, pim->vrf->name, ifp->name, igmp, - ip_src_str, ip_dst_str); + &ip_hdr->ip_src, &ip_hdr->ip_dst); } if (igmp) pim_igmp_packet(igmp, (char *)buf, buf_size); - + else if (PIM_DEBUG_IGMP_PACKETS) { + zlog_debug("No IGMP socket on interface: %s with connected source: %pFX", + ifp->name, connected_src); + } } else if (ip_hdr->ip_p) { if (PIM_DEBUG_MROUTE_DETAIL) { - pim_inet4_dump("<src?>", ip_hdr->ip_src, src_str, - sizeof(src_str)); - pim_inet4_dump("<grp?>", ip_hdr->ip_dst, grp_str, - sizeof(grp_str)); zlog_debug( - "%s: no kernel upcall proto=%d src: %s dst: %s msg_size=%d", - __func__, ip_hdr->ip_p, src_str, grp_str, + "%s: no kernel upcall proto=%d src: %pI4 dst: %pI4 msg_size=%d", + __func__, ip_hdr->ip_p, &ip_hdr->ip_src, &ip_hdr->ip_dst, buf_size); } @@ -656,15 +656,11 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf, if (!ifp) return 0; if (PIM_DEBUG_MROUTE) { - pim_inet4_dump("<src?>", msg->im_src, src_str, - sizeof(src_str)); - pim_inet4_dump("<grp?>", msg->im_dst, grp_str, - sizeof(grp_str)); zlog_debug( - "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d size=%d", + "%s: pim kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%pI4,%pI4) on %s vifi=%d size=%d", __func__, igmpmsgtype2str[msg->im_msgtype], msg->im_msgtype, ip_hdr->ip_p, - pim->mroute_socket, src_str, grp_str, ifp->name, + pim->mroute_socket, &msg->im_src, &msg->im_dst, ifp->name, msg->im_vif, buf_size); } diff --git a/python/makevars.py b/python/makevars.py index 1a85fbd6f5..63bf8c5eeb 100644 --- a/python/makevars.py +++ b/python/makevars.py @@ -70,7 +70,7 @@ class MakeReVars(MakeVarsBase): repl_re = re.compile(r'\$(?:([A-Za-z])|\(([^\)]+)\))') def __init__(self, maketext): - super().__init__() + super(MakeReVars, self).__init__() self._vars = dict(self.var_re.findall(maketext.replace('\\\n', ''))) def replacevar(self, match): diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 6a120c8eff..d390ea8192 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -131,8 +131,8 @@ DEFPY(sharp_nht_data_dump, sharp_nht_data_dump_cmd, "sharp data nexthop", "Sharp routing Protocol\n" - "Nexthop information\n" - "Data Dump\n") + "Data about what is going on\n" + "Nexthop information\n") { sharp_nh_tracker_dump(vty); @@ -649,6 +649,51 @@ DEFPY (send_opaque_reg, return CMD_SUCCESS; } +DEFPY (neigh_discover, + neigh_discover_cmd, + "sharp neigh discover [vrf NAME$vrf_name] <A.B.C.D$dst4|X:X::X:X$dst6> IFNAME$ifname", + SHARP_STR + "Discover neighbours\n" + "Send an ARP/NDP request\n" + VRF_CMD_HELP_STR + "v4 Destination address\n" + "v6 Destination address\n" + "Interface name\n") +{ + struct vrf *vrf; + struct interface *ifp; + struct prefix prefix; + + memset(&prefix, 0, sizeof(prefix)); + + if (dst4.s_addr != 0) { + prefix.family = AF_INET; + prefix.prefixlen = 32; + prefix.u.prefix4 = dst4; + } else { + prefix.family = AF_INET6; + prefix.prefixlen = 128; + prefix.u.prefix6 = dst6; + } + + vrf = vrf_lookup_by_name(vrf_name ? vrf_name : VRF_DEFAULT_NAME); + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name ? vrf_name : VRF_DEFAULT_NAME); + return CMD_WARNING; + } + + ifp = if_lookup_by_name_vrf(ifname, vrf); + if (ifp == NULL) { + vty_out(vty, "%% Can't find interface %s\n", ifname); + return CMD_WARNING; + } + + sharp_zebra_send_arp(ifp, &prefix); + + return CMD_SUCCESS; +} + void sharp_vty_init(void) { install_element(ENABLE_NODE, &install_routes_data_dump_cmd); @@ -666,6 +711,7 @@ void sharp_vty_init(void) install_element(ENABLE_NODE, &send_opaque_cmd); 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(VIEW_NODE, &show_debugging_sharpd_cmd); diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index e0f16d71f5..08f5a07b7e 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -663,6 +663,11 @@ void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance, } +void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p) +{ + zclient_send_neigh_discovery_req(zclient, ifp, p); +} + void sharp_zebra_init(void) { struct zclient_options opt = {.receive_notify = true}; diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index e40585aa6a..0a44fa694f 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -58,4 +58,7 @@ void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance, void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance, uint32_t session_id, uint32_t type); +extern void sharp_zebra_send_arp(const struct interface *ifp, + const struct prefix *p); + #endif diff --git a/staticd/static_nb.c b/staticd/static_nb.c index 419a6a5366..51704426f0 100644 --- a/staticd/static_nb.c +++ b/staticd/static_nb.c @@ -76,6 +76,13 @@ const struct frr_yang_module_info frr_staticd_info = { } }, { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srte-color", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy, + } + }, + { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create, @@ -154,6 +161,13 @@ const struct frr_yang_module_info frr_staticd_info = { } }, { + .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color", + .cbs = { + .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify, + .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy, + } + }, + { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", .cbs = { .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create, diff --git a/staticd/static_nb.h b/staticd/static_nb.h index e46c156265..d145c31f77 100644 --- a/staticd/static_nb.h +++ b/staticd/static_nb.h @@ -45,6 +45,10 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa struct nb_cb_modify_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_onlink_destroy( struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy( + struct nb_cb_destroy_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( struct nb_cb_create_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( @@ -85,6 +89,10 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr struct nb_cb_modify_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_destroy( struct nb_cb_destroy_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify( + struct nb_cb_modify_args *args); +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( + struct nb_cb_destroy_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( struct nb_cb_create_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( @@ -140,6 +148,8 @@ int routing_control_plane_protocols_name_validate( #define FRR_STATIC_ROUTE_NH_ONLINK_XPATH "/onlink" +#define FRR_STATIC_ROUTE_NH_COLOR_XPATH "/srte-color" + #define FRR_STATIC_ROUTE_NH_BH_XPATH "/bh-type" #define FRR_STATIC_ROUTE_NH_LABEL_XPATH "/mpls-label-stack" diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c index 40c4d85fb6..e89832069c 100644 --- a/staticd/static_nb_config.c +++ b/staticd/static_nb_config.c @@ -162,7 +162,7 @@ static bool static_nexthop_create(struct nb_cb_create_args *args, yang_dnode_get_string(args->dnode, "./gateway")); nh = static_add_nexthop(rn, pn, info->safi, info->svrf, nh_type, - &ipaddr, ifname, nh_vrf); + &ipaddr, ifname, nh_vrf, 0); if (!nh) { char buf[SRCDEST2STR_BUFFER]; @@ -302,6 +302,26 @@ static int static_nexthop_onlink_modify(struct nb_cb_modify_args *args) return NB_OK; } +static int static_nexthop_color_modify(struct nb_cb_modify_args *args) +{ + struct static_nexthop *nh; + + nh = nb_running_get_entry(args->dnode, NULL, true); + nh->color = yang_dnode_get_uint32(args->dnode, NULL); + + return NB_OK; +} + +static int static_nexthop_color_destroy(struct nb_cb_destroy_args *args) +{ + struct static_nexthop *nh; + + nh = nb_running_unset_entry(args->dnode); + nh->color = 0; + + return NB_OK; +} + static int static_nexthop_bh_type_modify(struct nb_cb_modify_args *args) { struct static_nexthop *nh; @@ -705,6 +725,44 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa } return NB_OK; } + +/* + * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srte-color + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_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: + if (static_nexthop_color_modify(args) != NB_OK) + return NB_ERR; + + break; + } + return NB_OK; +} + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_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: + if (static_nexthop_color_destroy(args) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + /* * XPath: * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry @@ -1124,6 +1182,44 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr /* * XPath: + * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color + */ +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_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: + if (static_nexthop_color_modify(args) != NB_OK) + return NB_ERR; + + break; + } + return NB_OK; +} + + +int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_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: + if (static_nexthop_color_destroy(args) != NB_OK) + return NB_ERR; + break; + } + return NB_OK; +} + +/* + * XPath: * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry */ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( diff --git a/staticd/static_routes.c b/staticd/static_routes.c index 3836109e36..d9f2faabaf 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -200,7 +200,7 @@ struct static_nexthop * static_add_nexthop(struct route_node *rn, struct static_path *pn, safi_t safi, struct static_vrf *svrf, static_types type, struct ipaddr *ipaddr, const char *ifname, - const char *nh_vrf) + const char *nh_vrf, uint32_t color) { struct static_nexthop *nh; struct static_vrf *nh_svrf; @@ -218,6 +218,7 @@ static_add_nexthop(struct route_node *rn, struct static_path *pn, safi_t safi, nh = XCALLOC(MTYPE_STATIC_NEXTHOP, sizeof(struct static_nexthop)); nh->type = type; + nh->color = color; nh->nh_vrf_id = nh_svrf->vrf->vrf_id; strlcpy(nh->nh_vrfname, nh_svrf->vrf->name, sizeof(nh->nh_vrfname)); diff --git a/staticd/static_routes.h b/staticd/static_routes.h index 89ef544023..e5c10d18a4 100644 --- a/staticd/static_routes.h +++ b/staticd/static_routes.h @@ -132,6 +132,9 @@ struct static_nexthop { * are specified. */ bool onlink; + + /* SR-TE color */ + uint32_t color; }; DECLARE_DLIST(static_nexthop_list, struct static_nexthop, list); @@ -156,7 +159,7 @@ extern struct static_nexthop * static_add_nexthop(struct route_node *rn, struct static_path *pn, safi_t safi, struct static_vrf *svrf, static_types type, struct ipaddr *ipaddr, const char *ifname, - const char *nh_vrf); + const char *nh_vrf, uint32_t color); extern void static_install_nexthop(struct route_node *rn, struct static_path *pn, struct static_nexthop *nh, safi_t safi, diff --git a/staticd/static_vty.c b/staticd/static_vty.c index ac18f6adf4..4763406934 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -51,7 +51,8 @@ static int static_route_leak(struct vty *vty, const char *svrf, const char *gate_str, const char *ifname, const char *flag_str, const char *tag_str, const char *distance_str, const char *label_str, - const char *table_str, bool onlink) + const char *table_str, bool onlink, + const char *color_str) { int ret; struct prefix p, src; @@ -249,6 +250,17 @@ static int static_route_leak(struct vty *vty, const char *svrf, nb_cli_enqueue_change(vty, ab_xpath, NB_OP_MODIFY, "false"); } + if (type == STATIC_IPV4_GATEWAY + || type == STATIC_IPV6_GATEWAY + || type == STATIC_IPV4_GATEWAY_IFNAME + || type == STATIC_IPV6_GATEWAY_IFNAME) { + strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath)); + strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_COLOR_XPATH, + sizeof(ab_xpath)); + if (color_str) + nb_cli_enqueue_change(vty, ab_xpath, + NB_OP_MODIFY, color_str); + } if (label_str) { /* copy of label string (start) */ char *ostr; @@ -330,7 +342,7 @@ static int static_route(struct vty *vty, afi_t afi, safi_t safi, return static_route_leak(vty, vrf_name, vrf_name, afi, safi, negate, dest_str, mask_str, src_str, gate_str, ifname, flag_str, tag_str, distance_str, label_str, - table_str, false); + table_str, false, NULL); } /* Write static route configuration. */ @@ -441,6 +453,12 @@ int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi, if (nh->onlink) vty_out(vty, " onlink"); + /* + * SR-TE color + */ + if (nh->color != 0) + vty_out(vty, " color %u", nh->color); + vty_out(vty, "\n"); write = 1; @@ -550,7 +568,7 @@ DEFPY_YANG(ip_route_blackhole_vrf, return static_route_leak(vty, vrfname, vrfname, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, NULL, NULL, flag, tag_str, distance_str, label, table_str, - false); + false, NULL); } DEFPY_YANG(ip_route_address_interface, @@ -567,6 +585,7 @@ DEFPY_YANG(ip_route_address_interface, |table (1-4294967295) \ |nexthop-vrf NAME \ |onlink$onlink \ + |color (1-4294967295) \ }]", NO_STR IP_STR "Establish static routes\n" @@ -584,7 +603,9 @@ DEFPY_YANG(ip_route_address_interface, "Table to configure\n" "The table number to configure\n" VRF_CMD_HELP_STR - "Treat the nexthop as directly attached to the interface\n") + "Treat the nexthop as directly attached to the interface\n" + "SR-TE color\n" + "The SR-TE color to configure\n") { const char *nh_vrf; const char *flag = NULL; @@ -604,7 +625,7 @@ DEFPY_YANG(ip_route_address_interface, return static_route_leak(vty, vrf, nh_vrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, - !!onlink); + !!onlink, color_str); } DEFPY_YANG(ip_route_address_interface_vrf, @@ -620,7 +641,8 @@ DEFPY_YANG(ip_route_address_interface_vrf, |table (1-4294967295) \ |nexthop-vrf NAME \ |onlink$onlink \ - }]", + |color (1-4294967295) \ + }]", NO_STR IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" @@ -636,7 +658,9 @@ DEFPY_YANG(ip_route_address_interface_vrf, "Table to configure\n" "The table number to configure\n" VRF_CMD_HELP_STR - "Treat the nexthop as directly attached to the interface\n") + "Treat the nexthop as directly attached to the interface\n" + "SR-TE color\n" + "The SR-TE color to configure\n") { const char *nh_vrf; const char *flag = NULL; @@ -663,7 +687,7 @@ DEFPY_YANG(ip_route_address_interface_vrf, return static_route_leak(vty, vrfname, nh_vrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, - !!onlink); + !!onlink, color_str); } DEFPY_YANG(ip_route, @@ -678,6 +702,7 @@ DEFPY_YANG(ip_route, |label WORD \ |table (1-4294967295) \ |nexthop-vrf NAME \ + |color (1-4294967295) \ }]", NO_STR IP_STR "Establish static routes\n" @@ -694,7 +719,9 @@ DEFPY_YANG(ip_route, MPLS_LABEL_HELPSTR "Table to configure\n" "The table number to configure\n" - VRF_CMD_HELP_STR) + VRF_CMD_HELP_STR + "SR-TE color\n" + "The SR-TE color to configure\n") { const char *nh_vrf; const char *flag = NULL; @@ -715,7 +742,7 @@ DEFPY_YANG(ip_route, return static_route_leak(vty, vrf, nh_vrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, - false); + false, color_str); } DEFPY_YANG(ip_route_vrf, @@ -729,6 +756,7 @@ DEFPY_YANG(ip_route_vrf, |label WORD \ |table (1-4294967295) \ |nexthop-vrf NAME \ + |color (1-4294967295) \ }]", NO_STR IP_STR "Establish static routes\n" @@ -744,7 +772,9 @@ DEFPY_YANG(ip_route_vrf, MPLS_LABEL_HELPSTR "Table to configure\n" "The table number to configure\n" - VRF_CMD_HELP_STR) + VRF_CMD_HELP_STR + "SR-TE color\n" + "The SR-TE color to configure\n") { const char *nh_vrf; const char *flag = NULL; @@ -772,7 +802,7 @@ DEFPY_YANG(ip_route_vrf, return static_route_leak(vty, vrfname, nh_vrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, - false); + false, color_str); } DEFPY_YANG(ipv6_route_blackhole, @@ -859,7 +889,7 @@ DEFPY_YANG(ipv6_route_blackhole_vrf, return static_route_leak(vty, vrfname, vrfname, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, NULL, NULL, flag, tag_str, distance_str, label, table_str, - false); + false, NULL); } DEFPY_YANG(ipv6_route_address_interface, @@ -875,6 +905,7 @@ DEFPY_YANG(ipv6_route_address_interface, |table (1-4294967295) \ |nexthop-vrf NAME \ |onlink$onlink \ + |color (1-4294967295) \ }]", NO_STR IPV6_STR @@ -893,7 +924,9 @@ DEFPY_YANG(ipv6_route_address_interface, "Table to configure\n" "The table number to configure\n" VRF_CMD_HELP_STR - "Treat the nexthop as directly attached to the interface\n") + "Treat the nexthop as directly attached to the interface\n" + "SR-TE color\n" + "The SR-TE color to configure\n") { const char *nh_vrf; const char *flag = NULL; @@ -914,7 +947,7 @@ DEFPY_YANG(ipv6_route_address_interface, return static_route_leak(vty, vrf, nh_vrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, - !!onlink); + !!onlink, color_str); } DEFPY_YANG(ipv6_route_address_interface_vrf, @@ -929,6 +962,7 @@ DEFPY_YANG(ipv6_route_address_interface_vrf, |table (1-4294967295) \ |nexthop-vrf NAME \ |onlink$onlink \ + |color (1-4294967295) \ }]", NO_STR IPV6_STR @@ -946,7 +980,9 @@ DEFPY_YANG(ipv6_route_address_interface_vrf, "Table to configure\n" "The table number to configure\n" VRF_CMD_HELP_STR - "Treat the nexthop as directly attached to the interface\n") + "Treat the nexthop as directly attached to the interface\n" + "SR-TE color\n" + "The SR-TE color to configure\n") { const char *nh_vrf; const char *flag = NULL; @@ -973,7 +1009,7 @@ DEFPY_YANG(ipv6_route_address_interface_vrf, return static_route_leak(vty, vrfname, nh_vrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, - table_str, !!onlink); + table_str, !!onlink, color_str); } DEFPY_YANG(ipv6_route, @@ -987,6 +1023,7 @@ DEFPY_YANG(ipv6_route, |label WORD \ |table (1-4294967295) \ |nexthop-vrf NAME \ + |color (1-4294967295) \ }]", NO_STR IPV6_STR @@ -1004,7 +1041,9 @@ DEFPY_YANG(ipv6_route, MPLS_LABEL_HELPSTR "Table to configure\n" "The table number to configure\n" - VRF_CMD_HELP_STR) + VRF_CMD_HELP_STR + "SR-TE color\n" + "The SR-TE color to configure\n") { const char *nh_vrf; const char *flag = NULL; @@ -1024,7 +1063,7 @@ DEFPY_YANG(ipv6_route, return static_route_leak(vty, vrf, nh_vrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, - false); + false, color_str); } DEFPY_YANG(ipv6_route_vrf, @@ -1037,6 +1076,7 @@ DEFPY_YANG(ipv6_route_vrf, |label WORD \ |table (1-4294967295) \ |nexthop-vrf NAME \ + |color (1-4294967295) \ }]", NO_STR IPV6_STR @@ -1053,7 +1093,9 @@ DEFPY_YANG(ipv6_route_vrf, MPLS_LABEL_HELPSTR "Table to configure\n" "The table number to configure\n" - VRF_CMD_HELP_STR) + VRF_CMD_HELP_STR + "SR-TE color\n" + "The SR-TE color to configure\n") { const char *nh_vrf; const char *flag = NULL; @@ -1080,7 +1122,7 @@ DEFPY_YANG(ipv6_route_vrf, return static_route_leak(vty, vrfname, nh_vrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, - table_str, false); + table_str, false, color_str); } DEFPY_YANG(debug_staticd, debug_staticd_cmd, diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index d8a4b7f0cb..1bdbb69d00 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -431,6 +431,10 @@ extern void static_zebra_route_add(struct route_node *rn, api_nh->vrf_id = nh->nh_vrf_id; if (nh->onlink) SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK); + if (nh->color != 0) { + SET_FLAG(api.message, ZAPI_MESSAGE_SRTE); + api_nh->srte_color = nh->color; + } nh->state = STATIC_SENT_TO_ZEBRA; diff --git a/tests/.gitignore b/tests/.gitignore index 5414cb8cc9..5e809a81e6 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -13,6 +13,7 @@ /isisd/test_fuzz_isis_tlv /isisd/test_fuzz_isis_tlv_tests.h /isisd/test_isis_lspdb +/isisd/test_isis_spf /isisd/test_isis_vertex_queue /lib/cli/test_cli /lib/cli/test_cli_clippy.c diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c index f6a892df42..0979622eb3 100644 --- a/tests/bgpd/test_peer_attr.c +++ b/tests/bgpd/test_peer_attr.c @@ -1387,7 +1387,7 @@ static void bgp_startup(void) master = thread_master_create(NULL); yang_init(true); - nb_init(master, NULL, 0); + nb_init(master, NULL, 0, false); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE); bgp_option_set(BGP_OPT_NO_LISTEN); vrf_init(NULL, NULL, NULL, NULL, NULL); diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index 68ed16d513..7f15996daa 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -156,7 +156,7 @@ int main(int argc, char **argv) vty_init(master, false); lib_cmd_init(); yang_init(true); - nb_init(master, NULL, 0); + nb_init(master, NULL, 0, false); /* OSPF vty inits. */ test_vty_init(); diff --git a/tests/isisd/test_common.c b/tests/isisd/test_common.c new file mode 100644 index 0000000000..536847a1da --- /dev/null +++ b/tests/isisd/test_common.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * Renato Westphal + * + * 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 "isisd/isisd.h" +#include "isisd/isis_dynhn.h" +#include "isisd/isis_mt.h" + +#include "test_common.h" + +struct thread_master *master; +struct zebra_privs_t isisd_privs; + +int isis_sock_init(struct isis_circuit *circuit) +{ + return 0; +} + +const struct isis_test_node * +test_topology_find_node(const struct isis_topology *topology, + const char *hostname, uint8_t pseudonode_id) +{ + for (size_t i = 0; topology->nodes[i].hostname[0]; i++) + if (strmatch(hostname, topology->nodes[i].hostname) + && pseudonode_id == topology->nodes[i].pseudonode_id) + return &topology->nodes[i]; + + return NULL; +} + +const struct isis_topology * +test_topology_find(struct isis_topology *test_topologies, uint16_t number) +{ + for (size_t i = 0; test_topologies[i].number; i++) + if (test_topologies[i].number == number) + return &test_topologies[i]; + + return NULL; +} + +static const struct isis_test_node * +test_find_adjacency(const struct isis_test_node *tnode, const char *hostname) +{ + for (size_t i = 0; tnode->adjacencies[i].hostname[0]; i++) { + const struct isis_test_adj *tadj; + + tadj = &tnode->adjacencies[i]; + if (strmatch(hostname, tadj->hostname)) + return tnode; + } + + return NULL; +} + +static struct isis_lsp *lsp_add(struct lspdb_head *lspdb, + struct isis_area *area, int level, + const uint8_t *sysid, uint8_t pseudonode_id) +{ + struct isis_lsp *lsp; + uint8_t lspid[ISIS_SYS_ID_LEN + 2]; + + memcpy(lspid, sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(lspid) = pseudonode_id; + LSP_FRAGMENT(lspid) = 0; + + lsp = lsp_new(area, lspid, 6000, 1, 0, 0, NULL, level); + lsp->tlvs = isis_alloc_tlvs(); + lspdb_add(lspdb, lsp); + + return lsp; +} + +static void lsp_add_ip_reach(struct isis_lsp *lsp, + const struct isis_test_node *tnode, + const char *prefix_str, uint32_t *next_sid_index) +{ + struct prefix prefix; + struct sr_prefix_cfg pcfg = {}; + struct sr_prefix_cfg *pcfg_p = NULL; + + if (str2prefix(prefix_str, &prefix) != 1) { + zlog_debug("%s: invalid network: %s", __func__, prefix_str); + return; + } + + if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { + pcfg_p = &pcfg; + + pcfg.sid = *next_sid_index; + *next_sid_index = *next_sid_index + 1; + pcfg.sid_type = SR_SID_VALUE_TYPE_INDEX; + pcfg.last_hop_behavior = SR_LAST_HOP_BEHAVIOR_PHP; + } + + if (prefix.family == AF_INET) + isis_tlvs_add_extended_ip_reach(lsp->tlvs, + (struct prefix_ipv4 *)&prefix, + 10, false, pcfg_p); + else + isis_tlvs_add_ipv6_reach(lsp->tlvs, ISIS_MT_IPV6_UNICAST, + (struct prefix_ipv6 *)&prefix, 10, + false, pcfg_p); +} + +static void lsp_add_reach(struct isis_lsp *lsp, + const struct isis_test_node *tnode, + const uint8_t *ne_id, uint8_t pseudonode_id, + uint32_t metric, int family, mpls_label_t *next_label) +{ + uint8_t nodeid[ISIS_SYS_ID_LEN + 1]; + uint16_t mtid; + struct isis_ext_subtlvs *ext = NULL; + + memcpy(nodeid, ne_id, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID(nodeid) = pseudonode_id; + + if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { + struct isis_adj_sid *adj_sid; + + adj_sid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*adj_sid)); + adj_sid->family = family; + SET_FLAG(adj_sid->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG); + SET_FLAG(adj_sid->flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG); + if (family == AF_INET6) + SET_FLAG(adj_sid->flags, EXT_SUBTLV_LINK_ADJ_SID_FFLG); + adj_sid->weight = 0; + adj_sid->sid = *next_label; + *next_label = *next_label + 1; + + ext = isis_alloc_ext_subtlvs(); + isis_tlvs_add_adj_sid(ext, adj_sid); + } + + mtid = (family == AF_INET) ? ISIS_MT_IPV4_UNICAST + : ISIS_MT_IPV6_UNICAST; + + isis_tlvs_add_extended_reach(lsp->tlvs, mtid, nodeid, metric, ext); +} + +static void lsp_add_router_capability(struct isis_lsp *lsp, + const struct isis_test_node *tnode) +{ + struct isis_router_cap cap = {}; + + if (!tnode->router_id) + return; + + if (inet_pton(AF_INET, tnode->router_id, &cap.router_id) != 1) { + zlog_debug("%s: invalid router-id: %s", __func__, + tnode->router_id); + return; + } + + if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { + cap.srgb.flags = + ISIS_SUBTLV_SRGB_FLAG_I | ISIS_SUBTLV_SRGB_FLAG_V; + cap.srgb.lower_bound = tnode->srgb.lower_bound + ? tnode->srgb.lower_bound + : SRGB_DFTL_LOWER_BOUND; + cap.srgb.range_size = tnode->srgb.range_size + ? tnode->srgb.range_size + : SRGB_DFTL_RANGE_SIZE; + cap.algo[0] = SR_ALGORITHM_SPF; + cap.algo[1] = SR_ALGORITHM_UNSET; + } + + isis_tlvs_set_router_capability(lsp->tlvs, &cap); +} + +static void lsp_add_mt_router_info(struct isis_lsp *lsp, + const struct isis_test_node *tnode) +{ + if (tnode->protocols.ipv4) + isis_tlvs_add_mt_router_info(lsp->tlvs, ISIS_MT_IPV4_UNICAST, 0, + false); + if (tnode->protocols.ipv6) + isis_tlvs_add_mt_router_info(lsp->tlvs, ISIS_MT_IPV6_UNICAST, 0, + false); +} + +static void lsp_add_protocols_supported(struct isis_lsp *lsp, + const struct isis_test_node *tnode) +{ + struct nlpids nlpids = {}; + + if (!tnode->protocols.ipv4 && !tnode->protocols.ipv6) + return; + + if (tnode->protocols.ipv4) { + nlpids.nlpids[nlpids.count] = NLPID_IP; + nlpids.count++; + } + if (tnode->protocols.ipv6) { + nlpids.nlpids[nlpids.count] = NLPID_IPV6; + nlpids.count++; + } + isis_tlvs_set_protocols_supported(lsp->tlvs, &nlpids); +} + +static int topology_load_node_level(const struct isis_topology *topology, + const struct isis_test_node *tnode, + size_t tnode_index, struct isis_area *area, + struct lspdb_head *lspdb, int level) +{ + struct isis_lsp *lsp; + uint32_t next_sid_index = (tnode_index + 1) * 10; + mpls_label_t next_label = 16; + + lsp = lsp_add(lspdb, area, level, tnode->sysid, tnode->pseudonode_id); + lsp_add_mt_router_info(lsp, tnode); + lsp_add_protocols_supported(lsp, tnode); + lsp_add_router_capability(lsp, tnode); + + /* Add IP Reachability Information. */ + for (size_t i = 0; tnode->networks[i]; i++) { + if (i > MAX_NETWORKS) { + zlog_debug( + "%s: node has too many networks (maximum is %u)", + __func__, MAX_NETWORKS); + return -1; + } + lsp_add_ip_reach(lsp, tnode, tnode->networks[i], + &next_sid_index); + } + + /* Add IS Reachability Information. */ + for (size_t i = 0; tnode->adjacencies[i].hostname[0]; i++) { + const struct isis_test_adj *tadj; + const struct isis_test_node *tadj_node; + + if (i > MAX_ADJACENCIES) { + zlog_debug( + "%s: node has too many adjacencies (maximum is %u)", + __func__, MAX_ADJACENCIES); + return -1; + } + + tadj = &tnode->adjacencies[i]; + tadj_node = test_topology_find_node(topology, tadj->hostname, + tadj->pseudonode_id); + if (!tadj_node) { + zlog_debug( + "%s: node \"%s\" has an adjacency with non-existing node \"%s\"", + __func__, tnode->hostname, tadj->hostname); + return -1; + } + if (!test_find_adjacency(tadj_node, tnode->hostname)) { + zlog_debug( + "%s: node \"%s\" has an one-way adjacency with node \"%s\"", + __func__, tnode->hostname, tadj->hostname); + return -1; + } + + if (tnode->pseudonode_id || tadj_node->pseudonode_id + || (tnode->protocols.ipv4 && tadj_node->protocols.ipv4)) + lsp_add_reach(lsp, tnode, tadj_node->sysid, + tadj_node->pseudonode_id, tadj->metric, + AF_INET, &next_label); + if (tadj_node->pseudonode_id + || (tnode->protocols.ipv6 && tadj_node->protocols.ipv6)) + lsp_add_reach(lsp, tnode, tadj_node->sysid, + tadj_node->pseudonode_id, tadj->metric, + AF_INET6, &next_label); + } + + return 0; +} + +static int topology_load_node(const struct isis_topology *topology, + const struct isis_test_node *tnode, + size_t tnode_index, struct isis_area *area, + struct lspdb_head lspdb[]) +{ + int ret; + + isis_dynhn_insert(tnode->sysid, tnode->hostname, tnode->level); + + for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { + if ((tnode->level & level) == 0) + continue; + + ret = topology_load_node_level(topology, tnode, tnode_index, + area, &lspdb[level - 1], level); + if (ret != 0) + return ret; + } + + return 0; +} + +int test_topology_load(const struct isis_topology *topology, + struct isis_area *area, struct lspdb_head lspdb[]) +{ + for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) + lsp_db_init(&lspdb[level - 1]); + + for (size_t i = 0; topology->nodes[i].hostname[0]; i++) { + const struct isis_test_node *tnode = &topology->nodes[i]; + int ret; + + if (i > MAX_NODES) { + zlog_debug( + "%s: topology has too many nodes (maximum is %u)", + __func__, MAX_NODES); + return -1; + } + + ret = topology_load_node(topology, tnode, i, area, lspdb); + if (ret != 0) + return ret; + } + + return 0; +} diff --git a/tests/isisd/test_common.h b/tests/isisd/test_common.h new file mode 100644 index 0000000000..6fd0d3813e --- /dev/null +++ b/tests/isisd/test_common.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * Renato Westphal + * + * 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 _COMMON_ISIS_H +#define _COMMON_ISIS_H + +#include "isisd/isisd.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_spf_private.h" + +#define MAX_HOSTNAME 16 +#define MAX_NETWORKS 8 +#define MAX_ADJACENCIES 8 +#define MAX_NODES 12 + +#define SRGB_DFTL_LOWER_BOUND 16000 +#define SRGB_DFTL_RANGE_SIZE 8000 + +struct isis_test_adj { + char hostname[MAX_HOSTNAME]; + uint8_t pseudonode_id; + uint32_t metric; +}; + +struct isis_test_node { + char hostname[MAX_HOSTNAME]; + uint8_t sysid[ISIS_SYS_ID_LEN]; + uint8_t pseudonode_id; + int level; + struct { + bool ipv4; + bool ipv6; + } protocols; + const char *router_id; + struct { + uint32_t lower_bound; + uint32_t range_size; + } srgb; + const char *networks[MAX_NETWORKS + 1]; + struct isis_test_adj adjacencies[MAX_ADJACENCIES + 1]; + uint8_t flags; +}; +#define F_ISIS_TEST_NODE_SR 0x01 + +struct isis_topology { + uint16_t number; + struct isis_test_node nodes[MAX_NODES + 1]; +}; + +/* Prototypes. */ +extern int isis_sock_init(struct isis_circuit *circuit); +extern const struct isis_test_node * +test_topology_find_node(const struct isis_topology *topology, + const char *hostname, uint8_t pseudonode_id); +extern const struct isis_topology * +test_topology_find(struct isis_topology *test_topologies, uint16_t number); +extern int test_topology_load(const struct isis_topology *topology, + struct isis_area *area, + struct lspdb_head lspdb[]); + +/* Global variables. */ +extern struct thread_master *master; +extern struct zebra_privs_t isisd_privs; +extern struct isis_topology test_topologies[]; + +#endif /* _COMMON_ISIS_H */ diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 917ea66f13..97aade6578 100644 --- a/tests/isisd/test_fuzz_isis_tlv.c +++ b/tests/isisd/test_fuzz_isis_tlv.c @@ -14,16 +14,9 @@ #include "isisd/isis_circuit.h" #include "isisd/isis_tlvs.h" -#define TEST_STREAM_SIZE 1500 - -struct thread_master *master; -int isis_sock_init(struct isis_circuit *circuit); -int isis_sock_init(struct isis_circuit *circuit) -{ - return 0; -} +#include "test_common.h" -struct zebra_privs_t isisd_privs; +#define TEST_STREAM_SIZE 1500 static bool atexit_registered; diff --git a/tests/isisd/test_isis_lspdb.c b/tests/isisd/test_isis_lspdb.c index f0baa482c7..244922ea4e 100644 --- a/tests/isisd/test_isis_lspdb.c +++ b/tests/isisd/test_isis_lspdb.c @@ -2,15 +2,7 @@ #include "isisd/isis_lsp.c" -struct thread_master *master; - -int isis_sock_init(struct isis_circuit *circuit); -int isis_sock_init(struct isis_circuit *circuit) -{ - return 0; -} - -struct zebra_privs_t isisd_privs; +#include "test_common.h" static void test_lsp_build_list_nonzero_ht(void) { @@ -82,6 +74,7 @@ static void test_lsp_build_list_nonzero_ht(void) int main(int argc, char **argv) { + struct isis *isis = NULL; isis = calloc(sizeof(*isis), 1); test_lsp_build_list_nonzero_ht(); return 0; diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c new file mode 100644 index 0000000000..73bb531dc0 --- /dev/null +++ b/tests/isisd/test_isis_spf.c @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * Renato Westphal + * + * 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 <lib/version.h> +#include "getopt.h" +#include "thread.h" +#include "vty.h" +#include "command.h" +#include "log.h" +#include "vrf.h" +#include "yang.h" + +#include "isisd/isisd.h" +#include "isisd/isis_dynhn.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_spf_private.h" + +#include "test_common.h" + +enum test_type { + TEST_SPF = 1, + TEST_REVERSE_SPF, +}; + +#define F_DISPLAY_LSPDB 0x01 +#define F_IPV4_ONLY 0x02 +#define F_IPV6_ONLY 0x04 +#define F_LEVEL1_ONLY 0x08 +#define F_LEVEL2_ONLY 0x10 + +static struct isis *isis; + +static void test_run_spf(struct vty *vty, const struct isis_topology *topology, + const struct isis_test_node *root, + struct isis_area *area, struct lspdb_head *lspdb, + int level, int tree, bool reverse) +{ + struct isis_spftree *spftree; + enum spf_type spf_type; + + /* Run SPF. */ + spf_type = reverse ? SPF_TYPE_REVERSE : SPF_TYPE_FORWARD; + spftree = isis_spftree_new(area, lspdb, root->sysid, level, tree, + spf_type, F_SPFTREE_NO_ADJACENCIES); + isis_run_spf(spftree); + + /* Print the SPT and the corresponding routing table. */ + isis_print_spftree(vty, spftree); + isis_print_routes(vty, spftree); + + /* Cleanup SPF tree. */ + isis_spftree_del(spftree); +} + +static int test_run(struct vty *vty, const struct isis_topology *topology, + const struct isis_test_node *root, enum test_type test_type, + uint8_t flags) +{ + struct isis_area *area; + + /* Init topology. */ + memcpy(isis->sysid, root->sysid, sizeof(isis->sysid)); + area = isis_area_create("1", NULL); + area->is_type = IS_LEVEL_1_AND_2; + area->srdb.enabled = true; + if (test_topology_load(topology, area, area->lspdb) != 0) { + vty_out(vty, "%% Failed to load topology\n"); + return CMD_WARNING; + } + + for (int level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) { + if (level == IS_LEVEL_1 && CHECK_FLAG(flags, F_LEVEL2_ONLY)) + continue; + if (level == IS_LEVEL_2 && CHECK_FLAG(flags, F_LEVEL1_ONLY)) + continue; + if ((root->level & level) == 0) + continue; + + /* Print the LDPDB. */ + if (CHECK_FLAG(flags, F_DISPLAY_LSPDB)) + show_isis_database_lspdb(vty, area, level - 1, + &area->lspdb[level - 1], NULL, + ISIS_UI_LEVEL_DETAIL); + + for (int tree = SPFTREE_IPV4; tree <= SPFTREE_IPV6; tree++) { + if (tree == SPFTREE_IPV4 + && CHECK_FLAG(flags, F_IPV6_ONLY)) + continue; + if (tree == SPFTREE_IPV6 + && CHECK_FLAG(flags, F_IPV4_ONLY)) + continue; + + switch (test_type) { + case TEST_SPF: + test_run_spf(vty, topology, root, area, + &area->lspdb[level - 1], level, + tree, false); + break; + case TEST_REVERSE_SPF: + test_run_spf(vty, topology, root, area, + &area->lspdb[level - 1], level, + tree, true); + break; + } + } + } + + /* Cleanup IS-IS area. */ + isis_area_destroy(area); + + /* Cleanup hostnames. */ + dyn_cache_cleanup_all(); + + return CMD_SUCCESS; +} + +DEFUN(test_isis, test_isis_cmd, + "test isis topology (1-13) root HOSTNAME\ + <\ + spf\ + |reverse-spf\ + >\ + [display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]", + "Test command\n" + "IS-IS routing protocol\n" + "Test topology\n" + "Test topology number\n" + "SPF root\n" + "SPF root hostname\n" + "Normal Shortest Path First\n" + "Reverse Shortest Path First\n" + "Display the LSPDB\n" + "Do IPv4 processing only\n" + "Do IPv6 processing only\n" + "Skip L2 LSPs\n" + "Skip L1 LSPs\n") +{ + uint16_t topology_number; + const struct isis_topology *topology; + const struct isis_test_node *root; + enum test_type test_type; + uint8_t flags = 0; + int idx = 0; + + /* Load topology. */ + argv_find(argv, argc, "topology", &idx); + topology_number = atoi(argv[idx + 1]->arg); + topology = test_topology_find(test_topologies, topology_number); + if (!topology) { + vty_out(vty, "%% Topology \"%s\" not found\n", + argv[idx + 1]->arg); + return CMD_WARNING; + } + + /* Find root node. */ + argv_find(argv, argc, "root", &idx); + root = test_topology_find_node(topology, argv[idx + 1]->arg, 0); + if (!root) { + vty_out(vty, "%% Node \"%s\" not found\n", argv[idx + 1]->arg); + return CMD_WARNING; + } + + /* Parse test information. */ + if (argv_find(argv, argc, "spf", &idx)) + test_type = TEST_SPF; + else if (argv_find(argv, argc, "reverse-spf", &idx)) + test_type = TEST_REVERSE_SPF; + else + return CMD_WARNING; + + /* Parse control flags. */ + if (argv_find(argv, argc, "display-lspdb", &idx)) + SET_FLAG(flags, F_DISPLAY_LSPDB); + if (argv_find(argv, argc, "ipv4-only", &idx)) + SET_FLAG(flags, F_IPV4_ONLY); + else if (argv_find(argv, argc, "ipv6-only", &idx)) + SET_FLAG(flags, F_IPV6_ONLY); + if (argv_find(argv, argc, "level-1-only", &idx)) + SET_FLAG(flags, F_LEVEL1_ONLY); + else if (argv_find(argv, argc, "level-2-only", &idx)) + SET_FLAG(flags, F_LEVEL2_ONLY); + + return test_run(vty, topology, root, test_type, flags); +} + +static void vty_do_exit(int isexit) +{ + printf("\nend.\n"); + + isis_finish(isis); + cmd_terminate(); + vty_terminate(); + yang_terminate(); + thread_master_free(master); + + log_memstats(stderr, "test-isis-spf"); + if (!isexit) + exit(0); +} + +struct option longopts[] = {{"help", no_argument, NULL, 'h'}, + {"debug", no_argument, NULL, 'd'}, + {0}}; + +/* Help information display. */ +static void usage(char *progname, int status) +{ + if (status != 0) + fprintf(stderr, "Try `%s --help' for more information.\n", + progname); + else { + printf("Usage : %s [OPTION...]\n\ +isisd SPF test program.\n\n\ +-u, --debug Enable debugging\n\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", + progname, FRR_BUG_ADDRESS); + } + exit(status); +} + +int main(int argc, char **argv) +{ + char *p; + char *progname; + struct thread thread; + bool debug = false; + + /* Set umask before anything for security */ + umask(0027); + + /* get program name */ + progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); + + while (1) { + int opt; + + opt = getopt_long(argc, argv, "hd", longopts, 0); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + case 'd': + debug = true; + break; + case 'h': + usage(progname, 0); + break; + default: + usage(progname, 1); + break; + } + } + + /* master init. */ + master = thread_master_create(NULL); + isis_master_init(master); + + /* Library inits. */ + cmd_init(1); + cmd_hostname_set("test"); + vty_init(master, false); + yang_init(true); + if (debug) + zlog_aux_init("NONE: ", LOG_DEBUG); + else + zlog_aux_init("NONE: ", ZLOG_DISABLED); + + /* IS-IS inits. */ + yang_module_load("frr-isisd"); + isis = isis_new(VRF_DEFAULT_NAME); + listnode_add(im->isis, isis); + SET_FLAG(im->options, F_ISIS_UNIT_TEST); + debug_spf_events |= DEBUG_SPF_EVENTS; + debug_events |= DEBUG_EVENTS; + debug_rte_events |= DEBUG_RTE_EVENTS; + + /* Install test command. */ + install_element(VIEW_NODE, &test_isis_cmd); + + /* Read input from .in file. */ + vty_stdio(vty_do_exit); + + /* Fetch next active thread. */ + while (thread_fetch(master, &thread)) + thread_call(&thread); + + /* Not reached. */ + exit(0); +} diff --git a/tests/isisd/test_isis_spf.in b/tests/isisd/test_isis_spf.in new file mode 100644 index 0000000000..d9a61782e9 --- /dev/null +++ b/tests/isisd/test_isis_spf.in @@ -0,0 +1,16 @@ +test isis topology 1 root rt1 spf +test isis topology 2 root rt1 spf +test isis topology 3 root rt1 spf ipv4-only +test isis topology 4 root rt1 spf ipv4-only +test isis topology 5 root rt1 spf ipv4-only +test isis topology 6 root rt1 spf ipv4-only +test isis topology 7 root rt1 spf ipv4-only +test isis topology 8 root rt1 spf ipv4-only +test isis topology 9 root rt1 spf +test isis topology 10 root rt1 spf +test isis topology 11 root rt1 spf +test isis topology 12 root rt1 spf ipv4-only +test isis topology 13 root rt1 spf ipv4-only + +test isis topology 4 root rt1 reverse-spf ipv4-only +test isis topology 11 root rt1 reverse-spf diff --git a/tests/isisd/test_isis_spf.py b/tests/isisd/test_isis_spf.py new file mode 100644 index 0000000000..21e7ef6269 --- /dev/null +++ b/tests/isisd/test_isis_spf.py @@ -0,0 +1,4 @@ +import frrtest + +class TestIsisSPF(frrtest.TestRefOut): + program = './test_isis_spf' diff --git a/tests/isisd/test_isis_spf.refout b/tests/isisd/test_isis_spf.refout new file mode 100644 index 0000000000..ed0569947c --- /dev/null +++ b/tests/isisd/test_isis_spf.refout @@ -0,0 +1,703 @@ +test# test isis topology 1 root rt1 spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+ rt3 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 30 - rt2 -
+ 10.0.255.5/32 30 - rt3 -
+ 10.0.255.6/32 40 - rt2 -
+ - rt3 -
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt2 - rt6(4)
+ rt3 -
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::2/128 20 - rt2 -
+ 2001:db8::3/128 20 - rt3 -
+ 2001:db8::4/128 30 - rt2 -
+ 2001:db8::5/128 30 - rt3 -
+ 2001:db8::6/128 40 - rt2 -
+ - rt3 -
+
+test# test isis topology 2 root rt1 spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt5 TE-IS 10 rt5 - rt1(4)
+rt2 TE-IS 15 rt2 - rt1(4)
+rt1
+rt6 TE-IS 20 rt4 - rt4(4)
+ rt5 - rt5(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)
+10.0.255.2/32 IP TE 25 rt2 - rt2(4)
+rt3 TE-IS 30 rt3 - rt1(4)
+10.0.255.6/32 IP TE 30 rt4 - rt6(4)
+ rt5 -
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 25 - rt2 -
+ 10.0.255.3/32 40 - rt3 -
+ 10.0.255.4/32 20 - rt4 -
+ 10.0.255.5/32 20 - rt5 -
+ 10.0.255.6/32 30 - rt4 -
+ - rt5 -
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt5 TE-IS 10 rt5 - rt1(4)
+rt2 TE-IS 15 rt2 - rt1(4)
+rt1
+rt6 TE-IS 20 rt4 - rt4(4)
+ rt5 - rt5(4)
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)
+2001:db8::5/128 IP6 internal 20 rt5 - rt5(4)
+2001:db8::2/128 IP6 internal 25 rt2 - rt2(4)
+rt3 TE-IS 30 rt3 - rt1(4)
+2001:db8::6/128 IP6 internal 30 rt4 - rt6(4)
+ rt5 -
+2001:db8::3/128 IP6 internal 40 rt3 - rt3(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::2/128 25 - rt2 -
+ 2001:db8::3/128 40 - rt3 -
+ 2001:db8::4/128 20 - rt4 -
+ 2001:db8::5/128 20 - rt5 -
+ 2001:db8::6/128 30 - rt4 -
+ - rt5 -
+
+test# test isis topology 3 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 40 rt2 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 30 - rt2 -
+ 10.0.255.5/32 40 - rt2 -
+ 10.0.255.6/32 40 - rt2 -
+
+test# test isis topology 4 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 30 - rt2 -
+ 10.0.255.5/32 30 - rt3 -
+ 10.0.255.6/32 40 - rt2 -
+ 10.0.255.7/32 40 - rt3 -
+ 10.0.255.8/32 50 - rt2 -
+
+test# test isis topology 5 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+ rt3 - rt7(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+ rt3 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 30 - rt2 -
+ 10.0.255.5/32 30 - rt3 -
+ 10.0.255.6/32 40 - rt2 -
+ 10.0.255.7/32 40 - rt3 -
+ 10.0.255.8/32 50 - rt2 -
+ - rt3 -
+
+test# test isis topology 6 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 -
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+ rt3 -
+rt5 TE-IS 40 rt2 - rt6(4)
+ rt3 -
+rt8 TE-IS 40 rt2 - rt6(4)
+ rt3 -
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+ rt3 -
+rt7 TE-IS 50 rt2 - rt5(4)
+ rt3 - rt8(4)
+10.0.255.5/32 IP TE 50 rt2 - rt5(4)
+ rt3 -
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+ rt3 -
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)
+ rt3 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 30 - rt2 -
+ - rt3 -
+ 10.0.255.5/32 50 - rt2 -
+ - rt3 -
+ 10.0.255.6/32 40 - rt2 -
+ - rt3 -
+ 10.0.255.7/32 60 - rt2 -
+ - rt3 -
+ 10.0.255.8/32 50 - rt2 -
+ - rt3 -
+
+test# test isis topology 7 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt5 TE-IS 20 rt4 - rt4(4)
+rt7 TE-IS 20 rt4 - rt4(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt2 TE-IS 30 rt4 - rt5(4)
+rt6 TE-IS 30 rt4 - rt5(4)
+rt8 TE-IS 30 rt4 - rt5(4)
+ rt7(4)
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)
+10.0.255.7/32 IP TE 30 rt4 - rt7(4)
+rt10 TE-IS 40 rt4 - rt7(4)
+rt3 TE-IS 40 rt4 - rt2(4)
+ rt6(4)
+rt9 TE-IS 40 rt4 - rt8(4)
+rt11 TE-IS 40 rt4 - rt8(4)
+10.0.255.2/32 IP TE 40 rt4 - rt2(4)
+10.0.255.6/32 IP TE 40 rt4 - rt6(4)
+10.0.255.8/32 IP TE 40 rt4 - rt8(4)
+rt12 TE-IS 50 rt4 - rt9(4)
+ rt11(4)
+10.0.255.10/32 IP TE 50 rt4 - rt10(4)
+10.0.255.3/32 IP TE 50 rt4 - rt3(4)
+10.0.255.9/32 IP TE 50 rt4 - rt9(4)
+10.0.255.11/32 IP TE 50 rt4 - rt11(4)
+10.0.255.12/32 IP TE 60 rt4 - rt12(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------
+ 10.0.255.2/32 40 - rt4 -
+ 10.0.255.3/32 50 - rt4 -
+ 10.0.255.4/32 20 - rt4 -
+ 10.0.255.5/32 30 - rt4 -
+ 10.0.255.6/32 40 - rt4 -
+ 10.0.255.7/32 30 - rt4 -
+ 10.0.255.8/32 40 - rt4 -
+ 10.0.255.9/32 50 - rt4 -
+ 10.0.255.10/32 50 - rt4 -
+ 10.0.255.11/32 50 - rt4 -
+ 10.0.255.12/32 60 - rt4 -
+
+test# test isis topology 8 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt4 TE-IS 10 rt4 - rt1(4)
+rt3 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt2 - rt2(4)
+rt7 TE-IS 20 rt4 - rt4(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)
+rt6 TE-IS 30 rt2 - rt3(4)
+ rt5(4)
+rt8 TE-IS 30 rt2 - rt5(4)
+rt10 TE-IS 30 rt4 - rt7(4)
+10.0.255.3/32 IP TE 30 rt2 - rt3(4)
+10.0.255.5/32 IP TE 30 rt2 - rt5(4)
+10.0.255.7/32 IP TE 30 rt4 - rt7(4)
+rt9 TE-IS 40 rt2 - rt8(4)
+rt11 TE-IS 40 rt2 - rt8(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.8/32 IP TE 40 rt2 - rt8(4)
+10.0.255.10/32 IP TE 40 rt4 - rt10(4)
+rt12 TE-IS 50 rt2 - rt9(4)
+ rt11(4)
+10.0.255.9/32 IP TE 50 rt2 - rt9(4)
+10.0.255.11/32 IP TE 50 rt2 - rt11(4)
+10.0.255.12/32 IP TE 60 rt2 - rt12(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 30 - rt2 -
+ 10.0.255.4/32 20 - rt4 -
+ 10.0.255.5/32 30 - rt2 -
+ 10.0.255.6/32 40 - rt2 -
+ 10.0.255.7/32 30 - rt4 -
+ 10.0.255.8/32 40 - rt2 -
+ 10.0.255.9/32 50 - rt2 -
+ 10.0.255.10/32 40 - rt4 -
+ 10.0.255.11/32 50 - rt2 -
+ 10.0.255.12/32 60 - rt2 -
+
+test# test isis topology 9 root rt1 spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+rt9 TE-IS 40 rt2 - rt5(4)
+10.0.255.5/32 IP TE 40 rt2 - rt5(4)
+rt6 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt7 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt8 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+10.0.255.9/32 IP TE 50 rt2 - rt9(4)
+10.0.255.6/32 IP TE 60 rt2 - rt6(4)
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)
+10.0.255.8/32 IP TE 60 rt2 - rt8(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 30 - rt2 -
+ 10.0.255.5/32 40 - rt2 -
+ 10.0.255.6/32 60 - rt2 -
+ 10.0.255.7/32 60 - rt2 -
+ 10.0.255.8/32 60 - rt2 -
+ 10.0.255.9/32 50 - rt2 -
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt5 TE-IS 30 rt2 - rt4(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+rt9 TE-IS 40 rt2 - rt5(4)
+2001:db8::5/128 IP6 internal 40 rt2 - rt5(4)
+rt6 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt7 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+rt8 TE-IS 50 rt2 - rt4(4)
+ rt9(4)
+2001:db8::9/128 IP6 internal 50 rt2 - rt9(4)
+2001:db8::6/128 IP6 internal 60 rt2 - rt6(4)
+2001:db8::7/128 IP6 internal 60 rt2 - rt7(4)
+2001:db8::8/128 IP6 internal 60 rt2 - rt8(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::2/128 20 - rt2 -
+ 2001:db8::3/128 20 - rt3 -
+ 2001:db8::4/128 30 - rt2 -
+ 2001:db8::5/128 40 - rt2 -
+ 2001:db8::6/128 60 - rt2 -
+ 2001:db8::7/128 60 - rt2 -
+ 2001:db8::8/128 60 - rt2 -
+ 2001:db8::9/128 50 - rt2 -
+
+test# test isis topology 10 root rt1 spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 20 rt3 - rt1(4)
+rt4 TE-IS 20 rt4 - rt1(4)
+rt5 TE-IS 20 rt2 - rt2(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+rt6 TE-IS 30 rt3 - rt3(4)
+rt7 TE-IS 30 rt4 - rt4(4)
+rt8 TE-IS 30 rt2 - rt5(4)
+10.0.255.3/32 IP TE 30 rt3 - rt3(4)
+10.0.255.4/32 IP TE 30 rt4 - rt4(4)
+10.0.255.5/32 IP TE 30 rt2 - rt5(4)
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)
+10.0.255.7/32 IP TE 40 rt4 - rt7(4)
+10.0.255.8/32 IP TE 40 rt2 - rt8(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 30 - rt3 -
+ 10.0.255.4/32 30 - rt4 -
+ 10.0.255.5/32 30 - rt2 -
+ 10.0.255.6/32 40 - rt3 -
+ 10.0.255.7/32 40 - rt4 -
+ 10.0.255.8/32 40 - rt2 -
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 20 rt3 - rt1(4)
+rt4 TE-IS 20 rt4 - rt1(4)
+rt5 TE-IS 20 rt2 - rt2(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+rt6 TE-IS 30 rt3 - rt3(4)
+rt7 TE-IS 30 rt4 - rt4(4)
+rt8 TE-IS 30 rt2 - rt5(4)
+2001:db8::3/128 IP6 internal 30 rt3 - rt3(4)
+2001:db8::4/128 IP6 internal 30 rt4 - rt4(4)
+2001:db8::5/128 IP6 internal 30 rt2 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)
+2001:db8::7/128 IP6 internal 40 rt4 - rt7(4)
+2001:db8::8/128 IP6 internal 40 rt2 - rt8(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::2/128 20 - rt2 -
+ 2001:db8::3/128 30 - rt3 -
+ 2001:db8::4/128 30 - rt4 -
+ 2001:db8::5/128 30 - rt2 -
+ 2001:db8::6/128 40 - rt3 -
+ 2001:db8::7/128 40 - rt4 -
+ 2001:db8::8/128 40 - rt2 -
+
+test# test isis topology 11 root rt1 spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt2 pseudo_TE-IS 20 rt3 - rt3(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+ rt3 -
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 30 - rt2 -
+ 10.0.255.5/32 30 - rt3 -
+ 10.0.255.6/32 40 - rt2 -
+ - rt3 -
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt2 pseudo_TE-IS 20 rt3 - rt3(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+ rt3 - rt5(4)
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt2 - rt6(4)
+ rt3 -
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::2/128 20 - rt2 -
+ 2001:db8::3/128 20 - rt3 -
+ 2001:db8::4/128 30 - rt2 -
+ 2001:db8::5/128 30 - rt3 -
+ 2001:db8::6/128 40 - rt2 -
+ - rt3 -
+
+test# test isis topology 12 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+rt9 TE-IS 40 rt3 - rt7(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+rt10 TE-IS 50 rt2 - rt8(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+10.0.255.9/32 IP TE 50 rt3 - rt9(4)
+10.0.255.10/32 IP TE 60 rt2 - rt10(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ ------------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 30 - rt2 -
+ 10.0.255.5/32 30 - rt3 -
+ 10.0.255.6/32 40 - rt2 -
+ 10.0.255.7/32 40 - rt3 -
+ 10.0.255.8/32 50 - rt2 -
+ 10.0.255.9/32 50 - rt3 -
+ 10.0.255.10/32 60 - rt2 -
+
+test# test isis topology 13 root rt1 spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+ rt3 - rt3(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+rt6 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+ rt6(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+ rt3 -
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 30 - rt2 -
+ - rt3 -
+ 10.0.255.5/32 30 - rt3 -
+ 10.0.255.6/32 30 - rt3 -
+ 10.0.255.7/32 40 - rt3 -
+
+test#
+test# test isis topology 4 root rt1 reverse-spf ipv4-only
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt2 - rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt4 TE-IS 20 rt2 - rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt2 - rt4(4)
+rt7 TE-IS 30 rt3 - rt5(4)
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+rt8 TE-IS 40 rt2 - rt6(4)
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.2/32 20 - rt2 -
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.4/32 30 - rt2 -
+ 10.0.255.5/32 30 - rt3 -
+ 10.0.255.6/32 40 - rt2 -
+ 10.0.255.7/32 40 - rt3 -
+ 10.0.255.8/32 50 - rt2 -
+
+test# test isis topology 11 root rt1 reverse-spf
+IS-IS paths to level-1 routers that speak IP
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+10.0.255.1/32 IP internal 0 rt1(4)
+rt2 TE-IS 10 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt2 pseudo_TE-IS 20 rt3 - rt3(4)
+rt4 TE-IS 20 rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+10.0.255.2/32 IP TE 20 rt2(4)
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt4(4)
+ rt5(4)
+10.0.255.4/32 IP TE 30 rt4(4)
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)
+
+IS-IS L1 IPv4 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -----------------------------------------------------
+ 10.0.255.3/32 20 - rt3 -
+ 10.0.255.5/32 30 - rt3 -
+ 10.0.255.6/32 40 - rt3 -
+
+IS-IS paths to level-1 routers that speak IPv6
+Vertex Type Metric Next-Hop Interface Parent
+rt1
+2001:db8::1/128 IP6 internal 0 rt1(4)
+rt2 TE-IS 10 rt1(4)
+rt3 TE-IS 10 rt3 - rt1(4)
+rt2 pseudo_TE-IS 20 rt3 - rt3(4)
+rt4 TE-IS 20 rt2(4)
+rt5 TE-IS 20 rt3 - rt3(4)
+2001:db8::2/128 IP6 internal 20 rt2(4)
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)
+rt6 TE-IS 30 rt3 - rt4(4)
+ rt5(4)
+2001:db8::4/128 IP6 internal 30 rt4(4)
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)
+
+IS-IS L1 IPv6 routing table:
+
+ Prefix Metric Interface Nexthop Label(s)
+ -------------------------------------------------------
+ 2001:db8::3/128 20 - rt3 -
+ 2001:db8::5/128 30 - rt3 -
+ 2001:db8::6/128 40 - rt3 -
+
+test# +end. diff --git a/tests/isisd/test_isis_vertex_queue.c b/tests/isisd/test_isis_vertex_queue.c index 869dd732eb..d2d9f6293a 100644 --- a/tests/isisd/test_isis_vertex_queue.c +++ b/tests/isisd/test_isis_vertex_queue.c @@ -2,14 +2,7 @@ #include "isisd/isis_spf.c" -struct thread_master *master; -int isis_sock_init(struct isis_circuit *circuit); -int isis_sock_init(struct isis_circuit *circuit) -{ - return 0; -} - -struct zebra_privs_t isisd_privs; +#include "test_common.h" static struct isis_vertex **vertices; static size_t vertex_count; diff --git a/tests/isisd/test_topologies.c b/tests/isisd/test_topologies.c new file mode 100644 index 0000000000..ee89407a79 --- /dev/null +++ b/tests/isisd/test_topologies.c @@ -0,0 +1,3307 @@ +/* + * Copyright (C) 2020 NetDEF, Inc. + * Renato Westphal + * + * 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 "isisd/isisd.h" + +#include "test_common.h" + +/* + * clang-format off + * + * All topologies have the following properties: + * - The System-ID is 0000.0000.000X, where X is the node number (in hex); + * - The Router-ID is 10.0.255.X, where X is the node number; + * - The default link metric is 10; + * - When SR is enabled, Adj-SIDs and Prefix-SIDs are generated automatically; + * - When SR is enabled, the default SRGB is [16000-23999] (can be overriden). + * + * Test topology 1: + * ================ + * + * +---------+ + * | | + * | RT1 | + * +----------+ +----------+ + * | | | | + * | +---------+ | + * | | + * | | + * | | + * +----+----+ +----+----+ + * | | | | + * | RT2 | | RT3 | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * +---+-+---+ +----+----+ + * | | | | + * | RT4 | | RT5 | + * | | | | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * | +---------+ | + * | | | | + * | | RT6 | | + * +----------+ +----------+ + * | | + * +---------+ + * + * Test topology 2: + * ================ + * + * +---------+ + * | | + * | RT1 | + * +----------+ +----------+ + * | | | | + * | +----+----+ | + * | | | + * 15 | | | 30 + * | | | + * +----+----+ | +----+----+ + * | | | | | + * | RT2 | | | RT3 | + * | | | | | + * | | | | | + * +----+----+ | +----+----+ + * | | | + * 40 | | | 40 + * | | | + * +----+----+ | +----+----+ + * | | | | | + * | RT4 | | | RT5 | + * | +----------+----------+ | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * | +---------+ | + * | | | | + * | | RT6 | | + * +----------+ +----------+ + * | | + * +---------+ + * + * Test topology 3: + * ================ + * + * +---------+ + * | | + * | RT1 | + * +----------+ +----------+ + * | | | | + * | +---------+ | + * | | + * | | + * | | + * +----+----+ +----+----+ + * | | | | + * | RT2 | | RT3 | + * | +---------------------+ | + * | | | | + * +----+----+ +----+----+ + * | | + * | | 30 + * | | + * +----+----+ +----+----+ + * | | | | + * | RT4 | | RT5 | + * | +---------------------+ | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * | +---------+ | + * | | | | + * | | RT6 | | + * +----------+ +----------+ + * | | + * +---------+ + * + * Test topology 4: + * ================ + * + * +---------+ +---------+ + * | | | | + * | RT1 | | RT2 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT3 | | RT4 | + * | | | | + * | | | | + * +---+-----+ +------+--+ + * |^ | + * ||200 | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT5 | 50 | RT6 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT7 | | RT8 | + * | | | | + * | | | | + * +---------+ +---------+ + * + * Test topology 5: + * ================ + * + * +---------+ +---------+ + * | | | | + * | RT1 | | RT2 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT3 | | RT4 | + * | | | | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT5 | | RT6 | + * | | | | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT7 | | RT8 | + * | +---------------------+ | + * | | | | + * +---------+ +---------+ + * + * Test topology 6: + * ================ + * + * +---------+ +---------+ + * | | | | + * | RT1 | | RT2 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT3 | | RT4 | + * | +---------------------+ | + * | | | | + * +---------+ +------+--+ + * | + * | + * | + * +---------+ +------+--+ + * | | | | + * | RT5 | | RT6 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT7 | | RT8 | + * | +---------------------+ | + * | | | | + * +---------+ +---------+ + * + * Test topology 7: + * ================ + * + * +---------+ +---------+ +---------+ + * | | | | | | + * | RT1 | 40 | RT2 | | RT3 | + * | +---------------------+ +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +------+--+ + * | | | + * | | | + * | | | + * +---+-----+ +----+----+ +------+--+ + * | | | | | | + * | RT4 | | RT5 | | RT6 | + * | +---------------------+ +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +------+--+ + * | | | + * | | | 30 + * | | | + * +---+-----+ +----+----+ +------+--+ + * | | | | | | + * | RT7 | | RT8 | | RT9 | + * | +---------------------+ +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +------+--+ + * | | | + * | 20 | | + * | | | + * +---+-----+ +----+----+ +------+--+ + * | | | | | | + * | RT10 | | RT11 | | RT12 | + * | +---------------------+ +---------------------+ | + * | | | | | | + * +---------+ +---------+ +---------+ + * + * Test topology 8: + * ================ + * + * +---------+ +---------+ +---------+ + * | | | | | | + * | RT1 | | RT2 | | RT3 | + * | +---------------------+ +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +------+--+ + * | | | + * | | | + * | | | + * +---+-----+ +----+----+ +------+--+ + * | | | | | | + * | RT4 | | RT5 | | RT6 | + * | | | +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +---------+ + * | | + * | | + * | | + * +---+-----+ +----+----+ +---------+ + * | | | | | | + * | RT7 | | RT8 | | RT9 | + * | | | +---------------------+ | + * | | | | | | + * +---+-----+ +----+----+ +------+--+ + * | | | + * | | | + * | | | + * +---+-----+ +----+----+ +------+--+ + * | | | | | | + * | RT10 | | RT11 | | RT12 | + * | +---------------------+ +---------------------+ | + * | | 30 | | | | + * +---------+ +---------+ +---------+ + * + * Test topology 9: + * ================ + * + * +---------+ + * | | + * | RT1 | + * +----------+ +----------+ + * | | | | + * | +---------+ | + * | | + * | | + * | | + * +----+----+ +----+----+ + * | | | | + * | RT2 | | RT3 | + * | | | | + * | | | | + * +----+----+ +------+--+ + * | | + * | | + * | |100 + * | +---------+ | + * | | | | + * +----------+ RT4 +------------+ + * +----------------| |----------------+ + * | +-+ +--+ | + * | | +---------+ | | + * | | | | + * | |30 |30 |30 + * | | | | + * +----+----+ +----+----+ +----+----+ +----+----+ + * | | | | | | | | + * | RT5 | | RT6 | | RT7 | | RT8 | + * | | | | | | | | + * | | | | | | | | + * +----+----+ +----+----+ +----+----+ +----+----+ + * | | | | + * | | | | + * | | | | + * | | +---------+ | | + * | +-+ +--+ | + * +----------------+ RT9 +----------------+ + * | | + * | | + * +---------+ + * + * Test topology 10: + * ================ + * + * +---------+ + * | | + * | RT1 | + * +----------+ +----------+ + * | | | | + * | +----+----+ | + * | | | + * | |20 |20 + * | | | + * +----+----+ +----+----+ +----+----+ + * | | | | | | + * | RT2 | | RT3 | | RT4 | + * | | | | | | + * | | | | | | + * +----+----+ +----+----+ +----+----+ + * | | | + * | | | + * | | | + * +----+----+ +----+----+ +----+----+ + * | | | | | | + * | RT5 | | RT6 | | RT7 | + * | | | | | | + * | | | | | | + * +----+----+ +----+----+ +----+----+ + * | | | + * | |50 |50 + * | | | + * | +----+----+ | + * | | | | + * +----------+ RT8 +----------+ + * | | + * | | + * +---------+ + * + * Test topology 11: + * ================ + * + * +---------+ + * | | + * | RT1 | + * | | + * | | + * +----+----+ + * | + * | + * | + * +---------+ | +---------+ + * | | | | | + * | RT2 |50 | | RT3 | + * | +----------+----------+ | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * +----+----+ +----+----+ + * | | | | + * | RT4 | | RT5 | + * | +---------------------+ | + * | | | | + * +----+----+ +----+----+ + * | | + * | | + * | | + * | +---------+ | + * | | | | + * | | RT6 | | + * +----------+ +----------+ + * | | + * +---------+ + * + * Test topology 12: + * ================ + * + * +---------+ +---------+ + * | | | | + * | RT1 | | RT2 | + * | +---------------------+ | + * | | | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT3 | | RT4 | + * | | | | + * | | | | + * +---+-----+ +------+--+ + * |^ | + * |400 | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT5 | | RT6 | + * | | | | + * | | | | + * +---+-----+ +------+--+ + * |^ | + * |200 | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT7 | | RT8 | + * | +---------------------+ | + * | | 100 | | + * +---+-----+ +------+--+ + * | | + * | | + * | | + * +---+-----+ +------+--+ + * | | | | + * | RT9 | | RT10 | + * | | | | + * | | | | + * +---------+ +---------+ + * + * Test topology 13: + * ================ + * + * +---------+ +---------+ + * | | | | + * | RT1 | | RT2 | + * | +---------------------+ | + * | | | | + * +---+-----+ +----+----+ + * | | + * | | + * | | + * | +----+----+ + * | | | + * | +----------+ RT4 | + * | | | | + * +---+-----+ | | | + * | | | +----+----+ + * | RT3 +----------+ | + * | +----------+ |100 + * | | | | + * +---+-----+ | +----+----+ + * | | | | + * | | | RT5 | + * | +----------+ | + * | | | + * | +----+----+ + * | | + * | | + * | | + * +---+-----+ +----+----+ + * | | | | + * | RT6 | | RT7 | + * | +---------------------+ | + * | | | | + * +---------+ +---------+ + */ + +struct isis_topology test_topologies[] = { + { + .number = 1, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.1/32", + "2001:db8::1/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.2/32", + "2001:db8::2/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.3/32", + "2001:db8::3/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.4/32", + "2001:db8::4/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.5/32", + "2001:db8::5/128", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.6/32", + "2001:db8::6/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 2, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.1/32", + "2001:db8::1/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .pseudonode_id = 1, + .metric = 10, + }, + { + .hostname = "rt2", + .metric = 15, + }, + { + .hostname = "rt3", + .metric = 30, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.2/32", + "2001:db8::2/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 15, + }, + { + .hostname = "rt4", + .metric = 40, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.3/32", + "2001:db8::3/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 30, + }, + { + .hostname = "rt5", + .metric = 40, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.4/32", + "2001:db8::4/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .pseudonode_id = 1, + .metric = 10, + }, + { + .hostname = "rt2", + .metric = 40, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.5/32", + "2001:db8::5/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .pseudonode_id = 1, + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 40, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.6/32", + "2001:db8::6/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .pseudonode_id = 1, + .level = IS_LEVEL_1, + .adjacencies = { + { + .hostname = "rt1", + .metric = 0, + }, + { + .hostname = "rt4", + .metric = 0, + }, + { + .hostname = "rt5", + .metric = 0, + }, + }, + }, + }, + }, + { + .number = 3, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 30, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 30, + }, + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 4, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 200, + }, + { + .hostname = "rt6", + .metric = 50, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 50, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 5, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 6, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 7, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 40, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 40, + }, + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt9", + .metric = 30, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + { + .hostname = "rt10", + .metric = 20, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + { + .hostname = "rt9", + .metric = 10, + }, + { + .hostname = "rt11", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt9", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.9", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.9/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 30, + }, + { + .hostname = "rt8", + .metric = 10, + }, + { + .hostname = "rt12", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt10", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.10", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.10/32", + }, + .adjacencies = { + { + .hostname = "rt7", + .metric = 20, + }, + { + .hostname = "rt11", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt11", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0b}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.11", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.11/32", + }, + .adjacencies = { + { + .hostname = "rt8", + .metric = 10, + }, + { + .hostname = "rt10", + .metric = 10, + }, + { + .hostname = "rt12", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt12", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.12", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.12/32", + }, + .adjacencies = { + { + .hostname = "rt9", + .metric = 10, + }, + { + .hostname = "rt11", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 8, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt10", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt9", + .metric = 10, + }, + { + .hostname = "rt11", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt9", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.9", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.9/32", + }, + .adjacencies = { + { + .hostname = "rt8", + .metric = 10, + }, + { + .hostname = "rt12", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt10", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.10", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.10/32", + }, + .adjacencies = { + { + .hostname = "rt7", + .metric = 10, + }, + { + .hostname = "rt11", + .metric = 30, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt11", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0b}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.11", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.11/32", + }, + .adjacencies = { + { + .hostname = "rt8", + .metric = 10, + }, + { + .hostname = "rt10", + .metric = 30, + }, + { + .hostname = "rt12", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt12", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0c}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.12", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.12/32", + }, + .adjacencies = { + { + .hostname = "rt9", + .metric = 10, + }, + { + .hostname = "rt11", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 9, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.1/32", + "2001:db8::1/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.2/32", + "2001:db8::2/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.3/32", + "2001:db8::3/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 100, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.4/32", + "2001:db8::4/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 100, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 30, + }, + { + .hostname = "rt7", + .metric = 30, + }, + { + .hostname = "rt8", + .metric = 30, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.5/32", + "2001:db8::5/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt9", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.6/32", + "2001:db8::6/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 30, + }, + { + .hostname = "rt9", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.7/32", + "2001:db8::7/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 30, + }, + { + .hostname = "rt9", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.8/32", + "2001:db8::8/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 30, + }, + { + .hostname = "rt9", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt9", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.9", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.9/32", + "2001:db8::9/128", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 10, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.1/32", + "2001:db8::1/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 20, + }, + { + .hostname = "rt4", + .metric = 20, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.2/32", + "2001:db8::2/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .srgb = { + .lower_bound = 20000, + .range_size = 8000, + }, + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.3/32", + "2001:db8::3/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 20, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.4/32", + "2001:db8::4/128", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 20, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.5/32", + "2001:db8::5/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.6/32", + "2001:db8::6/128", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 50, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.7/32", + "2001:db8::7/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 50, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.8/32", + "2001:db8::8/128", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 50, + }, + { + .hostname = "rt7", + .metric = 50, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 11, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.1/32", + "2001:db8::1/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .pseudonode_id = 1, + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.2/32", + "2001:db8::2/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .pseudonode_id = 1, + .metric = 50, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.3/32", + "2001:db8::3/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .pseudonode_id = 1, + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.4/32", + "2001:db8::4/128", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.5/32", + "2001:db8::5/128", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + .ipv6 = true, + }, + .networks = { + "10.0.255.6/32", + "2001:db8::6/128", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .pseudonode_id = 1, + .level = IS_LEVEL_1, + .adjacencies = { + { + .hostname = "rt1", + .metric = 0, + }, + { + .hostname = "rt2", + .metric = 0, + }, + { + .hostname = "rt3", + .metric = 0, + }, + }, + }, + }, + }, + { + .number = 12, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 400, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 200, + }, + { + .hostname = "rt8", + .metric = 100, + }, + { + .hostname = "rt9", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt8", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x08}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.8", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.8/32", + }, + .adjacencies = { + { + .hostname = "rt6", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 100, + }, + { + .hostname = "rt10", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt9", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x09}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.9", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.9/32", + }, + .adjacencies = { + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt10", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0a}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.10", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.10/32", + }, + .adjacencies = { + { + .hostname = "rt8", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + .number = 13, + .nodes = { + { + .hostname = "rt1", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.1", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.1/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt2", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.2", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.2/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt3", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x03}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.3", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.3/32", + }, + .adjacencies = { + { + .hostname = "rt1", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt4", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x04}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.4", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.4/32", + }, + .adjacencies = { + { + .hostname = "rt2", + .metric = 10, + }, + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt5", + .metric = 100, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt5", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x05}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.5", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.5/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt4", + .metric = 100, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt6", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.6", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.6/32", + }, + .adjacencies = { + { + .hostname = "rt3", + .metric = 10, + }, + { + .hostname = "rt7", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + { + .hostname = "rt7", + .sysid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x07}, + .level = IS_LEVEL_1, + .router_id = "10.0.255.7", + .protocols = { + .ipv4 = true, + }, + .networks = { + "10.0.255.7/32", + }, + .adjacencies = { + { + .hostname = "rt5", + .metric = 10, + }, + { + .hostname = "rt6", + .metric = 10, + }, + }, + .flags = F_ISIS_TEST_NODE_SR, + }, + }, + }, + { + /* sentinel */ + }, +}; diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 3cade4a2c9..44cc6efe84 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -80,7 +80,7 @@ int main(int argc, char **argv) vty_init(master, false); lib_cmd_init(); yang_init(true); - nb_init(master, NULL, 0); + nb_init(master, NULL, 0, false); test_init(argc, argv); diff --git a/tests/lib/cli/test_commands.c b/tests/lib/cli/test_commands.c index 2b345c91e8..cb512211a4 100644 --- a/tests/lib/cli/test_commands.c +++ b/tests/lib/cli/test_commands.c @@ -209,7 +209,7 @@ static void test_init(void) cmd_init(1); yang_init(true); - nb_init(master, NULL, 0); + nb_init(master, NULL, 0, false); install_node(&bgp_node); install_node(&rip_node); diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index 202a321e1a..b5f257fa2f 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -400,7 +400,7 @@ int main(int argc, char **argv) vty_init(master, false); lib_cmd_init(); yang_init(true); - nb_init(master, modules, array_size(modules)); + nb_init(master, modules, array_size(modules), false); /* Create artificial data. */ create_data(num_vrfs, num_interfaces, num_routes); diff --git a/tests/subdir.am b/tests/subdir.am index 04053a6f46..e54bfc4a35 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -23,6 +23,7 @@ else TESTS_ISISD = \ tests/isisd/test_fuzz_isis_tlv \ tests/isisd/test_isis_lspdb \ + tests/isisd/test_isis_spf \ tests/isisd/test_isis_vertex_queue \ # end endif @@ -111,6 +112,7 @@ noinst_HEADERS += \ tests/helpers/c/tests.h \ tests/lib/cli/common_cli.h \ tests/lib/test_typelist.h \ + tests/isisd/test_common.h \ # end # @@ -168,16 +170,21 @@ tests_bgpd_test_peer_attr_SOURCES = tests/bgpd/test_peer_attr.c tests_isisd_test_fuzz_isis_tlv_CFLAGS = $(TESTS_CFLAGS) -I$(top_builddir)/tests/isisd tests_isisd_test_fuzz_isis_tlv_CPPFLAGS = $(TESTS_CPPFLAGS) -I$(top_builddir)/tests/isisd tests_isisd_test_fuzz_isis_tlv_LDADD = $(ISISD_TEST_LDADD) -tests_isisd_test_fuzz_isis_tlv_SOURCES = tests/isisd/test_fuzz_isis_tlv.c +tests_isisd_test_fuzz_isis_tlv_SOURCES = tests/isisd/test_fuzz_isis_tlv.c tests/isisd/test_common.c nodist_tests_isisd_test_fuzz_isis_tlv_SOURCES = tests/isisd/test_fuzz_isis_tlv_tests.h tests_isisd_test_isis_lspdb_CFLAGS = $(TESTS_CFLAGS) tests_isisd_test_isis_lspdb_CPPFLAGS = $(TESTS_CPPFLAGS) tests_isisd_test_isis_lspdb_LDADD = $(ISISD_TEST_LDADD) -tests_isisd_test_isis_lspdb_SOURCES = tests/isisd/test_isis_lspdb.c +tests_isisd_test_isis_lspdb_SOURCES = tests/isisd/test_isis_lspdb.c tests/isisd/test_common.c +tests_isisd_test_isis_spf_CFLAGS = $(TESTS_CFLAGS) +tests_isisd_test_isis_spf_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_isisd_test_isis_spf_LDADD = $(ISISD_TEST_LDADD) +tests_isisd_test_isis_spf_SOURCES = tests/isisd/test_isis_spf.c tests/isisd/test_common.c tests/isisd/test_topologies.c +nodist_tests_isisd_test_isis_spf_SOURCES = yang/frr-isisd.yang.c tests_isisd_test_isis_vertex_queue_CFLAGS = $(TESTS_CFLAGS) tests_isisd_test_isis_vertex_queue_CPPFLAGS = $(TESTS_CPPFLAGS) tests_isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD) -tests_isisd_test_isis_vertex_queue_SOURCES = tests/isisd/test_isis_vertex_queue.c +tests_isisd_test_isis_vertex_queue_SOURCES = tests/isisd/test_isis_vertex_queue.c tests/isisd/test_common.c tests_lib_cxxcompat_CFLAGS = $(TESTS_CFLAGS) $(CXX_COMPAT_CFLAGS) $(WERROR) tests_lib_cxxcompat_CPPFLAGS = $(TESTS_CPPFLAGS) @@ -327,6 +334,9 @@ EXTRA_DIST += \ tests/isisd/test_fuzz_isis_tlv.py \ tests/isisd/test_fuzz_isis_tlv_tests.h.gz \ tests/isisd/test_isis_lspdb.py \ + tests/isisd/test_isis_spf.py \ + tests/isisd/test_isis_spf.in \ + tests/isisd/test_isis_spf.refout \ tests/isisd/test_isis_vertex_queue.py \ tests/lib/cli/test_commands.in \ tests/lib/cli/test_commands.py \ 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 4602b039a6..8525838d96 100755 --- a/tests/topotests/all-protocol-startup/test_all_protocol_startup.py +++ b/tests/topotests/all-protocol-startup/test_all_protocol_startup.py @@ -263,6 +263,22 @@ def test_error_messages_daemons(): if log: error_logs += "r%s LDPd StdErr Output:\n" % i error_logs += log + + log = net['r1'].getStdErr('nhrpd') + if log: + error_logs += "r%s NHRPd StdErr Output:\n" % i + error_logs += log + + log = net['r1'].getStdErr('babeld') + if log: + error_logs += "r%s BABELd StdErr Output:\n" % i + error_logs += log + + log = net['r1'].getStdErr('pbrd') + if log: + error_logs += "r%s PBRd StdErr Output:\n" % i + error_logs += log + log = net['r%s' % i].getStdErr('zebra') if log: error_logs += "r%s Zebra StdErr Output:\n" @@ -1182,6 +1198,19 @@ def test_shutdown_check_stderr(): log = net['r1'].getStdErr('bgpd') if log: print("\nBGPd StdErr Log:\n" + log) + + log = net['r1'].getStdErr('nhrpd') + if log: + print("\nNHRPd StdErr Log:\n" + log) + + log = net['r1'].getStdErr('pbrd') + if log: + print("\nPBRd StdErr Log:\n" + log) + + log = net['r1'].getStdErr('babeld') + if log: + print("\nBABELd StdErr Log:\n" + log) + if (net['r1'].daemon_available('ldpd')): log = net['r1'].getStdErr('ldpd') if log: diff --git a/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf index 4793155939..57f9cd9e3d 100644 --- a/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf +++ b/tests/topotests/bfd-isis-topo1/rt1/bfdd.conf @@ -8,10 +8,12 @@ bfd detect-multiplier 3 receive-interval 300 transmit-interval 300 + no shutdown ! peer 10.0.2.2 interface eth-rt3 detect-multiplier 3 receive-interval 300 transmit-interval 300 + no shutdown ! ! diff --git a/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf index a49cd4fa6b..6b34e337d3 100644 --- a/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf +++ b/tests/topotests/bfd-isis-topo1/rt2/bfdd.conf @@ -8,5 +8,6 @@ bfd detect-multiplier 3 receive-interval 300 transmit-interval 300 + no shutdown ! ! diff --git a/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf b/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf index 600054a0bc..22937fe46f 100644 --- a/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf +++ b/tests/topotests/bfd-isis-topo1/rt3/bfdd.conf @@ -8,5 +8,6 @@ bfd detect-multiplier 3 receive-interval 300 transmit-interval 300 + no shutdown ! ! diff --git a/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf b/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf index c0896353ae..4798d17c40 100644 --- a/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf +++ b/tests/topotests/bfd-profiles-topo1/r1/ospfd.conf @@ -1,5 +1,7 @@ interface r1-eth1 ip ospf area 0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf bfd ! router ospf diff --git a/tests/topotests/bfd-profiles-topo1/r6/ospfd.conf b/tests/topotests/bfd-profiles-topo1/r6/ospfd.conf index f16844401e..d8fce344a8 100644 --- a/tests/topotests/bfd-profiles-topo1/r6/ospfd.conf +++ b/tests/topotests/bfd-profiles-topo1/r6/ospfd.conf @@ -1,5 +1,7 @@ interface r6-eth0 ip ospf area 0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf bfd ! router ospf diff --git a/tests/topotests/bfd-topo2/r2/ospf6d.conf b/tests/topotests/bfd-topo2/r2/ospf6d.conf index f1cdb50285..48a729ce19 100644 --- a/tests/topotests/bfd-topo2/r2/ospf6d.conf +++ b/tests/topotests/bfd-topo2/r2/ospf6d.conf @@ -1,5 +1,7 @@ interface r2-eth2 ipv6 ospf6 bfd + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! router ospf6 ospf6 router-id 10.254.254.2 diff --git a/tests/topotests/bfd-topo2/r2/ospfd.conf b/tests/topotests/bfd-topo2/r2/ospfd.conf index 8e0c45980d..c786f1fe43 100644 --- a/tests/topotests/bfd-topo2/r2/ospfd.conf +++ b/tests/topotests/bfd-topo2/r2/ospfd.conf @@ -1,5 +1,7 @@ interface r2-eth1 ip ospf area 0.0.0.1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf bfd ! router ospf diff --git a/tests/topotests/bfd-topo2/r3/ospfd.conf b/tests/topotests/bfd-topo2/r3/ospfd.conf index cf2a1bdf76..932ab4da63 100644 --- a/tests/topotests/bfd-topo2/r3/ospfd.conf +++ b/tests/topotests/bfd-topo2/r3/ospfd.conf @@ -1,5 +1,7 @@ interface r3-eth0 ip ospf area 0.0.0.1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ip ospf bfd ! router ospf diff --git a/tests/topotests/bfd-topo2/r4/ospf6d.conf b/tests/topotests/bfd-topo2/r4/ospf6d.conf index 756597d6f8..57f7f6c079 100644 --- a/tests/topotests/bfd-topo2/r4/ospf6d.conf +++ b/tests/topotests/bfd-topo2/r4/ospf6d.conf @@ -1,5 +1,7 @@ interface r4-eth0 ipv6 ospf6 bfd + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 ! router ospf6 ospf6 router-id 10.254.254.4 diff --git a/tests/topotests/bfd-topo3/__init__.py b/tests/topotests/bfd-topo3/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bfd-topo3/__init__.py diff --git a/tests/topotests/bfd-topo3/r1/bfd-peers.json b/tests/topotests/bfd-topo3/r1/bfd-peers.json new file mode 100644 index 0000000000..56205d538b --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/bfd-peers.json @@ -0,0 +1,68 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "2001:db8:1::1", + "minimum-ttl": 253, + "multihop": true, + "passive-mode": true, + "peer": "2001:db8:3::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r1-eth0", + "local": "2001:db8:1::1", + "multihop": false, + "passive-mode": true, + "peer": "2001:db8:1::2", + "receive-interval": 600, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 600, + "remote-transmit-interval": 600, + "status": "up", + "transmit-interval": 600, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "192.168.1.1", + "minimum-ttl": 254, + "multihop": true, + "passive-mode": true, + "peer": "192.168.2.1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r1/bfdd.conf b/tests/topotests/bfd-topo3/r1/bfdd.conf new file mode 100644 index 0000000000..8e40b76d41 --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/bfdd.conf @@ -0,0 +1,17 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile fast-tx + receive-interval 600 + transmit-interval 600 + passive-mode + ! + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + passive-mode + ! +! diff --git a/tests/topotests/bfd-topo3/r1/bgpd.conf b/tests/topotests/bfd-topo3/r1/bgpd.conf new file mode 100644 index 0000000000..a0281d50c6 --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/bgpd.conf @@ -0,0 +1,20 @@ +router bgp 100 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::2 remote-as internal + neighbor 2001:db8:1::2 bfd profile fast-tx + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 ebgp-multihop 2 + neighbor 192.168.2.1 bfd profile slow-tx + neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 ebgp-multihop 3 + neighbor 2001:db8:3::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::2 activate + neighbor 192.168.2.1 activate + neighbor 2001:db8:3::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r1/zebra.conf b/tests/topotests/bfd-topo3/r1/zebra.conf new file mode 100644 index 0000000000..64aee48436 --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/zebra.conf @@ -0,0 +1,10 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bfd-topo3/r2/bfd-peers.json b/tests/topotests/bfd-topo3/r2/bfd-peers.json new file mode 100644 index 0000000000..cb8985b13e --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/bfd-peers.json @@ -0,0 +1,46 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r2-eth0", + "local": "2001:db8:1::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:1::1", + "receive-interval": 600, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 600, + "remote-transmit-interval": 600, + "status": "up", + "transmit-interval": 600, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r2-eth1", + "local": "2001:db8:2::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:2::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r2/bfdd.conf b/tests/topotests/bfd-topo3/r2/bfdd.conf new file mode 100644 index 0000000000..2a92e463e0 --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/bfdd.conf @@ -0,0 +1,15 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile fast-tx + receive-interval 600 + transmit-interval 600 + ! + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! +! diff --git a/tests/topotests/bfd-topo3/r2/bgpd.conf b/tests/topotests/bfd-topo3/r2/bgpd.conf new file mode 100644 index 0000000000..0e96033023 --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 100 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::1 remote-as internal + neighbor 2001:db8:1::1 bfd profile fast-tx + neighbor 2001:db8:2::1 remote-as external + neighbor 2001:db8:2::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::1 activate + neighbor 2001:db8:2::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r2/zebra.conf b/tests/topotests/bfd-topo3/r2/zebra.conf new file mode 100644 index 0000000000..c7e22d4804 --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ip address 192.168.1.2/24 + ipv6 address 2001:db8:1::2/64 +! +interface r2-eth1 + ip address 192.168.2.2/24 + ipv6 address 2001:db8:2::2/64 +! diff --git a/tests/topotests/bfd-topo3/r3/bfd-peers.json b/tests/topotests/bfd-topo3/r3/bfd-peers.json new file mode 100644 index 0000000000..8be35fd084 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/bfd-peers.json @@ -0,0 +1,68 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r3-eth1", + "local": "2001:db8:3::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:3::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r3-eth0", + "local": "2001:db8:2::1", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:2::2", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "192.168.2.1", + "minimum-ttl": 254, + "multihop": true, + "passive-mode": false, + "peer": "192.168.1.1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r3/bfdd.conf b/tests/topotests/bfd-topo3/r3/bfdd.conf new file mode 100644 index 0000000000..f7972c6ce5 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/bfdd.conf @@ -0,0 +1,11 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! +! diff --git a/tests/topotests/bfd-topo3/r3/bgpd.conf b/tests/topotests/bfd-topo3/r3/bgpd.conf new file mode 100644 index 0000000000..e14d2011a0 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 300 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 ebgp-multihop 2 + neighbor 192.168.1.1 bfd profile slow-tx + neighbor 2001:db8:2::2 remote-as external + neighbor 2001:db8:2::2 bfd profile slow-tx + neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 192.168.1.1 activate + neighbor 2001:db8:2::2 activate + neighbor 2001:db8:3::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r3/zebra.conf b/tests/topotests/bfd-topo3/r3/zebra.conf new file mode 100644 index 0000000000..14248fb6f7 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 192.168.2.1/24 + ipv6 address 2001:db8:2::1/64 +! +interface r3-eth1 + ip address 192.168.3.2/24 + ipv6 address 2001:db8:3::2/64 +! diff --git a/tests/topotests/bfd-topo3/r4/bfd-peers.json b/tests/topotests/bfd-topo3/r4/bfd-peers.json new file mode 100644 index 0000000000..e2e6722ef4 --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/bfd-peers.json @@ -0,0 +1,46 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "2001:db8:3::1", + "minimum-ttl": 253, + "multihop": true, + "passive-mode": false, + "peer": "2001:db8:1::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r4-eth0", + "local": "2001:db8:3::1", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:3::2", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r4/bfdd.conf b/tests/topotests/bfd-topo3/r4/bfdd.conf new file mode 100644 index 0000000000..f44abc0b8a --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/bfdd.conf @@ -0,0 +1,16 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! + profile slow-tx-mh + receive-interval 2000 + transmit-interval 2000 + minimum-ttl 250 + ! +! diff --git a/tests/topotests/bfd-topo3/r4/bgpd.conf b/tests/topotests/bfd-topo3/r4/bgpd.conf new file mode 100644 index 0000000000..3e81008d5d --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 400 + no bgp ebgp-requires-policy + neighbor 2001:db8:3::2 remote-as external + neighbor 2001:db8:3::2 bfd profile slow-tx + neighbor 2001:db8:1::1 remote-as external + neighbor 2001:db8:1::1 ebgp-multihop 3 + neighbor 2001:db8:1::1 bfd profile slow-tx-mh + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::1 activate + neighbor 2001:db8:3::2 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r4/zebra.conf b/tests/topotests/bfd-topo3/r4/zebra.conf new file mode 100644 index 0000000000..bf0cfcf42c --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/zebra.conf @@ -0,0 +1,10 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.4/32 +! +interface r4-eth0 + ip address 192.168.3.1/24 + ipv6 address 2001:db8:3::1/64 +! diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.dot b/tests/topotests/bfd-topo3/test_bfd_topo3.dot new file mode 100644 index 0000000000..502cea11f2 --- /dev/null +++ b/tests/topotests/bfd-topo3/test_bfd_topo3.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-topo3"; + + # 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\n192.168.1.0/24\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n192.168.2.0/24\n2001:db8:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n192.168.3.0/24\n2001:db8:3::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0\n.1"]; + r2 -- sw1 [label="eth0\n.2"]; + + r3 -- sw2 [label="eth0\n.1"]; + r2 -- sw2 [label="eth1\n.2"]; + + r4 -- sw3 [label="eth0\n.1"]; + r3 -- sw3 [label="eth2\n.2"]; +} diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.jpg b/tests/topotests/bfd-topo3/test_bfd_topo3.jpg Binary files differnew file mode 100644 index 0000000000..6b532560bf --- /dev/null +++ b/tests/topotests/bfd-topo3/test_bfd_topo3.jpg diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.py b/tests/topotests/bfd-topo3/test_bfd_topo3.py new file mode 100644 index 0000000000..bcee338a92 --- /dev/null +++ b/tests/topotests/bfd-topo3/test_bfd_topo3.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python + +# +# test_bfd_topo3.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 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_bfd_topo3.py: Test the FRR BFD daemon multi hop. +""" + +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 BFDTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 4 routers + 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 = 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["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BFDTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + daemon_file = "{}/{}/bfdd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BFD, daemon_file) + + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BGP, daemon_file) + + # Initialize all routers. + tgen.start_router() + + +def test_wait_bgp_convergence(): + "Wait for BGP to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_loopback_route(router, iptype, route, proto): + "Wait until route is present on RIB for protocol." + logger.info('waiting route {} in {}'.format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + 'show {} route json'.format(iptype), + { route: [{ 'protocol': proto }] } + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" OSPF convergence failure'.format(router) + assert result is None, assertmsg + + # Wait for R1 <-> R2 convergence. + expect_loopback_route('r1', 'ip', '10.254.254.2/32', 'bgp') + # Wait for R1 <-> R3 convergence. + expect_loopback_route('r1', 'ip', '10.254.254.3/32', 'bgp') + # Wait for R1 <-> R4 convergence. + expect_loopback_route('r1', 'ip', '10.254.254.4/32', 'bgp') + + # Wait for R2 <-> R1 convergence. + expect_loopback_route('r2', 'ip', '10.254.254.1/32', 'bgp') + # Wait for R2 <-> R3 convergence. + expect_loopback_route('r2', 'ip', '10.254.254.3/32', 'bgp') + # Wait for R2 <-> R4 convergence. + expect_loopback_route('r2', 'ip', '10.254.254.4/32', 'bgp') + + # Wait for R3 <-> R1 convergence. + expect_loopback_route('r3', 'ip', '10.254.254.1/32', 'bgp') + # Wait for R3 <-> R2 convergence. + expect_loopback_route('r3', 'ip', '10.254.254.2/32', 'bgp') + # Wait for R3 <-> R4 convergence. + expect_loopback_route('r3', 'ip', '10.254.254.4/32', 'bgp') + + # Wait for R4 <-> R1 convergence. + expect_loopback_route('r4', 'ip', '10.254.254.1/32', 'bgp') + # Wait for R4 <-> R2 convergence. + expect_loopback_route('r4', 'ip', '10.254.254.2/32', 'bgp') + # Wait for R4 <-> R3 convergence. + expect_loopback_route('r4', 'ip', '10.254.254.3/32', 'bgp') + + +def test_wait_bfd_convergence(): + "Wait for BFD to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test BFD configurations") + + def expect_bfd_configuration(router): + "Load JSON file and compare with 'show bfd peer json'" + logger.info('waiting BFD configuration on router {}'.format(router)) + bfd_config = json.loads(open('{}/{}/bfd-peers.json'.format(CWD, router)).read()) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + 'show bfd peers json', + bfd_config + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" BFD configuration failure'.format(router) + assert result is None, assertmsg + + expect_bfd_configuration('r1') + expect_bfd_configuration('r2') + expect_bfd_configuration('r3') + expect_bfd_configuration('r4') + + +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/bgp-auth/R1/bgpd.conf b/tests/topotests/bgp-auth/R1/bgpd.conf new file mode 100644 index 0000000000..1cb26c6537 --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65001 + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 password hello1 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 10 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 password hello2 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R1/bgpd_multi_vrf.conf b/tests/topotests/bgp-auth/R1/bgpd_multi_vrf.conf new file mode 100644 index 0000000000..aab35073cf --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd_multi_vrf.conf @@ -0,0 +1,40 @@ +log file /tmp/topotests/test_bgp_auth/R1/bgpd.log debugging +debug bgp neighbor-events + +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 password blue1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password blue2 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate + +router bgp 65001 vrf red + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo2 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 password red1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo2 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password red2 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R1/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp-auth/R1/bgpd_multi_vrf_prefix.conf new file mode 100644 index 0000000000..7e15720c7e --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd_multi_vrf_prefix.conf @@ -0,0 +1,37 @@ +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP_blue peer-group + neighbor TWO_GROUP_blue remote-as 65002 + neighbor TWO_GROUP_blue update-source 1.1.1.1 + neighbor TWO_GROUP_blue ebgp-multihop 3 + neighbor TWO_GROUP_blue password blue1 + neighbor THREE_GROUP_blue peer-group + neighbor THREE_GROUP_blue remote-as 65003 + neighbor THREE_GROUP_blue update-source 1.1.1.1 + neighbor THREE_GROUP_blue ebgp-multihop 3 + neighbor THREE_GROUP_blue password blue2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_blue + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_blue + address-family ipv4 unicast + neighbor TWO_GROUP_blue maximum-prefix 4294967295 + neighbor THREE_GROUP_blue maximum-prefix 4294967295 + +router bgp 65001 vrf red + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP_red peer-group + neighbor TWO_GROUP_red remote-as 65002 + neighbor TWO_GROUP_red update-source 1.1.1.1 + neighbor TWO_GROUP_red ebgp-multihop 3 + neighbor TWO_GROUP_red password red1 + neighbor THREE_GROUP_red peer-group + neighbor THREE_GROUP_red remote-as 65003 + neighbor THREE_GROUP_red update-source 1.1.1.1 + neighbor THREE_GROUP_red ebgp-multihop 3 + neighbor THREE_GROUP_red password red2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_red + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_red + address-family ipv4 unicast + neighbor TWO_GROUP_red maximum-prefix 4294967295 + neighbor THREE_GROUP_red maximum-prefix 4294967295 diff --git a/tests/topotests/bgp-auth/R1/bgpd_prefix.conf b/tests/topotests/bgp-auth/R1/bgpd_prefix.conf new file mode 100644 index 0000000000..9200b0501d --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65001 + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP peer-group + neighbor TWO_GROUP remote-as 65002 + neighbor TWO_GROUP update-source 1.1.1.1 + neighbor TWO_GROUP ebgp-multihop 3 + neighbor TWO_GROUP password hello1 + neighbor THREE_GROUP peer-group + neighbor THREE_GROUP remote-as 65003 + neighbor THREE_GROUP update-source 1.1.1.1 + neighbor THREE_GROUP ebgp-multihop 3 + neighbor THREE_GROUP password hello2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP + address-family ipv4 unicast + neighbor TWO_GROUP maximum-prefix 4294967295 + neighbor THREE_GROUP maximum-prefix 4294967295 diff --git a/tests/topotests/bgp-auth/R1/bgpd_vrf.conf b/tests/topotests/bgp-auth/R1/bgpd_vrf.conf new file mode 100644 index 0000000000..73aa8c1a03 --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd_vrf.conf @@ -0,0 +1,21 @@ +log file /tmp/topotests/test_bgp_auth/R1/bgpd.log debugging +debug bgp neighbor-events + +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password hello2 + address-family ipv4 unicast + neighbor 2.2.2.2 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R1/bgpd_vrf_prefix.conf b/tests/topotests/bgp-auth/R1/bgpd_vrf_prefix.conf new file mode 100644 index 0000000000..d68951b406 --- /dev/null +++ b/tests/topotests/bgp-auth/R1/bgpd_vrf_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65001 vrf blue + timers bgp 3 9 + bgp router-id 1.1.1.1 + neighbor TWO_GROUP_blue peer-group + neighbor TWO_GROUP_blue remote-as 65002 + neighbor TWO_GROUP_blue update-source 1.1.1.1 + neighbor TWO_GROUP_blue ebgp-multihop 3 + neighbor TWO_GROUP_blue password hello1 + neighbor THREE_GROUP_blue peer-group + neighbor THREE_GROUP_blue remote-as 65003 + neighbor THREE_GROUP_blue update-source 1.1.1.1 + neighbor THREE_GROUP_blue ebgp-multihop 3 + neighbor THREE_GROUP_blue password hello2 + bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_blue + bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_blue + address-family ipv4 unicast + neighbor TWO_GROUP_blue maximum-prefix 4294967295 + neighbor THREE_GROUP_blue maximum-prefix 4294967295 diff --git a/tests/topotests/bgp-auth/R1/ospfd.conf b/tests/topotests/bgp-auth/R1/ospfd.conf new file mode 100644 index 0000000000..79eb0e33da --- /dev/null +++ b/tests/topotests/bgp-auth/R1/ospfd.conf @@ -0,0 +1,4 @@ +router ospf + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 diff --git a/tests/topotests/bgp-auth/R1/ospfd_multi_vrf.conf b/tests/topotests/bgp-auth/R1/ospfd_multi_vrf.conf new file mode 100644 index 0000000000..e2a28000b8 --- /dev/null +++ b/tests/topotests/bgp-auth/R1/ospfd_multi_vrf.conf @@ -0,0 +1,9 @@ +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 + +router ospf vrf red + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 diff --git a/tests/topotests/bgp-auth/R1/ospfd_vrf.conf b/tests/topotests/bgp-auth/R1/ospfd_vrf.conf new file mode 100644 index 0000000000..0b7fbae8c4 --- /dev/null +++ b/tests/topotests/bgp-auth/R1/ospfd_vrf.conf @@ -0,0 +1,4 @@ +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.20.0.0/16 area 0 + network 1.1.1.1/32 area 0 diff --git a/tests/topotests/bgp-auth/R1/zebra.conf b/tests/topotests/bgp-auth/R1/zebra.conf new file mode 100644 index 0000000000..d39915335a --- /dev/null +++ b/tests/topotests/bgp-auth/R1/zebra.conf @@ -0,0 +1,21 @@ +log file zebra.log +! +interface lo + ip address 1.1.1.1/32 +interface lo1 vrf blue + ip address 1.1.1.1/32 +interface lo2 vrf red + ip address 1.1.1.1/32 +interface R1-eth0 + ip address 10.10.0.1/24 +interface R1-eth1 + ip address 10.20.0.1/24 +interface R1-eth2 vrf blue + ip address 10.10.0.1/24 +interface R1-eth3 vrf blue + ip address 10.20.0.1/24 +interface R1-eth4 vrf red + ip address 10.10.0.1/24 +interface R1-eth5 vrf red + ip address 10.20.0.1/24 +!
\ No newline at end of file diff --git a/tests/topotests/bgp-auth/R2/bgpd.conf b/tests/topotests/bgp-auth/R2/bgpd.conf new file mode 100644 index 0000000000..fa2a570ef9 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65002 + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/bgpd_multi_vrf.conf b/tests/topotests/bgp-auth/R2/bgpd_multi_vrf.conf new file mode 100644 index 0000000000..d5f70edf68 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd_multi_vrf.conf @@ -0,0 +1,37 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password blue1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate + +router bgp 65002 vrf red + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password red1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo2 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp-auth/R2/bgpd_multi_vrf_prefix.conf new file mode 100644 index 0000000000..d5f70edf68 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd_multi_vrf_prefix.conf @@ -0,0 +1,37 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password blue1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate + +router bgp 65002 vrf red + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password red1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo2 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/bgpd_prefix.conf b/tests/topotests/bgp-auth/R2/bgpd_prefix.conf new file mode 100644 index 0000000000..fa2a570ef9 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65002 + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/bgpd_vrf.conf b/tests/topotests/bgp-auth/R2/bgpd_vrf.conf new file mode 100644 index 0000000000..d1f3847420 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd_vrf.conf @@ -0,0 +1,18 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/bgpd_vrf_prefix.conf b/tests/topotests/bgp-auth/R2/bgpd_vrf_prefix.conf new file mode 100644 index 0000000000..d1f3847420 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/bgpd_vrf_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65002 vrf blue + timers bgp 3 9 + bgp router-id 2.2.2.2 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello1 + neighbor 3.3.3.3 remote-as 65003 + neighbor 3.3.3.3 update-source lo1 + neighbor 3.3.3.3 ebgp-multihop 3 + neighbor 3.3.3.3 timers 3 10 + neighbor 3.3.3.3 timers connect 10 + neighbor 3.3.3.3 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp-auth/R2/ospfd.conf b/tests/topotests/bgp-auth/R2/ospfd.conf new file mode 100644 index 0000000000..028b546a0c --- /dev/null +++ b/tests/topotests/bgp-auth/R2/ospfd.conf @@ -0,0 +1,4 @@ +router ospf + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 diff --git a/tests/topotests/bgp-auth/R2/ospfd_multi_vrf.conf b/tests/topotests/bgp-auth/R2/ospfd_multi_vrf.conf new file mode 100644 index 0000000000..a05dfb8e41 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/ospfd_multi_vrf.conf @@ -0,0 +1,9 @@ +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 + +router ospf vrf red + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 diff --git a/tests/topotests/bgp-auth/R2/ospfd_vrf.conf b/tests/topotests/bgp-auth/R2/ospfd_vrf.conf new file mode 100644 index 0000000000..b198d352e2 --- /dev/null +++ b/tests/topotests/bgp-auth/R2/ospfd_vrf.conf @@ -0,0 +1,4 @@ +router ospf vrf blue + network 10.10.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 2.2.2.2/32 area 0 diff --git a/tests/topotests/bgp-auth/R2/zebra.conf b/tests/topotests/bgp-auth/R2/zebra.conf new file mode 100644 index 0000000000..fece68472a --- /dev/null +++ b/tests/topotests/bgp-auth/R2/zebra.conf @@ -0,0 +1,21 @@ +log file zebra.log +! +interface lo + ip address 2.2.2.2/32 +interface lo1 vrf blue + ip address 2.2.2.2/32 +interface lo2 vrf red + ip address 2.2.2.2/32 +interface R2-eth0 + ip address 10.10.0.2/24 +interface R2-eth1 + ip address 10.30.0.2/24 +interface R2-eth2 vrf blue + ip address 10.10.0.2/24 +interface R2-eth3 vrf blue + ip address 10.30.0.2/24 +interface R2-eth4 vrf red + ip address 10.10.0.2/24 +interface R2-eth5 vrf red + ip address 10.30.0.2/24 +!
\ No newline at end of file diff --git a/tests/topotests/bgp-auth/R3/bgpd.conf b/tests/topotests/bgp-auth/R3/bgpd.conf new file mode 100644 index 0000000000..deccfd418b --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65003 + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/bgpd_multi_vrf.conf b/tests/topotests/bgp-auth/R3/bgpd_multi_vrf.conf new file mode 100644 index 0000000000..fe3e64d8d5 --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd_multi_vrf.conf @@ -0,0 +1,37 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password blue2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate + +router bgp 65003 vrf red + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password red2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo2 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp-auth/R3/bgpd_multi_vrf_prefix.conf new file mode 100644 index 0000000000..fe3e64d8d5 --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd_multi_vrf_prefix.conf @@ -0,0 +1,37 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password blue2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password blue3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate + +router bgp 65003 vrf red + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo2 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password red2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo2 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password red3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/bgpd_prefix.conf b/tests/topotests/bgp-auth/R3/bgpd_prefix.conf new file mode 100644 index 0000000000..deccfd418b --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65003 + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/bgpd_vrf.conf b/tests/topotests/bgp-auth/R3/bgpd_vrf.conf new file mode 100644 index 0000000000..c109aa801b --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd_vrf.conf @@ -0,0 +1,18 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/bgpd_vrf_prefix.conf b/tests/topotests/bgp-auth/R3/bgpd_vrf_prefix.conf new file mode 100644 index 0000000000..c109aa801b --- /dev/null +++ b/tests/topotests/bgp-auth/R3/bgpd_vrf_prefix.conf @@ -0,0 +1,18 @@ +router bgp 65003 vrf blue + timers bgp 3 9 + bgp router-id 3.3.3.3 + neighbor 1.1.1.1 remote-as 65001 + neighbor 1.1.1.1 update-source lo1 + neighbor 1.1.1.1 ebgp-multihop 3 + neighbor 1.1.1.1 timers 3 10 + neighbor 1.1.1.1 timers connect 10 + neighbor 1.1.1.1 password hello2 + neighbor 2.2.2.2 remote-as 65002 + neighbor 2.2.2.2 update-source lo1 + neighbor 2.2.2.2 ebgp-multihop 3 + neighbor 2.2.2.2 timers connect 10 + neighbor 2.2.2.2 timers 3 10 + neighbor 2.2.2.2 password hello3 + address-family ipv4 unicast + neighbor 1.1.1.1 activate + neighbor 2.2.2.2 activate diff --git a/tests/topotests/bgp-auth/R3/ospfd.conf b/tests/topotests/bgp-auth/R3/ospfd.conf new file mode 100644 index 0000000000..0f0a2e926a --- /dev/null +++ b/tests/topotests/bgp-auth/R3/ospfd.conf @@ -0,0 +1,4 @@ +router ospf + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 diff --git a/tests/topotests/bgp-auth/R3/ospfd_multi_vrf.conf b/tests/topotests/bgp-auth/R3/ospfd_multi_vrf.conf new file mode 100644 index 0000000000..f32d2a8423 --- /dev/null +++ b/tests/topotests/bgp-auth/R3/ospfd_multi_vrf.conf @@ -0,0 +1,9 @@ +router ospf vrf blue + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 +! +router ospf vrf red + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 diff --git a/tests/topotests/bgp-auth/R3/ospfd_vrf.conf b/tests/topotests/bgp-auth/R3/ospfd_vrf.conf new file mode 100644 index 0000000000..6465b635aa --- /dev/null +++ b/tests/topotests/bgp-auth/R3/ospfd_vrf.conf @@ -0,0 +1,4 @@ +router ospf vrf blue + network 10.20.0.0/16 area 0 + network 10.30.0.0/16 area 0 + network 3.3.3.3/32 area 0 diff --git a/tests/topotests/bgp-auth/R3/zebra.conf b/tests/topotests/bgp-auth/R3/zebra.conf new file mode 100644 index 0000000000..0fe3acdfd0 --- /dev/null +++ b/tests/topotests/bgp-auth/R3/zebra.conf @@ -0,0 +1,21 @@ +log file zebra.log +! +interface lo + ip address 3.3.3.3/32 +interface lo1 vrf blue + ip address 3.3.3.3/32 +interface lo2 vrf red + ip address 3.3.3.3/32 +interface R3-eth0 + ip address 10.20.0.3/24 +interface R3-eth1 + ip address 10.30.0.3/24 +interface R3-eth2 vrf blue + ip address 10.20.0.3/24 +interface R3-eth3 vrf blue + ip address 10.30.0.3/24 +interface R3-eth4 vrf red + ip address 10.20.0.3/24 +interface R3-eth5 vrf red + ip address 10.30.0.3/24 +!
\ No newline at end of file diff --git a/tests/topotests/bgp-auth/test_bgp_auth.py b/tests/topotests/bgp-auth/test_bgp_auth.py new file mode 100755 index 0000000000..6198997b86 --- /dev/null +++ b/tests/topotests/bgp-auth/test_bgp_auth.py @@ -0,0 +1,747 @@ +#!/usr/bin/env python + +# +# test_bgp_auth.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by Volta Networks +# +# 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_bgp_auth.py: Test BGP Md5 Authentication + + +------+ + +--------| |--------+ + | +------| R1 |------+ | + | | -----| |----+ | | + | | | +------+ | | | + | | | | | | + +------+ +------+ + | |------------| | + | R2 |------------| R3 | + | |------------| | + +------+ +------+ + + +setup is 3 routers with 3 links between each each link in a different vrf +Default, blue and red respectively +Tests check various fiddling with passwords and checking that the peer +establishment is as expected and passwords are not leaked across sockets +for bgp instances +""" + +import os +import sys +import json +import platform +from functools import partial +import pytest +from time import sleep + +# 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 + +from lib.common_config import apply_raw_config + +ERROR_LIST = ["Malformed", "Failure", "Unknown", "Incomplete"] + + +class InvalidCLIError(Exception): + """Raise when the CLI command is wrong""" + + pass + + +class TemplateTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # This function only purpose is to define allocation and relationship + # between routers, switches and hosts. + # + # + # Create routers + tgen.add_router("R1") + tgen.add_router("R2") + tgen.add_router("R3") + + # R1-R2 1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R2"]) + + # R1-R3 1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R3"]) + + # R2-R3 1 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["R2"]) + switch.add_link(tgen.gears["R3"]) + + # R1-R2 2 + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R2"]) + + # R1-R3 2 + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R3"]) + + # R2-R3 2 + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["R2"]) + switch.add_link(tgen.gears["R3"]) + + # R1-R2 3 + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R2"]) + + # R1-R3 2 + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["R1"]) + switch.add_link(tgen.gears["R3"]) + + # R2-R3 2 + switch = tgen.add_switch("s9") + switch.add_link(tgen.gears["R2"]) + switch.add_link(tgen.gears["R3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(TemplateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + # blue vrf + r1.run("ip link add blue type vrf table 1001") + r1.run("ip link set up dev blue") + r2.run("ip link add blue type vrf table 1001") + r2.run("ip link set up dev blue") + r3.run("ip link add blue type vrf table 1001") + r3.run("ip link set up dev blue") + + r1.run("ip link add lo1 type dummy") + r1.run("ip link set lo1 master blue") + r1.run("ip link set up dev lo1") + r2.run("ip link add lo1 type dummy") + r2.run("ip link set up dev lo1") + r2.run("ip link set lo1 master blue") + r3.run("ip link add lo1 type dummy") + r3.run("ip link set up dev lo1") + r3.run("ip link set lo1 master blue") + + r1.run("ip link set R1-eth2 master blue") + r1.run("ip link set R1-eth3 master blue") + r2.run("ip link set R2-eth2 master blue") + r2.run("ip link set R2-eth3 master blue") + r3.run("ip link set R3-eth2 master blue") + r3.run("ip link set R3-eth3 master blue") + + r1.run("ip link set up dev R1-eth2") + r1.run("ip link set up dev R1-eth3") + r2.run("ip link set up dev R2-eth2") + r2.run("ip link set up dev R2-eth3") + r3.run("ip link set up dev R3-eth2") + r3.run("ip link set up dev R3-eth3") + + # red vrf + r1.run("ip link add red type vrf table 1002") + r1.run("ip link set up dev red") + r2.run("ip link add red type vrf table 1002") + r2.run("ip link set up dev red") + r3.run("ip link add red type vrf table 1002") + r3.run("ip link set up dev red") + + r1.run("ip link add lo2 type dummy") + r1.run("ip link set lo2 master red") + r1.run("ip link set up dev lo2") + r2.run("ip link add lo2 type dummy") + r2.run("ip link set up dev lo2") + r2.run("ip link set lo2 master red") + r3.run("ip link add lo2 type dummy") + r3.run("ip link set up dev lo2") + r3.run("ip link set lo2 master red") + + r1.run("ip link set R1-eth4 master red") + r1.run("ip link set R1-eth5 master red") + r2.run("ip link set R2-eth4 master red") + r2.run("ip link set R2-eth5 master red") + r3.run("ip link set R3-eth4 master red") + r3.run("ip link set R3-eth5 master red") + + r1.run("ip link set up dev R1-eth4") + r1.run("ip link set up dev R1-eth5") + r2.run("ip link set up dev R2-eth4") + r2.run("ip link set up dev R2-eth5") + r3.run("ip link set up dev R3-eth4") + r3.run("ip link set up dev R3-eth5") + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.iteritems(): + 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)) + ) + 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() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def vrf_str(vrf): + if vrf == "": + vrf_str = "" + else: + vrf_str = "vrf {}".format(vrf) + + return vrf_str + + +def peer_name(rtr, prefix, vrf): + "generate VRF string for CLI" + if vrf == "": + vrf_str = "" + else: + vrf_str = "_" + vrf + + if prefix == "yes": + if rtr == "R2": + return "TWO_GROUP" + vrf_str + else: + return "THREE_GROUP" + vrf_str + else: + if rtr == "R2": + return "2.2.2.2" + else: + return "3.3.3.3" + + +def print_diag(vrf): + "print failure disagnostics" + + tgen = get_topogen() + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + print(rname + ":") + print(router.vtysh_cmd("show run")) + print(router.vtysh_cmd("show ip route {}".format(vrf_str(vrf)))) + print(router.vtysh_cmd("show bgp {} neighbor".format(vrf_str(vrf)))) + + +def configure(conf_file): + "configure from a file" + + tgen = get_topogen() + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + with open( + os.path.join(CWD, "{}/{}").format(router.name, conf_file), "r+" + ) as cfg: + new_config = cfg.read() + + output = router.vtysh_multicmd(new_config, pretty_output=False) + for out_err in ERROR_LIST: + if out_err.lower() in output.lower(): + raise InvalidCLIError("%s" % output) + + +def clear_bgp(vrf=""): + " clear bgp configuration for a vrf" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + router_list = tgen.routers() + if vrf == "": + r1.vtysh_cmd("conf t\nno router bgp 65001") + r2.vtysh_cmd("conf t\nno router bgp 65002") + r2.vtysh_cmd("conf t\nno router bgp 65003") + else: + r1.vtysh_cmd("conf t\nno router bgp 65001 vrf {}".format(vrf)) + r2.vtysh_cmd("conf t\nno router bgp 65002 vrf {}".format(vrf)) + r3.vtysh_cmd("conf t\nno router bgp 65003 vrf {}".format(vrf)) + + +def clear_ospf(vrf=""): + "clear ospf configuration for a vrf" + + tgen = get_topogen() + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + if vrf == "": + router.vtysh_cmd("conf t\nno router ospf") + else: + router.vtysh_cmd("conf t\nno router ospf vrf {}".format(vrf)) + + +def check_neigh_state(router, peer, state, vrf=""): + "check BGP neighbor state on a router" + + count = 0 + matched = False + neigh_output = "" + while count < 125: + if vrf == "": + neigh_output = router.vtysh_cmd("show bgp neighbors {} json".format(peer)) + else: + neigh_output = router.vtysh_cmd( + "show bgp vrf {} neighbors {} json".format(vrf, peer) + ) + neigh_output_json = json.loads(neigh_output) + if neigh_output_json[peer]["bgpState"] == state: + matched = True + break + count += 1 + sleep(1) + + assertmsg = "{} could not peer {} state expected {} got {} ".format( + router.name, peer, state, neigh_output_json[peer]["bgpState"] + ) + if matched != True: + print_diag(vrf) + assert matched == True, assertmsg + + +def check_all_peers_established(vrf=""): + "standard check for extablished peers per vrf" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + # do r1 last as he might be the dynamic one + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + +def check_vrf_peer_remove_passwords(vrf="", prefix="no"): + "selectively remove passwords checking state" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format( + vrf_str(vrf), peer_name("R2", prefix, vrf) + ) + ) + + check_neigh_state(r2, "1.1.1.1", "Connect", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf)) + ) + check_all_peers_established(vrf) + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format( + vrf_str(vrf), peer_name("R3", prefix, vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Connect", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "3.3.3.3", "Connect", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf)) + ) + check_all_peers_established(vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nno neighbor 3.3.3.3 password".format(vrf_str(vrf)) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Connect", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nno neighbor 2.2.2.2 password".format(vrf_str(vrf)) + ) + check_all_peers_established(vrf) + + +def check_vrf_peer_change_passwords(vrf="", prefix="no"): + "selectively change passwords checking state" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + check_all_peers_established(vrf) + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nneighbor {} password change1".format( + vrf_str(vrf), peer_name("R2", prefix, vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Connect", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nneighbor 1.1.1.1 password change1".format( + vrf_str(vrf) + ) + ) + check_all_peers_established(vrf) + + r1.vtysh_cmd( + "conf t\nrouter bgp 65001 {}\nneighbor {} password change2".format( + vrf_str(vrf), peer_name("R3", prefix, vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Established", vrf) + check_neigh_state(r3, "1.1.1.1", "Connect", vrf) + check_neigh_state(r3, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + # don't check dynamic downed peers - they are removed + if prefix == "no": + check_neigh_state(r1, "3.3.3.3", "Connect", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nneighbor 1.1.1.1 password change2".format( + vrf_str(vrf) + ) + ) + check_all_peers_established(vrf) + + r2.vtysh_cmd( + "conf t\nrouter bgp 65002 {}\nneighbor 3.3.3.3 password change3".format( + vrf_str(vrf) + ) + ) + check_neigh_state(r2, "1.1.1.1", "Established", vrf) + check_neigh_state(r2, "3.3.3.3", "Connect", vrf) + check_neigh_state(r3, "1.1.1.1", "Established", vrf) + check_neigh_state(r3, "2.2.2.2", "Connect", vrf) + check_neigh_state(r1, "2.2.2.2", "Established", vrf) + check_neigh_state(r1, "3.3.3.3", "Established", vrf) + + r3.vtysh_cmd( + "conf t\nrouter bgp 65003 {}\nneighbor 2.2.2.2 password change3".format( + vrf_str(vrf) + ) + ) + check_all_peers_established(vrf) + + +def test_default_peer_established(): + "default vrf 3 peers same password" + + check_all_peers_established() + clear_bgp() + # tgen.mininet_cli() + + +def test_default_peer_remove_passwords(): + "selectively remove passwords checking state" + + configure("bgpd.conf") + check_vrf_peer_remove_passwords() + clear_bgp() + + +def test_default_peer_change_passwords(): + "selectively change passwords checking state" + + configure("bgpd.conf") + check_vrf_peer_change_passwords() + clear_bgp() + + +def test_default_prefix_peer_established(): + "default vrf 3 peers same password with prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + configure("bgpd_prefix.conf") + check_all_peers_established() + clear_bgp() + # tgen.mininet_cli() + + +def test_prefix_peer_remove_passwords(): + "selectively remove passwords checking state with prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + configure("bgpd_prefix.conf") + check_vrf_peer_remove_passwords(prefix="yes") + clear_bgp() + + +def test_prefix_peer_change_passwords(): + "selecively change passwords checkig state with prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + configure("bgpd_prefix.conf") + check_vrf_peer_change_passwords(prefix="yes") + clear_bgp() + clear_ospf() + + +def test_vrf_peer_established(): + "default vrf 3 peers same password with VRF config" + + # clean routers and load vrf config + configure("bgpd_vrf.conf") + configure("ospfd_vrf.conf") + + check_all_peers_established("blue") + clear_bgp("blue") + # tgen.mininet_cli() + + +def test_vrf_peer_remove_passwords(): + "selectively remove passwords checking state with VRF config" + + configure("bgpd_vrf.conf") + check_vrf_peer_remove_passwords(vrf="blue") + clear_bgp("blue") + + +def test_vrf_peer_change_passwords(): + "selectively change passwords checking state with VRF config" + + configure("bgpd_vrf.conf") + check_vrf_peer_change_passwords(vrf="blue") + clear_bgp("blue") + + +def test_vrf_prefix_peer_established(): + "default vrf 3 peers same password with VRF prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + clear_bgp("blue") + return + + configure("bgpd_vrf_prefix.conf") + check_all_peers_established("blue") + clear_bgp("blue") + + +def test_vrf_prefix_peer_remove_passwords(): + "selectively remove passwords checking state with VRF prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + configure("bgpd_vrf_prefix.conf") + check_vrf_peer_remove_passwords(vrf="blue", prefix="yes") + clear_bgp("blue") + + +def test_vrf_prefix_peer_change_passwords(): + "selectively change passwords checking state with VRF prefix config" + + tgen = get_topogen() + r1 = tgen.gears["R1"] + r2 = tgen.gears["R2"] + r3 = tgen.gears["R3"] + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + clear_ospf("blue") + return + + configure("bgpd_vrf_prefix.conf") + check_vrf_peer_change_passwords(vrf="blue", prefix="yes") + clear_bgp("blue") + clear_ospf("blue") + + +def test_multiple_vrf_peer_established(): + "default vrf 3 peers same password with multiple VRFs" + + configure("bgpd_multi_vrf.conf") + configure("ospfd_multi_vrf.conf") + check_all_peers_established("blue") + check_all_peers_established("red") + clear_bgp("blue") + clear_bgp("red") + # tgen.mininet_cli() + + +def test_multiple_vrf_peer_remove_passwords(): + "selectively remove passwords checking state with multiple VRFs" + + configure("bgpd_multi_vrf.conf") + check_vrf_peer_remove_passwords("blue") + check_all_peers_established("red") + check_vrf_peer_remove_passwords("red") + check_all_peers_established("blue") + clear_bgp("blue") + clear_bgp("red") + # tgen.mininet_cli() + + +def test_multiple_vrf_peer_change_passwords(): + "selectively change passwords checking state with multiple VRFs" + + configure("bgpd_multi_vrf.conf") + check_vrf_peer_change_passwords("blue") + check_all_peers_established("red") + check_vrf_peer_change_passwords("red") + check_all_peers_established("blue") + clear_bgp("blue") + clear_bgp("red") + # tgen.mininet_cli() + + +def test_multiple_vrf_prefix_peer_established(): + "default vrf 3 peers same password with multilpe VRFs and prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + configure("bgpd_multi_vrf.conf") + configure("ospfd_multi_vrf.conf") + check_all_peers_established("blue") + check_all_peers_established("red") + clear_bgp("blue") + clear_bgp("red") + # tgen.mininet_cli() + + +def test_multiple_vrf_prefix_peer_remove_passwords(): + "selectively remove passwords checking state with multiple vrfs and prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + return + + configure("bgpd_multi_vrf_prefix.conf") + tgen = get_topogen() + check_vrf_peer_remove_passwords(vrf="blue", prefix="yes") + check_all_peers_established("red") + check_vrf_peer_remove_passwords(vrf="red", prefix="yes") + check_all_peers_established("blue") + clear_bgp("blue") + clear_bgp("red") + # tgen.mininet_cli() + + +def test_multiple_vrf_prefix_peer_change_passwords(): + "selectively change passwords checking state with multiple vrfs and prefix config" + + # only supported in kernel > 5.3 + if topotest.version_cmp(platform.release(), "5.3") < 0: + clear_bgp("blue") + clear_bgp("red") + clear_ospf("blue") + clear_ospf("red") + return + + configure("bgpd_multi_vrf_prefix.conf") + check_vrf_peer_change_passwords(vrf="blue", prefix="yes") + check_all_peers_established("red") + check_vrf_peer_change_passwords(vrf="red", prefix="yes") + check_all_peers_established("blue") + clear_bgp("blue") + clear_bgp("red") + clear_ospf("blue") + clear_ospf("red") + # tgen.mininet_cli() + + +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-basic-functionality-topo1/bgp_basic_functionality.json b/tests/topotests/bgp-basic-functionality-topo1/bgp_basic_functionality.json index c778ae4bed..ee1f1b74c0 100644 --- a/tests/topotests/bgp-basic-functionality-topo1/bgp_basic_functionality.json +++ b/tests/topotests/bgp-basic-functionality-topo1/bgp_basic_functionality.json @@ -1,36 +1,17 @@ { + "address_types": ["ipv4", "ipv6"], "ipv4base": "10.0.0.0", "ipv4mask": 30, "ipv6base": "fd00::", "ipv6mask": 64, - "link_ip_start": { - "ipv4": "10.0.0.0", - "v4mask": 30, - "ipv6": "fd00::", - "v6mask": 64 - }, - "lo_prefix": { - "ipv4": "1.0.", - "v4mask": 32, - "ipv6": "2001:DB8:F::", - "v6mask": 128 - }, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128}, "routers": { "r1": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback" - }, - "r2": { - "ipv4": "auto", - "ipv6": "auto" - }, - "r3": { - "ipv4": "auto", - "ipv6": "auto" - } + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} }, "bgp": { "local_as": "100", @@ -38,16 +19,16 @@ "ipv4": { "unicast": { "neighbor": { - "r2": { - "dest_link": { - "r1": {} - } - }, - "r3": { - "dest_link": { - "r1": {} - } - } + "r2": {"dest_link": {"r1": {}}}, + "r3": {"dest_link": {"r1": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r1": {}}}, + "r3": {"dest_link": {"r1": {}}} } } } @@ -56,19 +37,9 @@ }, "r2": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback" - }, - "r1": { - "ipv4": "auto", - "ipv6": "auto" - }, - "r3": { - "ipv4": "auto", - "ipv6": "auto" - } + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} }, "bgp": { "local_as": "100", @@ -76,16 +47,16 @@ "ipv4": { "unicast": { "neighbor": { - "r1": { - "dest_link": { - "r2": {} - } - }, - "r3": { - "dest_link": { - "r2": {} - } - } + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r3": {"dest_link": {"r2": {}}} } } } @@ -94,23 +65,10 @@ }, "r3": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback" - }, - "r1": { - "ipv4": "auto", - "ipv6": "auto" - }, - "r2": { - "ipv4": "auto", - "ipv6": "auto" - }, - "r4": { - "ipv4": "auto", - "ipv6": "auto" - } + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r2": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} }, "bgp": { "local_as": "100", @@ -118,21 +76,18 @@ "ipv4": { "unicast": { "neighbor": { - "r1": { - "dest_link": { - "r3": {} - } - }, - "r2": { - "dest_link": { - "r3": {} - } - }, - "r4": { - "dest_link": { - "r3": {} - } - } + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3": {}}}, + "r2": {"dest_link": {"r3": {}}}, + "r4": {"dest_link": {"r3": {}}} } } } @@ -141,29 +96,17 @@ }, "r4": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback" - }, - "r3": { - "ipv4": "auto", - "ipv6": "auto" - } + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r3": {"ipv4": "auto", "ipv6": "auto"} }, "bgp": { "local_as": "200", "address_family": { "ipv4": { - "unicast": { - "neighbor": { - "r3": { - "dest_link": { - "r4": {} - } - } - } - } + "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}} + }, + "ipv6": { + "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}} } } } diff --git a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py index 3441d68731..69e050caed 100755 --- a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py +++ b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py @@ -37,6 +37,8 @@ Test steps - Verify clear bgp - Test bgp convergence with loopback interface - Test advertise network using network command +- Verify routes not installed in zebra when /32 routes received + with loopback BGP session subnet """ import os @@ -59,6 +61,7 @@ from lib.topogen import Topogen, get_topogen from mininet.topo import Topo from lib.common_config import ( + step, start_topology, write_test_header, write_test_footer, @@ -66,6 +69,13 @@ from lib.common_config import ( create_static_routes, verify_rib, verify_admin_distance_for_static_routes, + check_address_types, + apply_raw_config, + addKernelRoute, + verify_fib_routes, + create_prefix_lists, + create_route_maps, + verify_bgp_community, ) from lib.topolog import logger from lib.bgp import ( @@ -76,6 +86,7 @@ from lib.bgp import ( verify_as_numbers, clear_bgp_and_verify, verify_bgp_timers_and_functionality, + verify_bgp_rib, ) from lib.topojson import build_topo_from_json, build_config_from_json @@ -90,6 +101,18 @@ except IOError: # Global Variable KEEPALIVETIMER = 2 HOLDDOWNTIMER = 6 +r1_ipv4_loopback = "1.0.1.0/24" +r2_ipv4_loopback = "1.0.2.0/24" +r3_ipv4_loopback = "1.0.3.0/24" +r4_ipv4_loopback = "1.0.4.0/24" +r1_ipv6_loopback = "2001:db8:f::1:0/120" +r2_ipv6_loopback = "2001:db8:f::2:0/120" +r3_ipv6_loopback = "2001:db8:f::3:0/120" +r4_ipv6_loopback = "2001:db8:f::4:0/120" +NETWORK = { + "ipv4": ["100.1.1.1/32", "100.1.1.2/32"], + "ipv6": ["100::1/128", "100::2/128"], +} class CreateTopo(Topo): @@ -131,7 +154,9 @@ def setup_module(mod): # Creating configuration from JSON build_config_from_json(tgen, topo) + global ADDR_TYPES global BGP_CONVERGENCE + ADDR_TYPES = check_address_types() BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format( BGP_CONVERGENCE @@ -522,6 +547,243 @@ def test_clear_bgp_and_verify(request): write_test_footer(tc_name) +def test_BGP_attributes_with_vrf_default_keyword_p0(request): + """ + TC_9: + Verify BGP functionality for default vrf with + "vrf default" keyword. + """ + + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + #reset_config_on_routers(tgen) + + step("Configure static routes and redistribute in BGP on R3") + for addr_type in ADDR_TYPES: + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "no_of_ip": 4, + "next_hop": "Null0", + } + ] + } + } + + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_2 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}, + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Create a route-map to match a specific prefix and modify" + "BGP attributes for matched prefix" + ) + input_dict_2 = { + "r3": { + "prefix_lists": { + "ipv4": { + "ABC": [ + { + "seqid": 10, + "action": "permit", + "network": NETWORK["ipv4"][0], + } + ] + }, + "ipv6": { + "XYZ": [ + { + "seqid": 100, + "action": "permit", + "network": NETWORK["ipv6"][0], + } + ] + }, + } + } + } + result = create_prefix_lists(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + if addr_type == "ipv4": + pf_list = "ABC" + else: + pf_list = "XYZ" + + input_dict_6 = { + "r3": { + "route_maps": { + "BGP_ATTR_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 10, + "match": {addr_type: {"prefix_lists": pf_list}}, + "set": { + "aspath": {"as_num": 500, "as_action": "prepend"}, + "localpref": 500, + "origin": "egp", + "community": {"num": "500:500", "action": "additive"}, + "large_community": { + "num": "500:500:500", + "action": "additive", + }, + }, + }, + {"action": "permit", "seq_id": 20}, + ] + }, + "BGP_ATTR_{}".format(addr_type): [ + { + "action": "permit", + "seq_id": 100, + "match": {addr_type: {"prefix_lists": pf_list}}, + "set": { + "aspath": {"as_num": 500, "as_action": "prepend"}, + "localpref": 500, + "origin": "egp", + "community": {"num": "500:500", "action": "additive"}, + "large_community": { + "num": "500:500:500", + "action": "additive", + }, + }, + }, + {"action": "permit", "seq_id": 200}, + ], + } + } + + result = create_route_maps(tgen, input_dict_6) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Apply the route-map on R3 in outbound direction for peer R4") + + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "BGP_ATTR_ipv4", + "direction": "out", + } + ] + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "BGP_ATTR_ipv6", + "direction": "out", + } + ] + } + } + } + } + } + }, + } + } + } + } + + result = create_router_bgp(tgen, topo, input_dict_7) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "verify modified attributes for specific prefix with 'vrf default'" + "keyword on R4" + ) + for addr_type in ADDR_TYPES: + dut = "r4" + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "vrf": "default", + "largeCommunity": "500:500:500", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + dut = "r4" + input_dict = { + "r3": { + "static_routes": [ + { + "network": NETWORK[addr_type][0], + "vrf": "default", + "community": "500:500", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + result = verify_rib(tgen, addr_type, dut, input_dict) + assert result is True, "Testcase : Failed \n Error: {}".format(tc_name, result) + + input_dict_4 = {"largeCommunity": "500:500:500", "community": "500:500"} + + result = verify_bgp_community( + tgen, addr_type, dut, [NETWORK[addr_type][0]], input_dict_4 + ) + assert result is True, "Test case {} : Should fail \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + def test_bgp_with_loopback_interface(request): """ Test BGP with loopback interface @@ -588,6 +850,298 @@ def test_bgp_with_loopback_interface(request): write_test_footer(tc_name) +def test_bgp_with_loopback_with_same_subnet_p1(request): + """ + Verify routes not installed in zebra when /32 routes received + with loopback BGP session subnet + """ + + tgen = get_topogen() + if BGP_CONVERGENCE is not True: + pytest.skip("skipped because of BGP Convergence failure") + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + step("Delete BGP seesion created initially") + input_dict_r1 = { + "r1": {"bgp": {"delete": True}}, + "r2": {"bgp": {"delete": True}}, + "r3": {"bgp": {"delete": True}}, + "r4": {"bgp": {"delete": True}}, + } + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Create BGP session over loop address") + topo_modify = deepcopy(topo) + + for routerN in sorted(topo["routers"].keys()): + for addr_type in ADDR_TYPES: + for bgp_neighbor in topo_modify["routers"][routerN]["bgp"][ + "address_family" + ][addr_type]["unicast"]["neighbor"].keys(): + + # Adding ['source_link'] = 'lo' key:value pair + topo_modify["routers"][routerN]["bgp"]["address_family"][addr_type][ + "unicast" + ]["neighbor"][bgp_neighbor]["dest_link"] = { + "lo": {"source_link": "lo", "ebgp_multihop": 2} + } + + result = create_router_bgp(tgen, topo_modify["routers"]) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Disable IPv6 BGP nbr from ipv4 address family") + raw_config = { + "r1": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + "r2": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r2"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + "r3": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + ), + "no neighbor {} activate".format( + topo["routers"]["r4"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + "r4": { + "raw_config": [ + "router bgp {}".format(topo["routers"]["r4"]["bgp"]["local_as"]), + "address-family ipv4 unicast", + "no neighbor {} activate".format( + topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0] + ), + ] + }, + } + + step("Configure kernel routes") + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + r1_ipv4_lo = topo["routers"]["r1"]["links"]["lo"]["ipv4"] + r1_ipv6_lo = topo["routers"]["r1"]["links"]["lo"]["ipv6"] + r2_ipv4_lo = topo["routers"]["r2"]["links"]["lo"]["ipv4"] + r2_ipv6_lo = topo["routers"]["r2"]["links"]["lo"]["ipv6"] + r3_ipv4_lo = topo["routers"]["r3"]["links"]["lo"]["ipv4"] + r3_ipv6_lo = topo["routers"]["r3"]["links"]["lo"]["ipv6"] + r4_ipv4_lo = topo["routers"]["r4"]["links"]["lo"]["ipv4"] + r4_ipv6_lo = topo["routers"]["r4"]["links"]["lo"]["ipv6"] + + r1_r2 = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + r2_r1 = topo["routers"]["r2"]["links"]["r1"]["ipv6"].split("/")[0] + r1_r3 = topo["routers"]["r1"]["links"]["r3"]["ipv6"].split("/")[0] + r3_r1 = topo["routers"]["r3"]["links"]["r1"]["ipv6"].split("/")[0] + r2_r3 = topo["routers"]["r2"]["links"]["r3"]["ipv6"].split("/")[0] + r3_r2 = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0] + r3_r4 = topo["routers"]["r3"]["links"]["r4"]["ipv6"].split("/")[0] + r4_r3 = topo["routers"]["r4"]["links"]["r3"]["ipv6"].split("/")[0] + + r1_r2_ipv4 = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0] + r2_r1_ipv4 = topo["routers"]["r2"]["links"]["r1"]["ipv4"].split("/")[0] + r1_r3_ipv4 = topo["routers"]["r1"]["links"]["r3"]["ipv4"].split("/")[0] + r3_r1_ipv4 = topo["routers"]["r3"]["links"]["r1"]["ipv4"].split("/")[0] + r2_r3_ipv4 = topo["routers"]["r2"]["links"]["r3"]["ipv4"].split("/")[0] + r3_r2_ipv4 = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0] + r3_r4_ipv4 = topo["routers"]["r3"]["links"]["r4"]["ipv4"].split("/")[0] + r4_r3_ipv4 = topo["routers"]["r4"]["links"]["r3"]["ipv4"].split("/")[0] + + r1_r2_intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + r2_r1_intf = topo["routers"]["r2"]["links"]["r1"]["interface"] + r1_r3_intf = topo["routers"]["r1"]["links"]["r3"]["interface"] + r3_r1_intf = topo["routers"]["r3"]["links"]["r1"]["interface"] + r2_r3_intf = topo["routers"]["r2"]["links"]["r3"]["interface"] + r3_r2_intf = topo["routers"]["r3"]["links"]["r2"]["interface"] + r3_r4_intf = topo["routers"]["r3"]["links"]["r4"]["interface"] + r4_r3_intf = topo["routers"]["r4"]["links"]["r3"]["interface"] + + ipv4_list = [ + ("r1", r1_r2_intf, r2_ipv4_loopback), + ("r1", r1_r3_intf, r3_ipv4_loopback), + ("r2", r2_r1_intf, r1_ipv4_loopback), + ("r2", r2_r3_intf, r3_ipv4_loopback), + ("r3", r3_r1_intf, r1_ipv4_loopback), + ("r3", r3_r2_intf, r2_ipv4_loopback), + ("r3", r3_r4_intf, r4_ipv4_loopback), + ("r4", r4_r3_intf, r3_ipv4_loopback), + ] + + ipv6_list = [ + ("r1", r1_r2_intf, r2_ipv6_loopback, r2_r1), + ("r1", r1_r3_intf, r3_ipv6_loopback, r3_r1), + ("r2", r2_r1_intf, r1_ipv6_loopback, r1_r2), + ("r2", r2_r3_intf, r3_ipv6_loopback, r3_r2), + ("r3", r3_r1_intf, r1_ipv6_loopback, r1_r3), + ("r3", r3_r2_intf, r2_ipv6_loopback, r2_r3), + ("r3", r3_r4_intf, r4_ipv6_loopback, r4_r3), + ("r4", r4_r3_intf, r3_ipv6_loopback, r3_r4), + ] + + for dut, intf, loop_addr in ipv4_list: + result = addKernelRoute(tgen, dut, intf, loop_addr) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + for dut, intf, loop_addr, next_hop in ipv6_list: + result = addKernelRoute(tgen, dut, intf, loop_addr, next_hop) + assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result) + + step("Configure static routes") + + input_dict = { + "r1": { + "static_routes": [ + {"network": r2_ipv4_loopback, "next_hop": r2_r1_ipv4}, + {"network": r3_ipv4_loopback, "next_hop": r3_r1_ipv4}, + {"network": r2_ipv6_loopback, "next_hop": r2_r1}, + {"network": r3_ipv6_loopback, "next_hop": r3_r1}, + ] + }, + "r2": { + "static_routes": [ + {"network": r1_ipv4_loopback, "next_hop": r1_r2_ipv4}, + {"network": r3_ipv4_loopback, "next_hop": r3_r2_ipv4}, + {"network": r1_ipv6_loopback, "next_hop": r1_r2}, + {"network": r3_ipv6_loopback, "next_hop": r3_r2}, + ] + }, + "r3": { + "static_routes": [ + {"network": r1_ipv4_loopback, "next_hop": r1_r3_ipv4}, + {"network": r2_ipv4_loopback, "next_hop": r2_r3_ipv4}, + {"network": r4_ipv4_loopback, "next_hop": r4_r3_ipv4}, + {"network": r1_ipv6_loopback, "next_hop": r1_r3}, + {"network": r2_ipv6_loopback, "next_hop": r2_r3}, + {"network": r4_ipv6_loopback, "next_hop": r4_r3}, + ] + }, + "r4": { + "static_routes": [ + {"network": r3_ipv4_loopback, "next_hop": r3_r4_ipv4}, + {"network": r3_ipv6_loopback, "next_hop": r3_r4}, + ] + }, + } + result = create_static_routes(tgen, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify BGP session convergence") + + result = verify_bgp_convergence(tgen, topo_modify) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Configure redistribute connected on R2 and R4") + input_dict_1 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + }, + "r4": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + "ipv6": { + "unicast": {"redistribute": [{"redist_type": "connected"}]} + }, + } + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify Ipv4 and Ipv6 network installed in R1 RIB but not in FIB") + input_dict_r1 = { + "r1": { + "static_routes": [ + {"network": "1.0.2.17/32"}, + {"network": "2001:db8:f::2:17/128"}, + ] + } + } + + dut = "r1" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib(tgen, addr_type, dut, input_dict_r1, protocol=protocol) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1, expected=False) + assert result is not True, "Testcase {} : Failed \n" + "Expected behavior: routes should not present in fib \n" + "Error: {}".format(tc_name, result) + + step("Verify Ipv4 and Ipv6 network installed in r3 RIB but not in FIB") + input_dict_r3 = { + "r3": { + "static_routes": [ + {"network": "1.0.4.17/32"}, + {"network": "2001:db8:f::4:17/128"}, + ] + } + } + dut = "r3" + protocol = "bgp" + for addr_type in ADDR_TYPES: + result = verify_rib( + tgen, addr_type, dut, input_dict_r3, protocol=protocol, fib=None + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_fib_routes(tgen, addr_type, dut, input_dict_r1, expected=False) + assert result is not True, "Testcase {} : Failed \n" + "Expected behavior: routes should not present in fib \n" + "Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf index 772675ddff..2db7edb806 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf +++ b/tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf @@ -2,3 +2,12 @@ router ospf network 10.20.0.0/16 area 0 network 10.20.20.20/32 area 0 +! +int P1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int P1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf index d337201f71..c7a76a98ed 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf @@ -4,6 +4,7 @@ router bgp 65000 no bgp default ipv4-unicast neighbor 10.30.30.30 remote-as 65000 neighbor 10.30.30.30 update-source lo + neighbor 10.30.30.30 timers 3 10 address-family l2vpn evpn neighbor 10.30.30.30 activate advertise-all-vni diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json index d9f2182aa0..2937504244 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json @@ -3,12 +3,11 @@ "type":"L2", "vrf":"default", "vxlanInterface":"vxlan101", - "ifindex":5, "vtepIp":"10.10.10.10", "mcastGroup":"0.0.0.0", "advertiseGatewayMacip":"No", "numMacs":5, - "numArpNd":2, + "numArpNd":3, "numRemoteVteps":[ "10.30.30.30" ] diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf index 31c7fc4188..f1c2b42dc1 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf @@ -2,3 +2,8 @@ router ospf network 10.20.0.0/16 area 0 network 10.10.10.10/32 area 0 +! +int PE1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf index 938ec7bca9..e2699475c9 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf @@ -3,8 +3,6 @@ log file zebra.log ! interface lo ip address 10.10.10.10/32 -interface PE1-eth0 - ip address 10.10.1.1/24 interface PE1-eth1 ip address 10.20.1.1/24 ! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf index d99e33fc06..0a24158bb8 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf @@ -4,6 +4,7 @@ router bgp 65000 no bgp default ipv4-unicast neighbor 10.10.10.10 remote-as 65000 neighbor 10.10.10.10 update-source lo + neighbor 10.10.10.10 timers 3 10 ! address-family l2vpn evpn neighbor 10.10.10.10 activate diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json index 13255ab4f2..0853147a00 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json @@ -3,12 +3,11 @@ "type":"L2", "vrf":"default", "vxlanInterface":"vxlan101", - "ifindex":5, "vtepIp":"10.30.30.30", "mcastGroup":"0.0.0.0", "advertiseGatewayMacip":"No", "numMacs":5, - "numArpNd":2, + "numArpNd":3, "numRemoteVteps":[ "10.10.10.10" ] diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf index c1a8308db5..065c993303 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf @@ -2,3 +2,8 @@ router ospf network 10.20.0.0/16 area 0 network 10.30.30.30/32 area 0 +! +int PE2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf index 07b83f6395..9738916ab0 100644 --- a/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf +++ b/tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf @@ -3,6 +3,4 @@ interface lo ip address 10.30.30.30/32 interface PE2-eth0 ip address 10.20.2.3/24 -interface PE2-eth1 - ip address 10.10.1.3/24 ! diff --git a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py index ad72540185..90144f5c66 100755 --- a/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py +++ b/tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py @@ -29,6 +29,7 @@ import os import sys import json from functools import partial +from time import sleep import pytest # Save the Current Working Directory to find configuration files. @@ -97,6 +98,7 @@ def setup_module(mod): # set up PE bridges with the EVPN member interfaces facing the CE hosts pe1.run("ip link add name br101 type bridge stp_state 0") + pe1.run("ip addr add 10.10.1.1/24 dev br101") pe1.run("ip link set dev br101 up") pe1.run( "ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.10.10.10 nolearning" @@ -106,6 +108,7 @@ def setup_module(mod): pe1.run("ip link set dev PE1-eth0 master br101") pe2.run("ip link add name br101 type bridge stp_state 0") + pe2.run("ip addr add 10.10.1.3/24 dev br101") pe2.run("ip link set dev br101 up") pe2.run( "ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.30.30.30 nolearning" @@ -142,6 +145,15 @@ def teardown_module(mod): tgen.stop_topology() +def show_vni_json_elide_ifindex(pe, vni, expected): + output_json = pe.vtysh_cmd("show evpn vni {} json".format(vni), isjson=True) + + if "ifindex" in output_json: + output_json.pop("ifindex") + + return topotest.json_cmp(output_json, expected) + + def test_pe1_converge_evpn(): "Wait for protocol convergence" @@ -154,9 +166,7 @@ def test_pe1_converge_evpn(): json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name) expected = json.loads(open(json_file).read()) - test_func = partial( - topotest.router_json_cmp, pe1, "show evpn vni 101 json", expected - ) + test_func = partial(show_vni_json_elide_ifindex, pe1, 101, expected) _, result = topotest.run_and_expect(test_func, None, count=125, wait=1) assertmsg = '"{}" JSON output mismatches'.format(pe1.name) assert result is None, assertmsg @@ -175,9 +185,7 @@ def test_pe2_converge_evpn(): json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name) expected = json.loads(open(json_file).read()) - test_func = partial( - topotest.router_json_cmp, pe2, "show evpn vni 101 json", expected - ) + test_func = partial(show_vni_json_elide_ifindex, pe2, 101, expected) _, result = topotest.run_and_expect(test_func, None, count=125, wait=1) assertmsg = '"{}" JSON output mismatches'.format(pe2.name) assert result is None, assertmsg @@ -194,7 +202,7 @@ def mac_learn_test(host, local): mac_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) mac_output_json = json.loads(mac_output) assertmsg = "Local MAC output does not match interface mac {}".format(mac) - assert mac_output_json[mac]["type"] == "local" + assert mac_output_json[mac]["type"] == "local", assertmsg def mac_test_local_remote(local, remote): @@ -275,6 +283,103 @@ def test_local_remote_mac_pe2(): # Memory leak test template +def ip_learn_test(tgen, host, local, remote, ip_addr): + "check the host IP gets learned by the VNI" + host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name)) + int_lines = host_output.splitlines() + mac_line = int_lines[7].split(": ") + mac = mac_line[1] + print(host_output) + + # check we have a local association between the MAC and IP + local_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac)) + print(local_output) + local_output_json = json.loads(local_output) + mac_type = local_output_json[mac]["type"] + assertmsg = "Failed to learn local IP address on host {}".format(host.name) + assert local_output_json[mac]["neighbors"] != "none", assertmsg + learned_ip = local_output_json[mac]["neighbors"]["active"][0] + + assertmsg = "local learned mac wrong type: {} ".format(mac_type) + assert mac_type == "local", assertmsg + + assertmsg = "learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + assert ip_addr == learned_ip, assertmsg + + # now lets check the remote + count = 0 + converged = False + while count < 30: + remote_output = remote.vtysh_cmd( + "show evpn mac vni 101 mac {} json".format(mac) + ) + print(remote_output) + remote_output_json = json.loads(remote_output) + type = remote_output_json[mac]["type"] + if not remote_output_json[mac]["neighbors"] == "none": + # due to a kernel quirk, learned IPs can be inactive + if ( + remote_output_json[mac]["neighbors"]["active"] + or remote_output_json[mac]["neighbors"]["inactive"] + ): + converged = True + break + count += 1 + sleep(1) + + print("tries: {}".format(count)) + assertmsg = "{} remote learned mac no address: {} ".format(host.name, mac) + # some debug for this failure + if not converged == True: + log_output = remote.run("cat zebra.log") + print(log_output) + + assert converged == True, assertmsg + if remote_output_json[mac]["neighbors"]["active"]: + learned_ip = remote_output_json[mac]["neighbors"]["active"][0] + else: + learned_ip = remote_output_json[mac]["neighbors"]["inactive"][0] + assertmsg = "remote learned mac wrong type: {} ".format(type) + assert type == "remote", assertmsg + + assertmsg = "remote learned address mismatch with configured address host: {} learned: {}".format( + ip_addr, learned_ip + ) + assert ip_addr == learned_ip, assertmsg + + +def test_ip_pe1_learn(): + "run the IP learn test for PE1" + + tgen = get_topogen() + host1 = tgen.gears["host1"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + pe2.vtysh_cmd("debug zebra vxlan") + pe2.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host1.run("ping -c1 10.10.1.1") + ip_learn_test(tgen, host1, pe1, pe2, "10.10.1.55") + # tgen.mininet_cli() + + +def test_ip_pe2_learn(): + "run the IP learn test for PE2" + + tgen = get_topogen() + host2 = tgen.gears["host2"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + pe1.vtysh_cmd("debug zebra vxlan") + pe1.vtysh_cmd("debug zebra kernel") + # lets populate that arp cache + host2.run("ping -c1 10.10.1.3") + ip_learn_test(tgen, host2, pe2, pe1, "10.10.1.56") + # tgen.mininet_cli() + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json new file mode 100644 index 0000000000..70b70de1d1 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json @@ -0,0 +1,20 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.1", + "as":65000, + "vrfId":0, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.2":{ + "remoteAs":65000, + "state":"Idle (Admin)" + }, + "192.168.101.2":{ + "remoteAs":65100, + "state":"Idle (Admin)" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r1/bgp_summary.json b/tests/topotests/bgp_features/r1/bgp_summary.json new file mode 100644 index 0000000000..57aa992fab --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_summary.json @@ -0,0 +1,28 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.1", + "as":65000, + "vrfId":0, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.2":{ + "hostname":"r2", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":7, + "state":"Established" + }, + "192.168.101.2":{ + "hostname":"r4", + "remoteAs":65100, + "outq":0, + "inq":0, + "pfxRcd":3, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r1/bgpd.conf b/tests/topotests/bgp_features/r1/bgpd.conf new file mode 100644 index 0000000000..7aea2dc161 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgpd.conf @@ -0,0 +1,39 @@ +! +hostname r1 +log file bgpd.log +! +router bgp 65000 + timers bgp 3 10 + coalesce-time 0 + bgp router-id 192.168.0.1 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65000 + neighbor 192.168.0.2 description Router R2 (iBGP) + neighbor 192.168.0.2 update-source lo + neighbor 192.168.0.2 timers connect 5 + neighbor 192.168.101.2 remote-as 65100 + neighbor 192.168.101.2 description Router R4 (eBGP AS 65100) + neighbor 192.168.101.2 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.0.0/24 + network 192.168.1.0/24 + network 192.168.2.0/24 + network 192.168.3.0/24 + network 192.168.6.0/24 + network 192.168.8.0/24 + neighbor 192.168.101.2 route-map testmap-in in + neighbor 192.168.101.2 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r1/ospf6d.conf b/tests/topotests/bgp_features/r1/ospf6d.conf new file mode 100644 index 0000000000..532da39fb7 --- /dev/null +++ b/tests/topotests/bgp_features/r1/ospf6d.conf @@ -0,0 +1,22 @@ +log file ospf6d.log +! +debug ospf6 neighbor +! +interface r1-lo +! +interface r1-eth0 +! +interface r1-eth1 +! +interface r1-eth2 +! +router ospf6 + ospf6 router-id 192.168.0.1 + log-adjacency-changes + interface r1-lo area 0.0.0.0 + interface r1-eth0 area 0.0.0.0 + interface r1-eth1 area 0.0.0.0 + interface r1-eth2 area 0.0.0.0 +! +line vty +! diff --git a/tests/topotests/bgp_features/r1/ospf_neighbor.json b/tests/topotests/bgp_features/r1/ospf_neighbor.json new file mode 100644 index 0000000000..ecb97d8d74 --- /dev/null +++ b/tests/topotests/bgp_features/r1/ospf_neighbor.json @@ -0,0 +1,16 @@ +{ + "neighbors":{ + "192.168.0.2":[ + { + "priority":1, + "state":"Full\/DR" + } + ], + "192.168.0.3":[ + { + "priority":1, + "state":"Full\/DR" + } + ] + } +} diff --git a/tests/topotests/bgp_features/r1/ospfd.conf b/tests/topotests/bgp_features/r1/ospfd.conf new file mode 100644 index 0000000000..8f1711db7b --- /dev/null +++ b/tests/topotests/bgp_features/r1/ospfd.conf @@ -0,0 +1,23 @@ +log file ospfd.log +! +debug ospf event +debug ospf zebra +! +router ospf + ospf router-id 192.168.0.1 + log-adjacency-changes + network 192.168.0.0/20 area 0.0.0.0 + timers throttle spf 0 0 0 + timers lsa min-arrival 10 + timers throttle lsa all 0 + refresh timer 10 +! +line vty +interface r1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +interface r1-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_features/r1/zebra.conf b/tests/topotests/bgp_features/r1/zebra.conf new file mode 100644 index 0000000000..61564f1a1a --- /dev/null +++ b/tests/topotests/bgp_features/r1/zebra.conf @@ -0,0 +1,28 @@ +! +hostname r1 +log file zebra.log +! +interface lo + ip address 192.168.0.1/32 + ipv6 address fc00::1/128 +! +interface r1-eth0 + description SW6 Stub Network + ip address 192.168.6.1/24 + ipv6 address fc00:0:0:6::1/64 +! +interface r1-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.1.1/24 + ipv6 address fc00:0:0:1::1/64 +! +interface r1-eth2 + description SW2 R1-R3 OSPF Network + ip address 192.168.3.1/24 + ipv6 address fc00:0:0:3::1/64 +! +interface r1-eth3 + description SW4 R1-R4 eBGP Network + ip address 192.168.101.1/24 + ipv6 address fc00:100:0:1::1/64 +! diff --git a/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json new file mode 100644 index 0000000000..95de3e3096 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json @@ -0,0 +1,20 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.2", + "as":65000, + "vrfId":0, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.1":{ + "remoteAs":65000, + "state":"Active" + }, + "192.168.201.2":{ + "remoteAs":65200, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r2/bgp_summary.json b/tests/topotests/bgp_features/r2/bgp_summary.json new file mode 100644 index 0000000000..f29204116c --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_summary.json @@ -0,0 +1,28 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.0.2", + "as":65000, + "vrfId":0, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.168.0.1":{ + "hostname":"r1", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":8, + "state":"Established" + }, + "192.168.201.2":{ + "hostname":"r5", + "remoteAs":65200, + "outq":0, + "inq":0, + "pfxRcd":2, + "state":"Established" + } + }, + "totalPeers":2 +} +} diff --git a/tests/topotests/bgp_features/r2/bgpd.conf b/tests/topotests/bgp_features/r2/bgpd.conf new file mode 100644 index 0000000000..3daf9842ce --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgpd.conf @@ -0,0 +1,39 @@ +! +hostname r2 +log file bgpd.log +! +router bgp 65000 + bgp router-id 192.168.0.2 + timers bgp 3 10 + coalesce-time 0 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as 65000 + neighbor 192.168.0.1 description Router R1 (iBGP) + neighbor 192.168.0.1 update-source lo + neighbor 192.168.0.1 timers connect 5 + neighbor 192.168.201.2 remote-as 65200 + neighbor 192.168.201.2 description Router R5 (eBGP AS 65200) + neighbor 192.168.201.2 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.0.0/24 + network 192.168.1.0/24 + network 192.168.2.0/24 + network 192.168.3.0/24 + network 192.168.7.0/24 + network 192.168.8.0/24 + neighbor 192.168.201.2 route-map testmap-in in + neighbor 192.168.201.2 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r2/ospf6d.conf b/tests/topotests/bgp_features/r2/ospf6d.conf new file mode 100644 index 0000000000..283d205489 --- /dev/null +++ b/tests/topotests/bgp_features/r2/ospf6d.conf @@ -0,0 +1,22 @@ +log file ospf6d.log +! +debug ospf6 neighbor +! +interface r2-lo +! +interface r2-eth0 +! +interface r2-eth1 +! +interface r2-eth2 +! +router ospf6 + ospf6 router-id 192.168.0.2 + log-adjacency-changes + interface r2-lo area 0.0.0.0 + interface r2-eth0 area 0.0.0.0 + interface r2-eth1 area 0.0.0.0 + interface r2-eth2 area 0.0.0.0 +! +line vty +! diff --git a/tests/topotests/bgp_features/r2/ospf_neighbor.json b/tests/topotests/bgp_features/r2/ospf_neighbor.json new file mode 100644 index 0000000000..5fcbd82ee1 --- /dev/null +++ b/tests/topotests/bgp_features/r2/ospf_neighbor.json @@ -0,0 +1,16 @@ +{ + "neighbors":{ + "192.168.0.1":[ + { + "priority":1, + "state":"Full\/Backup" + } + ], + "192.168.0.3":[ + { + "priority":1, + "state":"Full\/DR" + } + ] + } +} diff --git a/tests/topotests/bgp_features/r2/ospfd.conf b/tests/topotests/bgp_features/r2/ospfd.conf new file mode 100644 index 0000000000..2174fddb11 --- /dev/null +++ b/tests/topotests/bgp_features/r2/ospfd.conf @@ -0,0 +1,23 @@ +log file ospfd.log +! +debug ospf event +debug ospf zebra +! +router ospf + ospf router-id 192.168.0.2 + log-adjacency-changes + network 192.168.0.0/20 area 0.0.0.0 + timers throttle spf 0 0 0 + timers lsa min-arrival 10 + timers throttle lsa all 0 + refresh timer 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +line vty +! diff --git a/tests/topotests/bgp_features/r2/zebra.conf b/tests/topotests/bgp_features/r2/zebra.conf new file mode 100644 index 0000000000..1d427da6f5 --- /dev/null +++ b/tests/topotests/bgp_features/r2/zebra.conf @@ -0,0 +1,28 @@ +! +hostname r2 +log file zebra.log +! +interface lo + ip address 192.168.0.2/32 + ipv6 address fc00::2/128 +! +interface r2-eth0 + description SW7 Stub Network + ip address 192.168.7.1/24 + ipv6 address fc00:0:0:7::1/64 +! +interface r2-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.1.2/24 + ipv6 address fc00:0:0:1::2/64 +! +interface r2-eth2 + description SW1 R2-R3 OSPF Network + ip address 192.168.2.1/24 + ipv6 address fc00:0:0:2::1/64 +! +interface r2-eth3 + description SW5 R2-R5 eBGP Network + ip address 192.168.201.1/24 + ipv6 address fc00:200:0:1::1/64 +! diff --git a/tests/topotests/bgp_features/r3/bgp_summary.json b/tests/topotests/bgp_features/r3/bgp_summary.json new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_features/r3/bgp_summary.json diff --git a/tests/topotests/bgp_features/r3/ospf6d.conf b/tests/topotests/bgp_features/r3/ospf6d.conf new file mode 100644 index 0000000000..7a6623f979 --- /dev/null +++ b/tests/topotests/bgp_features/r3/ospf6d.conf @@ -0,0 +1,22 @@ +log file ospf6d.log +! +debug ospf6 neighbor +! +interface r3-lo +! +interface r3-eth0 +! +interface r3-eth1 +! +interface r3-eth2 +! +router ospf6 + ospf6 router-id 192.168.0.3 + log-adjacency-changes + interface r3-lo area 0.0.0.0 + interface r3-eth0 area 0.0.0.0 + interface r3-eth1 area 0.0.0.0 + interface r3-eth2 area 0.0.0.0 +! +line vty +! diff --git a/tests/topotests/bgp_features/r3/ospf_neighbor.json b/tests/topotests/bgp_features/r3/ospf_neighbor.json new file mode 100644 index 0000000000..e90a70a8f6 --- /dev/null +++ b/tests/topotests/bgp_features/r3/ospf_neighbor.json @@ -0,0 +1,16 @@ +{ + "neighbors":{ + "192.168.0.1":[ + { + "priority":1, + "state":"Full\/Backup" + } + ], + "192.168.0.2":[ + { + "priority":1, + "state":"Full\/Backup" + } + ] + } +} diff --git a/tests/topotests/bgp_features/r3/ospfd.conf b/tests/topotests/bgp_features/r3/ospfd.conf new file mode 100644 index 0000000000..795344fbe6 --- /dev/null +++ b/tests/topotests/bgp_features/r3/ospfd.conf @@ -0,0 +1,23 @@ +log file ospfd.log +! +debug ospf event +debug ospf zebra +! +router ospf + ospf router-id 192.168.0.3 + log-adjacency-changes + network 192.168.0.0/20 area 0.0.0.0 + timers throttle spf 0 0 0 + timers lsa min-arrival 10 + timers throttle lsa all 0 + refresh timer 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +int r3-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +line vty +! diff --git a/tests/topotests/bgp_features/r3/zebra.conf b/tests/topotests/bgp_features/r3/zebra.conf new file mode 100644 index 0000000000..62ba04d81b --- /dev/null +++ b/tests/topotests/bgp_features/r3/zebra.conf @@ -0,0 +1,23 @@ +! +hostname r3 +log file zebra.log +! +interface lo + ip address 192.168.0.3/32 + ipv6 address fc00::3/128 +! +interface r3-eth0 + description SW8 Stub Network + ip address 192.168.8.1/24 + ipv6 address fc00:0:0:8::1/64 +! +interface r3-eth1 + description SW1 R2-R3 OSPF Network + ip address 192.168.2.2/24 + ipv6 address fc00:0:0:2::2/64 +! +interface r3-eth2 + description SW2 R1-R3 OSPF Network + ip address 192.168.3.2/24 + ipv6 address fc00:0:0:3::2/64 +! diff --git a/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json new file mode 100644 index 0000000000..c22cbdadcd --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json @@ -0,0 +1,15 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.100.1", + "as":65100, + "vrfId":0, + "vrfName":"default", + "peerCount":1, + "peers":{ + "192.168.101.1":{ + "remoteAs":65000, + "state":"Active" } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_features/r4/bgp_summary.json b/tests/topotests/bgp_features/r4/bgp_summary.json new file mode 100644 index 0000000000..d147602c74 --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgp_summary.json @@ -0,0 +1,19 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.100.1", + "as":65100, + "vrfId":0, + "vrfName":"default", + "peerCount":1, + "peers":{ + "192.168.101.1":{ + "hostname":"r1", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":6, + "state":"Established" } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_features/r4/bgpd.conf b/tests/topotests/bgp_features/r4/bgpd.conf new file mode 100644 index 0000000000..fe1a4d4ffe --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgpd.conf @@ -0,0 +1,33 @@ +! +hostname r4 +log file bgpd.log +! +router bgp 65100 + bgp router-id 192.168.100.1 + timers bgp 3 10 + coalesce-time 0 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.101.1 remote-as 65000 + neighbor 192.168.101.1 description Router R1 (eBGP AS 65000) + neighbor 192.168.101.1 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.100.0/24 + network 192.168.101.0/24 + network 192.168.102.0/24 + neighbor 192.168.101.1 default-originate + neighbor 192.168.101.1 route-map testmap-in in + neighbor 192.168.101.1 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r4/zebra.conf b/tests/topotests/bgp_features/r4/zebra.conf new file mode 100644 index 0000000000..08e3e1aec9 --- /dev/null +++ b/tests/topotests/bgp_features/r4/zebra.conf @@ -0,0 +1,18 @@ +! +hostname r4 +log file zebra.log +! +interface lo + ip address 192.168.100.1/32 + ipv6 address fc00:100::1/128 +! +interface r4-eth0 + description SW5 Stub Network + ip address 192.168.102.1/24 + ipv6 address fc00:100:0:2::1/64 +! +interface r4-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.101.2/24 + ipv6 address fc00:100:0:1::2/64 +! diff --git a/tests/topotests/bgp_features/r5/bgp_summary.json b/tests/topotests/bgp_features/r5/bgp_summary.json new file mode 100644 index 0000000000..f303aa6c72 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_summary.json @@ -0,0 +1,20 @@ +{ +"ipv4Unicast":{ + "routerId":"192.168.200.1", + "as":65200, + "vrfId":0, + "vrfName":"default", + "peerCount":1, + "peers":{ + "192.168.201.1":{ + "hostname":"r2", + "remoteAs":65000, + "outq":0, + "inq":0, + "pfxRcd":6, + "state":"Established" + } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_features/r5/bgpd.conf b/tests/topotests/bgp_features/r5/bgpd.conf new file mode 100644 index 0000000000..8504213b41 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgpd.conf @@ -0,0 +1,33 @@ +! +hostname r5 +log file bgpd.log +! +router bgp 65200 + bgp router-id 192.168.200.1 + timers bgp 3 10 + coalesce-time 0 + bgp log-neighbor-changes + no bgp ebgp-requires-policy + neighbor 192.168.201.1 remote-as 65000 + neighbor 192.168.201.1 description Router R2 (eBGP AS 65000) + neighbor 192.168.201.1 timers connect 5 + ! + address-family ipv4 unicast + network 192.168.200.0/24 + network 192.168.201.0/24 + network 192.168.202.0/24 + neighbor 192.168.101.1 default-originate + neighbor 192.168.201.1 route-map testmap-in in + neighbor 192.168.201.1 route-map testmap-out out + exit-address-family +! +! +! +route-map testmap-in permit 999 +! +route-map testmap-out permit 999 +! +! +line vty +! + diff --git a/tests/topotests/bgp_features/r5/zebra.conf b/tests/topotests/bgp_features/r5/zebra.conf new file mode 100644 index 0000000000..4d9064a593 --- /dev/null +++ b/tests/topotests/bgp_features/r5/zebra.conf @@ -0,0 +1,18 @@ +! +hostname r5 +log file zebra.log +! +interface lo + ip address 192.168.200.1/32 + ipv6 address fc00:200::1/128 +! +interface r5-eth0 + description SW6 Stub Network + ip address 192.168.202.1/24 + ipv6 address fc00:200:0:2::1/64 +! +interface r5-eth1 + description SW0 R1-R2 OSPF & BGP Network + ip address 192.168.201.2/24 + ipv6 address fc00:200:0:1::2/64 +! diff --git a/tests/topotests/bgp_features/test_bgp_features.dot b/tests/topotests/bgp_features/test_bgp_features.dot new file mode 100644 index 0000000000..70b126ce8b --- /dev/null +++ b/tests/topotests/bgp_features/test_bgp_features.dot @@ -0,0 +1,83 @@ +## GraphViz file for test_all_protocol_startup +## +## Color coding: +######################### +## Main FRR: #f08080 red +## No protocol: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #33ff99 light green +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +## LDP IPv4 #fedbe2 light pink +##### Colors (see http://www.color-hex.com/) + +graph test_all_protocol_startup { + overlap=false; + constraint=false; + + // title + labelloc="t"; + label="Test Topologoy BGP Features"; + rankdir = TB; + + ###################### + # Routers + ###################### + + # Main FRR Router with all protocols + R4 [shape=doubleoctagon, label="R4 FRR\nAS 65100\nlo: 192.168.100.1/32\nfc00:100::1/128", fillcolor="#f08080", style=filled]; + R5 [shape=doubleoctagon, label="R5 FRR\nAS 65200\nlo: 192.168.200.1/32\nfc00:200::1/128", fillcolor="#f08080", style=filled]; + #{ rank = same {R4, R5}} + + R1 [shape=doubleoctagon, label="R1 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled]; + R2 [shape=doubleoctagon, label="R2 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled]; + #{ rank = same { R1, R2}} + + R3 [shape=doubleoctagon, label="R3 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled]; + + ###################### + # Network Lists + ###################### + + SW1_R1_R2 [label="SW1 OSPF & iBGP\n192.168.1.0/24\nfc00:0:0:1::/64", fillcolor="#32b835", style=filled]; + SW2_R2_R3 [label="SW2 OSPF\n192.168.2.0/24\nfc00:0:0:2::/64", fillcolor="#19e3d9", style=filled]; + SW3_R3_R1 [label="SW3 OSPF\n192.168.3.0/24\nfc00:0:0:3::/64", fillcolor="#19e3d9", style=filled]; + + SW4_R4_R1 [label="SW4 eBGP\n192.168.101.0/24\nfc00:100:0:1::/64", fillcolor="#fedbe2", style=filled]; + SW5_R5_R2 [label="SW5 eBGP\n192.168.201.0/24\nfc00:200:0:1::/64", fillcolor="#fedbe2", style=filled]; + #{ rank = same {SW4_R4_R1, SW5_R5_R2}} + + SW6_STUB_R1 [label="SW6\n192.168.6.0/24\nfc00:0:0:6::/64", fillcolor="#d0e0d0", style=filled]; + SW7_STUB_R2 [label="SW7\n192.168.7.0/24\nfc00:0:0:7::/64", fillcolor="#d0e0d0", style=filled]; + SW8_STUB_R3 [label="SW8\n192.168.8.0/24\nfc00:0:0:8::/64", fillcolor="#d0e0d0", style=filled]; + SW9_STUB_R4 [label="SW9\n192.168.102.0/24\nfc00:100:0:2::/64", fillcolor="#d0e0d0", style=filled]; + SW10_STUB_R5 [label="SW10\n192.168.202.0/24\nfc00:200:0:2::/64", fillcolor="#d0e0d0", style=filled]; + + + ###################### + # Network Connections + ###################### + + R1 -- SW6_STUB_R1 [label = "eth0\n.1\n::1"]; + R2 -- SW7_STUB_R2 [label = "eth0\n.1\n::1"]; + R3 -- SW8_STUB_R3 [label = "eth0\n.1\n::1"]; + R4 -- SW9_STUB_R4 [label = "eth0\n.1\n::1"]; + R5 -- SW10_STUB_R5 [label = "eth0\n.1\n::1"]; + + R1 -- SW1_R1_R2 [label = "eth1\n.1\n::1"]; + R1 -- SW3_R3_R1 [label = "eth2\n.1\n::1"]; + R2 -- SW1_R1_R2 [label = "eth1\n.2\n::2"]; + R2 -- SW2_R2_R3 [label = "eth2\n.1\n::1"]; + R3 -- SW2_R2_R3 [label = "eth1\n.2\n::2"]; + R3 -- SW3_R3_R1 [label = "eth2\n.2\n::2"]; + + R1 -- SW4_R4_R1 [label = "eth3\n.1\n::1"]; + R2 -- SW5_R5_R2 [label = "eth3\n.1\n::1"]; + R4 -- SW4_R4_R1 [label = "eth1\n.2\n::2"]; + R5 -- SW5_R5_R2 [label = "eth1\n.2\n::2"]; + +} diff --git a/tests/topotests/bgp_features/test_bgp_features.pdf b/tests/topotests/bgp_features/test_bgp_features.pdf Binary files differnew file mode 100644 index 0000000000..cb52a54762 --- /dev/null +++ b/tests/topotests/bgp_features/test_bgp_features.pdf diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py new file mode 100755 index 0000000000..a27aaf9ec7 --- /dev/null +++ b/tests/topotests/bgp_features/test_bgp_features.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python + +# +# test_bgp_features.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 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_bgp_features.py: Test various BGP features. +""" + +import json +import functools +import os +import sys +import pytest +import re + +# 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 + +##################################################### +# +# Network Topology Definition +# +##################################################### + + +class BGPFeaturesTopo1(Topo): + "BGP Features Topology 1" + + def build(self, **_opts): + tgen = get_topogen(self) + + # Create the routers + for rtrNum in range(1, 6): + tgen.add_router("r{}".format(rtrNum)) + + # Setup Switches and connections + for swNum in range(1, 11): + tgen.add_switch("sw{}".format(swNum)) + + # Add connections to stub switches + tgen.gears["r1"].add_link(tgen.gears["sw6"]) + tgen.gears["r2"].add_link(tgen.gears["sw7"]) + tgen.gears["r3"].add_link(tgen.gears["sw8"]) + tgen.gears["r4"].add_link(tgen.gears["sw9"]) + tgen.gears["r5"].add_link(tgen.gears["sw10"]) + + # Add connections to R1-R2-R3 core + tgen.gears["r1"].add_link(tgen.gears["sw1"]) + tgen.gears["r1"].add_link(tgen.gears["sw3"]) + tgen.gears["r2"].add_link(tgen.gears["sw1"]) + tgen.gears["r2"].add_link(tgen.gears["sw2"]) + tgen.gears["r3"].add_link(tgen.gears["sw2"]) + tgen.gears["r3"].add_link(tgen.gears["sw3"]) + + # Add connections to external R4/R5 Routers + tgen.gears["r1"].add_link(tgen.gears["sw4"]) + tgen.gears["r4"].add_link(tgen.gears["sw4"]) + tgen.gears["r2"].add_link(tgen.gears["sw5"]) + tgen.gears["r5"].add_link(tgen.gears["sw5"]) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def setup_module(module): + tgen = Topogen(BGPFeaturesTopo1, module.__name__) + tgen.start_topology() + + # Starting Routers + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + if os.path.exists(os.path.join(CWD, "{}/bgpd.conf".format(rname))): + logger.info("{} uses BGPd".format(rname)) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + if os.path.exists(os.path.join(CWD, "{}/ospfd.conf".format(rname))): + logger.info("{} uses OSPFd".format(rname)) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + if os.path.exists(os.path.join(CWD, "{}/ospf6d.conf".format(rname))): + logger.info("{} uses OSPF6d".format(rname)) + router.load_config( + TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname)) + ) + router.start() + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def test_ospf_convergence(): + "Test for OSPFv2 topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check Router r1, r2 & r3 OSPF + for rtrNum in range(1, 4): + logger.info("Checking OSPFv2 convergence on router r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/ospf_neighbor.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip ospf neighbor json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "OSPF router R{} did not converge".format(rtrNum) + assert res is None, assertmsg + + +def test_bgp_convergence(): + "Test for BGP topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check Router r1 & r2 BGP + for rtrNum in [1, 2, 4, 5]: + logger.info("Checking BGP IPv4 convergence on router r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP router R{} did not converge".format(rtrNum) + assert res is None, assertmsg + + # tgen.mininet_cli() + + +def test_bgp_shutdown(): + "Test BGP instance shutdown" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net['r1'].cmd('vtysh -c \"conf t\" -c \"router bgp 65000\" -c \"bgp shutdown message ABCDabcd\"') + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4]: + logger.info("Checking BGP Summary after shutdown of R1 BGP on router r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_shutdown_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP sessions on router R{} are in incorrect state (not down as expected?)".format(rtrNum) + assert res is None, assertmsg + + +def test_bgp_shutdown_message(): + "Test BGP Peer Shutdown Message" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rtrNum in [2, 4]: + logger.info("Checking BGP shutdown received on router r{}".format(rtrNum)) + + shut_message = tgen.net['r{}'.format(rtrNum)].cmd( + 'tail bgpd.log | grep "NOTIFICATION.*Cease/Administratively Shutdown"') + assertmsg = "BGP shutdown message not received on router R{}".format(rtrNum) + assert shut_message != '', assertmsg + + m = re.search('.*([0-9]+ bytes[ 0-9a-fA-F]+)', shut_message) + if m: + found = m.group(1) + else: + found = '' + assertmsg = "Incorrect BGP shutdown message received on router R{}".format(rtrNum) + assert found == '8 bytes 41 42 43 44 61 62 63 64', assertmsg + + # tgen.mininet_cli() + + +def test_bgp_no_shutdown(): + "Test BGP instance no shutdown" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.net['r1'].cmd('vtysh -c \"conf t\" -c \"router bgp 65000\" -c \"no bgp shutdown\"') + + # Check BGP Summary on local and remote routers + for rtrNum in [1, 2, 4]: + logger.info("Checking BGP Summary after removing bgp shutdown on router r1 on router r{}".format(rtrNum)) + + router = tgen.gears["r{}".format(rtrNum)] + reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum)) + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "BGP sessions on router R{} are in incorrect state (not down as expected?)".format(rtrNum) + assert res is None, assertmsg + + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_flowspec/__init__.py b/tests/topotests/bgp_flowspec/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_flowspec/__init__.py diff --git a/tests/topotests/bgp_flowspec/exabgp.env b/tests/topotests/bgp_flowspec/exabgp.env new file mode 100644 index 0000000000..a328e04962 --- /dev/null +++ b/tests/topotests/bgp_flowspec/exabgp.env @@ -0,0 +1,54 @@ + +[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' +##daemonize = false + +[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_flowspec/peer1/exabgp.cfg b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg new file mode 100644 index 0000000000..cd1fae5aba --- /dev/null +++ b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg @@ -0,0 +1,32 @@ +neighbor 10.0.1.1 { +router-id 10.0.1.101; +local-address 10.0.1.101; +local-as 100; +peer-as 100; +flow { +route { +match { +source 1.1.1.2/32; +destination 3.3.3.3/32; +packet-length <200; +} +then { +redirect 50.0.0.2; +rate-limit 55; +} +} +#end route 1 +route { +match { +source 1::2/128/0; +destination 3::3/128/0; +packet-length <200; +} +then { +redirect 50::2; +rate-limit 55; +} +} +#end route 2 +} +} diff --git a/tests/topotests/bgp_flowspec/r1/bgpd.conf b/tests/topotests/bgp_flowspec/r1/bgpd.conf new file mode 100644 index 0000000000..6dc91b24a9 --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/bgpd.conf @@ -0,0 +1,18 @@ +! +hostname r1 +password zebra +log stdout debugging +router bgp 100 + bgp router-id 10.0.1.1 + neighbor 10.0.1.101 remote-as 100 + neighbor 10.0.1.101 update-source 10.0.1.1 + address-family ipv6 flowspec + local-install r1-eth0 + neighbor 10.0.1.101 activate + exit-address-family + address-family ipv4 flowspec + local-install r1-eth0 + neighbor 10.0.1.101 activate + exit-address-family + ! +! diff --git a/tests/topotests/bgp_flowspec/r1/summary.txt b/tests/topotests/bgp_flowspec/r1/summary.txt new file mode 100644 index 0000000000..d5dfe0b6c2 --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/summary.txt @@ -0,0 +1,53 @@ +{ +"ipv4Unicast":{ + "routerId":"10.0.1.1", + "as":100, + "vrfId":0, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":0, + "pfxSnt":0, + "state":"Established" + } + }, + "totalPeers":1 +}, +"ipv4Flowspec":{ + "routerId":"10.0.1.1", + "as":100, + "vrfId":0, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":1, + "pfxSnt":0, + "state":"Established" + } + }, + "totalPeers":1 +}, +"ipv6Flowspec":{ + "routerId":"10.0.1.1", + "as":100, + "vrfId":0, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":1, + "pfxSnt":0, + "state":"Established" + } + }, + "totalPeers":1 +} +} diff --git a/tests/topotests/bgp_flowspec/r1/zebra.conf b/tests/topotests/bgp_flowspec/r1/zebra.conf new file mode 100644 index 0000000000..e4d5a21194 --- /dev/null +++ b/tests/topotests/bgp_flowspec/r1/zebra.conf @@ -0,0 +1,8 @@ +! +hostname r1 +password zebra +interface r1-eth0 + ip address 10.0.1.1/24 + ipv6 address 1001::1/112 +! + diff --git a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py new file mode 100755 index 0000000000..a7e2c31cde --- /dev/null +++ b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py @@ -0,0 +1,208 @@ +#!/usr/bin/env python + +# +# test_bgp_flowspec_topo.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2019 by 6WIND +# +# 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_bgp_flowspec_topo.py: Test BGP topology with Flowspec EBGP peering + + + +------+------+ + | peer1 | + | BGP peer 1 | + |192.168.0.161| + | | + +------+------+ + .2 | r1-eth0 + | + ~~~~~~~~~ + +---~~ s1 ~~------+ + ~~ ~~ + ~~~~~~~~~ + | 10.0.1.1 r1-eth0 + | 1001::1 r1-eth0 + +--------+--------+ + | r1 | + |BGP 192.168.0.162| + | | + | | + | | + +-----------------+ + +""" + +import json +import functools +import os +import sys +import pytest +import getopt + +# 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 +from lib.lutil import lUtil +from lib.lutil import luCommand + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +class BGPFLOWSPECTopo1(Topo): + "BGP EBGP Flowspec Topology 1" + + def build(self, **_opts): + tgen = get_topogen(self) + + # Setup Routers + tgen.add_router("r1") + + # Setup Control Path Switch 1. r1-eth0 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + ## Add eBGP ExaBGP neighbors + peer_ip = "10.0.1.101" ## peer + peer_route = "via 10.0.1.1" ## router + peer = tgen.add_exabgp_peer("peer1", ip=peer_ip, defaultRoute=peer_route) + switch.add_link(peer) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + tgen = Topogen(BGPFLOWSPECTopo1, module.__name__) + + tgen.start_topology() + # check for zebra capability + router = tgen.gears["r1"] + + # Get r1 reference and run Daemons + logger.info("Launching BGP and ZEBRA on r1") + router = tgen.gears["r1"] + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1")) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1")) + ) + router.start() + + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.iteritems(): + peer_dir = os.path.join(CWD, pname) + env_file = os.path.join(CWD, "exabgp.env") + peer.start(peer_dir, env_file) + logger.info(pname) + + +def teardown_module(module): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + "Test for BGP topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp convergence") + + # Expected result + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/summary.txt") + + expected = json.loads(open(reffile).read()) + + test_func = functools.partial( + topotest.router_json_cmp, router, "show bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=90, wait=0.5) + assertmsg = "BGP router network did not converge" + assert res is None, assertmsg + + +def test_bgp_flowspec(): + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP") + output = router.vtysh_cmd( + "show bgp ipv4 flowspec 3.3.3.3", isjson=False, daemon="bgpd" + ) + logger.info(output) + if ( + "NH 50.0.0.2" not in output + or "FS:redirect IP" not in output + or "Packet Length < 200" not in output + ): + assertmsg = "traffic to 3.3.3.3 should have been detected as FS entry. NOK" + assert 0, assertmsg + else: + logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP OK") + + logger.info("Check BGP FS entry for 3::3 with redirect IP") + output = router.vtysh_cmd( + "show bgp ipv6 flowspec 3::3", isjson=False, daemon="bgpd" + ) + logger.info(output) + if ( + "NH 50::2" not in output + or "FS:redirect IP" not in output + or "Packet Length < 200" not in output + ): + assertmsg = "traffic to 3::3 should have been detected as FS entry. NOK" + assert 0, assertmsg + else: + logger.info("Check BGP FS entry for 3::3 with redirect IP OK") + +if __name__ == "__main__": + + args = ["-s"] + sys.argv[1:] + ret = pytest.main(args) + + sys.exit(ret) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf index 2712e54f12..8b885341eb 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf @@ -10,6 +10,7 @@ router bgp 5226 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5226 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 5.1.0.0/24 route-map rm-nh network 5.1.1.0/24 route-map rm-nh diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf index 69305512cb..2accaae163 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf @@ -10,6 +10,7 @@ router bgp 5226 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5226 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 5.1.0.0/24 route-map rm-nh network 5.1.1.0/24 route-map rm-nh diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf index 3ad95c3612..42eff1b9b1 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf @@ -10,6 +10,7 @@ router bgp 5226 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5226 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 5.1.2.0/24 route-map rm-nh network 5.1.3.0/24 route-map rm-nh diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf index 502c4c8b2f..a564da9411 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf @@ -11,8 +11,10 @@ router bgp 5226 neighbor 192.168.1.2 remote-as 5226 neighbor 192.168.1.2 update-source 192.168.1.1 neighbor 192.168.1.2 route-reflector-client + neighbor 192.168.1.2 timers 3 10 neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 1.1.1.1 + neighbor 2.2.2.2 timers 3 10 ! address-family ipv4 unicast redistribute vnc-direct diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf index c5097e214f..460a8fb016 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf index 95890f25b9..3167306700 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf @@ -10,10 +10,13 @@ router bgp 5226 no bgp ebgp-requires-policy neighbor 1.1.1.1 remote-as 5226 neighbor 1.1.1.1 update-source 2.2.2.2 + neighbor 1.1.1.1 timers 3 10 neighbor 3.3.3.3 remote-as 5226 neighbor 3.3.3.3 update-source 2.2.2.2 + neighbor 3.3.3.3 timers 3 10 neighbor 4.4.4.4 remote-as 5226 neighbor 4.4.4.4 update-source 2.2.2.2 + neighbor 4.4.4.4 timers 3 10 address-family ipv4 unicast no neighbor 1.1.1.1 activate no neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf index 8678813665..dbed6189c8 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf @@ -5,3 +5,15 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf index 2f7de073c3..445da08248 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf @@ -11,8 +11,10 @@ router bgp 5226 neighbor 192.168.1.2 remote-as 5226 neighbor 192.168.1.2 update-source 192.168.1.2 neighbor 192.168.1.2 route-reflector-client + neighbor 192.168.1.2 timers 3 10 neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 3.3.3.3 + neighbor 2.2.2.2 timers 3 10 ! address-family ipv4 unicast redistribute vnc-direct diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf index c7c358f9dc..0e64ed6fc5 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf @@ -7,3 +7,11 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf index 720d06dbf1..1941352450 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf @@ -11,8 +11,10 @@ router bgp 5226 neighbor 192.168.1.2 remote-as 5226 neighbor 192.168.1.2 update-source 192.168.1.1 neighbor 192.168.1.2 route-reflector-client + neighbor 192.168.1.2 timers 3 10 neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 4.4.4.4 + neighbor 2.2.2.2 timers 3 10 ! address-family ipv4 unicast redistribute vnc-direct diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf index 83d09c09a4..89e37df479 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf index b81cd33c4f..6e6b9edde2 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf @@ -12,6 +12,7 @@ router bgp 5227 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5227 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 99.0.0.1/32 network 5.1.0.0/24 route-map rm-nh diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf index f18e5b852e..618acabd9f 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf @@ -12,6 +12,7 @@ router bgp 5227 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5227 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 99.0.0.2/32 network 5.1.0.0/24 route-map rm-nh diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf index 54a0933588..85c5973e6f 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf @@ -12,6 +12,7 @@ router bgp 5227 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as 5227 neighbor 192.168.1.1 update-source 192.168.1.2 + neighbor 192.168.1.1 timers 3 10 address-family ipv4 unicast network 99.0.0.3/32 network 5.1.2.0/24 route-map rm-nh diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf index 5289628480..6a5075a000 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf @@ -12,6 +12,7 @@ router bgp 5228 vrf ce4-cust2 no bgp ebgp-requires-policy neighbor 192.168.2.1 remote-as 5228 neighbor 192.168.2.1 update-source 192.168.2.2 + neighbor 192.168.2.1 timers 3 10 address-family ipv4 unicast network 99.0.0.4/32 network 5.4.2.0/24 route-map rm-nh diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf index 5da53ae1e7..8d42cfc0d8 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf @@ -18,6 +18,7 @@ router bgp 5226 no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 1.1.1.1 + neighbor 2.2.2.2 timers 3 10 address-family ipv4 unicast no neighbor 2.2.2.2 activate @@ -35,6 +36,7 @@ router bgp 5227 vrf r1-cust1 neighbor 192.168.1.2 remote-as 5227 neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 timers 3 10 address-family ipv4 unicast neighbor 192.168.1.2 activate diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf index c5097e214f..460a8fb016 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf index e4a6b8e32c..7b42b770b5 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf @@ -12,10 +12,13 @@ router bgp 5226 no bgp ebgp-requires-policy neighbor 1.1.1.1 remote-as 5226 neighbor 1.1.1.1 update-source 2.2.2.2 + neighbor 1.1.1.1 timers 3 10 neighbor 3.3.3.3 remote-as 5226 neighbor 3.3.3.3 update-source 2.2.2.2 + neighbor 3.3.3.3 timers 3 10 neighbor 4.4.4.4 remote-as 5226 neighbor 4.4.4.4 update-source 2.2.2.2 + neighbor 4.4.4.4 timers 3 10 address-family ipv4 unicast no neighbor 1.1.1.1 activate no neighbor 3.3.3.3 activate diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf index 8678813665..dbed6189c8 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf @@ -5,3 +5,15 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf index a861469c7a..6c9640eea0 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf @@ -13,6 +13,7 @@ router bgp 5226 no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 3.3.3.3 + neighbor 2.2.2.2 timers 3 10 address-family ipv4 unicast no neighbor 2.2.2.2 activate @@ -29,6 +30,7 @@ router bgp 5227 vrf r3-cust1 neighbor 192.168.1.2 remote-as 5227 neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 timers 3 10 address-family ipv4 unicast neighbor 192.168.1.2 activate diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf index c7c358f9dc..0e64ed6fc5 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf @@ -7,3 +7,11 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf index 480f95954e..ca9e627172 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf @@ -16,6 +16,7 @@ router bgp 5226 no bgp ebgp-requires-policy neighbor 2.2.2.2 remote-as 5226 neighbor 2.2.2.2 update-source 4.4.4.4 + neighbor 2.2.2.2 timers 3 10 address-family ipv4 unicast no neighbor 2.2.2.2 activate @@ -32,6 +33,7 @@ router bgp 5227 vrf r4-cust1 neighbor 192.168.1.2 remote-as 5227 neighbor 192.168.1.2 update-source 192.168.1.1 + neighbor 192.168.1.2 timers 3 10 address-family ipv4 unicast neighbor 192.168.1.2 activate @@ -52,6 +54,7 @@ router bgp 5228 vrf r4-cust2 neighbor 192.168.2.2 remote-as 5228 neighbor 192.168.2.2 update-source 192.168.2.1 + neighbor 192.168.2.2 timers 3 10 address-family ipv4 unicast neighbor 192.168.2.2 activate diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf index 83d09c09a4..89e37df479 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! 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 27c4430a52..c36e66a60e 100755 --- 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 @@ -70,7 +70,6 @@ from lib.common_config import ( create_route_maps, shutdown_bringup_interface, start_router_daemons, - kill_router_daemons, create_static_routes, create_vrf_cfg, create_interfaces_cfg, diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf index c5097e214f..460a8fb016 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf index 8678813665..dbed6189c8 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf @@ -5,3 +5,15 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf index c7c358f9dc..0e64ed6fc5 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf @@ -7,3 +7,11 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf index 83d09c09a4..89e37df479 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf +++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf @@ -6,3 +6,7 @@ router ospf network 0.0.0.0/4 area 0 redistribute static ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json new file mode 100644 index 0000000000..b1d7d09db8 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json @@ -0,0 +1,563 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["11.11.11.1/32", "11.11.11.11/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["11:11::1/128", "11:11::11/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["10.10.10.10/32", "10.10.10.100/32"], + "next_hop":"Null0" + }, + { + "network": ["10:10::10/128", "10:10::100/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["22.22.22.2/32", "22.22.22.22/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["22:22::2/128", "22:22::22/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["20.20.20.20/32", "20.20.20.200/32"], + "next_hop":"Null0" + }, + { + "network": ["20:20::20/128", "20:20::200/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["30.30.30.3/32", "30.30.30.30/32"], + "next_hop":"Null0" + }, + { + "network": ["30:30::3/128", "30:30::30/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["40.40.40.4/32", "40.40.40.40/32"], + "next_hop":"Null0" + }, + { + "network": ["40:40::4/128", "40:40::40/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json new file mode 100644 index 0000000000..b1d7d09db8 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json @@ -0,0 +1,563 @@ +{ + "address_types": ["ipv4","ipv6"], + "ipv4base": "10.0.0.0", + "ipv4mask": 30, + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv4": "10.0.0.0", + "v4mask": 30, + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv4": "1.0.", + "v4mask": 32, + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["11.11.11.1/32", "11.11.11.11/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["11:11::1/128", "11:11::11/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["10.10.10.10/32", "10.10.10.100/32"], + "next_hop":"Null0" + }, + { + "network": ["10:10::10/128", "10:10::100/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r2": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"}, + "r3-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r4-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "vrfs":[ + { + "name": "ISR", + "id": "1" + } + ], + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "next_hop_self": true, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["22.22.22.2/32", "22.22.22.22/32"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["22:22::2/128", "22:22::22/128"], + "next_hop":"Null0", + "vrf": "ISR" + }, + { + "network": ["20.20.20.20/32", "20.20.20.200/32"], + "next_hop":"Null0" + }, + { + "network": ["20:20::20/128", "20:20::200/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r3": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["30.30.30.3/32", "30.30.30.30/32"], + "next_hop":"Null0" + }, + { + "network": ["30:30::3/128", "30:30::30/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + }, + "r4": { + "links": { + "r1-link1": {"ipv4": "auto", "ipv6": "auto"}, + "r2-link1": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "keepalivetimer": 1, + "holddowntimer": 3, + "route_maps": [{ + "name": "rmap_global", + "direction": "in" + }] + } + } + } + } + } + } + } + } + ], + "static_routes":[ + { + "network": ["40.40.40.4/32", "40.40.40.40/32"], + "next_hop":"Null0" + }, + { + "network": ["40:40::4/128", "40:40::40/128"], + "next_hop":"Null0" + }, + { + "network": ["50.50.50.5/32", "50.50.50.50/32"], + "next_hop":"Null0" + }, + { + "network": ["50:50::5/128", "50:50::50/128"], + "next_hop":"Null0" + } + ], + "route_maps": { + "rmap_global": [{ + "action": "permit", + "set": { + "ipv6": { + "nexthop": "prefer-global" + } + } + }] + } + } + } +} 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 new file mode 100755 index 0000000000..1947548b3e --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py @@ -0,0 +1,1848 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: + +1. Verify that dynamically imported routes are further advertised + to iBGP peers(peer in cluster). +2. Verify matching a prefix based on community attribute and + importing it by stripping off this value +3. Verify the route-map operation along with dynamic import command. +4. Verifying the JSON outputs for all supported commands +""" + +import os +import sys +import json +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, '../lib/')) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, write_test_header, check_address_types, + write_test_footer, reset_config_on_routers, + verify_rib, step, create_route_maps, + shutdown_bringup_interface, create_static_routes, + create_prefix_lists, create_bgp_community_lists, + create_interface_in_kernel, + check_router_status, verify_cli_json, + get_frr_ipv6_linklocal, verify_fib_routes +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, create_router_bgp, + clear_bgp, verify_bgp_community, verify_bgp_rib +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_vrf_dynamic_route_leak_topo1.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.10/32", "ipv6": "10:10::10/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} +NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +NETWORK4_1 = {"ipv4": "40.40.40.4/32", "ipv6": "40:40::4/128"} +NETWORK4_2 = {"ipv4": "40.40.40.40/32", "ipv6": "40:40::40/128"} +NETWORK4_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK4_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} +LOOPBACK_1 = {"ipv4": "10.0.0.7/24", "ipv6": "fd00:0:0:1::7/64", + "ipv4_mask": "255.255.255.0", "ipv6_mask": None} +LOOPBACK_2 = {"ipv4": "10.0.0.16/24", "ipv6": "fd00:0:0:3::5/64", + "ipv4_mask": "255.255.255.0", "ipv6_mask": None} +PREFERRED_NEXT_HOP = "global" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('BGP vrf dynamic route leak tests will not run ' + '(have kernel "{}", but it requires >= 4.19)'.\ + format(platform.release())) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}". \ + format(BGP_CONVERGENCE) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info("Testsuite end time: {}". + format(time.asctime(time.localtime(time.time())))) + logger.info("=" * 40) + +##################################################### +# +# Local APIs +# +##################################################### + +def disable_route_map_to_prefer_global_next_hop(tgen, topo): + """ + This API is to remove prefer global route-map applied on neighbors + + Parameter: + ---------- + * `tgen` : Topogen object + * `topo` : Input JSON data + + Returns: + -------- + True/errormsg + + """ + + logger.info("Remove prefer-global rmap applied on neighbors") + input_dict = { + "r1": { + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + }, + "r2": { + "bgp": + [ + { + "local_as": "100", + "vrf": "ISR", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "100", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r2-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + }, + "r3": { + "bgp": + [ + { + "local_as": "300", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "300", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + }, + "r4": { + "bgp": + [ + { + "local_as": "400", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + }, + { + "local_as": "400", + "address_family": { + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": { + "route_maps": [{ + "name": "rmap_global", + "direction": "in", + "delete": True + }] + } + } + } + } + } + } + } + } + ] + } + } + + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + return True + + +##################################################### +# +# Testcases +# +##################################################### + +def test_dynamic_imported_routes_advertised_to_iBGP_peer_p0(request): + """ + TC5_FUNC_5: + 1.5.5. Verify that dynamically imported routes are further advertised + to iBGP peers(peer in cluster). + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Redistribute configured static routes into BGP process" + " on R1 and R3/R4") + + input_dict_1={} + DUT = ["r1", "r3", "r4"] + VRFS = ["default", "default", "default"] + AS_NUM = [100, 300, 400] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that R1 receives BGP routes from R3 and R4 in " + "vrf default.") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_1[addr_type], \ + NETWORK3_2[addr_type], \ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + input_routes_r4 = { + "r4": { + "static_routes": [{ + "network": [ + NETWORK4_1[addr_type], \ + NETWORK4_2[addr_type], \ + NETWORK4_3[addr_type], \ + NETWORK4_4[addr_type] + ] + }] + } + } + + DUT = ["r1", "r2"] + INPUT_DICT = [input_routes_r3, input_routes_r4] + + for dut, routes in zip(DUT, INPUT_DICT): + result = verify_bgp_rib(tgen, addr_type, dut, routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + result = verify_fib_routes(tgen, addr_type, dut, routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Import from default vrf into vrf ISR on R1") + + input_dict_isr={} + DUT = ["r1", "r2"] + VRFS = ["ISR", "ISR"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "default" + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that default vrf's imported routes are installed " + "in RIB/FIB of vrf ISR on R1:") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_1[addr_type], \ + NETWORK3_2[addr_type], \ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ], + "vrf": "ISR" + }] + } + } + + input_routes_r4 = { + "r4": { + "static_routes": [{ + "network": [ + NETWORK4_1[addr_type], \ + NETWORK4_2[addr_type], \ + NETWORK4_3[addr_type], \ + NETWORK4_4[addr_type] + ], + "vrf": "ISR" + }] + } + } + + INPUT_DICT_VRF = [input_routes_r3, input_routes_r4] + + for routes in INPUT_DICT_VRF: + result = verify_bgp_rib(tgen, addr_type, "r1", routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + result = verify_fib_routes(tgen, addr_type, "r1", routes) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + intf_r2_r1 = topo["routers"]["r2"]["links"]["r1-link1"] + for addr_type in ADDR_TYPES: + + step("Create a loopback10 interface on R1 with below IP address and " + "associate with vrf ISR:") + + create_interface_in_kernel(tgen, "r1", "loopback2", + LOOPBACK_2[addr_type], + "ISR", + LOOPBACK_2["{}_mask".\ + format(addr_type)]) + + for addr_type in ADDR_TYPES: + + step("On router R1 Change the next-hop of static routes in vrf " + "ISR to LOOPBACK_1") + + input_routes_r1= { + "r1": { + "static_routes":[ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "next_hop":"Null0", + "delete": True + } + ] + } + } + + result = create_static_routes(tgen, input_routes_r1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + input_routes_r1= { + "r1": { + "static_routes":[ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "next_hop": (intf_r2_r1[addr_type]).split("/")[0] + } + ] + } + } + + result = create_static_routes(tgen, input_routes_r1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that, though R1 originating BGP routes with next-hop" + " 24.1.1.2/24::1:2, which is local to R2(but in default vrf)" + ", R2 must receives and install all routes from R1 in vrf ISR.") + step("Verify on R2, that it now rejects 10.10.10.x routes originated " + "from R1. As next-hop IP is local to R2's vrf ISR.") + + input_routes_r1= { + "r1": { + "static_routes":[ + { + "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]], + "vrf": "ISR" + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Routes are still present \n Error {}". \ + format(tc_name, result)) + + write_test_footer(tc_name) + + +def test_dynamic_imported_matching_prefix_based_on_community_list_p0(request): + """ + TC7_FUNC_7: + 1.5.7. Verify matching a prefix based on community attribute and + importing it by stripping off this value + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100" + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + } + ] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "none"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + input_dict_comm = { + "community": "100:100" + } + + result = verify_bgp_community(tgen, addr_type, dut, [NETWORK1_1[addr_type]], + input_dict_comm, expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error: Commnunity is not stipped off, {}".format( + tc_name, result)) + + for addr_type in ADDR_TYPES: + + step("Remove/re-add route-map XYZ from redistribution.") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + }, + "delete": True + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that all the routes disappear from vrf default when " + "route-map is removed from redistribution, and appear again " + "when route-map is re-added to redistribution in vrf ISR.") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + for addr_type in ADDR_TYPES: + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Remove/re-add route-map IMP form import statement.") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type), + "delete": True + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that when route-map IMP is removed all the prefixes of" + " vrf ISR are imported to vrf default. However when route-map " + "IMP is re-added only 11.11.11.1 and 11:11::1 (with community " + "value) are imported.") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Delete/Re-add prefix-list ABC.") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit", + "delete": True + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_pf["r1"]["prefix_lists"][addr_type]["pflist_ABC_{}".\ + format(addr_type)][0]["delete"]=False + + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Delete/Re-add community-list COMM.") + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + "delete": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_cl["r1"]["bgp_community_lists"][0]["delete"]=False + + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Delete/Re-add route-map XYZ.") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + }, + "delete": True + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_rm["r1"]["route_maps"]["rmap_XYZ_{}".format(addr_type)][0]["delete"]=False + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + step("Delete/Re-add route-map IMP.") + + input_dict_rm2 = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "none"} + }, + "delete": True + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm2) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + input_dict_rm2["r1"]["route_maps"]["rmap_IMP_{}".format(addr_type)][0]["delete"]=False + + result = create_route_maps(tgen, input_dict_rm2) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + write_test_footer(tc_name) + + +def test_routemap_operatons_with_dynamic_import_p0(request): + """ + TC8_FUNC_8: + 1.5.8. Verify the route-map operation along with dynamic import command. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100" + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + } + ] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "500:500"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Applying route-map first followed by import VRF command.") + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR", + "delete": True + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that until 'import VRF command' is not configured, " + "routes are not imported. After configuring 'import VRF command'" + " repeat step-4 for verification") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still present \n {}".\ + format(tc_name, result)) + + for addr_type in ADDR_TYPES: + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Delete/re-add import vrf ISR command multiple times in default" + "vrf.") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR", + "delete": True + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify that when import vrf ISR command is deleted, " + "all routes of vrf ISR disappear from default vrf and " + "when it's re-configured, repeat step-4 for verification.") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Routes are still present, Error {}". \ + format(tc_name, result)) + + input_dict_isr["r1"]["bgp"][0]["address_family"][addr_type]["unicast"][ + "import"]["delete"]=False + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + for addr_type in ADDR_TYPES: + + step("Delete and re-configure route-map IMP from global config when " + "import and route-maps are applied in a ISR vrf.") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "500:500"} + }, + "delete": True + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Routes are still present, Error {}". \ + format(tc_name, result)) + + input_dict_rm["r1"]["route_maps"]["rmap_IMP_{}".\ + format(addr_type)][0]["delete"]=False + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + input_dict_comm = { + "community": "500:500" + } + + result = verify_bgp_community(tgen, addr_type, dut, [NETWORK1_1[addr_type]], + input_dict_comm) + assert result is True, ( + "Testcase {} : Failed \n Error: {}".format( + tc_name, result)) + + write_test_footer(tc_name) + + +def test_verify_cli_json_p1(request): + """ + TC8_FUNC_9: + 1.5.9. Verifying the JSON outputs for all supported commands: + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + input_dict = { + "r1":{ + "cli": ["show bgp vrf default ipv4 summary", + "show bgp vrf all ipv6 summary", + "show bgp neighbors" + ] + } + } + + result = verify_cli_json(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py new file mode 100755 index 0000000000..6c106060b8 --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py @@ -0,0 +1,962 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: + +1. Verify that Changing route-map configurations(match/set clauses) on + the fly it takes immediate effect. +2. Verify BGP best path selection algorithm works fine when + routes are imported from ISR to default vrf and vice versa. +""" + +import os +import sys +import json +import time +import pytest +import platform + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, '../lib/')) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp +from mininet.topo import Topo + +from lib.common_config import ( + start_topology, write_test_header, check_address_types, + write_test_footer, + verify_rib, step, create_route_maps, + create_static_routes, stop_router, start_router, + create_prefix_lists, + create_bgp_community_lists, + check_router_status, + get_frr_ipv6_linklocal, + shutdown_bringup_interface +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, create_router_bgp, + verify_bgp_community, verify_bgp_attributes, + verify_best_path_as_per_bgp_attribute, verify_bgp_rib +) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology creation +jsonFile = "{}/bgp_vrf_dynamic_route_leak_topo2.json".format(CWD) +try: + with open(jsonFile, "r") as topoJson: + topo = json.load(topoJson) +except IOError: + assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"} +NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"} + +PREFERRED_NEXT_HOP = "global" + + +class CreateTopo(Topo): + """ + Test BasicTopo - topology 1 + + * `Topo`: Topology object + """ + + def build(self, *_args, **_opts): + """Build function""" + tgen = get_topogen(self) + + # Building topology from json file + build_topo_from_json(tgen, topo) + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + global topo + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + tgen = Topogen(CreateTopo, mod.__name__) + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start deamons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), '4.19') < 0: + error_msg = ('BGP vrf dynamic route leak tests will not run ' + '(have kernel "{}", but it requires >= 4.19)'.\ + format(platform.release())) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}". \ + format(BGP_CONVERGENCE) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info("Testsuite end time: {}". + format(time.asctime(time.localtime(time.time())))) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + +def test_bgp_best_path_with_dynamic_import_p0(request): + """ + TC6_FUNC_6: + 1.5.6. Verify BGP best path selection algorithm works fine when + routes are imported from ISR to default vrf and vice versa. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Redistribute configured static routes into BGP process" + " on R1/R2 and R3") + + input_dict_1={} + DUT = ["r1", "r2", "r3", "r4"] + VRFS = ["ISR", "ISR", "default", "default"] + AS_NUM = [100, 100, 300, 400] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static" + }] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Import from default vrf into vrf ISR on R1 and R2 as below") + + input_dict_vrf={} + DUT = ["r1", "r2"] + VRFS = ["ISR", "ISR"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_vrf.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "default" + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_vrf) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + input_dict_default={} + DUT = ["r1", "r2"] + VRFS = ["default", "default"] + AS_NUM = [100, 100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_default.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_default) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + step("Verify ECMP/Next-hop/Imported routes Vs Locally originated " + "routes/eBGP routes vs iBGP routes --already covered in almost" + " all tests") + + for addr_type in ADDR_TYPES: + + step("Verify Pre-emption") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type] + ] + }] + } + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + nh_r4_r1 = topo["routers"]["r4"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r3, + next_hop=[nh_r4_r1]) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + step("Shutdown interface connected to r1 from r4:") + shutdown_bringup_interface(tgen, 'r4', intf_r4_r1, False) + + for addr_type in ADDR_TYPES: + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type] + ] + }] + } + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + nh_r4_r1 = topo["routers"]["r4"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + + step("Verify next-hop is changed") + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r3, + next_hop=[nh_r3_r1]) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + step("Bringup interface connected to r1 from r4:") + shutdown_bringup_interface(tgen, 'r4', intf_r4_r1, True) + + for addr_type in ADDR_TYPES: + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type] + ] + }] + } + } + + intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"] + + if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP: + nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1) + nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1) + else: + nh_r3_r1 = topo["routers"]["r3"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + nh_r4_r1 = topo["routers"]["r4"]["links"]\ + ["r1-link1"][addr_type].split("/")[0] + + step("Verify next-hop is not chnaged aftr shutdown:") + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r3, + next_hop=[nh_r3_r1]) + assert result is True, ( + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result)) + + step("Active-Standby scenario(as-path prepend and Local pref)") + + for addr_type in ADDR_TYPES: + + step("Create prefix-list") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pf_ls_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK3_4[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Create route-map to match prefix-list and set localpref 500") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH1_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "locPrf": 500 + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + step("Create route-map to match prefix-list and set localpref 600") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH2_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 20, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "locPrf": 600 + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + input_dict_rma={ + "r1": { + "bgp": + [ + { + "local_as": "100", + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_PATH1_{}".\ + format(addr_type), + "direction": "in" + }] + } + } + }, + "r4": { + "dest_link": { + "r1-link1": { + "route_maps": [{ + "name": "rmap_PATH2_{}".\ + format(addr_type), + "direction": "in" + }] + } + } + } + } + } + } + } + } + ]} + } + + result = create_router_bgp(tgen, topo, input_dict_rma) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + dut = "r1" + attribute = "locPrf" + + for addr_type in ADDR_TYPES: + + step("Verify bestpath is installed as per highest localpref") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + result = verify_best_path_as_per_bgp_attribute(tgen, addr_type, dut, + input_routes_r3, + attribute) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Create route-map to match prefix-list and set localpref 700") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH1_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "locPrf": 700 + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify bestpath is changed as per highest localpref") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + result = verify_best_path_as_per_bgp_attribute(tgen, addr_type, dut, + input_routes_r3, + attribute) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Create route-map to match prefix-list and set as-path prepend") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_PATH2_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 20, + "match": { + addr_type: { + "prefix_lists": + "pf_ls_{}".format(addr_type) + } + }, + "set": { + "localpref": 700, + "path": { + "as_num": "111", + "as_action": "prepend" + } + } + }] + } + } + } + + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + attribute = "path" + + for addr_type in ADDR_TYPES: + + step("Verify bestpath is changed as per shortest as-path") + + input_routes_r3 = { + "r3": { + "static_routes": [{ + "network": [ + NETWORK3_3[addr_type], \ + NETWORK3_4[addr_type] + ] + }] + } + } + + result = verify_best_path_as_per_bgp_attribute(tgen, addr_type, dut, + input_routes_r3, + attribute) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_route_map_match_set_clauses_p1(request): + """ + TC13_CHAOS_4: + 1.5.13. Verify that Changing route-map configurations(match/set clauses) on + the fly it takes immediate effect. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + build_config_from_json(tgen, topo) + + if tgen.routers_have_failure(): + check_router_status(tgen) + + for addr_type in ADDR_TYPES: + + step("Configure route-map to set community attribute for a specific" + "prefix on R1 in vrf ISR") + + input_dict_pf = { + "r1": { + "prefix_lists": { + addr_type: { + "pflist_ABC_{}".format(addr_type): [{ + "seqid": 10, + "network": NETWORK1_1[addr_type], + "action": "permit" + }] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_pf) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result) + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100" + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_XYZ_{}".format(addr_type): [{ + "action": "permit", + "match": { + addr_type: { + "prefix_lists": + "pflist_ABC_{}".format(addr_type) + } + }, + "set": { + "community": {"num": "100:100"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply this route-map on R1 to vrf ISR while redistributing the" + " prefixes into BGP") + + input_dict_1={} + DUT = ["r1"] + VRFS = ["ISR"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_1.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "redistribute": [{ + "redist_type": "static", + "attribute": { + "route-map" : "rmap_XYZ_{}".\ + format(addr_type) + } + } + ] + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} :Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Configure another route-map for filtering the prefixes based on" + " community attribute while importing into default vrf") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "community": {"num": "none"} + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Apply the route-map while Importing vrf ISR's prefixes into " + "default vrf on router R1:") + + input_dict_isr={} + DUT = ["r1"] + VRFS = ["default"] + AS_NUM = [100] + + for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM): + temp = {dut: {"bgp": []}} + input_dict_isr.update(temp) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "ISR" + } + } + } + } + }) + + temp[dut]["bgp"].append( + { + "local_as": as_num, + "vrf": vrf, + "address_family": { + addr_type: { + "unicast": { + "import": { + "vrf": "route-map rmap_IMP_{}".format(addr_type) + } + } + } + } + }) + + result = create_router_bgp(tgen, topo, input_dict_isr) + assert result is True, "Testcase {} : Failed \n Error: {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify on R1 that only prefixes with community value 100:100" + "in vrf ISR are imported to vrf default. While importing, the" + " community value has been stripped off:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1) + assert result is True, \ + "Testcase {} : Failed \n Error {}". \ + format(tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Add set clause in route-map IMP:") + + input_dict_rm = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [{ + "action": "permit", + "seq_id": 10, + "match": { + "community_list": {"id": "COMM"} + }, + "set": { + "large_community": {"num": "100:100:100"}, + "locPrf": 500, + "path": { + "as_num": "100 100", + "as_action": "prepend" + } + } + }] + } + } + } + result = create_route_maps(tgen, input_dict_rm) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + step("Verify that as we continue adding different attributes " + "step-by-step in route-map IMP those attributes gets " + "attached to prefixes:") + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + input_dict_comm = { + "largeCommunity": "100:100:100" + } + + result = verify_bgp_community(tgen, addr_type, dut, [NETWORK1_1[addr_type]], + input_dict_comm) + assert result is True, ( + "Testcase {} : Failed \n Error {}".format( + tc_name, result)) + + input_rmap = { + "r1": { + "route_maps": { + "rmap_IMP_{}".format(addr_type): [ + { + "set": { + "locPrf": 500 + } + } + ] + } + } + } + + result = verify_bgp_attributes(tgen, addr_type, "r1",\ + [NETWORK1_1[addr_type]], + rmap_name="rmap_IMP_{}".format(addr_type),\ + input_dict=input_rmap) + assert result is True, "Testcase : Failed \n Error: {}".format( + tc_name, result) + + step("Change community-list to match a different value then " + "100:100.") + + input_dict_cl = { + "r1": { + "bgp_community_lists": [ + { + "community_type": "expanded", + "action": "permit", + "name": "COMM", + "value": "100:100", + "delete": True + } + ] + } + } + result = create_bgp_community_lists(tgen, input_dict_cl) + assert result is True, 'Testcase {} : Failed \n Error: {}'.format( + tc_name, result) + + for addr_type in ADDR_TYPES: + + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": [ + NETWORK1_1[addr_type] + ], + "vrf": "default" + }] + } + } + + result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, + expected=False) + assert result is not True, ( + "Testcase {} : Failed \n Error : Routes are still " + "present {}".\ + format(tc_name, result)) + + write_test_footer(tc_name) + + +if __name__ == '__main__': + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py index 941593e51f..e913105e43 100755 --- a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py +++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py @@ -62,7 +62,6 @@ from lib.common_config import ( verify_rib, step, start_router_daemons, - kill_router_daemons, create_static_routes, create_vrf_cfg, create_route_maps, diff --git a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py index 3cdec760f7..9e385823fc 100755 --- a/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py +++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py @@ -68,7 +68,6 @@ from lib.common_config import ( create_route_maps, verify_cli_json, start_router_daemons, - kill_router_daemons, create_static_routes, stop_router, start_router, @@ -250,8 +249,8 @@ def prerequisite_config_for_test_suite(tgen): } result = configure_vxlan(tgen, vxlan_input) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result ) step("Configure bridge interface") @@ -268,8 +267,8 @@ def prerequisite_config_for_test_suite(tgen): } } result = configure_brctl(tgen, topo, brctl_input) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result ) step("Configure default routes") @@ -406,8 +405,8 @@ def test_RD_verification_manual_and_auto_p0(request): result = verify_attributes_for_evpn_routes( tgen, topo, dut, input_routes, rd="auto", rd_peer="e1" ) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result ) step( @@ -437,8 +436,8 @@ def test_RD_verification_manual_and_auto_p0(request): result = verify_attributes_for_evpn_routes( tgen, topo, dut, input_routes, rd="50.50.50.50:50" ) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result ) step( @@ -472,8 +471,8 @@ def test_RD_verification_manual_and_auto_p0(request): result = verify_attributes_for_evpn_routes( tgen, topo, dut, input_routes, rd="100.100.100.100:100" ) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result ) step( @@ -672,8 +671,8 @@ def test_RT_verification_manual_p0(request): result = verify_attributes_for_evpn_routes( tgen, topo, dut, input_routes, rt="100:100" ) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result ) step( @@ -711,8 +710,8 @@ def test_RT_verification_manual_p0(request): result = verify_attributes_for_evpn_routes( tgen, topo, dut, input_routes, rt=["100:100", "500:500"] ) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result ) step( @@ -834,8 +833,8 @@ def test_RT_verification_manual_p0(request): result = verify_attributes_for_evpn_routes( tgen, topo, dut, input_routes, rt=["100:100", "500:500"] ) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result ) step("Delete RT export value 100:100 for vrf RED on Edge-1") @@ -1277,14 +1276,14 @@ def test_evpn_routes_from_VNFs_p1(request): for dut in ["d1", "d2"]: input_routes = {key: topo["routers"][key] for key in ["r1"]} result = verify_evpn_routes(tgen, topo, dut, input_routes) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result ) input_routes = {key: topo["routers"][key] for key in ["r2"]} result = verify_evpn_routes(tgen, topo, dut, input_routes) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result + assert result is True, "Testcase {} on {} :Failed \n Error: {}".format( + tc_name, dut, result ) step( @@ -1861,7 +1860,7 @@ def test_bgp_attributes_for_evpn_address_family_p1(request, attribute): temp[dut]["bgp"].append({"local_as": as_num, "delete": True}) result = create_router_bgp(tgen, topo, input_dict_2) - assert result is True, "Testcase {} :Failed \n Error: {}".format( + assert result is True, "Testcase {} on d1 :Failed \n Error: {}".format( tc_name, result ) diff --git a/tests/topotests/isis-sr-topo1/rt1/isisd.conf b/tests/topotests/isis-sr-topo1/rt1/isisd.conf index 70ae1b07f5..26ec4eb261 100644 --- a/tests/topotests/isis-sr-topo1/rt1/isisd.conf +++ b/tests/topotests/isis-sr-topo1/rt1/isisd.conf @@ -21,6 +21,7 @@ interface eth-sw1 router isis 1 net 49.0000.0000.0000.0001.00 is-type level-1 + lsp-gen-interval 2 topology ipv6-unicast segment-routing on segment-routing global-block 16000 23999 diff --git a/tests/topotests/isis-sr-topo1/rt2/isisd.conf b/tests/topotests/isis-sr-topo1/rt2/isisd.conf index 733f26bc62..8704a28b6c 100644 --- a/tests/topotests/isis-sr-topo1/rt2/isisd.conf +++ b/tests/topotests/isis-sr-topo1/rt2/isisd.conf @@ -32,6 +32,7 @@ interface eth-rt4-2 router isis 1 net 49.0000.0000.0000.0002.00 is-type level-1 + lsp-gen-interval 2 topology ipv6-unicast segment-routing on segment-routing global-block 16000 23999 diff --git a/tests/topotests/isis-sr-topo1/rt3/isisd.conf b/tests/topotests/isis-sr-topo1/rt3/isisd.conf index ceb982ca32..5a0add22a9 100644 --- a/tests/topotests/isis-sr-topo1/rt3/isisd.conf +++ b/tests/topotests/isis-sr-topo1/rt3/isisd.conf @@ -32,6 +32,7 @@ interface eth-rt5-2 router isis 1 net 49.0000.0000.0000.0003.00 is-type level-1 + lsp-gen-interval 2 topology ipv6-unicast segment-routing on segment-routing global-block 17000 24999 diff --git a/tests/topotests/isis-sr-topo1/rt4/isisd.conf b/tests/topotests/isis-sr-topo1/rt4/isisd.conf index 07a7867cbb..39003b9d7b 100644 --- a/tests/topotests/isis-sr-topo1/rt4/isisd.conf +++ b/tests/topotests/isis-sr-topo1/rt4/isisd.conf @@ -39,6 +39,7 @@ interface eth-rt6 router isis 1 net 49.0000.0000.0000.0004.00 is-type level-1 + lsp-gen-interval 2 topology ipv6-unicast segment-routing on segment-routing global-block 16000 23999 diff --git a/tests/topotests/isis-sr-topo1/rt5/isisd.conf b/tests/topotests/isis-sr-topo1/rt5/isisd.conf index b0fcdede07..e693ca156c 100644 --- a/tests/topotests/isis-sr-topo1/rt5/isisd.conf +++ b/tests/topotests/isis-sr-topo1/rt5/isisd.conf @@ -39,6 +39,7 @@ interface eth-rt6 router isis 1 net 49.0000.0000.0000.0005.00 is-type level-1 + lsp-gen-interval 2 topology ipv6-unicast segment-routing on segment-routing global-block 16000 23999 diff --git a/tests/topotests/isis-sr-topo1/rt6/isisd.conf b/tests/topotests/isis-sr-topo1/rt6/isisd.conf index 16c34dfae6..3b85dbae4e 100644 --- a/tests/topotests/isis-sr-topo1/rt6/isisd.conf +++ b/tests/topotests/isis-sr-topo1/rt6/isisd.conf @@ -27,6 +27,7 @@ interface eth-rt5 router isis 1 net 49.0000.0000.0000.0006.00 is-type level-1 + lsp-gen-interval 2 topology ipv6-unicast segment-routing on segment-routing global-block 16000 23999 diff --git a/tests/topotests/isis-topo1-vrf/__init__.py b/tests/topotests/isis-topo1-vrf/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/__init__.py diff --git a/tests/topotests/isis-topo1-vrf/r1/isisd.conf b/tests/topotests/isis-topo1-vrf/r1/isisd.conf new file mode 100755 index 0000000000..4ac4597015 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/isisd.conf @@ -0,0 +1,15 @@ +hostname r1 +debug isis adj-packets +debug isis events +debug isis update-packets +interface r1-eth0 + ip router isis 1 vrf r1-cust1 + ipv6 router isis 1 vrf r1-cust1 + isis circuit-type level-2-only +! +router isis 1 vrf r1-cust1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0000.00 + metric-style wide + redistribute ipv4 connected level-2 + redistribute ipv6 connected level-2 +! diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_route.json b/tests/topotests/isis-topo1-vrf/r1/r1_route.json new file mode 100644 index 0000000000..790808f2cd --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/r1_route.json @@ -0,0 +1,57 @@ +{ + "10.0.10.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "ip": "10.0.20.1" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "isis", + "selected": true, + "vrfId": 3, + "vrfName": "r1-cust1" + } + ], + "10.0.20.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "afi": "ipv4", + "interfaceIndex": 2, + "interfaceName": "r1-eth0", + "ip": "10.0.20.1" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "isis", + "vrfId": 3, + "vrfName": "r1-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r1-eth0" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "connected", + "selected": true, + "vrfId": 3, + "vrfName": "r1-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_route6.json b/tests/topotests/isis-topo1-vrf/r1/r1_route6.json new file mode 100644 index 0000000000..332cbb3290 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/r1_route6.json @@ -0,0 +1,40 @@ +{ + "2001:db8:1:1::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r1-eth0" + } + ], + "prefix": "2001:db8:1:1::/64", + "protocol": "connected", + "selected": true, + "vrfId": 3, + "vrfName": "r1-cust1" + } + ], + "2001:db8:2:1::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r1-eth0" + } + ], + "prefix": "2001:db8:2:1::/64", + "protocol": "isis", + "selected": true, + "vrfId": 3, + "vrfName": "r1-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_route6_linux.json b/tests/topotests/isis-topo1-vrf/r1/r1_route6_linux.json new file mode 100755 index 0000000000..d1ace402ba --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/r1_route6_linux.json @@ -0,0 +1,14 @@ +{ + "2001:db8:1:1::/64": { + "dev": "r1-eth0", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:2:1::/64": { + "dev": "r1-eth0", + "metric": "20", + "pref": "medium", + "proto": "187" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_route_linux.json b/tests/topotests/isis-topo1-vrf/r1/r1_route_linux.json new file mode 100755 index 0000000000..6af22297e9 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/r1_route_linux.json @@ -0,0 +1,13 @@ +{ + "10.0.10.0/24": { + "dev": "r1-eth0", + "metric": "20", + "proto": "187", + "via": "10.0.20.1" + }, + "10.0.20.0/24": { + "dev": "r1-eth0", + "proto": "kernel", + "scope": "link" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r1/r1_topology.json b/tests/topotests/isis-topo1-vrf/r1/r1_topology.json new file mode 100644 index 0000000000..8e3cdc7bd6 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/r1_topology.json @@ -0,0 +1,80 @@ +{ + "1": { + "level-1": { + "ipv4": [ + { + "vertex": "r1" + } + ], + "ipv6": [ + { + "vertex": "r1" + } + ] + }, + "level-2": { + "ipv4": [ + { + "vertex": "r1" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.20.0/24" + }, + { + "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", + "vertex": "10.0.20.0/24" + }, + { + "interface": "r3", + "metric": "TE", + "next-hop": "20", + "parent": "r1-eth0", + "type": "IP", + "vertex": "10.0.10.0/24" + } + ], + "ipv6": [ + { + "vertex": "r1" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:1:1::/64" + }, + { + "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", + "vertex": "2001:db8:2:1::/64" + } + ] + } + } +} diff --git a/tests/topotests/isis-topo1-vrf/r1/zebra.conf b/tests/topotests/isis-topo1-vrf/r1/zebra.conf new file mode 100755 index 0000000000..fa1c02e5f8 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r1/zebra.conf @@ -0,0 +1,9 @@ +hostname r1 +interface r1-eth0 vrf r1-cust1 + ip address 10.0.20.2/24 + ipv6 address 2001:db8:1:1::2/64 +! +interface lo + ip address 10.254.0.1/32 + ipv6 address 2001:db8:F::1/128 +! diff --git a/tests/topotests/isis-topo1-vrf/r2/isisd.conf b/tests/topotests/isis-topo1-vrf/r2/isisd.conf new file mode 100755 index 0000000000..4c68540265 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/isisd.conf @@ -0,0 +1,15 @@ +hostname r2 +debug isis adj-packets +debug isis events +debug isis update-packets +interface r2-eth0 + ip router isis 1 vrf r2-cust1 + ipv6 router isis 1 vrf r2-cust1 + isis circuit-type level-2-only +! +router isis 1 vrf r2-cust1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00 + metric-style wide + redistribute ipv4 connected level-2 + redistribute ipv6 connected level-2 +! diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_route.json b/tests/topotests/isis-topo1-vrf/r2/r2_route.json new file mode 100644 index 0000000000..b3ac86d218 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/r2_route.json @@ -0,0 +1,57 @@ +{ + "10.0.11.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r2-eth0", + "ip": "10.0.21.1" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "isis", + "selected": true, + "vrfId": 3, + "vrfName": "r2-cust1" + } + ], + "10.0.21.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "afi": "ipv4", + "interfaceIndex": 2, + "interfaceName": "r2-eth0", + "ip": "10.0.21.1" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "isis", + "vrfId": 3, + "vrfName": "r2-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r2-eth0" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "connected", + "selected": true, + "vrfId": 3, + "vrfName": "r2-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_route6.json b/tests/topotests/isis-topo1-vrf/r2/r2_route6.json new file mode 100644 index 0000000000..c8d11b4922 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/r2_route6.json @@ -0,0 +1,40 @@ +{ + "2001:db8:1:2::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r2-eth0" + } + ], + "prefix": "2001:db8:1:2::/64", + "protocol": "connected", + "selected": true, + "vrfId": 3, + "vrfName": "r2-cust1" + } + ], + "2001:db8:2:2::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r2-eth0" + } + ], + "prefix": "2001:db8:2:2::/64", + "protocol": "isis", + "selected": true, + "vrfId": 3, + "vrfName": "r2-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_route6_linux.json b/tests/topotests/isis-topo1-vrf/r2/r2_route6_linux.json new file mode 100755 index 0000000000..27423e1936 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/r2_route6_linux.json @@ -0,0 +1,14 @@ +{ + "2001:db8:1:2::/64": { + "dev": "r2-eth0", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:2:2::/64": { + "dev": "r2-eth0", + "metric": "20", + "pref": "medium", + "proto": "187" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_route_linux.json b/tests/topotests/isis-topo1-vrf/r2/r2_route_linux.json new file mode 100755 index 0000000000..744b0780f3 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/r2_route_linux.json @@ -0,0 +1,13 @@ +{ + "10.0.11.0/24": { + "dev": "r2-eth0", + "metric": "20", + "proto": "187", + "via": "10.0.21.1" + }, + "10.0.21.0/24": { + "dev": "r2-eth0", + "proto": "kernel", + "scope": "link" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r2/r2_topology.json b/tests/topotests/isis-topo1-vrf/r2/r2_topology.json new file mode 100644 index 0000000000..72022a8167 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/r2_topology.json @@ -0,0 +1,80 @@ +{ + "1": { + "level-1": { + "ipv4": [ + { + "vertex": "r2" + } + ], + "ipv6": [ + { + "vertex": "r2" + } + ] + }, + "level-2": { + "ipv4": [ + { + "vertex": "r2" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.21.0/24" + }, + { + "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", + "vertex": "10.0.21.0/24" + }, + { + "interface": "r4", + "metric": "TE", + "next-hop": "20", + "parent": "r2-eth0", + "type": "IP", + "vertex": "10.0.11.0/24" + } + ], + "ipv6": [ + { + "vertex": "r2" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:1:2::/64" + }, + { + "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", + "vertex": "2001:db8:2:2::/64" + } + ] + } + } +}
\ No newline at end of file diff --git a/tests/topotests/isis-topo1-vrf/r2/zebra.conf b/tests/topotests/isis-topo1-vrf/r2/zebra.conf new file mode 100755 index 0000000000..a62af1749e --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r2/zebra.conf @@ -0,0 +1,9 @@ +hostname r2 +interface r2-eth0 vrf r2-cust1 + ip address 10.0.21.2/24 + ipv6 address 2001:db8:1:2::2/64 +! +interface lo + ip address 10.254.0.2/32 + ipv6 address 2001:db8:F::2/128 +! diff --git a/tests/topotests/isis-topo1-vrf/r3/isisd.conf b/tests/topotests/isis-topo1-vrf/r3/isisd.conf new file mode 100755 index 0000000000..ca01876690 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/isisd.conf @@ -0,0 +1,22 @@ +hostname r3 +debug isis adj-packets +debug isis events +debug isis update-packets +interface r3-eth0 + ip router isis 1 vrf r3-cust1 + ipv6 router isis 1 vrf r3-cust1 + isis circuit-type level-2-only +! +interface r3-eth1 + ip router isis 1 vrf r3-cust1 + ipv6 router isis 1 vrf r3-cust1 + isis circuit-type level-1 +! +router isis 1 vrf r3-cust1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00 + metric-style wide + redistribute ipv4 connected level-1 + redistribute ipv4 connected level-2 + redistribute ipv6 connected level-1 + redistribute ipv6 connected level-2 +! diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_route.json b/tests/topotests/isis-topo1-vrf/r3/r3_route.json new file mode 100644 index 0000000000..709d6b9aeb --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/r3_route.json @@ -0,0 +1,112 @@ +{ + "10.0.10.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "afi": "ipv4", + "interfaceIndex": 3, + "interfaceName": "r3-eth1", + "ip": "10.0.10.1" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "isis", + "vrfId": 4, + "vrfName": "r3-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r3-eth1" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r3-cust1" + } + ], + "10.0.11.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r3-eth1", + "ip": "10.0.10.1" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r3-cust1" + } + ], + "10.0.20.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "afi": "ipv4", + "interfaceIndex": 2, + "interfaceName": "r3-eth0", + "ip": "10.0.20.2" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "isis", + "vrfId": 4, + "vrfName": "r3-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r3-eth0" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r3-cust1" + } + ], + "10.0.21.0/24": [ + { + "distance": 115, + "metric": 30, + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r3-eth1", + "ip": "10.0.10.1" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r3-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_route6.json b/tests/topotests/isis-topo1-vrf/r3/r3_route6.json new file mode 100644 index 0000000000..3a7c3861fa --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/r3_route6.json @@ -0,0 +1,78 @@ +{ + "2001:db8:1:1::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r3-eth0" + } + ], + "prefix": "2001:db8:1:1::/64", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r3-cust1" + } + ], + "2001:db8:1:2::/64": [ + { + "distance": 115, + "metric": 30, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r3-eth1" + } + ], + "prefix": "2001:db8:1:2::/64", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r3-cust1" + } + ], + "2001:db8:2:1::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r3-eth1" + } + ], + "prefix": "2001:db8:2:1::/64", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r3-cust1" + } + ], + "2001:db8:2:2::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r3-eth1" + } + ], + "prefix": "2001:db8:2:2::/64", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r3-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_route6_linux.json b/tests/topotests/isis-topo1-vrf/r3/r3_route6_linux.json new file mode 100755 index 0000000000..bc527d2e1e --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/r3_route6_linux.json @@ -0,0 +1,26 @@ +{ + "2001:db8:1:1::/64": { + "dev": "r3-eth0", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:1:2::/64": { + "dev": "r3-eth1", + "metric": "20", + "pref": "medium", + "proto": "187" + }, + "2001:db8:2:1::/64": { + "dev": "r3-eth1", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:2:2::/64": { + "dev": "r3-eth1", + "metric": "20", + "pref": "medium", + "proto": "187" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_route_linux.json b/tests/topotests/isis-topo1-vrf/r3/r3_route_linux.json new file mode 100755 index 0000000000..515d376475 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/r3_route_linux.json @@ -0,0 +1,24 @@ +{ + "10.0.10.0/24": { + "dev": "r3-eth1", + "proto": "kernel", + "scope": "link" + }, + "10.0.11.0/24": { + "dev": "r3-eth1", + "metric": "20", + "proto": "187", + "via": "10.0.10.1" + }, + "10.0.20.0/24": { + "dev": "r3-eth0", + "proto": "kernel", + "scope": "link" + }, + "10.0.21.0/24": { + "dev": "r3-eth1", + "metric": "20", + "proto": "187", + "via": "10.0.10.1" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r3/r3_topology.json b/tests/topotests/isis-topo1-vrf/r3/r3_topology.json new file mode 100644 index 0000000000..62b895766e --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/r3_topology.json @@ -0,0 +1,132 @@ +{ + "1": { + "level-1": { + "ipv4": [ + { + "vertex": "r3" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.10.0/24" + }, + { + "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", + "vertex": "10.0.10.0/24" + }, + { + "interface": "r5", + "metric": "TE", + "next-hop": "20", + "parent": "r3-eth1", + "type": "IP", + "vertex": "10.0.11.0/24" + }, + { + "interface": "r5", + "metric": "TE", + "next-hop": "30", + "parent": "r3-eth1", + "type": "IP", + "vertex": "10.0.21.0/24" + } + ], + "ipv6": [ + { + "vertex": "r3" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:2:1::/64" + }, + { + "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", + "vertex": "2001:db8:2:2::/64" + }, + { + "interface": "r5", + "metric": "internal", + "next-hop": "30", + "parent": "r3-eth1", + "type": "IP6", + "vertex": "2001:db8:1:2::/64" + } + ] + }, + "level-2": { + "ipv4": [ + { + "vertex": "r3" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.20.0/24" + }, + { + "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", + "vertex": "10.0.20.0/24" + } + ], + "ipv6": [ + { + "vertex": "r3" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:1:1::/64" + }, + { + "interface": "r3-eth0", + "metric": "10", + "next-hop": "r3", + "parent": "r3(4)", + "type": "TE-IS", + "vertex": "r3" + } + ] + } + } +} diff --git a/tests/topotests/isis-topo1-vrf/r3/zebra.conf b/tests/topotests/isis-topo1-vrf/r3/zebra.conf new file mode 100755 index 0000000000..ac0b810fce --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r3/zebra.conf @@ -0,0 +1,13 @@ +hostname r3 +interface r3-eth0 vrf r3-cust1 + ip address 10.0.20.1/24 + ipv6 address 2001:db8:1:1::1/64 +! +interface r3-eth1 vrf r3-cust1 + ip address 10.0.10.2/24 + ipv6 address 2001:db8:2:1::2/64 +! +interface lo + ip address 10.254.0.3/32 + ipv6 address 2001:db8:F::3/128 +! diff --git a/tests/topotests/isis-topo1-vrf/r4/isisd.conf b/tests/topotests/isis-topo1-vrf/r4/isisd.conf new file mode 100755 index 0000000000..74b1603d85 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/isisd.conf @@ -0,0 +1,25 @@ +hostname r4 +debug isis adj-packets +debug isis events +debug isis update-packets +debug isis lsp-gen +debug isis lsp-sched + +interface r4-eth0 + ip router isis 1 vrf r4-cust1 + ipv6 router isis 1 vrf r4-cust1 + isis circuit-type level-2-only +! +interface r4-eth1 + ip router isis 1 vrf r4-cust1 + ipv6 router isis 1 vrf r4-cust1 + isis circuit-type level-1 +! +router isis 1 vrf r4-cust1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0004.00 + metric-style wide + redistribute ipv4 connected level-1 + redistribute ipv4 connected level-2 + redistribute ipv6 connected level-1 + redistribute ipv6 connected level-2 +! diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_route.json b/tests/topotests/isis-topo1-vrf/r4/r4_route.json new file mode 100644 index 0000000000..c464607a2b --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/r4_route.json @@ -0,0 +1,105 @@ +{ + "10.0.10.0/24": [ + { + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r4-eth1", + "ip": "10.0.11.1" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r4-cust1" + } + ], + "10.0.11.0/24": [ + { + "nexthops": [ + { + "afi": "ipv4", + "interfaceIndex": 3, + "interfaceName": "r4-eth1", + "ip": "10.0.11.1" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "isis", + "vrfId": 4, + "vrfName": "r4-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r4-eth1" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r4-cust1" + } + ], + "10.0.20.0/24": [ + { + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r4-eth1", + "ip": "10.0.11.1" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r4-cust1" + } + ], + "10.0.21.0/24": [ + { + "nexthops": [ + { + "afi": "ipv4", + "interfaceIndex": 2, + "interfaceName": "r4-eth0", + "ip": "10.0.21.2" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "isis", + "vrfId": 4, + "vrfName": "r4-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r4-eth0" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r4-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_route6.json b/tests/topotests/isis-topo1-vrf/r4/r4_route6.json new file mode 100644 index 0000000000..8d3ea570f0 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/r4_route6.json @@ -0,0 +1,78 @@ +{ + "2001:db8:1:1::/64": [ + { + "distance": 115, + "metric": 30, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r4-eth1" + } + ], + "prefix": "2001:db8:1:1::/64", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r4-cust1" + } + ], + "2001:db8:1:2::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r4-eth0" + } + ], + "prefix": "2001:db8:1:2::/64", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r4-cust1" + } + ], + "2001:db8:2:1::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r4-eth1" + } + ], + "prefix": "2001:db8:2:1::/64", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r4-cust1" + } + ], + "2001:db8:2:2::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r4-eth1" + } + ], + "prefix": "2001:db8:2:2::/64", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r4-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_route6_linux.json b/tests/topotests/isis-topo1-vrf/r4/r4_route6_linux.json new file mode 100755 index 0000000000..b1cd5b9db9 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/r4_route6_linux.json @@ -0,0 +1,26 @@ +{ + "2001:db8:1:1::/64": { + "dev": "r4-eth1", + "metric": "20", + "pref": "medium", + "proto": "187" + }, + "2001:db8:1:2::/64": { + "dev": "r4-eth0", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:2:1::/64": { + "dev": "r4-eth1", + "metric": "20", + "pref": "medium", + "proto": "187" + }, + "2001:db8:2:2::/64": { + "dev": "r4-eth1", + "metric": "256", + "pref": "medium", + "proto": "kernel" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_route_linux.json b/tests/topotests/isis-topo1-vrf/r4/r4_route_linux.json new file mode 100755 index 0000000000..3198b85789 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/r4_route_linux.json @@ -0,0 +1,24 @@ +{ + "10.0.10.0/24": { + "dev": "r4-eth1", + "metric": "20", + "proto": "187", + "via": "10.0.11.1" + }, + "10.0.11.0/24": { + "dev": "r4-eth1", + "proto": "kernel", + "scope": "link" + }, + "10.0.20.0/24": { + "dev": "r4-eth1", + "metric": "20", + "proto": "187", + "via": "10.0.11.1" + }, + "10.0.21.0/24": { + "dev": "r4-eth0", + "proto": "kernel", + "scope": "link" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r4/r4_topology.json b/tests/topotests/isis-topo1-vrf/r4/r4_topology.json new file mode 100644 index 0000000000..0d69550cad --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/r4_topology.json @@ -0,0 +1,132 @@ +{ + "1": { + "level-1": { + "ipv4": [ + { + "vertex": "r4" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.11.0/24" + }, + { + "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", + "vertex": "10.0.10.0/24" + }, + { + "interface": "r5", + "metric": "TE", + "next-hop": "20", + "parent": "r4-eth1", + "type": "IP", + "vertex": "10.0.11.0/24" + }, + { + "interface": "r5", + "metric": "TE", + "next-hop": "30", + "parent": "r4-eth1", + "type": "IP", + "vertex": "10.0.20.0/24" + } + ], + "ipv6": [ + { + "vertex": "r4" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:2:2::/64" + }, + { + "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", + "vertex": "2001:db8:2:1::/64" + }, + { + "interface": "r5", + "metric": "internal", + "next-hop": "30", + "parent": "r4-eth1", + "type": "IP6", + "vertex": "2001:db8:1:1::/64" + } + ] + }, + "level-2": { + "ipv4": [ + { + "vertex": "r4" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.21.0/24" + }, + { + "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", + "vertex": "10.0.21.0/24" + } + ], + "ipv6": [ + { + "vertex": "r4" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:1:2::/64" + }, + { + "interface": "r4-eth0", + "metric": "10", + "next-hop": "r2", + "parent": "r4(4)", + "type": "TE-IS", + "vertex": "r2" + } + ] + } + } +} diff --git a/tests/topotests/isis-topo1-vrf/r4/zebra.conf b/tests/topotests/isis-topo1-vrf/r4/zebra.conf new file mode 100755 index 0000000000..9c8941f7a5 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r4/zebra.conf @@ -0,0 +1,13 @@ +hostname r4 +interface r4-eth0 vrf r4-cust1 + ip address 10.0.21.1/24 + ipv6 address 2001:db8:1:2::1/64 +! +interface r4-eth1 vrf r4-cust1 + ip address 10.0.11.2/24 + ipv6 address 2001:db8:2:2::2/64 +! +interface lo + ip address 10.254.0.4/32 + ipv6 address 2001:db8:F::4/128 +! diff --git a/tests/topotests/isis-topo1-vrf/r5/isisd.conf b/tests/topotests/isis-topo1-vrf/r5/isisd.conf new file mode 100755 index 0000000000..9e9b030455 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/isisd.conf @@ -0,0 +1,21 @@ +hostname r5 +debug isis adj-packets +debug isis events +debug isis update-packets +interface r5-eth0 + ip router isis 1 vrf r5-cust1 + ipv6 router isis 1 vrf r5-cust1 + isis circuit-type level-1 +! +interface r5-eth1 + ip router isis 1 vrf r5-cust1 + ipv6 router isis 1 vrf r5-cust1 + isis circuit-type level-1 +! +router isis 1 vrf r5-cust1 + net 10.0000.0000.0000.0000.0000.0000.0000.0000.0005.00 + metric-style wide + is-type level-1 + redistribute ipv4 connected level-1 + redistribute ipv6 connected level-1 +! diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_route.json b/tests/topotests/isis-topo1-vrf/r5/r5_route.json new file mode 100644 index 0000000000..58aee5ddcc --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/r5_route.json @@ -0,0 +1,106 @@ +{ + "10.0.10.0/24": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "afi": "ipv4", + "interfaceIndex": 2, + "interfaceName": "r5-eth0", + "ip": "10.0.10.2" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "isis", + "vrfId": 4, + "vrfName": "r5-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r5-eth0" + } + ], + "prefix": "10.0.10.0/24", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r5-cust1" + } + ], + "10.0.11.0/24": [ + { + "nexthops": [ + { + "afi": "ipv4", + "interfaceIndex": 3, + "interfaceName": "r5-eth1", + "ip": "10.0.11.2" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "isis", + "vrfId": 4, + "vrfName": "r5-cust1" + }, + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r5-eth1" + } + ], + "prefix": "10.0.11.0/24", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r5-cust1" + } + ], + "10.0.20.0/24": [ + { + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r5-eth0", + "ip": "10.0.10.2" + } + ], + "prefix": "10.0.20.0/24", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r5-cust1" + } + ], + "10.0.21.0/24": [ + { + "nexthops": [ + { + "active": true, + "afi": "ipv4", + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r5-eth1", + "ip": "10.0.11.2" + } + ], + "prefix": "10.0.21.0/24", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r5-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_route6.json b/tests/topotests/isis-topo1-vrf/r5/r5_route6.json new file mode 100644 index 0000000000..e32bbcc2c1 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/r5_route6.json @@ -0,0 +1,78 @@ +{ + "2001:db8:1:1::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r5-eth0" + } + ], + "prefix": "2001:db8:1:1::/64", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r5-cust1" + } + ], + "2001:db8:1:2::/64": [ + { + "distance": 115, + "metric": 20, + "nexthops": [ + { + "active": true, + "afi": "ipv6", + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r5-eth1" + } + ], + "prefix": "2001:db8:1:2::/64", + "protocol": "isis", + "selected": true, + "vrfId": 4, + "vrfName": "r5-cust1" + } + ], + "2001:db8:2:1::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 2, + "interfaceName": "r5-eth0" + } + ], + "prefix": "2001:db8:2:1::/64", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r5-cust1" + } + ], + "2001:db8:2:2::/64": [ + { + "nexthops": [ + { + "active": true, + "directlyConnected": true, + "fib": true, + "interfaceIndex": 3, + "interfaceName": "r5-eth1" + } + ], + "prefix": "2001:db8:2:2::/64", + "protocol": "connected", + "selected": true, + "vrfId": 4, + "vrfName": "r5-cust1" + } + ] +} diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_route6_linux.json b/tests/topotests/isis-topo1-vrf/r5/r5_route6_linux.json new file mode 100755 index 0000000000..3db3c93ea6 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/r5_route6_linux.json @@ -0,0 +1,26 @@ +{ + "2001:db8:1:1::/64": { + "dev": "r5-eth0", + "metric": "20", + "pref": "medium", + "proto": "187" + }, + "2001:db8:1:2::/64": { + "dev": "r5-eth1", + "metric": "20", + "pref": "medium", + "proto": "187" + }, + "2001:db8:2:1::/64": { + "dev": "r5-eth0", + "metric": "256", + "pref": "medium", + "proto": "kernel" + }, + "2001:db8:2:2::/64": { + "dev": "r5-eth1", + "metric": "256", + "pref": "medium", + "proto": "kernel" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_route_linux.json b/tests/topotests/isis-topo1-vrf/r5/r5_route_linux.json new file mode 100755 index 0000000000..6a38ba864a --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/r5_route_linux.json @@ -0,0 +1,24 @@ +{ + "10.0.10.0/24": { + "dev": "r5-eth0", + "proto": "kernel", + "scope": "link" + }, + "10.0.11.0/24": { + "dev": "r5-eth1", + "proto": "kernel", + "scope": "link" + }, + "10.0.20.0/24": { + "dev": "r5-eth0", + "metric": "20", + "proto": "187", + "via": "10.0.10.2" + }, + "10.0.21.0/24": { + "dev": "r5-eth1", + "metric": "20", + "proto": "187", + "via": "10.0.11.2" + } +} diff --git a/tests/topotests/isis-topo1-vrf/r5/r5_topology.json b/tests/topotests/isis-topo1-vrf/r5/r5_topology.json new file mode 100644 index 0000000000..b4ed6a069d --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/r5_topology.json @@ -0,0 +1,124 @@ +{ + "1": { + "level-1": { + "ipv4": [ + { + "vertex": "r5" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.10.0/24" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP", + "vertex": "10.0.11.0/24" + }, + { + "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", + "vertex": "r4" + }, + { + "interface": "r3", + "metric": "TE", + "next-hop": "20", + "parent": "r5-eth0", + "type": "IP", + "vertex": "10.0.20.0/24" + }, + { + "interface": "r3", + "metric": "TE", + "next-hop": "20", + "parent": "r5-eth0", + "type": "IP", + "vertex": "10.0.10.0/24" + }, + { + "interface": "r4", + "metric": "TE", + "next-hop": "20", + "parent": "r5-eth1", + "type": "IP", + "vertex": "10.0.21.0/24" + }, + { + "interface": "r4", + "metric": "TE", + "next-hop": "20", + "parent": "r5-eth1", + "type": "IP", + "vertex": "10.0.11.0/24" + } + ], + "ipv6": [ + { + "vertex": "r5" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:2:1::/64" + }, + { + "metric": "internal", + "parent": "0", + "type": "IP6", + "vertex": "2001:db8:2:2::/64" + }, + { + "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", + "vertex": "r4" + }, + { + "interface": "r3", + "metric": "internal", + "next-hop": "20", + "parent": "r5-eth0", + "type": "IP6", + "vertex": "2001:db8:1:1::/64" + }, + { + "interface": "r4", + "metric": "internal", + "next-hop": "20", + "parent": "r5-eth1", + "type": "IP6", + "vertex": "2001:db8:1:2::/64" + } + ] + }, + "level-2": { + "ipv4": [], + "ipv6": [] + } + } +}
\ No newline at end of file diff --git a/tests/topotests/isis-topo1-vrf/r5/zebra.conf b/tests/topotests/isis-topo1-vrf/r5/zebra.conf new file mode 100755 index 0000000000..c6bc6302fc --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/r5/zebra.conf @@ -0,0 +1,13 @@ +hostname r5 +interface r5-eth0 vrf r5-cust1 + ip address 10.0.10.1/24 + ipv6 address 2001:db8:2:1::1/64 +! +interface r5-eth1 vrf r5-cust1 + ip address 10.0.11.1/24 + ipv6 address 2001:db8:2:2::1/64 +! +interface lo + ip address 10.254.0.5/32 + ipv6 address 2001:db8:F::5/128 +! diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.dot b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.dot new file mode 100755 index 0000000000..01f9ba780f --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.dot @@ -0,0 +1,100 @@ +## 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="isis topo1"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1\n10.254.0.1\n2001:DB8:F::1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2\n10.254.0.2\n2001:DB8:F::2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3\n10.254.0.3\n2001:DB8:F::3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4\n10.254.0.4\n2001:DB8:F::4", + fillcolor="#f08080", + style=filled, + ]; + r5 [ + shape=doubleoctagon + label="r5\n10.254.0.5\n2001:DB8:F::5", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n10.0.20.0/24\n2001:DB8:1:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n10.0.21.0/24\n2001:DB8:1:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n10.0.10.0/24\n2001:DB8:2:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw4 [ + shape=oval, + label="sw4\n10.0.11.0/24\n2001:DB8:2:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + subgraph cluster0 { + label="level 2"; + + r1 -- sw1 [label="eth0\n.2"]; + r2 -- sw2 [label="eth0\n.2"]; + } + + subgraph cluster1 { + label="level 1/2"; + + r3 -- sw1 [label="eth0\n.1"]; + r3 -- sw3 [label="eth1\n.2"]; + + r4 -- sw4 [label="eth1\n.2"]; + r4 -- sw2 [label="eth0\n.1"]; + } + + subgraph cluster2 { + label="level 1"; + + r5 -- sw3 [label="eth0\n.1"]; + r5 -- sw4 [label="eth1\n.1"]; + } +} diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.jpg b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.jpg Binary files differnew file mode 100755 index 0000000000..4ad730f2a0 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.jpg diff --git a/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py new file mode 100755 index 0000000000..a0e34b71b0 --- /dev/null +++ b/tests/topotests/isis-topo1-vrf/test_isis_topo1_vrf.py @@ -0,0 +1,455 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2020 by Niral Networks, Inc. ("Niral Networks") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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_isis_topo1_vrf.py: Test ISIS vrf topology. +""" + +import collections +import functools +import json +import os +import re +import sys +import pytest +import platform + +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 ISISTopo1(Topo): + "Simple two layer ISIS vrf topology" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Add ISIS routers: + # r1 r2 + # | sw1 | sw2 + # r3 r4 + # | | + # sw3 sw4 + # \ / + # r5 + for routern in range(1, 6): + tgen.add_router("r{}".format(routern)) + + # r1 <- sw1 -> r3 + sw = tgen.add_switch("sw1") + sw.add_link(tgen.gears["r1"]) + sw.add_link(tgen.gears["r3"]) + + # r2 <- sw2 -> r4 + sw = tgen.add_switch("sw2") + sw.add_link(tgen.gears["r2"]) + sw.add_link(tgen.gears["r4"]) + + # r3 <- sw3 -> r5 + sw = tgen.add_switch("sw3") + sw.add_link(tgen.gears["r3"]) + sw.add_link(tgen.gears["r5"]) + + # r4 <- sw4 -> r5 + sw = tgen.add_switch("sw4") + sw.add_link(tgen.gears["r4"]) + sw.add_link(tgen.gears["r5"]) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(ISISTopo1, mod.__name__) + 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", + "ip link add loop1 type dummy", + "ip link set {0}-eth0 master {0}-cust1", + "ip link set {0}-eth1 master {0}-cust1", + ] + + # For all registered routers, load the zebra configuration file + for rname, router in tgen.routers().iteritems(): + # 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) + ) + + for rname, router in tgen.routers().iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, + os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, + os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + # After loading the configurations, this function loads configured daemons. + tgen.start_router() + + has_version_20 = False + for router in tgen.routers().values(): + if router.has_version("<", "4"): + has_version_20 = True + + if has_version_20: + logger.info("Skipping ISIS vrf tests for FRR 2.0") + tgen.set_error("ISIS has convergence problems with IPv6") + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + # move back rx-eth0 to default VRF + # delete rx-vrf + tgen.stop_topology() + +def test_isis_convergence(): + "Wait for the protocol to converge before starting to test" + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for ISIS protocol to converge") + + for rname, router in tgen.routers().iteritems(): + filename = "{0}/{1}/{1}_topology.json".format(CWD, rname) + expected = json.loads(open(filename).read()) + def compare_isis_topology(router, expected): + "Helper function to test ISIS vrf topology convergence." + actual = show_isis_topology(router) + + return topotest.json_cmp(actual, expected) + + test_func = functools.partial(compare_isis_topology, router, expected) + (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=120) + assert result, "ISIS did not converge on {}:\n{}".format(rname, diff) + +def test_isis_route_installation(): + "Check whether all expected routes are present" + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking routers for installed ISIS vrf routes") + # Check for routes in 'show ip route vrf {}-cust1 json' + for rname, router in tgen.routers().iteritems(): + filename = "{0}/{1}/{1}_route.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) + actual = router.vtysh_cmd("show ip route vrf {0}-cust1 json".format(rname) , isjson=True) + # Older FRR versions don't list interfaces in some ISIS routes + if router.has_version("<", "3.1"): + for network, routes in expected.iteritems(): + for route in routes: + if route["protocol"] != "isis": + continue + + for nexthop in route["nexthops"]: + nexthop.pop("interfaceIndex", None) + nexthop.pop("interfaceName", None) + + assertmsg = "Router '{}' routes mismatch".format(rname) + assert topotest.json_cmp(actual, expected) is None, assertmsg + + +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) + + 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().iteritems(): + filename = "{0}/{1}/{1}_route_linux.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) + actual = topotest.ip4_vrf_route(router) + + # Older FRR versions install routes using different proto + if router.has_version("<", "3.1"): + for network, netoptions in expected.iteritems(): + if "proto" in netoptions and netoptions["proto"] == "187": + netoptions["proto"] = "zebra" + + assertmsg = "Router '{}' OS routes mismatch".format(rname) + assert topotest.json_cmp(actual, expected) is None, assertmsg + +def test_isis_route6_installation(): + "Check whether all expected routes are present" + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking routers for installed ISIS vrf IPv6 routes") + # Check for routes in 'show ipv6 route vrf {}-cust1 json' + for rname, router in tgen.routers().iteritems(): + filename = "{0}/{1}/{1}_route6.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) + actual = router.vtysh_cmd("show ipv6 route vrf {}-cust1 json".format(rname) , isjson=True) + + # Older FRR versions don't list interfaces in some ISIS routes + if router.has_version("<", "3.1"): + for network, routes in expected.iteritems(): + for route in routes: + if route["protocol"] != "isis": + continue + + for nexthop in route["nexthops"]: + nexthop.pop("interfaceIndex", None) + nexthop.pop("interfaceName", None) + + assertmsg = "Router '{}' routes mismatch".format(rname) + assert topotest.json_cmp(actual, expected) is None, assertmsg + +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) + + 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().iteritems(): + filename = "{0}/{1}/{1}_route6_linux.json".format(CWD, rname) + expected = json.loads(open(filename, "r").read()) + actual = topotest.ip6_vrf_route(router) + + # Older FRR versions install routes using different proto + if router.has_version("<", "3.1"): + for network, netoptions in expected.iteritems(): + if "proto" in netoptions and netoptions["proto"] == "187": + netoptions["proto"] = "zebra" + + assertmsg = "Router '{}' OS routes mismatch".format(rname) + assert topotest.json_cmp(actual, expected) 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)) + + +# +# Auxiliary functions +# + + +def dict_merge(dct, merge_dct): + """ + Recursive dict merge. Inspired by :meth:``dict.update()``, instead of + updating only top-level keys, dict_merge recurses down into dicts nested + to an arbitrary depth, updating keys. The ``merge_dct`` is merged into + ``dct``. + :param dct: dict onto which the merge is executed + :param merge_dct: dct merged into dct + :return: None + + Source: + https://gist.github.com/angstwad/bf22d1822c38a92ec0a9 + """ + for k, v in merge_dct.iteritems(): + if ( + k in dct + and isinstance(dct[k], dict) + and isinstance(merge_dct[k], collections.Mapping) + ): + dict_merge(dct[k], merge_dct[k]) + else: + dct[k] = merge_dct[k] + + +def parse_topology(lines, level): + """ + Parse the output of 'show isis topology level-X' into a Python dict. + """ + areas = {} + area = None + ipv = None + + for line in lines: + area_match = re.match(r"Area (.+):", line) + if area_match: + area = area_match.group(1) + if area not in areas: + areas[area] = {level: {"ipv4": [], "ipv6": []}} + ipv = None + continue + elif area is None: + continue + + if re.match(r"IS\-IS paths to level-. routers that speak IPv6", line): + ipv = "ipv6" + continue + if re.match(r"IS\-IS paths to level-. routers that speak IP", line): + ipv = "ipv4" + continue + + item_match = re.match(r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", line) + if item_match is not None: + # 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 + + 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), + } + ) + continue + + item_match = re.match(r"([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)", 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), + } + ) + continue + + item_match = re.match(r"([^ ]+)", line) + if item_match is not None: + areas[area][level][ipv].append({"vertex": item_match.group(1)}) + continue + + return areas + + +def show_isis_topology(router): + """ + Get the ISIS vrf topology in a dictionary format. + + Sample: + { + 'area-name': { + 'level-1': [ + { + 'vertex': 'r1' + } + ], + 'level-2': [ + { + 'vertex': '10.0.0.1/24', + 'type': 'IP', + 'parent': '0', + 'metric': 'internal' + } + ] + }, + 'area-name-2': { + 'level-2': [ + { + "interface": "rX-ethY", + "metric": "Z", + "next-hop": "rA", + "parent": "rC(B)", + "type": "TE-IS", + "vertex": "rD" + } + ] + } + } + """ + l1out = topotest.normalize_text( + router.vtysh_cmd("show isis vrf {}-cust1 topology level-1".format(router.name)) + ).splitlines() + l2out = topotest.normalize_text( + router.vtysh_cmd("show isis vrf {}-cust1 topology level-2".format(router.name)) + ).splitlines() + + l1 = parse_topology(l1out, "level-1") + l2 = parse_topology(l2out, "level-2") + + dict_merge(l1, l2) + return l1 + diff --git a/tests/topotests/ldp-oc-acl-topo1/r1/ospfd.conf b/tests/topotests/ldp-oc-acl-topo1/r1/ospfd.conf index 6daf034d18..87d5703d9e 100644 --- a/tests/topotests/ldp-oc-acl-topo1/r1/ospfd.conf +++ b/tests/topotests/ldp-oc-acl-topo1/r1/ospfd.conf @@ -5,3 +5,7 @@ router ospf router-id 1.1.1.1 network 0.0.0.0/0 area 0 ! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r2/ospfd.conf b/tests/topotests/ldp-oc-acl-topo1/r2/ospfd.conf index 8678813665..51317202bb 100644 --- a/tests/topotests/ldp-oc-acl-topo1/r2/ospfd.conf +++ b/tests/topotests/ldp-oc-acl-topo1/r2/ospfd.conf @@ -5,3 +5,11 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r3/ospfd.conf b/tests/topotests/ldp-oc-acl-topo1/r3/ospfd.conf index 202be238ec..4566976b7b 100644 --- a/tests/topotests/ldp-oc-acl-topo1/r3/ospfd.conf +++ b/tests/topotests/ldp-oc-acl-topo1/r3/ospfd.conf @@ -6,3 +6,7 @@ router ospf router-id 3.3.3.3 network 0.0.0.0/0 area 0 ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-acl-topo1/r4/ospfd.conf b/tests/topotests/ldp-oc-acl-topo1/r4/ospfd.conf index 569dbc54e2..5aae885a12 100644 --- a/tests/topotests/ldp-oc-acl-topo1/r4/ospfd.conf +++ b/tests/topotests/ldp-oc-acl-topo1/r4/ospfd.conf @@ -5,3 +5,7 @@ router ospf router-id 4.4.4.4 network 0.0.0.0/0 area 0 ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-topo1/r1/ospfd.conf b/tests/topotests/ldp-oc-topo1/r1/ospfd.conf index 6daf034d18..87d5703d9e 100644 --- a/tests/topotests/ldp-oc-topo1/r1/ospfd.conf +++ b/tests/topotests/ldp-oc-topo1/r1/ospfd.conf @@ -5,3 +5,7 @@ router ospf router-id 1.1.1.1 network 0.0.0.0/0 area 0 ! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-topo1/r2/ospfd.conf b/tests/topotests/ldp-oc-topo1/r2/ospfd.conf index 8678813665..51317202bb 100644 --- a/tests/topotests/ldp-oc-topo1/r2/ospfd.conf +++ b/tests/topotests/ldp-oc-topo1/r2/ospfd.conf @@ -5,3 +5,11 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-topo1/r3/ospfd.conf b/tests/topotests/ldp-oc-topo1/r3/ospfd.conf index 202be238ec..4566976b7b 100644 --- a/tests/topotests/ldp-oc-topo1/r3/ospfd.conf +++ b/tests/topotests/ldp-oc-topo1/r3/ospfd.conf @@ -6,3 +6,7 @@ router ospf router-id 3.3.3.3 network 0.0.0.0/0 area 0 ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-oc-topo1/r4/ospfd.conf b/tests/topotests/ldp-oc-topo1/r4/ospfd.conf index 569dbc54e2..5aae885a12 100644 --- a/tests/topotests/ldp-oc-topo1/r4/ospfd.conf +++ b/tests/topotests/ldp-oc-topo1/r4/ospfd.conf @@ -5,3 +5,7 @@ router ospf router-id 4.4.4.4 network 0.0.0.0/0 area 0 ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-topo1/r1/ospfd.conf b/tests/topotests/ldp-topo1/r1/ospfd.conf index 6daf034d18..87d5703d9e 100644 --- a/tests/topotests/ldp-topo1/r1/ospfd.conf +++ b/tests/topotests/ldp-topo1/r1/ospfd.conf @@ -5,3 +5,7 @@ router ospf router-id 1.1.1.1 network 0.0.0.0/0 area 0 ! +int r1-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-topo1/r2/ospfd.conf b/tests/topotests/ldp-topo1/r2/ospfd.conf index 8678813665..dbed6189c8 100644 --- a/tests/topotests/ldp-topo1/r2/ospfd.conf +++ b/tests/topotests/ldp-topo1/r2/ospfd.conf @@ -5,3 +5,15 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-topo1/r3/ospfd.conf b/tests/topotests/ldp-topo1/r3/ospfd.conf index 202be238ec..bd86fe4f24 100644 --- a/tests/topotests/ldp-topo1/r3/ospfd.conf +++ b/tests/topotests/ldp-topo1/r3/ospfd.conf @@ -6,3 +6,11 @@ router ospf router-id 3.3.3.3 network 0.0.0.0/0 area 0 ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-topo1/r4/ospfd.conf b/tests/topotests/ldp-topo1/r4/ospfd.conf index 569dbc54e2..5aae885a12 100644 --- a/tests/topotests/ldp-topo1/r4/ospfd.conf +++ b/tests/topotests/ldp-topo1/r4/ospfd.conf @@ -5,3 +5,7 @@ router ospf router-id 4.4.4.4 network 0.0.0.0/0 area 0 ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-vpls-topo1/r1/ospfd.conf b/tests/topotests/ldp-vpls-topo1/r1/ospfd.conf index 6daf034d18..76ea32fb61 100644 --- a/tests/topotests/ldp-vpls-topo1/r1/ospfd.conf +++ b/tests/topotests/ldp-vpls-topo1/r1/ospfd.conf @@ -5,3 +5,11 @@ router ospf router-id 1.1.1.1 network 0.0.0.0/0 area 0 ! +int r1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r1-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-vpls-topo1/r2/ospfd.conf b/tests/topotests/ldp-vpls-topo1/r2/ospfd.conf index 8678813665..7b3ddfe371 100644 --- a/tests/topotests/ldp-vpls-topo1/r2/ospfd.conf +++ b/tests/topotests/ldp-vpls-topo1/r2/ospfd.conf @@ -5,3 +5,11 @@ router ospf router-id 2.2.2.2 network 0.0.0.0/0 area 0 ! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r2-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-vpls-topo1/r3/ospfd.conf b/tests/topotests/ldp-vpls-topo1/r3/ospfd.conf index b6bafba205..b424f2e108 100644 --- a/tests/topotests/ldp-vpls-topo1/r3/ospfd.conf +++ b/tests/topotests/ldp-vpls-topo1/r3/ospfd.conf @@ -5,3 +5,11 @@ router ospf router-id 3.3.3.3 network 0.0.0.0/0 area 0 ! +int r3-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py index 600d640a70..a1662dc411 100755 --- a/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py +++ b/tests/topotests/ldp-vpls-topo1/test_ldp_vpls_topo1.py @@ -154,7 +154,7 @@ def teardown_module(mod): tgen.stop_topology() -def router_compare_json_output(rname, command, reference): +def router_compare_json_output(rname, command, reference, count=80, wait=1): "Compare router JSON output" logger.info('Comparing router "%s" "%s" output', rname, command) @@ -163,9 +163,9 @@ def router_compare_json_output(rname, command, reference): filename = "{}/{}/{}".format(CWD, rname, reference) expected = json.loads(open(filename).read()) - # Run test function until we get an result. Wait at most 80 seconds. + # Run test function until we get an result. test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected) - _, diff = topotest.run_and_expect(test_func, None, count=160, wait=0.5) + _, diff = topotest.run_and_expect(test_func, None, count, wait) assertmsg = '"{}" JSON output mismatches the expected result'.format(rname) assert diff is None, assertmsg @@ -279,10 +279,12 @@ def test_ldp_pseudowires_after_link_down(): tgen.gears["r1"].peer_link_enable("r1-eth1", False) topotest.sleep(5, "Waiting for the network to reconverge") - # check if the pseudowire is still up (using an alternate path for nexthop resolution) + # check if the pseudowire is still up (using an alternate path + # for nexthop resolution). Give some extra wait time. for rname in ["r1", "r2", "r3"]: router_compare_json_output( - rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref" + rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref", + count=160, wait=1 ) diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index 7b1eead944..e2f887bb89 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -46,6 +46,7 @@ from lib.common_config import ( LOGDIR = "/tmp/topotests/" TMPDIR = None + def create_router_bgp(tgen, topo, input_dict=None, build=False, load_config=True): """ API to configure bgp on router @@ -455,6 +456,15 @@ def __create_bgp_unicast_neighbor( cmd = "no {}".format(cmd) config_data.append(cmd) + import_vrf_data = addr_data.setdefault("import", {}) + if import_vrf_data: + cmd = "import vrf {}".format(import_vrf_data["vrf"]) + + del_action = import_vrf_data.setdefault("delete", False) + if del_action: + cmd = "no {}".format(cmd) + config_data.append(cmd) + if "neighbor" in addr_data: neigh_data = __create_bgp_neighbor( topo, input_dict, router, addr_type, add_neigh @@ -1219,11 +1229,17 @@ def verify_bgp_convergence(tgen, topo, dut=None): no_of_peer += 1 if no_of_peer == total_peer: - logger.info("[DUT: %s] VRF: %s, BGP is Converged for %s address-family", - router, vrf, addr_type) + logger.info( + "[DUT: %s] VRF: %s, BGP is Converged for %s address-family", + router, + vrf, + addr_type, + ) else: - errormsg = ("[DUT: %s] VRF: %s, BGP is not converged for %s address-family" % - (router, vrf, addr_type)) + errormsg = ( + "[DUT: %s] VRF: %s, BGP is not converged for %s address-family" + % (router, vrf, addr_type) + ) return errormsg logger.debug("Exiting API: verify_bgp_convergence()") @@ -2553,10 +2569,19 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) missing_routes = [] st_found = False nh_found = False + vrf = static_route.setdefault("vrf", None) + community = static_route.setdefault("community", None) + largeCommunity = static_route.setdefault("largeCommunity", None) + if vrf: cmd = "{} vrf {} {}".format(command, vrf, addr_type) + if community: + cmd = "{} community {}".format(cmd, community) + + if largeCommunity: + cmd = "{} large-community {}".format(cmd, largeCommunity) else: cmd = "{} {}".format(command, addr_type) @@ -2594,6 +2619,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) if not isinstance(next_hop, list): next_hop = [next_hop] list1 = next_hop + found_hops = [ rib_r["ip"] for rib_r in rib_routes_json["routes"][st_rt][0][ @@ -2601,6 +2627,7 @@ def verify_bgp_rib(tgen, addr_type, dut, input_dict, next_hop=None, aspath=None) ] ] list2 = found_hops + missing_list_of_nexthops = set(list2).difference(list1) additional_nexthops_in_required_nhs = set( list1 diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 156a5f7ea4..1846d43138 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -30,7 +30,6 @@ from functools import wraps from re import search as re_search from tempfile import mkdtemp -import StringIO import os import sys import ConfigParser @@ -38,6 +37,13 @@ import traceback import socket import ipaddress +if sys.version_info[0] > 2: + import io + import configparser +else: + import StringIO + import ConfigParser as configparser + from lib.topolog import logger, logger_config from lib.topogen import TopoRouter, get_topogen from lib.topotest import interface_set_status @@ -61,7 +67,7 @@ TMPDIR = None # NOTE: to save execution logs to log file frrtest_log_dir must be configured # in `pytest.ini`. -config = ConfigParser.ConfigParser() +config = configparser.ConfigParser() config.read(PYTESTINI_PATH) config_section = "topogen" @@ -393,6 +399,14 @@ def check_router_status(tgen): logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True +def getStrIO(): + """ + Return a StringIO object appropriate for the current python version. + """ + if sys.version_info[0] > 2: + return io.StringIO() + else: + return StringIO.StringIO() def reset_config_on_routers(tgen, routerName=None): """ @@ -465,7 +479,7 @@ def reset_config_on_routers(tgen, routerName=None): raise InvalidCLIError("Unknown error in %s", output) f = open(dname, "r") - delta = StringIO.StringIO() + delta = getStrIO() delta.write("configure terminal\n") t_delta = f.read() @@ -499,7 +513,7 @@ def reset_config_on_routers(tgen, routerName=None): output = router.vtysh_multicmd(delta.getvalue(), pretty_output=False) delta.close() - delta = StringIO.StringIO() + delta = getStrIO() cfg = router.run("vtysh -c 'show running'") for line in cfg.split("\n"): line = line.strip() @@ -713,8 +727,8 @@ def start_topology(tgen): os.chdir("{}/{}".format(TMPDIR, rname)) os.system("touch zebra.conf bgpd.conf") - except IOError as (errno, strerror): - logger.error("I/O error({0}): {1}".format(errno, strerror)) + except IOError as err: + logger.error("I/O error({0}): {1}".format(err.errno, err.strerror)) # Loading empty zebra.conf file to router, to start the zebra deamon router.load_config( @@ -1224,7 +1238,8 @@ def interface_status(tgen, topo, input_dict): return True -def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict=False): +def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, + return_is_dict=False): """ Retries function execution, if return is an errormsg or exception @@ -1236,11 +1251,13 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict """ def _retry(func): + @wraps(func) def func_retry(*args, **kwargs): - _wait = kwargs.pop("wait", wait) - _attempts = kwargs.pop("attempts", attempts) + _wait = kwargs.pop('wait', wait) + _attempts = kwargs.pop('attempts', attempts) _attempts = int(_attempts) + expected = True if _attempts < 0: raise ValueError("attempts must be 0 or greater") @@ -1248,40 +1265,40 @@ def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0, return_is_dict logger.info("Waiting for [%s]s as initial delay", initial_wait) sleep(initial_wait) - _return_is_str = kwargs.pop("return_is_str", return_is_str) - _return_is_dict = kwargs.pop("return_is_str", return_is_dict) + _return_is_str = kwargs.pop('return_is_str', return_is_str) + _return_is_dict = kwargs.pop('return_is_str', return_is_dict) for i in range(1, _attempts + 1): try: - _expected = kwargs.setdefault("expected", True) - kwargs.pop("expected") + _expected = kwargs.setdefault('expected', True) + if _expected is False: + expected = _expected + kwargs.pop('expected') ret = func(*args, **kwargs) - logger.debug("Function returned %s" % ret) + logger.debug("Function returned %s", ret) if _return_is_str and isinstance(ret, bool) and _expected: return ret - if ( - isinstance(ret, str) or isinstance(ret, unicode) - ) and _expected is False: + if (isinstance(ret, str) or isinstance(ret, unicode)) and _expected is False: return ret if _return_is_dict and isinstance(ret, dict): return ret - if _attempts == i: + if _attempts == i and expected: generate_support_bundle() return ret except Exception as err: - if _attempts == i: + if _attempts == i and expected: generate_support_bundle() - logger.info("Max number of attempts (%r) reached", _attempts) + logger.info("Max number of attempts (%r) reached", + _attempts) raise else: logger.info("Function returned %s", err) if i < _attempts: - logger.info("Retry [#%r] after sleeping for %ss" % (i, _wait)) + logger.info("Retry [#%r] after sleeping for %ss" + % (i, _wait)) sleep(_wait) - func_retry._original = func return func_retry - return _retry @@ -2191,7 +2208,7 @@ def addKernelRoute( if mask == "32" or mask == "128": grp_addr = ip - if not re_search(r"{}".format(grp_addr), result) and mask is not "0": + if not re_search(r"{}".format(grp_addr), result) and mask != "0": errormsg = ( "[DUT: {}]: Kernal route is not added for group" " address {} Config output: {}".format(router, grp_addr, output) @@ -2281,7 +2298,7 @@ def configure_vxlan(tgen, input_dict): cmd = "{} local {}".format(cmd, local_addr) if learning == "no": - cmd = "{} {} learning".format(cmd, learning) + cmd = "{} nolearning".format(cmd) elif learning == "yes": cmd = "{} learning".format(cmd) @@ -2352,7 +2369,9 @@ def configure_brctl(tgen, topo, input_dict): ): ip_cmd_list = [] - cmd = "ip link add name {} type bridge stp_state {}".format(brctl_name, stp) + cmd = "ip link add name {} type bridge stp_state {}".format( + brctl_name, stp + ) logger.info("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) @@ -2502,7 +2521,7 @@ def verify_rib( errormsg(str) or True """ - logger.info("Entering lib API: verify_rib()") + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) router_list = tgen.routers() additional_nexthops_in_required_nhs = [] @@ -2817,7 +2836,517 @@ def verify_rib( " routes are: {}\n".format(addr_type, dut, found_routes) ) - logger.info("Exiting lib API: verify_rib()") + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): + """ + Data will be read from input_dict or input JSON file, API will generate + same prefixes, which were redistributed by either create_static_routes() or + advertise_networks_using_network_command() and will verify next_hop and + each prefix/routes is present in "show ip/ipv6 fib json" + command o/p. + + Parameters + ---------- + * `tgen` : topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test, for which user wants to test the data + * `input_dict` : input dict, has details of static routes + * `next_hop`[optional]: next_hop which needs to be verified, + default: static + + Usage + ----- + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": ["1.1.1.1/32], + "next_hop": "Null0", + "vrf": "RED" + }] + } + } + result = result = verify_fib_routes(tgen, "ipv4, "r1", input_routes_r1) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + router_list = tgen.routers() + for routerInput in input_dict.keys(): + for router, rnode in router_list.iteritems(): + if router != dut: + continue + + logger.info("Checking router %s FIB routes:", router) + + # Verifying RIB routes + if addr_type == "ipv4": + command = "show ip fib" + else: + command = "show ipv6 fib" + + found_routes = [] + missing_routes = [] + + if "static_routes" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["static_routes"] + + for static_route in static_routes: + if "vrf" in static_route and static_route["vrf"] is not None: + + logger.info( + "[DUT: {}]: Verifying routes for VRF:" + " {}".format(router, static_route["vrf"]) + ) + + cmd = "{} vrf {}".format(command, static_route["vrf"]) + + else: + cmd = "{}".format(command) + + cmd = "{} json".format(cmd) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "[DUT: {}]: No route found in fib".format(router) + return errormsg + + network = static_route["network"] + if "no_of_ip" in static_route: + no_of_ip = static_route["no_of_ip"] + else: + no_of_ip = 1 + + # Generating IPs for verification + ip_list = generate_ips(network, no_of_ip) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + #st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0][ + "nexthops" + ]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + missing_routes.append(st_rt) + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format( + next_hop, st_rt, dut + ) + ) + return errormsg + + else: + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in FIB:" " {}".format( + dut, missing_routes + ) + return errormsg + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if found_routes: + logger.info( + "[DUT: %s]: Verified routes in FIB, found" " routes are: %s\n", + dut, + found_routes, + ) + + continue + + if "bgp" in input_dict[routerInput]: + if ( + "advertise_networks" + not in input_dict[routerInput]["bgp"]["address_family"][addr_type][ + "unicast" + ] + ): + continue + + found_routes = [] + missing_routes = [] + advertise_network = input_dict[routerInput]["bgp"]["address_family"][ + addr_type + ]["unicast"]["advertise_networks"] + + # Continue if there are no network advertise + if len(advertise_network) == 0: + continue + + for advertise_network_dict in advertise_network: + if "vrf" in advertise_network_dict: + cmd = "{} vrf {} json".format(command, static_route["vrf"]) + else: + cmd = "{} json".format(command) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "No route found in rib of router {}..".format(router) + return errormsg + + start_ip = advertise_network_dict["network"] + if "no_of_network" in advertise_network_dict: + no_of_network = advertise_network_dict["no_of_network"] + else: + no_of_network = 1 + + # Generating IPs for verification + ip_list = generate_ips(start_ip, no_of_network) + st_found = False + nh_found = False + + for st_rt in ip_list: + #st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0]["nexthops"]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + missing_routes.append(st_rt) + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format(next_hop, st_rt, dut) + ) + return errormsg + else: + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in FIB: " "{} \n".format( + dut, missing_routes + ) + return errormsg + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if found_routes: + logger.info( + "[DUT: {}]: Verified routes FIB" + ", found routes are: {}\n".format(dut, found_routes) + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) +def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): + """ + Data will be read from input_dict or input JSON file, API will generate + same prefixes, which were redistributed by either create_static_routes() or + advertise_networks_using_network_command() and will verify next_hop and + each prefix/routes is present in "show ip/ipv6 fib json" + command o/p. + + Parameters + ---------- + * `tgen` : topogen object + * `addr_type` : ip type, ipv4/ipv6 + * `dut`: Device Under Test, for which user wants to test the data + * `input_dict` : input dict, has details of static routes + * `next_hop`[optional]: next_hop which needs to be verified, + default: static + + Usage + ----- + input_routes_r1 = { + "r1": { + "static_routes": [{ + "network": ["1.1.1.1/32], + "next_hop": "Null0", + "vrf": "RED" + }] + } + } + result = result = verify_fib_routes(tgen, "ipv4, "r1", input_routes_r1) + + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + router_list = tgen.routers() + for routerInput in input_dict.keys(): + for router, rnode in router_list.iteritems(): + if router != dut: + continue + + logger.info("Checking router %s FIB routes:", router) + + # Verifying RIB routes + if addr_type == "ipv4": + command = "show ip fib" + else: + command = "show ipv6 fib" + + found_routes = [] + missing_routes = [] + + if "static_routes" in input_dict[routerInput]: + static_routes = input_dict[routerInput]["static_routes"] + + for static_route in static_routes: + if "vrf" in static_route and static_route["vrf"] is not None: + + logger.info( + "[DUT: {}]: Verifying routes for VRF:" + " {}".format(router, static_route["vrf"]) + ) + + cmd = "{} vrf {}".format(command, static_route["vrf"]) + + else: + cmd = "{}".format(command) + + cmd = "{} json".format(cmd) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "[DUT: {}]: No route found in fib".format(router) + return errormsg + + network = static_route["network"] + if "no_of_ip" in static_route: + no_of_ip = static_route["no_of_ip"] + else: + no_of_ip = 1 + + # Generating IPs for verification + ip_list = generate_ips(network, no_of_ip) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0][ + "nexthops" + ]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + missing_routes.append(st_rt) + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format( + next_hop, st_rt, dut + ) + ) + return errormsg + + else: + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in FIB:" " {}".format( + dut, missing_routes + ) + return errormsg + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if found_routes: + logger.info( + "[DUT: %s]: Verified routes in FIB, found" " routes are: %s\n", + dut, + found_routes, + ) + + continue + + if "bgp" in input_dict[routerInput]: + if ( + "advertise_networks" + not in input_dict[routerInput]["bgp"]["address_family"][addr_type][ + "unicast" + ] + ): + continue + + found_routes = [] + missing_routes = [] + advertise_network = input_dict[routerInput]["bgp"]["address_family"][ + addr_type + ]["unicast"]["advertise_networks"] + + # Continue if there are no network advertise + if len(advertise_network) == 0: + continue + + for advertise_network_dict in advertise_network: + if "vrf" in advertise_network_dict: + cmd = "{} vrf {} json".format(command, static_route["vrf"]) + else: + cmd = "{} json".format(command) + + rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) + + # Verifying output dictionary rib_routes_json is not empty + if bool(rib_routes_json) is False: + errormsg = "No route found in rib of router {}..".format(router) + return errormsg + + start_ip = advertise_network_dict["network"] + if "no_of_network" in advertise_network_dict: + no_of_network = advertise_network_dict["no_of_network"] + else: + no_of_network = 1 + + # Generating IPs for verification + ip_list = generate_ips(start_ip, no_of_network) + st_found = False + nh_found = False + + for st_rt in ip_list: + st_rt = str(ipaddress.ip_network(unicode(st_rt))) + + _addr_type = validate_ip_address(st_rt) + if _addr_type != addr_type: + continue + + if st_rt in rib_routes_json: + st_found = True + found_routes.append(st_rt) + + if next_hop: + if type(next_hop) is not list: + next_hop = [next_hop] + + count = 0 + for nh in next_hop: + for nh_dict in rib_routes_json[st_rt][0]["nexthops"]: + if nh_dict["ip"] != nh: + continue + else: + count += 1 + + if count == len(next_hop): + nh_found = True + else: + missing_routes.append(st_rt) + errormsg = ( + "Nexthop {} is Missing" + " for route {} in " + "RIB of router {}\n".format(next_hop, st_rt, dut) + ) + return errormsg + else: + missing_routes.append(st_rt) + + if len(missing_routes) > 0: + errormsg = "[DUT: {}]: Missing route in FIB: " "{} \n".format( + dut, missing_routes + ) + return errormsg + + if nh_found: + logger.info( + "Found next_hop {} for all routes in RIB" + " of router {}\n".format(next_hop, dut) + ) + + if found_routes: + logger.info( + "[DUT: {}]: Verified routes FIB" + ", found routes are: {}\n".format(dut, found_routes) + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return True diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 37b9715010..a6cc5280ec 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -335,9 +335,7 @@ class Topogen(object): logger.info("stopping topology: {}".format(self.modname)) errors = "" for gear in self.gears.values(): - gear.stop(False, False) - for gear in self.gears.values(): - errors += gear.stop(True, False) + errors += gear.stop() if len(errors) > 0: assert "Errors found post shutdown - details follow:" == 0, errors @@ -703,14 +701,26 @@ class TopoRouter(TopoGear): return result - def stop(self, wait=True, assertOnError=True): + def __stop_internal(self, wait=True, assertOnError=True): """ - Stop router: + Stop router, private internal version * Kill daemons """ - self.logger.debug("stopping") + self.logger.debug("stopping: wait {}, assert {}".format( + wait, assertOnError)) return self.tgen.net[self.name].stopRouter(wait, assertOnError) + + def stop(self): + """ + Stop router cleanly: + * Signal daemons twice, once without waiting, and then a second time + with a wait to ensure the daemons exit cleanly + """ + self.logger.debug("stopping") + self.__stop_internal(False, False) + return self.__stop_internal() + def startDaemons(self, daemons): """ Start Daemons: to start specific daemon(user defined daemon only) @@ -819,8 +829,7 @@ class TopoRouter(TopoGear): if memleak_file is None: return - self.stop(False, False) - self.stop(wait=True) + self.stop() self.logger.info("running memory leak report") self.tgen.net[self.name].report_memory_leaks(memleak_file, testname) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index b5fa2ea59b..e34d1cf0be 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -29,7 +29,6 @@ import re import sys import functools import glob -import StringIO import subprocess import tempfile import platform @@ -721,6 +720,49 @@ def ip4_route(node): return result +def ip4_vrf_route(node): + """ + Gets a structured return of the command 'ip route show vrf {0}-cust1'. + It can be used in conjuction with json_cmp() to provide accurate assert explanations. + + Return example: + { + '10.0.1.0/24': { + 'dev': 'eth0', + 'via': '172.16.0.1', + 'proto': '188', + }, + '10.0.2.0/24': { + 'dev': 'eth1', + 'proto': 'kernel', + } + } + """ + output = normalize_text( + node.run("ip route show vrf {0}-cust1".format(node.name))).splitlines() + + result = {} + for line in output: + columns = line.split(" ") + route = result[columns[0]] = {} + prev = None + for column in columns: + if prev == "dev": + route["dev"] = column + if prev == "via": + route["via"] = column + if prev == "proto": + # translate protocol names back to numbers + route["proto"] = proto_name_to_number(column) + if prev == "metric": + route["metric"] = column + if prev == "scope": + route["scope"] = column + prev = column + + return result + + def ip6_route(node): """ Gets a structured return of the command 'ip -6 route'. It can be used in @@ -761,6 +803,47 @@ def ip6_route(node): return result +def ip6_vrf_route(node): + """ + Gets a structured return of the command 'ip -6 route show vrf {0}-cust1'. + It can be used in conjuction with json_cmp() to provide accurate assert explanations. + + Return example: + { + '2001:db8:1::/64': { + 'dev': 'eth0', + 'proto': '188', + }, + '2001:db8:2::/64': { + 'dev': 'eth1', + 'proto': 'kernel', + } + } + """ + output = normalize_text( + node.run("ip -6 route show vrf {0}-cust1".format(node.name))).splitlines() + result = {} + for line in output: + columns = line.split(" ") + route = result[columns[0]] = {} + prev = None + for column in columns: + if prev == "dev": + route["dev"] = column + if prev == "via": + route["via"] = column + if prev == "proto": + # translate protocol names back to numbers + route["proto"] = proto_name_to_number(column) + if prev == "metric": + route["metric"] = column + if prev == "pref": + route["pref"] = column + prev = column + + return result + + def ip_rules(node): """ Gets a structured return of the command 'ip rule'. It can be used in @@ -1013,12 +1096,9 @@ class Router(Node): self.cmd("chown {0}:{0}vty /etc/{0}".format(self.routertype)) def terminate(self): - # Delete Running Quagga or FRR Daemons + # Stop running FRR daemons self.stopRouter() - # rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype) - # for d in StringIO.StringIO(rundaemons): - # self.cmd('kill -7 `cat %s`' % d.rstrip()) - # self.waitOutput() + # Disable forwarding set_sysctl(self, "net.ipv4.ip_forward", 0) set_sysctl(self, "net.ipv6.conf.all.forwarding", 0) @@ -1033,10 +1113,12 @@ class Router(Node): if re.search(r"No such file or directory", rundaemons): return 0 if rundaemons is not None: - for d in StringIO.StringIO(rundaemons): + bet = rundaemons.split('\n') + for d in bet[:-1]: daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() if daemonpid.isdigit() and pid_exists(int(daemonpid)): ret.append(os.path.basename(d.rstrip().rsplit(".", 1)[0])) + return ret def stopRouter(self, wait=True, assertOnError=True, minErrorVersion="5.1"): @@ -1046,7 +1128,9 @@ class Router(Node): if re.search(r"No such file or directory", rundaemons): return errors if rundaemons is not None: - for d in StringIO.StringIO(rundaemons): + dmns = rundaemons.split('\n') + # Exclude empty string at end of list + for d in dmns[:-1]: daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() if daemonpid.isdigit() and pid_exists(int(daemonpid)): daemonname = os.path.basename(d.rstrip().rsplit(".", 1)[0]) @@ -1080,7 +1164,9 @@ class Router(Node): if running: # 2nd round of kill if daemons didn't exit - for d in StringIO.StringIO(rundaemons): + dmns = rundaemons.split('\n') + # Exclude empty string at end of list + for d in dmns[:-1]: daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() if daemonpid.isdigit() and pid_exists(int(daemonpid)): logger.info( @@ -1265,7 +1351,7 @@ class Router(Node): zebra_path = os.path.join(self.daemondir, "zebra") zebra_option = self.daemons_options["zebra"] self.cmd( - "{0} {1} --log stdout --log-level debug -d > zebra.out 2> zebra.err".format( + "{0} {1} --log stdout --log-level debug -s 90000000 -d > zebra.out 2> zebra.err".format( zebra_path, zebra_option, self.logdir, self.name ) ) @@ -1330,7 +1416,9 @@ class Router(Node): for daemon in daemons: if rundaemons is not None and daemon in rundaemons: numRunning = 0 - for d in StringIO.StringIO(rundaemons): + dmns = rundaemons.split('\n') + # Exclude empty string at end of list + for d in dmns[:-1]: if re.search(r"%s" % daemon, d): daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() if daemonpid.isdigit() and pid_exists(int(daemonpid)): @@ -1351,8 +1439,9 @@ class Router(Node): self.name, daemon ), ) + # 2nd round of kill if daemons didn't exit - for d in StringIO.StringIO(rundaemons): + for d in dmns[:-1]: if re.search(r"%s" % daemon, d): daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip() if daemonpid.isdigit() and pid_exists( diff --git a/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json index 0416bd6ce2..952a26ed10 100644 --- a/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json @@ -3,8 +3,10 @@ "srNodes":[ { "routerID":"10.0.255.2", - "srgbSize":20000, - "srgbLabel":8000, + "srgbSize":8000, + "srgbLabel":16000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -34,6 +36,8 @@ "routerID":"10.0.255.4", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -47,12 +51,12 @@ "inputLabel":20400, "prefixRoute":[ { - "outputLabel":8400, + "outputLabel":16400, "interface":"r1-eth0", "nexthop":"10.0.0.2" }, { - "outputLabel":8400, + "outputLabel":16400, "interface":"r1-eth1", "nexthop":"10.0.1.2" } @@ -64,8 +68,14 @@ "routerID":"10.0.255.3", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ + { + "0":"SPF" + } ], + "nodeMsd":8, "extendedPrefix":[ { "prefix":"10.0.255.3\/32", @@ -73,12 +83,12 @@ "inputLabel":20300, "prefixRoute":[ { - "outputLabel":8300, + "outputLabel":16300, "interface":"r1-eth0", "nexthop":"10.0.0.2" }, { - "outputLabel":8300, + "outputLabel":16300, "interface":"r1-eth1", "nexthop":"10.0.1.2" } @@ -90,6 +100,8 @@ "routerID":"10.0.255.1", "srgbSize":10000, "srgbLabel":20000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -100,10 +112,10 @@ { "prefix":"10.0.255.1\/32", "sid":100, - "inputLabel":20100, + "inputLabel":0, "prefixRoute":[ { - "outputLabel":3, + "outputLabel":0, "interface":"lo", "nexthop":"10.0.255.1" } diff --git a/tests/topotests/ospf-sr-topo1/r1/ospfd.conf b/tests/topotests/ospf-sr-topo1/r1/ospfd.conf index 292d4e6367..0773153a76 100644 --- a/tests/topotests/ospf-sr-topo1/r1/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r1/ospfd.conf @@ -1,13 +1,18 @@ +debug ospf sr ! 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 @@ -17,6 +22,6 @@ router ospf segment-routing on segment-routing node-msd 16 segment-routing global-block 20000 29999 - segment-routing prefix 10.0.255.1/32 index 100 no-php-flag + segment-routing prefix 10.0.255.1/32 index 100 explicit-null ! diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json index 5ae2399e5c..6c87596acb 100644 --- a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json @@ -1,23 +1,14 @@ [ { - "inLabel":20100, - "installed":true, - "nexthops":[ - { - "type":"SR (OSPF)", - "outLabel":3, - "distance":150, - "installed":true - } - ] - }, - { "inLabel":20200, "installed":true, "nexthops":[ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.1.2" @@ -37,17 +28,13 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8300, + "outLabel":16300, + "outLabelStack":[ + 16300 + ], "distance":150, "installed":true, "nexthop":"10.0.1.2" - }, - { - "type":"SR (OSPF)", - "outLabel":8300, - "distance":150, - "installed":true, - "nexthop":"10.0.0.2" } ] }, @@ -57,17 +44,13 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8400, + "outLabel":16400, + "outLabelStack":[ + 16400 + ], "distance":150, "installed":true, "nexthop":"10.0.1.2" - }, - { - "type":"SR (OSPF)", - "outLabel":8400, - "distance":150, - "installed":true, - "nexthop":"10.0.0.2" } ] }, @@ -104,6 +87,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.1.2" @@ -117,6 +103,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.1.2" diff --git a/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json index eb202b82cd..1de780d84e 100644 --- a/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json @@ -3,8 +3,10 @@ "srNodes":[ { "routerID":"10.0.255.2", - "srgbSize":20000, - "srgbLabel":8000, + "srgbSize":8000, + "srgbLabel":16000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -95,13 +97,19 @@ "routerID":"10.0.255.4", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ + { + "0":"SPF" + } ], + "nodeMsd":12, "extendedPrefix":[ { "prefix":"10.0.255.4\/32", "sid":400, - "inputLabel":8400, + "inputLabel":16400, "prefixRoute":[ { "outputLabel":10400, @@ -116,13 +124,19 @@ "routerID":"10.0.255.3", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ + { + "0":"SPF" + } ], + "nodeMsd":8, "extendedPrefix":[ { "prefix":"10.0.255.3\/32", "sid":300, - "inputLabel":8300, + "inputLabel":16300, "prefixRoute":[ { "outputLabel":3, @@ -137,21 +151,27 @@ "routerID":"10.0.255.1", "srgbSize":10000, "srgbLabel":20000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ + { + "0":"SPF" + } ], + "nodeMsd":16, "extendedPrefix":[ { "prefix":"10.0.255.1\/32", "sid":100, - "inputLabel":8100, + "inputLabel":16100, "prefixRoute":[ { - "outputLabel":20100, + "outputLabel":0, "interface":"r2-eth0", "nexthop":"10.0.0.1" }, { - "outputLabel":20100, + "outputLabel":0, "interface":"r2-eth1", "nexthop":"10.0.1.1" } diff --git a/tests/topotests/ospf-sr-topo1/r2/ospfd.conf b/tests/topotests/ospf-sr-topo1/r2/ospfd.conf index b8c7b999e8..92dc2f7cd1 100644 --- a/tests/topotests/ospf-sr-topo1/r2/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r2/ospfd.conf @@ -6,17 +6,25 @@ interface lo ! 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 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 diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json index aedcc5b8f8..a885e88fc5 100644 --- a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json @@ -1,18 +1,21 @@ [ { - "inLabel":8100, + "inLabel":16100, "installed":true, "nexthops":[ { "type":"SR (OSPF)", - "outLabel":20100, + "outLabel":0, + "outLabelStack":[ + 0 + ], "distance":150, "installed":true, "nexthop":"10.0.1.1" }, { "type":"SR (OSPF)", - "outLabel":20100, + "outLabel":0, "distance":150, "installed":true, "nexthop":"10.0.0.1" @@ -20,12 +23,15 @@ ] }, { - "inLabel":8300, + "inLabel":16300, "installed":true, "nexthops":[ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.3.1" @@ -33,12 +39,15 @@ ] }, { - "inLabel":8400, + "inLabel":16400, "installed":true, "nexthops":[ { "type":"SR (OSPF)", "outLabel":10400, + "outLabelStack":[ + 10400 + ], "distance":150, "installed":true, "nexthop":"10.0.4.1" @@ -52,6 +61,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.4.1" @@ -65,6 +77,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.4.1" @@ -78,6 +93,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.0.1" @@ -91,6 +109,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.0.1" @@ -104,6 +125,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.1.1" @@ -117,6 +141,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.1.1" diff --git a/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json index b36fe674ad..e7371ff593 100644 --- a/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r3/ospf_srdb.json @@ -3,8 +3,10 @@ "srNodes":[ { "routerID":"10.0.255.2", - "srgbSize":20000, - "srgbLabel":8000, + "srgbSize":8000, + "srgbLabel":16000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -29,6 +31,8 @@ "routerID":"10.0.255.4", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -42,7 +46,7 @@ "inputLabel":10400, "prefixRoute":[ { - "outputLabel":8400, + "outputLabel":16400, "interface":"r3-eth0", "nexthop":"10.0.3.2" } @@ -54,6 +58,8 @@ "routerID":"10.0.255.3", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -97,6 +103,8 @@ "routerID":"10.0.255.1", "srgbSize":10000, "srgbLabel":20000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ { "0":"SPF" @@ -110,7 +118,7 @@ "inputLabel":10100, "prefixRoute":[ { - "outputLabel":8100, + "outputLabel":16100, "interface":"r3-eth0", "nexthop":"10.0.3.2" } diff --git a/tests/topotests/ospf-sr-topo1/r3/ospfd.conf b/tests/topotests/ospf-sr-topo1/r3/ospfd.conf index cf274bed2e..e2766f202d 100644 --- a/tests/topotests/ospf-sr-topo1/r3/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r3/ospfd.conf @@ -1,9 +1,13 @@ ! interface lo ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ! interface r3-eth0 ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ! ! router ospf @@ -11,6 +15,7 @@ router ospf capability opaque router-info area 0.0.0.0 segment-routing on + segment-routing local-block 5000 5999 segment-routing global-block 10000 19999 segment-routing node-msd 8 segment-routing prefix 10.0.255.3/32 index 300 diff --git a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json index 71e8366137..1b98ff4756 100644 --- a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json @@ -5,7 +5,10 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8100, + "outLabel":16100, + "outLabelStack":[ + 16100 + ], "distance":150, "installed":true, "nexthop":"10.0.3.2" @@ -19,6 +22,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.3.2" @@ -31,7 +37,10 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8400, + "outLabel":16400, + "outLabelStack":[ + 16400 + ], "distance":150, "installed":true, "nexthop":"10.0.3.2" @@ -45,6 +54,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.3.2" @@ -58,6 +70,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.3.2" diff --git a/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json index d92ec91c72..a241b32607 100644 --- a/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r4/ospf_srdb.json @@ -3,9 +3,14 @@ "srNodes":[ { "routerID":"10.0.255.2", - "srgbSize":20000, - "srgbLabel":8000, + "srgbSize":8000, + "srgbLabel":16000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ + { + "0":"SPF" + } ], "extendedPrefix":[ { @@ -26,6 +31,8 @@ "routerID":"10.0.255.4", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ { "0":"SPF" @@ -69,8 +76,14 @@ "routerID":"10.0.255.3", "srgbSize":10000, "srgbLabel":10000, + "srlbSize":1000, + "srlbLabel":5000, "algorithms":[ + { + "0":"SPF" + } ], + "nodeMsd":8, "extendedPrefix":[ { "prefix":"10.0.255.3\/32", @@ -78,7 +91,7 @@ "inputLabel":10300, "prefixRoute":[ { - "outputLabel":8300, + "outputLabel":16300, "interface":"r4-eth0", "nexthop":"10.0.4.2" } @@ -90,8 +103,14 @@ "routerID":"10.0.255.1", "srgbSize":10000, "srgbLabel":20000, + "srlbSize":1000, + "srlbLabel":15000, "algorithms":[ + { + "0":"SPF" + } ], + "nodeMsd":16, "extendedPrefix":[ { "prefix":"10.0.255.1\/32", @@ -99,7 +118,7 @@ "inputLabel":10100, "prefixRoute":[ { - "outputLabel":8100, + "outputLabel":16100, "interface":"r4-eth0", "nexthop":"10.0.4.2" } diff --git a/tests/topotests/ospf-sr-topo1/r4/ospfd.conf b/tests/topotests/ospf-sr-topo1/r4/ospfd.conf index 65fdce69f7..e80880af88 100644 --- a/tests/topotests/ospf-sr-topo1/r4/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r4/ospfd.conf @@ -1,9 +1,13 @@ ! interface lo ip ospf area 0.0.0.0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 ! 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 ! ! @@ -12,6 +16,7 @@ router ospf capability opaque router-info area 0.0.0.0 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-sr-topo1/r4/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json index b5767e1d7d..b5758f29a0 100644 --- a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json @@ -5,7 +5,10 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8100, + "outLabel":16100, + "outLabelStack":[ + 16100 + ], "distance":150, "installed":true, "nexthop":"10.0.4.2" @@ -19,6 +22,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.4.2" @@ -31,7 +37,10 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8300, + "outLabel":16300, + "outLabelStack":[ + 16300 + ], "distance":150, "installed":true, "nexthop":"10.0.4.2" @@ -45,6 +54,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true } @@ -57,6 +69,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.4.2" @@ -70,6 +85,9 @@ { "type":"SR (OSPF)", "outLabel":3, + "outLabelStack":[ + 3 + ], "distance":150, "installed":true, "nexthop":"10.0.4.2" diff --git a/tests/topotests/ospf-topo1/r1/ospf6d.conf b/tests/topotests/ospf-topo1/r1/ospf6d.conf index 58c9e292a9..ca3497b4a5 100644 --- a/tests/topotests/ospf-topo1/r1/ospf6d.conf +++ b/tests/topotests/ospf-topo1/r1/ospf6d.conf @@ -7,3 +7,7 @@ router ospf6 interface r1-eth0 area 0.0.0.0 interface r1-eth1 area 0.0.0.0 ! +int r1-eth1 + ipv6 ospf6 dead-interval 10 + ipv6 ospf6 hello-interval 2 +! diff --git a/tests/topotests/ospf-topo1/r1/ospfd.conf b/tests/topotests/ospf-topo1/r1/ospfd.conf index 1226b72f2b..3b5aa192c5 100644 --- a/tests/topotests/ospf-topo1/r1/ospfd.conf +++ b/tests/topotests/ospf-topo1/r1/ospfd.conf @@ -7,3 +7,7 @@ router ospf network 10.0.1.0/24 area 0 network 10.0.3.0/24 area 0 ! +int r1-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r2/ospf6d.conf b/tests/topotests/ospf-topo1/r2/ospf6d.conf index a4ef1469a7..44047e1a4e 100644 --- a/tests/topotests/ospf-topo1/r2/ospf6d.conf +++ b/tests/topotests/ospf-topo1/r2/ospf6d.conf @@ -7,3 +7,11 @@ router ospf6 interface r2-eth0 area 0.0.0.0 interface r2-eth1 area 0.0.0.0 ! +int r2-eth0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +int r2-eth1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r2/ospfd.conf b/tests/topotests/ospf-topo1/r2/ospfd.conf index 78d980315d..1a7ccdf728 100644 --- a/tests/topotests/ospf-topo1/r2/ospfd.conf +++ b/tests/topotests/ospf-topo1/r2/ospfd.conf @@ -7,3 +7,7 @@ router ospf network 10.0.2.0/24 area 0 network 10.0.3.0/24 area 0 ! +int r2-eth1 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r3/ospf6d.conf b/tests/topotests/ospf-topo1/r3/ospf6d.conf index 4ff53011cb..13ad9a7356 100644 --- a/tests/topotests/ospf-topo1/r3/ospf6d.conf +++ b/tests/topotests/ospf-topo1/r3/ospf6d.conf @@ -8,3 +8,15 @@ router ospf6 interface r3-eth1 area 0.0.0.0 interface r3-eth2 area 0.0.0.1 ! +int r3-eth0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +int r3-eth1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +int r3-eth2 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r3/ospfd.conf b/tests/topotests/ospf-topo1/r3/ospfd.conf index dbb7215d10..3b378c0f27 100644 --- a/tests/topotests/ospf-topo1/r3/ospfd.conf +++ b/tests/topotests/ospf-topo1/r3/ospfd.conf @@ -8,3 +8,11 @@ router ospf network 10.0.10.0/24 area 0 network 172.16.0.0/24 area 1 ! +int r3-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! +int r3-eth2 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r4/ospf6d.conf b/tests/topotests/ospf-topo1/r4/ospf6d.conf index 32942ea189..f9bde0e83c 100644 --- a/tests/topotests/ospf-topo1/r4/ospf6d.conf +++ b/tests/topotests/ospf-topo1/r4/ospf6d.conf @@ -7,3 +7,11 @@ router ospf6 interface r4-eth0 area 0.0.0.1 interface r4-eth1 area 0.0.0.1 ! +int r4-eth0 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! +int r4-eth1 + ipv6 ospf6 hello-interval 2 + ipv6 ospf6 dead-interval 10 +! diff --git a/tests/topotests/ospf-topo1/r4/ospfd.conf b/tests/topotests/ospf-topo1/r4/ospfd.conf index 01b29ca057..52d29322f8 100644 --- a/tests/topotests/ospf-topo1/r4/ospfd.conf +++ b/tests/topotests/ospf-topo1/r4/ospfd.conf @@ -7,3 +7,7 @@ router ospf network 172.16.0.0/24 area 1 network 172.16.1.0/24 area 1 ! +int r4-eth0 + ip ospf hello-interval 2 + ip ospf dead-interval 10 +! diff --git a/tests/topotests/ospf-topo2/r1/ospfd.conf b/tests/topotests/ospf-topo2/r1/ospfd.conf index 1e1bfca569..65843cbb83 100644 --- a/tests/topotests/ospf-topo2/r1/ospfd.conf +++ b/tests/topotests/ospf-topo2/r1/ospfd.conf @@ -1,6 +1,8 @@ ! interface r1-eth1 ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 ! router ospf ospf router-id 10.0.255.1 diff --git a/tests/topotests/ospf-topo2/r2/ospfd.conf b/tests/topotests/ospf-topo2/r2/ospfd.conf index af2acd8404..b032f1a8ac 100644 --- a/tests/topotests/ospf-topo2/r2/ospfd.conf +++ b/tests/topotests/ospf-topo2/r2/ospfd.conf @@ -1,6 +1,8 @@ ! interface r2-eth1 ip ospf network point-to-point + ip ospf hello-interval 2 + ip ospf dead-interval 10 ! router ospf ospf router-id 10.0.255.2 diff --git a/tests/topotests/zebra_netlink/__init__.py b/tests/topotests/zebra_netlink/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/zebra_netlink/__init__.py diff --git a/tests/topotests/zebra_netlink/r1/sharpd.conf b/tests/topotests/zebra_netlink/r1/sharpd.conf new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/zebra_netlink/r1/sharpd.conf diff --git a/tests/topotests/zebra_netlink/r1/v4_route.json b/tests/topotests/zebra_netlink/r1/v4_route.json new file mode 100644 index 0000000000..61e9bb240b --- /dev/null +++ b/tests/topotests/zebra_netlink/r1/v4_route.json @@ -0,0 +1,2802 @@ +{ + "2.1.3.7\/32":[ + { + "prefix":"2.1.3.7\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.8\/32":[ + { + "prefix":"2.1.3.8\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.9\/32":[ + { + "prefix":"2.1.3.9\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.10\/32":[ + { + "prefix":"2.1.3.10\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.11\/32":[ + { + "prefix":"2.1.3.11\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.12\/32":[ + { + "prefix":"2.1.3.12\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.13\/32":[ + { + "prefix":"2.1.3.13\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.14\/32":[ + { + "prefix":"2.1.3.14\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.15\/32":[ + { + "prefix":"2.1.3.15\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.16\/32":[ + { + "prefix":"2.1.3.16\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.17\/32":[ + { + "prefix":"2.1.3.17\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.18\/32":[ + { + "prefix":"2.1.3.18\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.19\/32":[ + { + "prefix":"2.1.3.19\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.20\/32":[ + { + "prefix":"2.1.3.20\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.21\/32":[ + { + "prefix":"2.1.3.21\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.22\/32":[ + { + "prefix":"2.1.3.22\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.23\/32":[ + { + "prefix":"2.1.3.23\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.24\/32":[ + { + "prefix":"2.1.3.24\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.25\/32":[ + { + "prefix":"2.1.3.25\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.26\/32":[ + { + "prefix":"2.1.3.26\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.27\/32":[ + { + "prefix":"2.1.3.27\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.28\/32":[ + { + "prefix":"2.1.3.28\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.29\/32":[ + { + "prefix":"2.1.3.29\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.30\/32":[ + { + "prefix":"2.1.3.30\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.31\/32":[ + { + "prefix":"2.1.3.31\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.32\/32":[ + { + "prefix":"2.1.3.32\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.33\/32":[ + { + "prefix":"2.1.3.33\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.34\/32":[ + { + "prefix":"2.1.3.34\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.35\/32":[ + { + "prefix":"2.1.3.35\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.36\/32":[ + { + "prefix":"2.1.3.36\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.37\/32":[ + { + "prefix":"2.1.3.37\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.38\/32":[ + { + "prefix":"2.1.3.38\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.39\/32":[ + { + "prefix":"2.1.3.39\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.40\/32":[ + { + "prefix":"2.1.3.40\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.41\/32":[ + { + "prefix":"2.1.3.41\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.42\/32":[ + { + "prefix":"2.1.3.42\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.43\/32":[ + { + "prefix":"2.1.3.43\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.44\/32":[ + { + "prefix":"2.1.3.44\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.45\/32":[ + { + "prefix":"2.1.3.45\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.46\/32":[ + { + "prefix":"2.1.3.46\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.47\/32":[ + { + "prefix":"2.1.3.47\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.48\/32":[ + { + "prefix":"2.1.3.48\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.49\/32":[ + { + "prefix":"2.1.3.49\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.50\/32":[ + { + "prefix":"2.1.3.50\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.51\/32":[ + { + "prefix":"2.1.3.51\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.52\/32":[ + { + "prefix":"2.1.3.52\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.53\/32":[ + { + "prefix":"2.1.3.53\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.54\/32":[ + { + "prefix":"2.1.3.54\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.55\/32":[ + { + "prefix":"2.1.3.55\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.56\/32":[ + { + "prefix":"2.1.3.56\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.57\/32":[ + { + "prefix":"2.1.3.57\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.58\/32":[ + { + "prefix":"2.1.3.58\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.59\/32":[ + { + "prefix":"2.1.3.59\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.60\/32":[ + { + "prefix":"2.1.3.60\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.61\/32":[ + { + "prefix":"2.1.3.61\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.62\/32":[ + { + "prefix":"2.1.3.62\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.63\/32":[ + { + "prefix":"2.1.3.63\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.64\/32":[ + { + "prefix":"2.1.3.64\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.65\/32":[ + { + "prefix":"2.1.3.65\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.66\/32":[ + { + "prefix":"2.1.3.66\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.67\/32":[ + { + "prefix":"2.1.3.67\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.68\/32":[ + { + "prefix":"2.1.3.68\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.69\/32":[ + { + "prefix":"2.1.3.69\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.70\/32":[ + { + "prefix":"2.1.3.70\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.71\/32":[ + { + "prefix":"2.1.3.71\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.72\/32":[ + { + "prefix":"2.1.3.72\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.73\/32":[ + { + "prefix":"2.1.3.73\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.74\/32":[ + { + "prefix":"2.1.3.74\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.75\/32":[ + { + "prefix":"2.1.3.75\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.76\/32":[ + { + "prefix":"2.1.3.76\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.77\/32":[ + { + "prefix":"2.1.3.77\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.78\/32":[ + { + "prefix":"2.1.3.78\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.79\/32":[ + { + "prefix":"2.1.3.79\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.80\/32":[ + { + "prefix":"2.1.3.80\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.81\/32":[ + { + "prefix":"2.1.3.81\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.82\/32":[ + { + "prefix":"2.1.3.82\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.83\/32":[ + { + "prefix":"2.1.3.83\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.84\/32":[ + { + "prefix":"2.1.3.84\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.85\/32":[ + { + "prefix":"2.1.3.85\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.86\/32":[ + { + "prefix":"2.1.3.86\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.87\/32":[ + { + "prefix":"2.1.3.87\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.88\/32":[ + { + "prefix":"2.1.3.88\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.89\/32":[ + { + "prefix":"2.1.3.89\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.90\/32":[ + { + "prefix":"2.1.3.90\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.91\/32":[ + { + "prefix":"2.1.3.91\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.92\/32":[ + { + "prefix":"2.1.3.92\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.93\/32":[ + { + "prefix":"2.1.3.93\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.94\/32":[ + { + "prefix":"2.1.3.94\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.95\/32":[ + { + "prefix":"2.1.3.95\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.96\/32":[ + { + "prefix":"2.1.3.96\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.97\/32":[ + { + "prefix":"2.1.3.97\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.98\/32":[ + { + "prefix":"2.1.3.98\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.99\/32":[ + { + "prefix":"2.1.3.99\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.100\/32":[ + { + "prefix":"2.1.3.100\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.101\/32":[ + { + "prefix":"2.1.3.101\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.102\/32":[ + { + "prefix":"2.1.3.102\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.103\/32":[ + { + "prefix":"2.1.3.103\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.104\/32":[ + { + "prefix":"2.1.3.104\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.105\/32":[ + { + "prefix":"2.1.3.105\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ], + "2.1.3.106\/32":[ + { + "prefix":"2.1.3.106\/32", + "protocol":"sharp", + "selected":true, + "destSelected":true, + "distance":150, + "metric":0, + "installed":true, + "table":254, + "internalStatus":16, + "internalFlags":9, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.1.1", + "afi":"ipv4", + "interfaceIndex":2, + "interfaceName":"r1-eth0", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/zebra_netlink/r1/zebra.conf b/tests/topotests/zebra_netlink/r1/zebra.conf new file mode 100644 index 0000000000..786be19ad4 --- /dev/null +++ b/tests/topotests/zebra_netlink/r1/zebra.conf @@ -0,0 +1,2 @@ +int r1-eth0 + ip address 192.168.1.1/24
\ No newline at end of file diff --git a/tests/topotests/zebra_netlink/test_zebra_netlink.py b/tests/topotests/zebra_netlink/test_zebra_netlink.py new file mode 100755 index 0000000000..7b692c75ab --- /dev/null +++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +# +# test_zebra_netlink.py +# +# Copyright (c) 2020 by +# +# 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_zebra_netlink.py: Test some basic interactions with kernel using Netlink + +""" + +import os +import re +import sys +import pytest +import json +import platform +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 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger +from lib.common_config import shutdown_bringup_interface + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +class ZebraTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + tgen.add_router("r1") + + # Create a empty network for router 1 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(ZebraTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + + router.load_config( + TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_zebra_netlink_batching(): + "Test the situation where dataplane fills netlink send buffer entirely." + logger.info( + "Test the situation where dataplane fills netlink send buffer entirely." + ) + tgen = get_topogen() + if tgen.routers_have_failure(): + ptyest.skip("skipped because of preview test failure") + r1 = tgen.gears["r1"] + + # Reduce the size of the buffer to hit the limit. + r1.vtysh_cmd("conf t\nzebra kernel netlink batch-tx-buf 256 256") + + r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 100") + json_file = "{}/r1/v4_route.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial(topotest.router_json_cmp, r1, "show ip route json", expected,) + _, result = topotest.run_and_expect(test_func, None, count=2, wait=0.5) + assertmsg = '"r1" JSON output mismatches' + assert result is None, assertmsg + + r1.vtysh_cmd("sharp remove routes 2.1.3.7 100") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf index 8845df5fc7..11f88e7101 100644 --- a/tools/etc/frr/support_bundle_commands.conf +++ b/tools/etc/frr/support_bundle_commands.conf @@ -38,8 +38,6 @@ PROC_NAME:zebra CMD_LIST_START show zebra show zebra client summary -show ip zebra route dump json -show ipv6 zebra route dump json show ip nht vrf all show route-map show memory diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 05410e3c25..8ffc313c04 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -116,7 +116,6 @@ class Vtysh(object): output = self('configure') if 'VTY configuration is locked by other VTY' in output: - print(output) log.error("vtysh 'configure' returned\n%s\n" % (output)) return False @@ -592,6 +591,7 @@ end line.startswith("vnc l2-group") or line.startswith("vnc nve-group") or line.startswith("peer") or + line.startswith("key ") or line.startswith("member pseudowire")): main_ctx_key = [] @@ -1203,7 +1203,11 @@ if __name__ == '__main__': group = parser.add_mutually_exclusive_group(required=True) group.add_argument('--reload', action='store_true', help='Apply the deltas', default=False) group.add_argument('--test', action='store_true', help='Show the deltas', default=False) - parser.add_argument('--debug', action='store_true', help='Enable debugs', default=False) + level_group = parser.add_mutually_exclusive_group() + level_group.add_argument('--debug', action='store_true', + help='Enable debugs (synonym for --log-level=debug)', default=False) + level_group.add_argument('--log-level', help='Log level', default="info", + choices=("critical", "error", "warning", "info", "debug")) parser.add_argument('--stdout', action='store_true', help='Log to STDOUT', default=False) parser.add_argument('--pathspace', '-N', metavar='NAME', help='Reload specified path/namespace', default=None) parser.add_argument('filename', help='Location of new frr config file') @@ -1220,8 +1224,7 @@ if __name__ == '__main__': # For --test log to stdout # For --reload log to /var/log/frr/frr-reload.log if args.test or args.stdout: - logging.basicConfig(level=logging.INFO, - format='%(asctime)s %(levelname)5s: %(message)s') + logging.basicConfig(format='%(asctime)s %(levelname)5s: %(message)s') # Color the errors and warnings in red logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) @@ -1232,7 +1235,6 @@ if __name__ == '__main__': os.makedirs('/var/log/frr/') logging.basicConfig(filename='/var/log/frr/frr-reload.log', - level=logging.INFO, format='%(asctime)s %(levelname)5s: %(message)s') # argparse should prevent this from happening but just to be safe... @@ -1240,45 +1242,49 @@ if __name__ == '__main__': raise Exception('Must specify --reload or --test') log = logging.getLogger(__name__) + if args.debug: + log.setLevel(logging.DEBUG) + else: + log.setLevel(args.log_level.upper()) + + if args.reload and not args.stdout: + # Additionally send errors and above to STDOUT, with no metadata, + # when we are logging to a file. This specifically does not follow + # args.log_level, and is analagous to behaviour in earlier versions + # which additionally logged most errors using print(). + + stdout_hdlr = logging.StreamHandler(sys.stdout) + stdout_hdlr.setLevel(logging.ERROR) + stdout_hdlr.setFormatter(logging.Formatter()) + log.addHandler(stdout_hdlr) + # Verify the new config file is valid if not os.path.isfile(args.filename): - msg = "Filename %s does not exist" % args.filename - print(msg) - log.error(msg) + log.error("Filename %s does not exist" % args.filename) sys.exit(1) if not os.path.getsize(args.filename): - msg = "Filename %s is an empty file" % args.filename - print(msg) - log.error(msg) + log.error("Filename %s is an empty file" % args.filename) sys.exit(1) # Verify that confdir is correct if not os.path.isdir(args.confdir): - msg = "Confdir %s is not a valid path" % args.confdir - print(msg) - log.error(msg) + log.error("Confdir %s is not a valid path" % args.confdir) sys.exit(1) # Verify that bindir is correct if not os.path.isdir(args.bindir) or not os.path.isfile(args.bindir + '/vtysh'): - msg = "Bindir %s is not a valid path to vtysh" % args.bindir - print(msg) - log.error(msg) + log.error("Bindir %s is not a valid path to vtysh" % args.bindir) sys.exit(1) # verify that the vty_socket, if specified, is valid if args.vty_socket and not os.path.isdir(args.vty_socket): - msg = 'vty_socket %s is not a valid path' % args.vty_socket - print(msg) - log.error(msg) + log.error('vty_socket %s is not a valid path' % args.vty_socket) sys.exit(1) # verify that the daemon, if specified, is valid if args.daemon and args.daemon not in ['zebra', 'bgpd', 'fabricd', 'isisd', 'ospf6d', 'ospfd', 'pbrd', 'pimd', 'ripd', 'ripngd', 'sharpd', 'staticd', 'vrrpd', 'ldpd']: - msg = "Daemon %s is not a valid option for 'show running-config'" % args.daemon - print(msg) - log.error(msg) + log.error("Daemon %s is not a valid option for 'show running-config'" % args.daemon) sys.exit(1) vtysh = Vtysh(args.bindir, args.confdir, args.vty_socket, args.pathspace) @@ -1300,14 +1306,9 @@ if __name__ == '__main__': break if not service_integrated_vtysh_config and not args.daemon: - msg = "'service integrated-vtysh-config' is not configured, this is required for 'service frr reload'" - print(msg) - log.error(msg) + log.error("'service integrated-vtysh-config' is not configured, this is required for 'service frr reload'") sys.exit(1) - if args.debug: - log.setLevel(logging.DEBUG) - log.info('Called via "%s"', str(args)) # Create a Config object from the config generated by newconf diff --git a/tools/gen_northbound_callbacks.c b/tools/gen_northbound_callbacks.c index 8dccbac3ae..eaab932228 100644 --- a/tools/gen_northbound_callbacks.c +++ b/tools/gen_northbound_callbacks.c @@ -194,7 +194,7 @@ static void generate_callback(const struct nb_callback_info *ncinfo, case NB_OP_MODIFY: case NB_OP_DESTROY: case NB_OP_MOVE: - printf("\tswitch (event) {\n" + printf("\tswitch (args->event) {\n" "\tcase NB_EV_VALIDATE:\n" "\tcase NB_EV_PREPARE:\n" "\tcase NB_EV_ABORT:\n" diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index ce274d4810..94f10757a2 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -114,6 +114,7 @@ struct quagga_signal_t vrrp_signals[] = { static const struct frr_yang_module_info *const vrrp_yang_modules[] = { &frr_filter_info, + &frr_vrf_info, &frr_interface_info, &frr_vrrpd_info, }; diff --git a/vrrpd/vrrp_northbound.c b/vrrpd/vrrp_northbound.c index e9cd714a95..f814963fe5 100644 --- a/vrrpd/vrrp_northbound.c +++ b/vrrpd/vrrp_northbound.c @@ -40,12 +40,31 @@ static int lib_interface_vrrp_vrrp_group_create(struct nb_cb_create_args *args) uint8_t version = 3; struct vrrp_vrouter *vr; - if (args->event != NB_EV_APPLY) + vrid = yang_dnode_get_uint8(args->dnode, "./virtual-router-id"); + version = yang_dnode_get_enum(args->dnode, "./version"); + + switch (args->event) { + case NB_EV_VALIDATE: + ifp = nb_running_get_entry(args->dnode, NULL, false); + if (ifp) { + vr = vrrp_lookup(ifp, vrid); + if (vr && vr->autoconf) { + snprintf( + args->errmsg, args->errmsg_len, + "Virtual Router with ID %d already exists on interface '%s'; created by VRRP autoconfiguration", + vrid, ifp->name); + return NB_ERR_VALIDATION; + } + } + return NB_OK; + case NB_EV_PREPARE: + case NB_EV_ABORT: return NB_OK; + case NB_EV_APPLY: + break; + } ifp = nb_running_get_entry(args->dnode, NULL, true); - vrid = yang_dnode_get_uint8(args->dnode, "./virtual-router-id"); - version = yang_dnode_get_enum(args->dnode, "./version"); vr = vrrp_vrouter_create(ifp, vrid, version); nb_running_set_entry(args->dnode, vr); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index d3fc8bc338..4edbb7a889 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1892,10 +1892,11 @@ DEFUNSH(VTYSH_LDPD, ldp_member_pseudowire_ifname, } #endif -DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, "router isis WORD", +DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, + "router isis WORD [vrf NAME]", ROUTER_STR "ISO IS-IS\n" - "ISO Routing area tag\n") + "ISO Routing area tag\n" VRF_CMD_HELP_STR) { vty->node = ISIS_NODE; return CMD_SUCCESS; diff --git a/yang/frr-bfdd.yang b/yang/frr-bfdd.yang index 13bad27b19..4b7785e690 100644 --- a/yang/frr-bfdd.yang +++ b/yang/frr-bfdd.yang @@ -185,6 +185,13 @@ module frr-bfdd { default true; description "Disables or enables the session administratively"; } + + leaf passive-mode { + type boolean; + default false; + description + "Don't attempt to start session establishment."; + } } grouping session-echo { @@ -204,6 +211,18 @@ module frr-bfdd { } } + grouping session-multi-hop { + description "BFD session multi hop settings."; + + leaf minimum-ttl { + type uint8 { + range 1..254; + } + description + "Minimum expected TTL on received packets."; + } + } + grouping session-states { /* * Local settings. @@ -363,6 +382,7 @@ module frr-bfdd { uses session-common; uses session-echo; + uses session-multi-hop; } container sessions { @@ -438,6 +458,7 @@ module frr-bfdd { } uses session-common; + uses session-multi-hop; container stats { uses session-states; diff --git a/yang/frr-bgp-bmp.yang b/yang/frr-bgp-bmp.yang new file mode 100644 index 0000000000..344448f104 --- /dev/null +++ b/yang/frr-bgp-bmp.yang @@ -0,0 +1,203 @@ +submodule frr-bgp-bmp { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + import ietf-inet-types { + prefix inet; + } + + import frr-bgp-types { + prefix frr-bt; + } + + include frr-bgp-common-multiprotocol; + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development + List: <mailto:dev@lists.frrouting.org>"; + description + "This submodule defines a model for managing FRR BGP BMP. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping bmp-incoming-session { + container incoming-session { + list session-list { + key "address tcp-port"; + leaf address { + type inet:ip-address; + description + "IPv4 address to listen on."; + } + + leaf tcp-port { + type uint32; + } + } + } + } + + grouping bmp-outgoing-session { + container outgoing-session { + list session-list { + key "hostname tcp-port"; + leaf hostname { + type string; + } + + leaf tcp-port { + type uint32; + } + + leaf min-retry-time { + type uint32 { + range "100..86400000"; + } + units "miliseconds"; + default "30000"; + description + "Minimum connection retry interval."; + } + + leaf max-retry-time { + type uint32 { + range "100..86400000"; + } + units "miliseconds"; + default "720000"; + description + "Maximum connection retry interval."; + } + } + } + } + + grouping bmp-afi-safis { + container afi-safis { + description + "List of address-families associated with the BGP + instance."; + list afi-safi { + key "afi-safi-name"; + description + "AFI, SAFI configuration available for the + neighbour or group."; + uses mp-afi-safi-config; + + uses mp-all-afi-safi-list-contents; + } + } + } + + grouping bmp-afi-safi-common-config { + container common-config { + leaf pre-policy { + type boolean; + default "false"; + description + "Send state before policy and filter processing."; + } + + leaf post-policy { + type boolean; + default "false"; + description + "Send state after policy and filter processing."; + } + } + } + + grouping global-bmp-config { + description + "Structural grouping used to include filter + configuration for BMP."; + container bmp-config { + description + "BMP related parameters."; + list target-list { + key "target-name"; + leaf target-name { + type string; + description + "Targets group name."; + } + + uses bmp-incoming-session; + + uses bmp-outgoing-session; + + leaf mirror { + type boolean; + default "false"; + description + "When set to 'TRUE' it send BMP route mirroring messages."; + } + + leaf stats-time { + type uint32 { + range "100..86400000"; + } + units "miliseconds"; + description + "Interval to send BMP Stats."; + } + + leaf ipv4-access-list { + type frr-bt:access-list-ref; + description + "Access list to restrict BMP sessions."; + } + + leaf ipv6-access-list { + type frr-bt:access-list-ref; + description + "Access list to restrict BMP sessions."; + } + + uses bmp-afi-safis; + } + + leaf mirror-buffer-limit { + type uint32 { + range "0..4294967294"; + } + units "bytes"; + description + "Maximum memory used for buffered mirroring messages."; + } + } + } +} diff --git a/yang/frr-bgp-common-multiprotocol.yang b/yang/frr-bgp-common-multiprotocol.yang new file mode 100644 index 0000000000..aefdf02ba6 --- /dev/null +++ b/yang/frr-bgp-common-multiprotocol.yang @@ -0,0 +1,209 @@ +submodule frr-bgp-common-multiprotocol { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + import frr-routing { + prefix frr-rt; + } + + include frr-bgp-common; + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development + List: <mailto:dev@lists.frrouting.org>"; + description + "This module contains general data definitions for use in BGP + Multiprotocol. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping mp-afi-safi-config { + description + "Configuration parameters used for all BGP AFI-SAFIs."; + leaf afi-safi-name { + type identityref { + base frr-rt:afi-safi-type; + } + description + "AFI, SAFI."; + } + } + + grouping mp-all-afi-safi-list-contents { + description + "A common grouping used for contents of the list that is used + for AFI-SAFI entries."; + container ipv4-unicast { + when "derived-from-or-self(../afi-safi-name, 'ipv4-unicast')" { + description + "Include this container for IPv4 Unicast specific + configuration."; + } + description + "IPv4 unicast configuration options."; + } + + container ipv6-unicast { + when "derived-from-or-self(../afi-safi-name, 'ipv6-unicast')" { + description + "Include this container for IPv6 Unicast specific + configuration."; + } + description + "IPv6 unicast configuration options."; + } + + container ipv4-labeled-unicast { + when "derived-from-or-self(../afi-safi-name, 'ipv4-labeled-unicast')" { + description + "Include this container for IPv4 Labeled Unicast specific + configuration."; + } + description + "IPv4 Labeled Unicast configuration options."; + } + + container ipv6-labeled-unicast { + when "derived-from-or-self(../afi-safi-name, 'ipv6-labeled-unicast')" { + description + "Include this container for IPv6 Labeled Unicast specific + configuration."; + } + description + "IPv6 Labeled Unicast configuration options."; + } + + container l3vpn-ipv4-unicast { + when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv4-unicast')" { + description + "Include this container for IPv4 Unicast L3VPN specific + configuration."; + } + description + "Unicast IPv4 L3VPN configuration options."; + } + + container l3vpn-ipv6-unicast { + when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv6-unicast')" { + description + "Include this container for unicast IPv6 L3VPN specific + configuration."; + } + description + "Unicast IPv6 L3VPN configuration options."; + } + + container l3vpn-ipv4-multicast { + when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv4-multicast')" { + description + "Include this container for multicast IPv4 L3VPN specific + configuration."; + } + description + "Multicast IPv4 L3VPN configuration options."; + } + + container l3vpn-ipv6-multicast { + when "derived-from-or-self(../afi-safi-name, 'l3vpn-ipv6-multicast')" { + description + "Include this container for multicast IPv6 L3VPN specific + configuration."; + } + description + "Multicast IPv6 L3VPN configuration options."; + } + + container l2vpn-vpls { + when "derived-from-or-self(../afi-safi-name, 'l2vpn-vpls')" { + description + "Include this container for BGP-signalled VPLS specific + configuration."; + } + description + "BGP-signalled VPLS configuration options."; + } + + container l2vpn-evpn { + when "derived-from-or-self(../afi-safi-name, 'l2vpn-evpn')" { + description + "Include this container for BGP EVPN specific + configuration."; + } + description + "BGP EVPN configuration options."; + } + + container ipv4-multicast { + when "derived-from-or-self(../afi-safi-name, 'ipv4-multicast')" { + description + "Include this container for IPv4 multicast specific + configuration."; + } + description + "IPv4 multicast configuration options."; + } + + container ipv6-multicast { + when "derived-from-or-self(../afi-safi-name, 'ipv6-multicast')" { + description + "Include this container for IPv6 multicast specific + configuration."; + } + description + "IPv6 multicast configuration options."; + } + + container ipv4-flowspec { + when "derived-from-or-self(../afi-safi-name, 'ipv4-flowspec')" { + description + "Include this container for IPv4 flowspec specific + configuration."; + } + description + "IPv4 flowspec configuration options."; + } + + container ipv6-flowspec { + when "derived-from-or-self(../afi-safi-name, 'ipv6-flowspec')" { + description + "Include this container for IPv6 flowspec specific + configuration."; + } + description + "IPv6 flowspec configuration options."; + } + } +} diff --git a/yang/frr-bgp-common-structure.yang b/yang/frr-bgp-common-structure.yang new file mode 100644 index 0000000000..8162527e90 --- /dev/null +++ b/yang/frr-bgp-common-structure.yang @@ -0,0 +1,807 @@ +submodule frr-bgp-common-structure { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + import ietf-inet-types { + prefix inet; + } + + import frr-interface { + prefix frr-interface; + } + + import ietf-bgp-types { + prefix bt; + } + + import frr-bgp-types { + prefix frr-bt; + } + + include frr-bgp-common; + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development + List: <mailto:dev@lists.frrouting.org>"; + description + "This submodule contains general data definitions for use in BGP. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping structure-neighbor-group-ebgp-multihop { + description + "Structural grouping used to include EBGP multi-hop + configuration for both BGP neighbors and peer groups."; + container ebgp-multihop { + description + "EBGP multi-hop parameters for the BGP group."; + choice hop-count-choice { + case default-hop-count { + leaf enabled { + type boolean; + default "false"; + description + "When enabled the referenced group or neighbors are + permitted to be indirectly connected - including cases + where the TTL can be decremented between the BGP peers."; + } + } + + case max-hop-count { + leaf multihop-ttl { + type uint8 { + range "1..255"; + } + description + "Time-to-live value to use when packets are sent to the + referenced group or neighbors and ebgp-multihop is + enabled."; + } + } + } + + leaf disable-connected-check { + type boolean; + default "false"; + description + "When set to 'true' it enforces EBGP neighbors perform multihop."; + } + } + } + + grouping neighbor-local-as-options { + container local-as { + leaf local-as { + type inet:as-number; + mandatory true; + description + "The local autonomous system number that is to be used when + establishing sessions with the remote peer or peer group, if + this differs from the global BGP router autonomous system + number."; + } + + leaf no-prepend { + when "../local-as != 0"; + type boolean; + default "false"; + description + "Do not prepend local-as to updates from EBGP peers. When + set to 'true' it will not prepend local-as to updates. When + set to 'false' it will prepend local-as to updates."; + } + + leaf no-replace-as { + type boolean; + default "false"; + description + "Do not prepend local-as to updates from IBGP peers."; + } + } + } + + grouping neighbor-bfd-options { + container bfd-options { + leaf enable { + type boolean; + default "false"; + description + "BFD support."; + } + + leaf detect-multiplier { + when "../enable = 'true'"; + type uint8 { + range "2..255"; + } + default "3"; + description + "Detect multiplier."; + } + + leaf required-min-rx { + when "../enable = 'true'"; + type uint16 { + range "50..60000"; + } + units "miliseconds"; + default "300"; + description + "Required min receive interval."; + } + + leaf desired-min-tx { + when "../enable = 'true'"; + type uint16 { + range "50..60000"; + } + units "miliseconds"; + default "300"; + description + "Desired min transmit interval."; + } + + leaf session-type { + when "../enable = 'true'"; + type frr-bt:bfd-session-type; + default "not-configured"; + description + "BFD session type."; + } + + leaf check-cp-failure { + when "../enable = 'true'"; + type boolean; + default "false"; + description + "Link dataplane status with BGP control plane."; + } + } + } + + grouping neighbor-remote-as { + container neighbor-remote-as { + leaf remote-as-type { + type frr-bt:as-type; + mandatory true; + description + "Remote AS type."; + } + + leaf remote-as { + when "../remote-as-type = 'as-specified'"; + type inet:as-number; + description + "The remote autonomous system number received in + the BGP OPEN message."; + reference + "RFC 4271"; + } + } + } + + grouping neighbor-update-source { + description + "Source of routing updates."; + container update-source { + description + "Source of routing updates config."; + choice source { + case ip-based { + leaf ip { + type inet:ip-address; + description + "IPv4 address/IPv6 address."; + } + } + + case interface-based { + leaf interface { + type frr-interface:interface-ref { + require-instance false; + } + description + "The local interface."; + } + } + } + } + } + + grouping structure-neighbor-group-add-paths { + description + "Structural grouping used to include ADD-PATHs configuration + and state for both BGP neighbors and peer groups."; + container add-paths { + description + "Parameters relating to the advertisement and receipt of + multiple paths for a single NLRI (add-paths)."; + reference + "RFC 7911: ADD-PATH."; + leaf path-type { + type frr-bt:add-path-type; + default "none"; + description + "Enable ability to receive multiple path advertisements for + an NLRI from the neighbor or group."; + } + } + } + + grouping structure-neighbor-group-as-path-options { + description + "Structural grouping used to include AS_PATH manipulation + configuration both BGP neighbors and peer groups."; + container as-path-options { + description + "AS_PATH manipulation parameters for the BGP neighbor or + group."; + choice allowas-in { + case occurence-based { + leaf allow-own-as { + type uint8 { + range "1..10"; + } + description + "Specify the number of occurrences of the local BGP + speaker's AS that can occur within the AS_PATH before it + is rejected."; + } + } + + case origin-based { + leaf allow-own-origin-as { + type boolean; + default "false"; + description + "When set to 'true' only accept my AS in the as-path + if the route was originated in my AS."; + } + } + } + + leaf replace-peer-as { + type boolean; + default "false"; + description + "Replace occurrences of the peer's AS in the AS_PATH with + the local autonomous system number. This is same as override + ASN CLI."; + } + } + } + + grouping structure-neighbor-group-capability-options { + description + "Structural grouping used to include capability + configuration for both BGP neighbors and peer groups."; + container capability-options { + description + "Capability manipulation parameters for the BGP neighbor or + group."; + leaf dynamic-capability { + type boolean; + default "false"; + description + "When set to 'true' dynamic capability is advertise to this peer."; + } + + leaf strict-capability { + type boolean; + default "false"; + description + "Strict capability negotiation match. When set to 'true' + remote and local capabilities are strictly compared + if capabilities are different, send Unsupported Capability + error then reset connection."; + } + + leaf extended-nexthop-capability { + type boolean; + default "false"; + description + "When set to 'true' extended next-hop capability is advertise + to this peer."; + } + + leaf capability-negotiate { + type boolean; + default "true"; + description + "When set to 'true' sending Capability Negotiation in the open + message is supressed to this peer."; + } + + leaf override-capability { + type boolean; + default "false"; + description + "Overrides the result of Capability Negotiation, ignoring remote + peer's capability value, when set to 'true'."; + } + } + } + + grouping structure-neighbor-default-originate-options { + description + "Structural grouping used to include default-originate + configuration for both BGP neighbors and peer groups."; + container default-originate-options { + description + "default originate parameters for the BGP neighbor or + group."; + leaf send-default-route { + type boolean; + default "false"; + description + "If set to 'true', send the default-route to the neighbour(s)."; + } + + leaf rmap-policy-export { + type frr-bt:rmap-ref; + description + "Route-map to specify criteria to originate default."; + } + } + } + + grouping structure-neighbor-prefix-limit { + container prefix-limit { + description + "Parameters relating to the prefix limit for the AFI-SAFI."; + list direction-list { + key "direction"; + leaf direction { + type frr-bt:direction; + description + "Prefix limit applied on Tx route-updates or Rx route-updates."; + } + + leaf max-prefixes { + type uint32; + mandatory true; + description + "Maximum number of prefixes that will be accepted from the + neighbour."; + } + + container prefix-limit-options { + when "../direction = 'in'"; + choice options { + case warning { + leaf warning-only { + type boolean; + default "false"; + description + "When set to 'true' only give warning message when limit + is exceeded."; + } + } + + case restart { + leaf restart-timer { + type uint16; + units "minutes"; + description + "Time interval in seconds after which the BGP session is + re-established after being torn down due to exceeding the + max-prefix limit."; + } + } + + case threshold { + leaf shutdown-threshold-pct { + type bt:percentage; + description + "Threshold on number of prefixes that can be received from + a neighbour before generation of warning messages or log + entries. Expressed as a percentage of max-prefixes."; + } + } + + case threshold-restart { + leaf tr-shutdown-threshold-pct { + type bt:percentage; + description + "Threshold on number of prefixes that can be received from + a neighbour before generation of warning messages or log + entries. Expressed as a percentage of max-prefixes."; + } + + leaf tr-restart-timer { + type uint32; + units "minutes"; + description + "Time interval in seconds after which the BGP session is + re-established after being torn down due to exceeding the + max-prefix limit."; + } + } + + case threshold-warning { + leaf tw-shutdown-threshold-pct { + type bt:percentage; + description + "Threshold on number of prefixes that can be received from + a neighbour before generation of warning messages or log + entries. Expressed as a percentage of max-prefixes."; + } + + leaf tw-warning-only { + type boolean; + default "false"; + description + "When set to 'true' only give warning message when limit + is exceeded."; + } + } + } + } + } + } + } + + grouping structure-neighbor-nexthop-self { + container nexthop-self { + description + "Parameters relating to the nexthop-self for the AFI-SAFI."; + leaf next-hop-self { + type boolean; + default "false"; + description + "When set to 'true', EBGP learned routes are announced with the + local speaker's nexthop."; + } + + leaf next-hop-self-force { + type boolean; + default "false"; + description + "When set to 'true', EBGP learned routes are announced with the + local speaker's nexthop."; + } + } + } + + grouping structure-neighbor-private-as { + container private-as { + description + "Parameters relating to the private-as for the AFI-SAFI."; + leaf remove-private-as-all { + type boolean; + default "false"; + description + "When set to 'true', private ASNs are removed from outbound updates; + applies to all AS numbers."; + } + + leaf remove-private-as-all-replace { + type boolean; + default "false"; + description + "When set to 'true', private ASNs are replaced by the local + speaker's ASN in all outbound updates; applies to all AS numbers."; + } + + leaf remove-private-as { + type boolean; + default "false"; + description + "When set to 'true', removes private ASNs in outbound updates; + applies to all AS numbers."; + } + + leaf remove-private-as-replace { + type boolean; + default "false"; + description + "When set to 'true', private ASNs are replaced with the local + speaker's ASN in all outbound updates; applies to all AS numbers."; + } + } + } + + grouping structure-neighbor-weight { + container weight { + description + "Parameters relating to the weight for the AFI-SAFI."; + leaf weight-attribute { + type uint16 { + range "0..65535"; + } + description + "Set default weight for routes from this neighbor."; + } + } + } + + grouping structure-neighbor-route-reflector { + container route-reflector { + description + "Parameters relating to the route-reflector for the AFI-SAFI."; + leaf route-reflector-client { + type boolean; + default "false"; + description + "Configure a neighbor as route reflector client."; + } + } + } + + grouping structure-neighbor-route-server { + container route-server { + description + "Parameters relating to the route-server for the AFI-SAFI."; + leaf route-server-client { + type boolean; + default "false"; + description + "Configure a neighbor as route server client."; + } + } + } + + grouping structure-neighbor-send-community { + container send-community { + description + "Parameters relating to the send-community for the AFI-SAFI."; + leaf send-community { + type boolean; + default "true"; + description + "Send standard community attribute to this neighbor."; + } + + leaf send-ext-community { + type boolean; + default "true"; + description + "Send extended community attribute to this neighbor."; + } + + leaf send-large-community { + type boolean; + default "false"; + description + "Send large community attribute to this neighbor."; + } + } + } + + grouping structure-neighbor-group-admin-shutdown { + description + "Structural grouping used to include admin-shutdown + configuration for both BGP neighbors and peer groups."; + container admin-shutdown { + description + "BGP Administrative Shutdown Communication."; + leaf enable { + type boolean; + mandatory true; + description + "When set to 'true', BGP shutdown communication is enabled."; + } + + leaf message { + type string; + description + "Shutdown message."; + reference + "draft-ietf-idr-shutdown-06"; + } + } + } + + grouping structure-neighbor-group-graceful-restart { + description + "Structural grouping used to include graceful-restart + configuration for both BGP neighbors and peer groups."; + container graceful-restart { + description + "BGP Graceful restart feature."; + choice mode { + case graceful-restart-mode { + leaf enable { + type boolean; + default "false"; + description + "Enable or disable the graceful-restart capability. + Setting this value to 'true' enables the graceful-restart + and helper both at peer level. Setting this value to 'false' + disables graceful restart and helper mode. The peer will inherit + global configuration."; + } + } + + case graceful-restart-helper-mode { + leaf graceful-restart-helper { + type boolean; + default "false"; + description + "Setting this value to 'true' enables helper mode for the peer + Setting this value to 'false' disables the helper mode. The + peer will inherit global configuration."; + } + } + + case graceful-restart-disable-mode { + leaf graceful-restart-disable { + type boolean; + default "false"; + description + "Setting this value to 'true' disables the graceful-restart + and helper Mode. Setting this value to 'false' causes the peer + to inherit global configuration."; + } + } + } + } + } + + grouping structure-neighbor-group-soft-reconfiguration { + description + "Structural grouping used to include soft-reconfiguration + configuration for both BGP neighbors and peer groups."; + leaf soft-reconfiguration { + type boolean; + default "false"; + description + "Allow inbound soft reconfiguration for this neighbor."; + } + } + + grouping structure-neighbor-group-attr-unchanged { + description + "Structural grouping used to include BGP route propagation + rules configuration for both BGP neighbors and peer groups."; + container attr-unchanged { + description + "BGP route propagation rules configuration."; + leaf as-path-unchanged { + type boolean; + default "false"; + description + "When set to 'true' as-path attribute is propagated unchanged."; + } + + leaf next-hop-unchanged { + type boolean; + default "false"; + description + "When set to 'true' next-hop attribute is propagated unchanged."; + } + + leaf med-unchanged { + type boolean; + default "false"; + description + "When set to 'true' med attribute is propagated unchanged."; + } + } + } + + grouping structure-neighbor-group-orf-capability { + description + "Structural grouping used to include orf + configuration for both BGP neighbors and peer groups."; + container orf-capability { + choice orf-update { + case send { + leaf orf-send { + type boolean; + default "false"; + description + "Setting to 'true' advertises the ORF capability."; + } + } + + case receive { + leaf orf-receive { + type boolean; + default "false"; + description + "When set to 'true' it receives the orf capability."; + } + } + + case both { + leaf orf-both { + type boolean; + default "false"; + description + "When set to 'true' it advertises/receives the orf capability."; + } + } + } + } + } + + grouping structure-neighbor-config-timers { + description + "Structural grouping used to include per neighbor timers + configuration for both BGP neighbors and peer groups."; + container timers { + leaf advertise-interval { + type uint16 { + range "0..600"; + } + units "seconds"; + description + "Minimum interval between sending BGP routing updates."; + } + + leaf connect-time { + type uint16 { + range "1..65535"; + } + units "seconds"; + description + "BGP connect timer."; + } + + uses neighbor-timers; + } + } + + grouping structure-neighbor-group-filter-config { + description + "Structural grouping used to include filter + configuration for both BGP neighbors and peer groups."; + container filter-config { + description + "BGP Policy configuration for both BGP neighbors and groups."; + uses rmap-policy-import; + + uses rmap-policy-export; + + uses plist-policy-import; + + uses plist-policy-export; + + uses access-list-policy-import; + + uses access-list-policy-export; + + uses as-path-filter-list-policy-import; + + uses as-path-filter-list-policy-export; + + uses unsupress-map-policy-import; + + uses unsupress-map-policy-export; + } + } +} diff --git a/yang/frr-bgp-common.yang b/yang/frr-bgp-common.yang new file mode 100644 index 0000000000..188cf856db --- /dev/null +++ b/yang/frr-bgp-common.yang @@ -0,0 +1,1108 @@ +submodule frr-bgp-common { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + import ietf-bgp-types { + prefix bt; + } + + import ietf-inet-types { + prefix inet; + } + + import frr-bgp-types { + prefix frr-bt; + } + + import frr-route-types { + prefix frr-rt-type; + } + + import frr-vrf { + prefix frr-vrf; + } + + import ietf-routing-types { + prefix rt-types; + } + + import frr-interface { + prefix frr-interface; + } + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development + List: <mailto:dev@lists.frrouting.org>"; + description + "This submodule contains general data definitions for use in BGP. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping rmap-policy-import { + leaf rmap-import { + type frr-bt:rmap-ref; + } + } + + grouping rmap-policy-export { + leaf rmap-export { + type frr-bt:rmap-ref; + } + } + + grouping unsupress-map-policy-import { + leaf unsupress-map-import { + type frr-bt:rmap-ref; + } + } + + grouping unsupress-map-policy-export { + leaf unsupress-map-export { + type frr-bt:rmap-ref; + } + } + + grouping plist-policy-import { + leaf plist-import { + type frr-bt:plist-ref; + } + } + + grouping plist-policy-export { + leaf plist-export { + type frr-bt:plist-ref; + } + } + + grouping access-list-policy-import { + leaf access-list-import { + type frr-bt:access-list-ref; + } + } + + grouping access-list-policy-export { + leaf access-list-export { + type frr-bt:access-list-ref; + } + } + + grouping as-path-filter-list-policy-import { + leaf as-path-filter-list-import { + type frr-bt:as-path-filter-ref; + } + } + + grouping as-path-filter-list-policy-export { + leaf as-path-filter-list-export { + type frr-bt:as-path-filter-ref; + } + } + + grouping route-selection-options { + description + "Configuration relating to route selection options."; + container route-selection-options { + description + "Parameters relating to options for route selection."; + leaf always-compare-med { + type boolean; + default "false"; + description + "Compare multi-exit discriminator (MED) value from + different ASes when selecting the best route. The + default behaviour is to only compare MEDs for paths + received from the same AS."; + } + + leaf deterministic-med { + type boolean; + default "false"; + description + "Compare multi-exit discriminator (MED) value from + Same ASes when selecting the best route."; + } + + leaf confed-med { + type boolean; + default "false"; + description + "Compare multi-exit discriminator (MED) value from + different Sub ASes when selecting the best route."; + } + + leaf missing-as-worst-med { + type boolean; + default "false"; + description + "Missing multi-exit discriminator (MED) value is + considered as value of infinity, making the path + least desirable when selecting the best route."; + } + + leaf aspath-confed { + type boolean; + default "false"; + description + "Compare path lengths including confederation sets + and sequences in selecting a route."; + } + + leaf ignore-as-path-length { + type boolean; + default "false"; + description + "Ignore the AS path length when selecting the best path. + The default is to use the AS path length and prefer paths + with shorter length."; + } + + leaf external-compare-router-id { + type boolean; + default "true"; + description + "When comparing similar routes received from external BGP + peers, use the router-id as a criterion to select the + active path."; + } + + leaf allow-multiple-as { + type boolean; + default "false"; + description + "Allow multi-path to use paths from different neighbouring + ASes. The default is to only consider multiple paths + from the same neighbouring AS."; + } + + leaf multi-path-as-set { + when "../allow-multiple-as = 'true'"; + type boolean; + default "false"; + description + "Multi-path with AS-SET, When set to 'true' it adds AS set + information for aggregate routes, When set to 'false' it + prevents AS set generation."; + } + } + } + + grouping med-config { + description + "Configuration relating to MED."; + container med-config { + leaf enable-med-admin { + type boolean; + default "false"; + description + "Flag to enable receiving of MED metric attribute + in routing updates."; + } + leaf max-med-admin { + type uint32 { + range "0..4294967295"; + } + default "4294967294"; + description + "Tells the router to announce routes with this MED value + This MED value is applicable for indefinite time."; + } + + leaf max-med-onstart-up-time { + type uint32 { + range "5..86400"; + } + units "seconds"; + description + "Tells the router to announce routes with MED value, + This MED value is applicable for this duration during + start-up."; + } + + leaf max-med-onstart-up-value { + type uint32 { + range "0..4294967295"; + } + default "4294967294"; + description + "Tells the router to announce routes with this MED value + This MED value is applicable for start-up time."; + } + } + } + + grouping route-reflector-config { + description + "Grouping used to include route reflector + configuration for BGP global."; + container route-reflector { + description + "Route reflector parameters for the BGP global."; + leaf route-reflector-cluster-id { + type bt:rr-cluster-id-type; + description + "Route Reflector cluster ID to use when local router is + configured as a route reflector. Commonly set at the + group level, but allows a different cluster ID to be set + for each neighbor."; + reference + "RFC 4456: BGP Route Reflection: An Alternative to + Full Mesh."; + } + + leaf no-client-reflect { + type boolean; + default "false"; + description + "When set to 'true', this disables route redistribution + by the Route Reflector. It is set 'true' when the client is + fully meshed to prevent sending of redundant route + advertisements."; + reference + "TODO: Add reference when IETF writes a draft describing + this."; + } + + leaf allow-outbound-policy { + type boolean; + default "false"; + description + "When set to 'true', this allow RR to modify the attributes of the + reflected IBGP routes as per the out route-map. It is set 'false' + RR will not modify the attributes of the reflected IBGP routes as + per the out route-map rules."; + } + } + } + + grouping global-bgp-config { + leaf instance-type-view { + type boolean; + default "false"; + description + "When set to 'true' BGP instance type is view. + When set to 'false' BGP instance type is regular."; + } + + leaf ebgp-multihop-connected-route-check { + type boolean; + default "false"; + description + "When set to 'true' it will disable checking if nexthop is connected + on EBGP sessions. When set to 'false' it will enable checking if + nexthop is connected on EBGP sessions."; + } + + leaf fast-external-failover { + type boolean; + default "true"; + description + "It's an interface tracking mechanism. When set to 'true' don't + immediately reset session if a link to a directly connected + external peer goes down. When set to 'false' it will immediately + reset session if a link to a directly connected external peer goes down."; + } + + leaf local-pref { + type uint32; + default "100"; + description + "BGP local preference attribute sent to internal peers to + indicate the degree of preference for externally learned + routes. The route with the highest local preference + value is preferred."; + reference + "RFC 4271."; + } + + leaf default-shutdown { + type boolean; + default "false"; + description + "Apply administrative shutdown to newly configured peers."; + } + + leaf ebgp-requires-policy { + type boolean; + default "true"; + description + "When set to 'true' BGP speaker requires in and out policy + for EBGP peers (RFC8212)."; + } + + leaf show-hostname { + type boolean; + default "false"; + description + "When set to 'true' BGP show hostname in certain command outputs."; + } + + leaf show-nexthop-hostname { + type boolean; + default "false"; + description + "When set to 'true' BGP show hostname for nexthop in certain + command outputs."; + } + } + + grouping global-neighbor-config { + container global-neighbor-config { + leaf dynamic-neighbors-limit { + type uint32 { + range "1..5000"; + } + description + "Maximum number of BGP Dynamic Neighbors that can be created."; + } + + leaf log-neighbor-changes { + type boolean; + default "false"; + description + "When set to 'true' BGP will start logging neighbor up/down and reset reason. + When set to 'false' BGP will stop logging neighbor up/down and reset reason."; + } + + container packet-quanta-config { + leaf wpkt-quanta { + type uint32 { + range "1..64"; + } + default "64"; + description + "How many packets to write to peer socket per run."; + } + + leaf rpkt-quanta { + type uint32 { + range "1..10"; + } + default "10"; + description + "How many packets to read from peer socket per I/O cycle."; + } + } + } + } + + grouping global-update-group-config { + container global-update-group-config { + leaf subgroup-pkt-queue-size { + type uint32 { + range "20..100"; + } + default "40"; + description + "Subgroup packet queue size."; + } + + leaf coalesce-time { + type uint32 { + range "0..4294967295"; + } + units "miliseconds"; + default "1000"; + description + "Configures the Subgroup coalesce timer."; + } + } + } + + grouping global-network-config { + leaf import-check { + type boolean; + default "false"; + description + "When set to 'true' bgp creates entries for network statements + if a matching prefix exists in the rib. When set to 'false' bgp + creates entries for networks that the router cannot reach."; + } + } + + grouping neighbor-timers { + leaf hold-time { + type uint16 { + range "0 | 3..65535"; + } + units "seconds"; + default "180"; + description + "Time interval (in seconds) for the HoldTimer established + with the peer. When read as operational data (ro), the + value of this object is calculated by this BGP speaker, + using the smaller of the values in hold-time that was + configured (rw) in the running datastore and the Hold Time + received in the OPEN message. + + This value must be at least three seconds + if it is not zero (0). + + If the Hold Timer has not been established + with the peer this object MUST have a value + of zero (0). + + If the configured value of hold-time object was + a value of (0), then when read this object MUST have a + value of (0) also."; + reference + "RFC 4271"; + } + + leaf keepalive { + type uint16 { + range "0..65535"; + } + units "seconds"; + default "60"; + description + "When used as a configuration (rw) value, this Time interval + (in seconds) for the KeepAlive timer configured for this BGP + speaker with this peer. The value of this object will only + determine the KEEPALIVE messages' frequency relative to + the value specified in configured value for hold-time. + + If the value of this object is zero (0), no periodical + KEEPALIVE messages are sent to the peer after the BGP + connection has been established. The suggested value for + this timer is 30 seconds.; + + The actual time interval for the KEEPALIVE messages is + indicated by operational value of keepalive. That value + of this object is calculated by this BGP speaker such that, + when compared with hold-time, it has the same proportion + that keepalive has, compared with hold-time. A + reasonable maximum value for this timer would be one third + of that of hold-time."; + reference + "RFC 4271"; + } + } + + grouping global-config-timers { + container global-config-timers { + leaf rmap-delay-time { + type uint16 { + range "0..600"; + } + units "seconds"; + default "5"; + description + "Time to wait before processing route-map changes."; + } + + leaf update-delay-time { + type uint16 { + range "0..3600"; + } + units "seconds"; + description + "Time to force initial delay for best-path and updates."; + } + + leaf establish-wait-time { + type uint16 { + range "1..3600"; + } + units "seconds"; + description + "Time to force initial delay for updates."; + } + + leaf connect-retry-interval { + type uint16 { + range "1..max"; + } + units "seconds"; + default "120"; + description + "Time interval (in seconds) for the ConnectRetryTimer. The + suggested value for this timer is 120 seconds."; + reference + "RFC 4271, This is the value used + to initialize the 'ConnectRetryTimer'."; + } + + uses neighbor-timers; + } + } + + grouping graceful-restart-config { + description + "Configuration parameters relating to BGP graceful restart."; + choice mode { + case graceful-restart-mode { + leaf enabled { + type boolean; + default "false"; + description + "Enable or disable the graceful-restart capability. When set to 'true' + it will enable graceful restart and helper both globally. When set + to 'false' it will enable the default behaviour global helper mode."; + } + } + + case graceful-restart-disable-mode { + leaf graceful-restart-disable { + type boolean; + default "false"; + description + "When set to 'true' it will disable graceful restart and helper both + globally. when set to 'false' it will enable the default behaviour + global helper mode."; + } + } + } + + leaf preserve-fw-entry { + type boolean; + default "false"; + description + "Sets F-bit indication that fib is preserved while doing Graceful Restart. + When set to 'true' Zebra would preserve the FIB entry on the restarting + node."; + } + + leaf restart-time { + type uint16 { + range "1..3600"; + } + units "seconds"; + default "120"; + description + "Estimated time (in seconds) for the local BGP speaker to + restart a session. This value is advertise in the graceful + restart BGP capability. This is a 12-bit value, referred to + as Restart Time in RFC4724. Per RFC4724, the suggested + default value is <= the hold-time value. This timer is + applicable for helper node."; + reference + "RFC 4724: Graceful Restart Mechanism for BGP."; + } + + leaf stale-routes-time { + type uint16 { + range "1..3600"; + } + units "seconds"; + default "360"; + description + "An upper-bound on the time that stale routes will be + retained by a router after a session is restarted. If an + End-of-RIB (EOR) marker is received prior to this timer + expiring stale-routes will be flushed upon its receipt - if + no EOR is received, then when this timer expires stale paths + will be purged. This timer is applicable for restarting node."; + reference + "RFC 4724: Graceful Restart Mechanism for BGP."; + } + + leaf selection-deferral-time { + type uint16 { + range "0..3600"; + } + units "seconds"; + default "360"; + description + "An upper-bound on the time that restarting router defers + the route selection process after restart."; + reference + "RFC 4724: Graceful Restart Mechanism for BGP."; + } + + leaf rib-stale-time { + type uint16 { + range "1..3600"; + } + units "seconds"; + default "500"; + description + "An upper-bound on the time that helper router holds the + stale routes in Zebra, When this timer gets expired Zebra + removes the stale routes."; + } + } + + grouping global-group-use-multiple-paths { + description + "Common grouping used for both global and groups which provides + configuration parameters relating to use of multiple paths."; + container use-multiple-paths { + description + "Parameters related to the use of multiple paths for the + same NLRI."; + container ebgp { + description + "Multi-Path parameters for EBGP."; + leaf maximum-paths { + type uint16; + default "64"; + description + "Maximum number of parallel paths to consider when using + BGP multi-path. The default is use a single path."; + } + } + + container ibgp { + description + "Multi-Path parameters for IBGP."; + leaf maximum-paths { + type uint16; + default "64"; + description + "Maximum number of parallel paths to consider when using + IBGP multi-path. The default is to use a single path."; + } + + leaf cluster-length-list { + when "../maximum-paths != 0"; + type boolean; + default "false"; + description + "When set to 'true' route with the shortest cluster-list + length is used. The cluster-list reflects the IBGP + reflection path the route has taken. It's the part + of route selection algo."; + } + } + } + } + + grouping global-redistribute { + description + "List of route redistribution per AFI."; + list redistribution-list { + key "route-type route-instance"; + leaf route-type { + type frr-rt-type:frr-route-types; + description + "Protocol route type."; + } + + leaf route-instance { + type uint16 { + range "0..65535"; + } + description + "Protocol Instance."; + } + + leaf metric { + type uint32 { + range "0..4294967295"; + } + description + "Metric for redistributed routes."; + } + + leaf rmap-policy-import { + type frr-bt:rmap-ref; + description + "Route-map to be applied for redistributed routes into the bgp."; + } + } + } + + grouping mp-afi-safi-network-config { + leaf label-index { + type rt-types:mpls-label; + description + "Label index to associate with the prefix."; + } + + leaf rmap-policy-export { + type frr-bt:rmap-ref; + description + "Route-map to modify the attributes for Routes going out + via BGP updates."; + } + } + + grouping mp-afi-safi-agg-route-config { + leaf as-set { + type boolean; + default "false"; + description + "When set to 'true' AS set path information is generated + for aggregate address. When set to 'false' AS set path + information is not generated."; + } + + leaf summary-only { + type boolean; + default "false"; + description + "When set to 'true' it advertise only the aggregate route + and suppress the advertisement of all the component routes. + When set to 'false' all more-specific routes summarized + by the aggregate route are advertised."; + } + + leaf rmap-policy-export { + type frr-bt:rmap-ref; + description + "Apply route map to aggregate network."; + } + } + + grouping admin-distance { + container admin-distance { + description + "Administrative distance (or preference) assigned to + routes received from different sources + (external, internal, and local)."; + leaf external { + type uint8 { + range "1..255"; + } + description + "Administrative distance for routes learned from + external BGP (EBGP)."; + } + + leaf internal { + type uint8 { + range "1..255"; + } + description + "Administrative distance for routes learned from + internal BGP (IBGP)."; + } + + leaf local { + type uint8 { + range "1..255"; + } + description + "Administrative distance for routes learned from + local."; + } + } + } + + grouping distance-per-route-config { + leaf distance { + type uint8 { + range "1..255"; + } + mandatory true; + description + "Administrative distance for route."; + } + + leaf access-list-policy-export { + type frr-bt:access-list-ref; + description + "Access-list policy applied on routes going from BGP to Zebra."; + } + } + + grouping route-flap-dampening { + container route-flap-dampening { + description + "Dampening feature"; + leaf enable { + type boolean; + default "false"; + description + "Enable route flap dampening."; + } + + leaf reach-decay { + when "../enable = 'true'"; + type uint8 { + range "1..45"; + } + units "seconds"; + default "15"; + description + "This value specifies the time desired for the instability + metric value to reach one-half of its current value when + the route is reachable. This half-life value determines + the rate at which the metric value is decayed. A smaller + half-life value makes a suppressed route reusable sooner + than a larger value. The accumulated penalty will be reduced + to half after this duration."; + } + + leaf reuse-above { + when "../enable = 'true'"; + type uint16 { + range "1..20000"; + } + default "750"; + description + "This is the value of the instability metric at which a + suppressed route becomes unsuppressed if it is reachable + but currently suppressed. The value assigned to + reuse-below must be less than suppress-above."; + } + + leaf suppress-above { + when "../enable = 'true'"; + type uint16 { + range "1..20000"; + } + default "2000"; + description + "This is the value of the instability metric at which + route suppression takes place. A route is not installed + in the forwarding information base (FIB), or announced + even if it is reachable during the period that it is + suppressed."; + } + + leaf unreach-decay { + when "../enable = 'true'"; + type uint8 { + range "1..255"; + } + units "seconds"; + default "60"; + description + "This value acts the same as reach-decay except that it + specifies the rate at which the instability metric is + decayed when a route is unreachable. It should have a + value greater than or equal to reach-decay."; + } + } + } + + grouping flow-spec-config { + container flow-spec-config { + description + "Flow spec feature."; + leaf interface { + type frr-interface:interface-ref { + require-instance false; + } + description + "The local interface."; + } + } + } + + grouping global-graceful-shutdown { + description + "Structural grouping used to include graceful-shutdown + configuration for both BGP neighbors and peer groups."; + container graceful-shutdown { + description + "BGP Graceful shutdown feature."; + leaf enable { + type boolean; + default "false"; + description + "Enable graceful-shutdown feature."; + } + } + } + + grouping global-filter-config { + description + "Structural grouping used to include filter + configuration for BGP RIB table."; + container filter-config { + description + "BGP table to RIB route download filter."; + uses rmap-policy-export; + } + } + + grouping route-distinguisher-params { + description + "Route distinguisher value as per RFC4364."; + leaf rd { + type rt-types:route-distinguisher; + description + "Route distinguisher value as per RFC4364."; + } + } + + grouping vpn-label-params { + description + "Label value for VRF."; + choice label-allocation-mode { + case manual { + leaf label { + type rt-types:mpls-label; + description + "Label index to associate with the prefix."; + } + } + + case auto { + leaf label-auto { + type boolean; + default "false"; + description + "Automatically assign a label."; + } + } + } + } + + grouping vpn-nexthop-params { + description + "Specify next hop to use for VRF advertised prefixes."; + leaf nexthop { + type inet:ip-address; + description + "Nexthop IP address."; + } + } + + grouping rt-list { + description + "Route Target list"; + leaf-list import-rt-list { + type rt-types:route-target; + description + "For routes leaked from vpn to current address-family: match any."; + } + + leaf-list export-rt-list { + type rt-types:route-target; + description + "For routes leaked from current address-family to vpn: set."; + } + } + + grouping vpn-route-target-params { + description + "Route Target value."; + leaf redirect-rt { + type rt-types:route-target; + description + "Flow-spec redirect type route target."; + } + + choice rt-direction { + case import-export { + uses rt-list; + } + case both { + leaf-list rt-list { + type rt-types:route-target; + description + "Both import: match any and export: set."; + } + } + } + } + + grouping vpn-import-params { + description + "VPN route leaking parameters."; + leaf import-vpn { + type boolean; + default "false"; + description + "Import routes from default instance VPN RIB."; + } + + leaf export-vpn { + type boolean; + default "false"; + description + "Export routes to default instance VPN RIB."; + } + + list import-vrf-list { + key "vrf"; + description + "List of VRFs to import routes from."; + leaf vrf { + type frr-vrf:vrf-ref { + require-instance false; + } + description + "Routing instance."; + } + } + + uses rmap-policy-import; + + uses rmap-policy-export; + } + + grouping global-afi-safi-vpn-config { + container vpn-config { + uses route-distinguisher-params; + + uses vpn-label-params; + + uses vpn-nexthop-params; + + uses vpn-import-params; + + uses vpn-route-target-params; + } + } + + grouping global-afi-safi-vpn-network-config { + list network-config { + key "rd"; + description + "A list of rd."; + uses route-distinguisher-params; + + list prefix-list { + key "prefix"; + description + "A list of prefix."; + leaf prefix { + type inet:ip-prefix; + description + "IP destination prefix."; + } + + leaf label-index { + type uint32; + mandatory true; + description + "Label index to associate with the prefix."; + } + + leaf rmap-policy-export { + type frr-bt:rmap-ref; + description + "Route-map to modify the attributes for Routes going out + via BGP updates."; + } + } + } + } +} diff --git a/yang/frr-bgp-neighbor.yang b/yang/frr-bgp-neighbor.yang new file mode 100644 index 0000000000..3b8d63c447 --- /dev/null +++ b/yang/frr-bgp-neighbor.yang @@ -0,0 +1,137 @@ +submodule frr-bgp-neighbor { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + include frr-bgp-common-structure; + include frr-bgp-common-multiprotocol; + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development + List: <mailto:dev@lists.frrouting.org>"; + description + "This submodule contains general data definitions for use in BGP neighbor. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping neighbor-parameters { + leaf password { + type string { + length "1..254"; + } + description + "Actual password."; + } + + leaf ttl-security { + type uint8; + description + "BGP Time To Live (TTL) security check."; + reference + "RFC 5082: The Generalized TTL Security Mechanism + (GTSM), + RFC 7454: BGP Operations and Security."; + } + + leaf solo { + type boolean; + default "false"; + description + "Solo peer - part of its own update group."; + } + + leaf enforce-first-as { + type boolean; + default "false"; + description + "When set to 'true' it will enforce the first AS for EBGP routes."; + } + + leaf description { + type string; + description + "An optional textual description (intended primarily for use + with a peer or group."; + } + + leaf passive-mode { + type boolean; + default "false"; + description + "Don't send open messages to this neighbor."; + } + + uses structure-neighbor-group-capability-options; + + uses neighbor-update-source; + + uses neighbor-remote-as; + + uses structure-neighbor-group-ebgp-multihop; + + uses neighbor-local-as-options; + + uses neighbor-bfd-options; + + uses structure-neighbor-group-admin-shutdown; + + uses structure-neighbor-group-graceful-restart; + + uses structure-neighbor-config-timers; + + container afi-safis { + description + "List of address-families associated with the BGP + instance."; + list afi-safi { + key "afi-safi-name"; + description + "AFI, SAFI configuration available for the + neighbour or group."; + uses mp-afi-safi-config; + + leaf enabled { + type boolean; + default "false"; + description + "This leaf indicates whether the IPv4 Unicast AFI, SAFI is + enabled for the neighbour or group."; + } + + uses mp-all-afi-safi-list-contents; + } + } + } +} diff --git a/yang/frr-bgp-peer-group.yang b/yang/frr-bgp-peer-group.yang new file mode 100644 index 0000000000..3ce628d2b7 --- /dev/null +++ b/yang/frr-bgp-peer-group.yang @@ -0,0 +1,89 @@ +submodule frr-bgp-peer-group { + yang-version 1.1; + + belongs-to frr-bgp { + prefix "bgp"; + } + + import ietf-inet-types { + prefix inet; + } + + include frr-bgp-common-structure; + include frr-bgp-neighbor; + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development + List: <mailto:dev@lists.frrouting.org>"; + description + "This submodule contains general data definitions for use in BGP + peer group. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + grouping bgp-peer-group-base { + description + "Parameters related to a BGP group."; + leaf peer-group-name { + type string; + description + "Name of the BGP peer-group."; + } + + leaf-list ipv4-listen-range { + type inet:ipv4-address; + description + "Configure BGP dynamic neighbors listen range."; + } + + leaf-list ipv6-listen-range { + type inet:ipv6-address; + description + "Configure BGP dynamic neighbors listen range."; + } + + uses neighbor-parameters; + } + + grouping bgp-peer-group-list { + description + "The list of BGP peer groups."; + list peer-group { + key "peer-group-name"; + description + "List of BGP peer-groups configured on the local system - + uniquely identified by peer-group name."; + uses bgp-peer-group-base; + } + } +} diff --git a/yang/frr-bgp-rpki.yang b/yang/frr-bgp-rpki.yang new file mode 100644 index 0000000000..e9b6752f26 --- /dev/null +++ b/yang/frr-bgp-rpki.yang @@ -0,0 +1,209 @@ +module frr-bgp-rpki { + yang-version 1.1; + namespace "http://frrouting.org/yang/frr-bgp-rpki"; + prefix frr-bgp-rpki; + + import ietf-inet-types { + prefix inet; + } + + import frr-vrf { + prefix frr-vrf; + } + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development + List: <mailto:dev@lists.frrouting.org>"; + description + "This module defines a model for managing FRR BGP RPKI. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + typedef transport-type { + type enumeration { + enum "TCP" { + value 1; + description + "Connection to server is TCP based."; + } + enum "SSH" { + value 2; + description + "Connection to server is SSH based."; + } + } + } + + grouping bgp-rpki-timers { + container rpki-timers { + description + "RPKI timers config."; + leaf polling-time { + type uint32 { + range "1..86400"; + } + units "seconds"; + default "3600"; + description + "Set the number of seconds the router waits until the + router asks the cache again for updated data."; + } + + leaf expire-time { + type uint32 { + range "600..172800"; + } + units "seconds"; + default "7200"; + description + "Set the expire interval."; + } + + leaf retry-time { + type uint16 { + range "1..7200"; + } + units "seconds"; + default "600"; + description + "Set the retry interval."; + } + } + } + + grouping bgp-rpki-cache-server { + container rpki-cache-server { + description + "Add a cache server to the socket."; + list cache-list { + key "preference"; + leaf preference { + type uint8 { + range "1..255"; + } + description + "Preference of the cache server."; + } + + leaf cache-type { + type transport-type; + mandatory true; + description + "Specifies a transport method for the RPKI cache."; + } + + choice server { + case ip-address { + leaf ip-address { + type inet:ip-address; + mandatory true; + } + } + + case host-name { + leaf ip-host-address { + type inet:host; + mandatory true; + } + } + } + + container transport { + container tcp { + when "../../cache-type = 'TCP'"; + description + "TCP server details."; + leaf tcp-port { + type uint32; + } + } + + container ssh { + when "../../cache-type = 'SSH'"; + description + "SSH login details"; + leaf ssh-port { + type uint32 { + range "1..65535"; + } + description + "SSH port on which session gets opened."; + } + + leaf user-name { + type string; + description + "SSH username to establish an SSH connection to the + cache server."; + } + + leaf private-key { + type string; + description + "Local path that includes the private key file of the router."; + } + + leaf public-key { + type string; + description + "Local path that includes the public key file of the router."; + } + + leaf server-public-ley { + type string; + description + "Server public key."; + } + } + } + } + } + } + + augment "/frr-vrf:lib/frr-vrf:vrf" { + container bgp-rpki { + description + "RPKI configuration parameters."; + leaf enable { + type boolean; + default "false"; + description + "When set to 'true' it enables the RPKI."; + } + + uses bgp-rpki-timers; + + uses bgp-rpki-cache-server; + } + } +} diff --git a/yang/frr-bgp-types.yang b/yang/frr-bgp-types.yang new file mode 100644 index 0000000000..0afdea1ba6 --- /dev/null +++ b/yang/frr-bgp-types.yang @@ -0,0 +1,154 @@ +module frr-bgp-types { + yang-version 1.1; + namespace "http://frrouting.org/yang/bgp-types"; + prefix frr-bt; + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development + List: <mailto:dev@lists.frrouting.org>"; + description + "This module contains general data definitions for use in BGP. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + typedef rmap-ref { + type string; + } + + typedef plist-ref { + type string; + } + + typedef access-list-ref { + type string; + } + + typedef as-path-filter-ref { + type string; + } + + typedef bgp-instance-type { + type enumeration { + enum "default" { + value 1; + description + "BGP instance default."; + } + enum "vrf" { + value 2; + description + "BGP instance vrf."; + } + enum "view" { + value 3; + description + "BGP instance view."; + } + } + } + + typedef as-type { + type enumeration { + enum "as-specified" { + value 1; + description + "AS has explicitly specified value."; + } + enum "internal" { + value 2; + description + "Internal BGP peer."; + } + enum "external" { + value 3; + description + "External BGP peer."; + } + } + } + + typedef add-path-type { + type enumeration { + enum "all" { + value 1; + description + "To advertise all paths to a neighbor."; + } + enum "per-as" { + value 2; + description + "To advertise the best path per each neighboring AS."; + } + enum "none" { + value 3; + description + "Add path feature is disabled."; + } + } + } + + typedef bfd-session-type { + type enumeration { + enum "single-hop" { + value 1; + description + "Single hop session."; + } + enum "multi-hop" { + value 2; + description + "Multiple hop session."; + } + enum "not-configured" { + value 3; + description + "Not Configured."; + } + } + } + + typedef direction { + type enumeration { + enum "in" { + value 1; + description + "IN, ingress, Rx."; + } + enum "out" { + value 2; + description + "OUT, egress, Tx."; + } + } + } +} diff --git a/yang/frr-bgp.yang b/yang/frr-bgp.yang new file mode 100644 index 0000000000..3e5b1da7d3 --- /dev/null +++ b/yang/frr-bgp.yang @@ -0,0 +1,1239 @@ +module frr-bgp { + yang-version 1.1; + namespace "http://frrouting.org/yang/bgp"; + prefix frr-bgp; + + import frr-routing { + prefix frr-rt; + } + + import ietf-inet-types { + prefix inet; + } + + import ietf-routing-types { + prefix rt-types; + } + + import frr-interface { + prefix frr-interface; + } + + include "frr-bgp-common-structure"; + + include "frr-bgp-common"; + + include "frr-bgp-common-multiprotocol"; + + include "frr-bgp-neighbor"; + + include "frr-bgp-peer-group"; + + include "frr-bgp-bmp"; + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development + List: <mailto:dev@lists.frrouting.org>"; + description + "This module defines a model for managing FRR bgpd daemon. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + identity bgp { + base frr-rt:routing-protocol; + description + "BGP protocol."; + } + + grouping mp-afi-unicast-common { + uses global-group-use-multiple-paths; + + uses global-redistribute; + + uses admin-distance; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol" { + container bgp { + when "../frr-rt:type = 'frr-bgp:bgp'" { + description + "BGP protocol augmentation of ietf-routing module + control-plane-protocol."; + } + description + "Top-level configuration for the BGP router."; + container global { + presence "Enables global configuration of BGP"; + description + "Global configuration for the BGP router."; + leaf local-as { + type inet:as-number; + mandatory true; + description + "Local autonomous system number of the router. Uses + the 32-bit as-number type from the model in RFC 6991."; + } + + uses frr-rt:router-id; + + container confederation { + description + "Configuration options specifying parameters when the + local router is within an autonomous system which is + part of a BGP confederation."; + leaf identifier { + type inet:as-number; + description + "Confederation identifier for the autonomous system."; + } + + leaf-list member-as { + type inet:as-number; + description + "Remote autonomous systems that are to be treated + as part of the local confederation."; + } + } + + uses med-config; + + uses route-reflector-config; + + uses route-selection-options; + + uses global-neighbor-config; + + container graceful-restart { + description + "Parameters relating the graceful restart mechanism for + BGP."; + uses graceful-restart-config; + } + + uses global-update-group-config; + + uses global-config-timers; + + uses global-bgp-config; + + uses global-network-config; + + uses global-graceful-shutdown; + + uses global-bmp-config; + + container afi-safis { + description + "List of address-families associated with the BGP + instance."; + list afi-safi { + key "afi-safi-name"; + description + "AFI, SAFI configuration available for the + neighbour or group."; + uses mp-afi-safi-config; + + uses mp-all-afi-safi-list-contents; + } + } + } + + container neighbors { + description + "Configuration for BGP neighbors."; + list neighbor { + key "remote-address"; + description + "List of BGP neighbors configured on the local system, + uniquely identified by remote IPv[46] address."; + leaf remote-address { + type inet:ip-address; + description + "The remote IP address of this entry's BGP peer."; + } + + leaf local-interface { + type frr-interface:interface-ref { + require-instance false; + } + description + "Neighbor's interface name."; + } + + leaf local-port { + type inet:port-number { + range "0..65535"; + } + description + "Neighbor's BGP TCP port number."; + } + + leaf peer-group { + type leafref { + path "../../../peer-groups/peer-group/peer-group-name"; + } + description + "The peer-group with which this neighbor is associated."; + } + + uses neighbor-parameters; + } + + list unnumbered-neighbor { + key "interface"; + description + "List of BGP neighbors configured on the local system, + uniquely identified by interfaces."; + leaf interface { + type frr-interface:interface-ref { + require-instance false; + } + description + "The local interface of this entry's BGP peer."; + } + + leaf v6only { + type boolean; + default "false"; + description + "When set to 'true' it will create a neighbor with v6 + link local only."; + } + + leaf peer-group { + type leafref { + path "../../../peer-groups/peer-group/peer-group-name"; + } + description + "The peer-group with which this neighbor is associated."; + } + + uses neighbor-parameters; + } + } + + container peer-groups { + description + "Configuration for BGP peer-groups."; + uses bgp-peer-group-list; + } + } + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-unicast" { + list network-config { + key "prefix"; + description + "A list of network routes."; + leaf prefix { + type inet:ipv4-prefix; + description + "IPv4 destination prefix."; + } + + leaf backdoor { + type boolean; + default "false"; + description + "Specify a BGP backdoor route."; + } + + uses mp-afi-safi-network-config; + } + + list aggregate-route { + key "prefix"; + description + "A list of aggregated routes."; + leaf prefix { + type inet:ipv4-prefix; + description + "IPv4 destination prefix."; + } + + uses mp-afi-safi-agg-route-config; + } + + list admin-distance-route { + key "prefix"; + description + "A list of routes with a particular admin distance."; + leaf prefix { + type inet:ipv4-prefix; + description + "IPv4 destination prefix."; + } + + uses distance-per-route-config; + } + + uses route-flap-dampening; + + uses mp-afi-unicast-common; + + uses global-filter-config; + + uses global-afi-safi-vpn-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv6-unicast" { + list network-config { + key "prefix"; + description + "A list of network routes."; + leaf prefix { + type inet:ipv6-prefix; + description + "IPv6 destination prefix."; + } + + leaf backdoor { + type boolean; + default "false"; + description + "Specify a BGP backdoor route."; + } + + uses mp-afi-safi-network-config; + } + + list aggregate-route { + key "prefix"; + description + "A list of aggregated routes."; + leaf prefix { + type inet:ipv6-prefix; + description + "IPv6 destination prefix."; + } + + uses mp-afi-safi-agg-route-config; + } + + list admin-distance-route { + key "prefix"; + description + "A list of routes with a particular admin distance."; + leaf prefix { + type inet:ipv6-prefix; + description + "IPv6 destination prefix."; + } + + uses distance-per-route-config; + } + + uses mp-afi-unicast-common; + + uses global-filter-config; + + uses global-afi-safi-vpn-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-labeled-unicast" { + uses global-group-use-multiple-paths; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv6-labeled-unicast" { + uses global-group-use-multiple-paths; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-multicast" { + list network-config { + key "prefix"; + description + "A list of network routes."; + leaf prefix { + type rt-types:ipv4-multicast-group-address; + description + "IPv4 multicast destination prefix."; + } + + leaf backdoor { + type boolean; + default "false"; + description + "Specify a BGP backdoor route."; + } + + uses mp-afi-safi-network-config; + } + + list aggregate-route { + key "prefix"; + description + "A list of aggregated routes."; + leaf prefix { + type rt-types:ipv4-multicast-group-address; + description + "IPv4 multicast destination prefix."; + } + + uses mp-afi-safi-agg-route-config; + } + + list admin-distance-route { + key "prefix"; + description + "A list of routes with a particular admin distance."; + leaf prefix { + type rt-types:ipv4-multicast-group-address; + description + "IPv4 multicast destination prefix."; + } + } + + uses admin-distance; + + uses route-flap-dampening; + + uses global-filter-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv6-multicast" { + list network-config { + key "prefix"; + description + "A list of network routes."; + leaf prefix { + type rt-types:ipv6-multicast-group-address; + description + "IPv6 multicast destination prefix."; + } + + leaf backdoor { + type boolean; + default "false"; + description + "Specify a BGP backdoor route."; + } + + uses mp-afi-safi-network-config; + } + + list aggregate-route { + key "prefix"; + description + "A list of aggregated routes."; + leaf prefix { + type rt-types:ipv6-multicast-group-address; + description + "IPv6 multicast destination prefix."; + } + + uses mp-afi-safi-agg-route-config; + } + + list admin-distance-route { + key "prefix"; + description + "A list of routes with a particular admin distance."; + leaf prefix { + type rt-types:ipv6-multicast-group-address; + description + "IPv6 multicast destination prefix."; + } + } + + uses admin-distance; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/ipv4-flowspec" { + uses flow-spec-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/l3vpn-ipv4-unicast" { + uses global-afi-safi-vpn-network-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/afi-safis/afi-safi/l3vpn-ipv6-unicast" { + uses global-afi-safi-vpn-network-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv4-unicast" { + uses bmp-afi-safi-common-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv4-multicast" { + uses bmp-afi-safi-common-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv6-unicast" { + uses bmp-afi-safi-common-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/global/bmp-config/target-list/afi-safis/afi-safi/ipv6-multicast" { + uses bmp-afi-safi-common-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-default-originate-options; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-weight; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-group-filter-config; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-unicast" { + leaf nexthop-local-unchanged { + type boolean; + default "false"; + description + "Configure treatment of outgoing link-local nexthop attribute. + When set to 'true' it leaves link-local nexthop unchanged + for this peer."; + } + + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/l3vpn-ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/l3vpn-ipv6-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/l2vpn-evpn" { + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv4-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/neighbor/afi-safis/afi-safi/ipv6-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-default-originate-options; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-weight; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + 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/ipv6-unicast" { + leaf nexthop-local-unchanged { + type boolean; + default "false"; + description + "Configure treatment of outgoing link-local nexthop attribute. + When set to 'true' it leaves link-local nexthop unchanged + for this peer."; + } + + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l3vpn-ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l3vpn-ipv6-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/l2vpn-evpn" { + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv4-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/neighbors/unnumbered-neighbor/afi-safis/afi-safi/ipv6-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-default-originate-options; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-weight; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + 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/ipv6-unicast" { + leaf nexthop-local-unchanged { + type boolean; + default "false"; + description + "Configure treatment of outgoing link-local nexthop attribute. + When set to 'true' it leaves link-local nexthop unchanged + for this peer."; + } + + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-multicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv4-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-labeled-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-group-orf-capability; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/l3vpn-ipv4-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/l3vpn-ipv6-unicast" { + uses structure-neighbor-group-add-paths; + + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-prefix-limit; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-private-as; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-send-community; + + uses structure-neighbor-group-soft-reconfiguration; + + uses structure-neighbor-weight; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/l2vpn-evpn" { + uses structure-neighbor-group-as-path-options; + + uses structure-neighbor-group-attr-unchanged; + + uses structure-neighbor-nexthop-self; + + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + 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" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } + + augment "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/bgp/peer-groups/peer-group/afi-safis/afi-safi/ipv6-flowspec" { + uses structure-neighbor-route-reflector; + + uses structure-neighbor-route-server; + + uses structure-neighbor-group-soft-reconfiguration; + } +} diff --git a/yang/frr-deviations-bgp-datacenter.yang b/yang/frr-deviations-bgp-datacenter.yang new file mode 100644 index 0000000000..9d2725b253 --- /dev/null +++ b/yang/frr-deviations-bgp-datacenter.yang @@ -0,0 +1,106 @@ +module frr-deviations-bgp-datacenter { + yang-version 1.1; + namespace "http://frrouting.org/yang/frr-deviations-bgp-datacenter"; + prefix frr-deviations-bgp-dc; + + import frr-routing { + prefix frr-rt; + } + + import frr-bgp { + prefix frr-bgp; + } + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> FRR Development + List: <mailto:dev@lists.frrouting.org>"; + description + "This module defines deviations for the frr-bgp module with + datacenter profile. + + Copyright 2020 FRRouting + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; + + revision 2019-12-03 { + description + "Initial revision."; + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-config-timers/frr-bgp:connect-retry-interval" { + deviate replace { + default "10"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-config-timers/frr-bgp:hold-time" { + deviate replace { + default "9"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-config-timers/frr-bgp:keepalive" { + deviate replace { + default "3"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:route-selection-options/frr-bgp:deterministic-med" { + deviate replace { + default "false"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:import-check" { + deviate replace { + default "true"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:show-hostname" { + deviate replace { + default "true"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:show-nexthop-hostname" { + deviate replace { + default "true"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:ebgp-requires-policy" { + deviate replace { + default "false"; + } + } + + deviation "/frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-bgp:bgp/frr-bgp:global/frr-bgp:global-neighbor-config/frr-bgp:log-neighbor-changes" { + deviate replace { + default "true"; + } + } +} diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang index c9e09bef4b..1e44c2569e 100644 --- a/yang/frr-filter.yang +++ b/yang/frr-filter.yang @@ -170,31 +170,22 @@ module frr-filter { description "Match any"; type empty; } - } - choice extended-value { - /* - * Legacy note: before using the new access-list format the - * cisco styled list only accepted identifiers using numbers - * and they had the following restriction: - * - * when "../number >= 100 and ../number <= 199 or - * ../number >= 2000 and ../number <= 2699"; - */ - description "Destination value to match"; - mandatory true; - - leaf destination-host { - description "Host to match"; - type inet:ipv4-address; - } - leaf destination-network { - description "Network to match"; - type inet:ipv4-prefix; - } - leaf destination-any { - description "Match any"; - type empty; + choice extended-value { + description "Destination value to match"; + + leaf destination-host { + description "Host to match"; + type inet:ipv4-address; + } + leaf destination-network { + description "Network to match"; + type inet:ipv4-prefix; + } + leaf destination-any { + description "Match any"; + type empty; + } } } } diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index 1bb693a1ef..00296516ef 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -340,6 +340,13 @@ module frr-isisd { "Area-tag associated to this circuit."; } + leaf vrf { + type string; + default "default"; + description + "VRF NAME."; + } + leaf ipv4-routing { type boolean; default "false"; @@ -793,7 +800,7 @@ module frr-isisd { description "Configuration of the IS-IS routing daemon."; list instance { - key "area-tag"; + key "area-tag vrf"; description "IS-IS routing instance."; leaf area-tag { @@ -802,6 +809,12 @@ module frr-isisd { "Area-tag associated to this routing instance."; } + leaf vrf { + type string; + description + "VRF NAME."; + } + leaf is-type { type level; default "level-1-2"; diff --git a/yang/frr-nexthop.yang b/yang/frr-nexthop.yang index c918a7cbfd..0cb0f93ee4 100644 --- a/yang/frr-nexthop.yang +++ b/yang/frr-nexthop.yang @@ -188,6 +188,16 @@ module frr-nexthop { "Nexthop is directly connected."; } + leaf srte-color { + when "../nh-type = 'ip4' or + ../nh-type = 'ip6' or + ../nh-type = 'ip4-ifindex' or + ../nh-type = 'ip6-ifindex'"; + type uint32; + description + "The nexthop SR-TE color"; + } + uses rt-types:mpls-label-stack { description "Nexthop's MPLS label stack."; diff --git a/yang/frr-ospfd.yang b/yang/frr-ospfd.yang index 324b66dd98..466dd42ce4 100644 --- a/yang/frr-ospfd.yang +++ b/yang/frr-ospfd.yang @@ -346,6 +346,13 @@ module frr-ospfd { "The reference bandwidth in terms of Mbits per second."; } + leaf use-arp { + type boolean; + default "true"; + description + "ARP for neighbor table entry."; + } + leaf capability-opaque { type boolean; default "false"; diff --git a/yang/frr-routing.yang b/yang/frr-routing.yang index d22e12074f..52607f9ad0 100644 --- a/yang/frr-routing.yang +++ b/yang/frr-routing.yang @@ -101,6 +101,76 @@ module frr-routing { "This identity represents the IPv6 multicast address family."; } + identity ipv4-labeled-unicast { + base afi-safi-type; + description + "This identity represents the IPv4 labeled unicast address family."; + } + + + identity ipv6-labeled-unicast { + base afi-safi-type; + description + "This identity represents the IPv6 labeled unicast address family."; + } + + + identity l3vpn-ipv4-unicast { + base afi-safi-type; + description + "This identity represents the L3vpn IPv4 unicast address family."; + } + + + identity l3vpn-ipv6-unicast { + base afi-safi-type; + description + "This identity represents the L3vpn IPv6 unicast address family."; + } + + + identity l3vpn-ipv4-multicast { + base afi-safi-type; + description + "This identity represents the L3vpn IPv4 multicast address family."; + } + + + identity l3vpn-ipv6-multicast { + base afi-safi-type; + description + "This identity represents the L3vpn IPv6 multicast address family."; + } + + + identity l2vpn-vpls { + base afi-safi-type; + description + "This identity represents the L2vpn VPLS address family."; + } + + + identity l2vpn-evpn { + base afi-safi-type; + description + "This identity represents the L2vpn EVPN address family."; + } + + + identity ipv4-flowspec { + base afi-safi-type; + description + "This identity represents the IPv4 flowspec address family."; + } + + + identity ipv6-flowspec { + base afi-safi-type; + description + "This identity represents the IPv6 flowspec address family."; + } + + identity control-plane-protocol { description "Base identity from which control-plane protocol identities are diff --git a/yang/ietf/ietf-bgp-types.yang b/yang/ietf/ietf-bgp-types.yang new file mode 100644 index 0000000000..9c7a6af76c --- /dev/null +++ b/yang/ietf/ietf-bgp-types.yang @@ -0,0 +1,525 @@ +module ietf-bgp-types { + yang-version "1.1"; + namespace "urn:ietf:params:xml:ns:yang:ietf-bgp-types"; + + prefix "bt"; + + import ietf-inet-types { + prefix inet; + } + + // meta + organization + "IETF IDR Working Group"; + + contact + "WG Web: <http://tools.ietf.org/wg/idr> + WG List: <idr@ietf.org> + + Authors: Mahesh Jethanandani (mjethanandani at gmail.com), + Keyur Patel (keyur at arrcus.com), + Susan Hares (shares at ndzh.com), + Jeffrey Haas (jhaas at pfrc.org)."; + description + "This module contains general data definitions for use in BGP + policy. It can be imported by modules that make use of BGP + attributes"; + + revision 2019-10-03 { + description + "Initial Version"; + reference + "RFC XXX, BGP Model for Service Provider Network."; + } + + identity bgp-capability { + description "Base identity for a BGP capability"; + } + + identity mp-bgp { + base bgp-capability; + description + "Multi-protocol extensions to BGP"; + reference + "RFC 4760"; + } + + identity route-refresh { + base bgp-capability; + description + "The BGP route-refresh functionality"; + reference + "RFC2918"; + } + + identity asn32 { + base bgp-capability; + description + "4-byte (32-bit) AS number functionality"; + reference + "RFC6793"; + } + + identity graceful-restart { + base bgp-capability; + description + "Graceful restart functionality"; + reference + "RFC4724"; + } + + identity add-paths { + base bgp-capability; + description + "BGP add-paths"; + reference + "RFC 7911."; + } + + identity afi-safi-type { + description + "Base identity type for AFI,SAFI tuples for BGP-4"; + reference + "RFC4760 - multi-protocol extensions for BGP-4"; + } + + identity ipv4-unicast { + base afi-safi-type; + description + "IPv4 unicast (AFI,SAFI = 1,1)"; + reference + "RFC4760"; + } + + identity ipv6-unicast { + base afi-safi-type; + description + "IPv6 unicast (AFI,SAFI = 2,1)"; + reference + "RFC4760"; + } + + identity ipv4-labeled-unicast { + base afi-safi-type; + description + "Labeled IPv4 unicast (AFI,SAFI = 1,4)"; + reference + "RFC3107"; + } + + identity ipv6-labeled-unicast { + base afi-safi-type; + description + "Labeled IPv6 unicast (AFI,SAFI = 2,4)"; + reference + "RFC3107"; + } + + identity l3vpn-ipv4-unicast { + base afi-safi-type; + description + "Unicast IPv4 MPLS L3VPN (AFI,SAFI = 1,128)"; + reference + "RFC4364"; + } + + identity l3vpn-ipv6-unicast { + base afi-safi-type; + description + "Unicast IPv6 MPLS L3VPN (AFI,SAFI = 2,128)"; + reference + "RFC4659"; + } + + identity l3vpn-ipv4-multicast { + base afi-safi-type; + description + "Multicast IPv4 MPLS L3VPN (AFI,SAFI = 1,129)"; + reference + "RFC6514"; + } + + identity l3vpn-ipv6-multicast { + base afi-safi-type; + description + "Multicast IPv6 MPLS L3VPN (AFI,SAFI = 2,129)"; + reference + "RFC6514"; + } + + identity l2vpn-vpls { + base afi-safi-type; + description + "BGP-signalled VPLS (AFI,SAFI = 25,65)"; + reference + "RFC4761"; + } + + identity l2vpn-evpn { + base afi-safi-type; + description + "BGP MPLS Based Ethernet VPN (AFI,SAFI = 25,70)"; + } + + identity bgp-well-known-std-community { + description + "Base identity for reserved communities within the standard + community space defined by RFC1997. These communities must + fall within the range 0xFFFF0000 to 0xFFFFFFFF"; + reference + "RFC 1997: BGP Communities Attribute."; + } + + identity no-export { + base bgp-well-known-std-community; + description + "Do not export NLRI received carrying this community outside + the bounds of this autonomous system, or this confederation if + the local autonomous system is a confederation member AS. This + community has a value of 0xFFFFFF01."; + reference + "RFC 1997: BGP Communities Attribute."; + } + + identity no-advertise { + base bgp-well-known-std-community; + description + "All NLRI received carrying this community must not be + advertised to other BGP peers. This community has a value of + 0xFFFFFF02."; + reference + "RFC 1997: BGP Communities Attribute."; + } + + identity no-export-subconfed { + base bgp-well-known-std-community; + description + "All NLRI received carrying this community must not be + advertised to external BGP peers - including over confederation + sub-AS boundaries. This community has a value of 0xFFFFFF03."; + reference + "RFC 1997: BGP Communities Attribute."; + } + + identity no-peer { + base bgp-well-known-std-community; + description + "An autonomous system receiving NLRI tagged with this community + is advised not to re-advertise the NLRI to external bi-lateral + peer autonomous systems. An AS may also filter received NLRI + from bilateral peer sessions when they are tagged with this + community value"; + reference + "RFC 3765: NOPEER Community for BGP."; + } + identity as-path-segment-type { + description + "Base AS Path Segment Type. In [BGP-4], the path segment type + is a 1-octet field with the following values defined."; + reference + "RFC 4271: A Border Gateway Protocol 4 (BGP-4), Section 4.3."; + } + + identity as-set { + base as-path-segment-type; + description + "Unordered set of autonomous systems that a route in the UPDATE + message has traversed."; + reference + "RFC 4271: A Border Gateway Protocol 4 (BGP-4), Section 4.3."; + } + + identity as-sequence { + base as-path-segment-type; + description + "Ordered set of autonomous systems that a route in the UPDATE + message has traversed."; + reference + "RFC 4271: A Border Gateway Protocol 4 (BGP-4), Section 4.3."; + } + + identity as-confed-sequence { + base as-path-segment-type; + description + "Ordered set of Member Autonomous Systems in the local + confederation that the UPDATE message has traversed."; + reference + "RFC 5065, Autonomous System Configuration for BGP."; + } + + identity as-confed-set { + base as-path-segment-type; + description + "Unordered set of Member Autonomous Systems in the local + confederation that the UPDATE message has traversed."; + reference + "RFC 5065, Autonomous System Configuration for BGP."; + } + + /* + * Features. + */ + feature send-communities { + description + "Enable the propogation of communities."; + } + + feature ttl-security { + description + "BGP Time To Live (TTL) security check support."; + reference + "RFC 5082, The Generalized TTL Security Mechanism (GTSM)"; + } + + feature bfd { + description + "Support for BFD detection of BGP neighbor reachability."; + reference + "RFC 5880, Bidirectional Forward Detection (BFD), + RFC 5881, Bidirectional Forward Detection for IPv4 and IPv6 + (Single Hop). + RFC 5883, Bidirectional Forwarding Detection (BFD) for Multihop + Paths"; + } + + typedef bgp-session-direction { + type enumeration { + enum INBOUND { + description + "Refers to all NLRI received from the BGP peer"; + } + enum OUTBOUND { + description + "Refers to all NLRI advertised to the BGP peer"; + } + } + description + "Type to describe the direction of NLRI transmission"; + } + + typedef bgp-well-known-community-type { + type identityref { + base bgp-well-known-std-community; + } + description + "Type definition for well-known IETF community attribute + values"; + reference + "IANA Border Gateway Protocol (BGP) Well Known Communities"; + } + + typedef bgp-std-community-type { + // TODO: further refine restrictions and allowed patterns + // 4-octet value: + // <as number> 2 octets + // <community value> 2 octets + type union { + type uint32 { + // per RFC 1997, 0x00000000 - 0x0000FFFF and 0xFFFF0000 - + // 0xFFFFFFFF are reserved + range "65536..4294901759"; // 0x00010000..0xFFFEFFFF + } + type string { + pattern '([0-9]+:[0-9]+)'; + } + } + description + "Type definition for standard community attributes"; + reference + "RFC 1997 - BGP Communities Attribute"; + } + + typedef bgp-ext-community-type { + // TODO: needs more work to make this more precise given the + // variability of extended community attribute specifications + // 8-octet value: + // <type> 2 octects + // <value> 6 octets + + type union { + type string { + // Type 1: 2-octet global and 4-octet local + // (AS number) (Integer) + pattern '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' + + '[1-9][0-9]{1,4}|[0-9]):' + + '(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-6]|' + + '[1-3][0-9]{9}|[1-9]([0-9]{1,7})?[0-9]|[1-9])'; + } + type string { + // Type 2: 4-octet global and 2-octet local + // (ipv4-address) (integer) + pattern '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|' + + '2[0-4][0-9]|25[0-5]):' + + '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' + + '[1-9][0-9]{1,4}|[0-9])'; + } + type string { + // route-target with Type 1 + // route-target:(ASN):(local-part) + pattern 'route\-target:(6[0-5][0-5][0-3][0-5]|' + + '[1-5][0-9]{4}|[1-9][0-9]{1,4}|[0-9]):' + + '(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-6]|' + + '[1-3][0-9]{9}|[1-9]([0-9]{1,7})?[0-9]|[1-9])'; + } + type string { + // route-target with Type 2 + // route-target:(IPv4):(local-part) + pattern 'route\-target:' + + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|' + + '2[0-4][0-9]|25[0-5]):' + + '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' + + '[1-9][0-9]{1,4}|[0-9])'; + } + type string { + // route-origin with Type 1 + pattern 'route\-origin:(6[0-5][0-5][0-3][0-5]|' + + '[1-5][0-9]{4}|[1-9][0-9]{1,4}|[0-9]):' + + '(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-6]|' + + '[1-3][0-9]{9}|[1-9]([0-9]{1,7})?[0-9]|[1-9])'; + } + type string { + // route-origin with Type 2 + pattern 'route\-origin:' + + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|' + + '25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|' + + '2[0-4][0-9]|25[0-5]):' + + '(6[0-5][0-5][0-3][0-5]|[1-5][0-9]{4}|' + + '[1-9][0-9]{1,4}|[0-9])'; + } + } + description + "Type definition for extended community attributes"; + reference + "RFC 4360 - BGP Extended Communities Attribute"; + } + + typedef bgp-community-regexp-type { + // TODO: needs more work to decide what format these regexps can + // take. + type string; + description + "Type definition for communities specified as regular + expression patterns"; + } + + typedef bgp-origin-attr-type { + type enumeration { + enum igp { + description "Origin of the NLRI is internal"; + } + enum egp { + description "Origin of the NLRI is EGP"; + } + enum incomplete { + description "Origin of the NLRI is neither IGP or EGP"; + } + } + description + "Type definition for standard BGP origin attribute"; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4), Sec 4.3"; + } + + typedef peer-type { + type enumeration { + enum internal { + description + "internal (iBGP) peer"; + } + enum external { + description + "external (eBGP) peer"; + } + enum confederation { + description + "Confederation as peer"; + } + } + description + "Labels a peer or peer group as explicitly internal, + external or confederation."; + } + + identity REMOVE_PRIVATE_AS_OPTION { + description + "Base identity for options for removing private autonomous + system numbers from the AS_PATH attribute"; + } + + identity PRIVATE_AS_REMOVE_ALL { + base REMOVE_PRIVATE_AS_OPTION; + description + "Strip all private autonomous system numbers from the AS_PATH. + This action is performed regardless of the other content of the + AS_PATH attribute, and for all instances of private AS numbers + within that attribute."; + } + + identity PRIVATE_AS_REPLACE_ALL { + base REMOVE_PRIVATE_AS_OPTION; + description + "Replace all instances of private autonomous system numbers in + the AS_PATH with the local BGP speaker's autonomous system + number. This action is performed regardless of the other + content of the AS_PATH attribute, and for all instances of + private AS number within that attribute."; + } + + typedef remove-private-as-option { + type identityref { + base REMOVE_PRIVATE_AS_OPTION; + } + description + "Set of options for configuring how private AS path numbers + are removed from advertisements"; + } + + typedef percentage { + type uint8 { + range "0..100"; + } + description + "Integer indicating a percentage value"; + } + + typedef rr-cluster-id-type { + type union { + type uint32; + type inet:ipv4-address; + } + description + "Union type for route reflector cluster ids: + option 1: 4-byte number + option 2: IP address"; + } + + typedef community-type { + type bits { + bit standard { + position 0; + description + "Send only standard communities."; + reference + "RFC 1997: BGP Communities Attribute."; + } + bit extended { + description + "Send only extended communities."; + reference + "RFC 4360: BGP Extended Communities Attribute."; + } + bit large { + description + "Send only large communities."; + reference + "RFC 8092: BGP Large Communities Attribute."; + } + } + description + "Type describing variations of community attributes. + The community types can be combined and a value of 0 + implies 'none'"; + } +} diff --git a/zebra/connected.c b/zebra/connected.c index 3b9ebe14a4..8c4ba163bd 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -393,10 +393,10 @@ void connected_down(struct interface *ifp, struct connected *ifc) * head. */ rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, - 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false); + 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false, true); rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, - 0, 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false); + 0, 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false, true); /* Schedule LSP forwarding entries for processing, if appropriate. */ if (zvrf->vrf->vrf_id == VRF_DEFAULT) { diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 4165fa1b3a..5bf47580a8 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -44,6 +44,8 @@ #include "zebra/interface.h" #include "zebra/zebra_dplane.h" #include "zebra/zebra_router.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" #include "zebra/zebra_vxlan_private.h" #include "zebra/kernel_netlink.h" #include "zebra/rt_netlink.h" diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 81f77d4f9b..5cd3e69299 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -987,7 +987,8 @@ int kernel_interface_set_master(struct interface *master, } /* Interface address modification. */ -static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx) +static ssize_t netlink_address_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { int bytelen; const struct prefix *p; @@ -997,64 +998,72 @@ static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx) struct { struct nlmsghdr n; struct ifaddrmsg ifa; - char buf[NL_PKT_BUF_SIZE]; - } req; + char buf[0]; + } *req = buf; + + if (buflen < sizeof(*req)) + return 0; p = dplane_ctx_get_intf_addr(ctx); - memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); + memset(req, 0, sizeof(*req)); bytelen = (p->family == AF_INET ? 4 : 16); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req->n.nlmsg_flags = NLM_F_REQUEST; if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL) cmd = RTM_NEWADDR; else cmd = RTM_DELADDR; - req.n.nlmsg_type = cmd; - req.ifa.ifa_family = p->family; + req->n.nlmsg_type = cmd; + req->ifa.ifa_family = p->family; - req.ifa.ifa_index = dplane_ctx_get_ifindex(ctx); + req->ifa.ifa_index = dplane_ctx_get_ifindex(ctx); - nl_attr_put(&req.n, sizeof(req), IFA_LOCAL, &p->u.prefix, bytelen); + if (!nl_attr_put(&req->n, buflen, IFA_LOCAL, &p->u.prefix, bytelen)) + return 0; if (p->family == AF_INET) { if (dplane_ctx_intf_is_connected(ctx)) { p = dplane_ctx_get_intf_dest(ctx); - nl_attr_put(&req.n, sizeof(req), IFA_ADDRESS, - &p->u.prefix, bytelen); + if (!nl_attr_put(&req->n, buflen, IFA_ADDRESS, + &p->u.prefix, bytelen)) + return 0; } else if (cmd == RTM_NEWADDR) { struct in_addr broad = { .s_addr = ipv4_broadcast_addr(p->u.prefix4.s_addr, p->prefixlen) }; - nl_attr_put(&req.n, sizeof(req), IFA_BROADCAST, &broad, - bytelen); + if (!nl_attr_put(&req->n, buflen, IFA_BROADCAST, &broad, + bytelen)) + return 0; } } /* p is now either address or destination/bcast addr */ - req.ifa.ifa_prefixlen = p->prefixlen; + req->ifa.ifa_prefixlen = p->prefixlen; if (dplane_ctx_intf_is_secondary(ctx)) - SET_FLAG(req.ifa.ifa_flags, IFA_F_SECONDARY); + SET_FLAG(req->ifa.ifa_flags, IFA_F_SECONDARY); if (dplane_ctx_intf_has_label(ctx)) { label = dplane_ctx_get_intf_label(ctx); - nl_attr_put(&req.n, sizeof(req), IFA_LABEL, label, - strlen(label) + 1); + if (!nl_attr_put(&req->n, buflen, IFA_LABEL, label, + strlen(label) + 1)) + return 0; } - return netlink_talk_info(netlink_talk_filter, &req.n, - dplane_ctx_get_ns(ctx), 0); + return NLMSG_ALIGN(req->n.nlmsg_len); } -enum zebra_dplane_result kernel_address_update_ctx(struct zebra_dplane_ctx *ctx) +enum netlink_msg_status +netlink_put_address_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) { - return (netlink_address_ctx(ctx) == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); + return netlink_batch_add_msg(bth, ctx, netlink_address_msg_encoder, + false); } int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index 29fd2aca35..0bbba81ca6 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -32,6 +32,10 @@ extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int interface_lookup_netlink(struct zebra_ns *zns); +extern enum netlink_msg_status +netlink_put_address_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); + /* * Set protodown status of interface. * diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index a4d22c12a4..ad0d4bf56b 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -20,12 +20,6 @@ #include <zebra.h> -#if defined(HANDLE_NETLINK_FUZZING) -#include <stdio.h> -#include <string.h> -#include "libfrr.h" -#endif /* HANDLE_NETLINK_FUZZING */ - #ifdef HAVE_NETLINK #include "linklist.h" @@ -84,6 +78,20 @@ #define RTPROT_MROUTED 17 #endif +#define NL_DEFAULT_BATCH_BUFSIZE (16 * NL_PKT_BUF_SIZE) + +/* + * We limit the batch's size to a number smaller than the length of the + * underlying buffer since the last message that wouldn't fit the batch would go + * over the upper boundary and then it would have to be encoded again into a new + * buffer. If the difference between the limit and the length of the buffer is + * big enough (bigger than the biggest Netlink message) then this situation + * won't occur. + */ +#define NL_DEFAULT_BATCH_SEND_THRESHOLD (15 * NL_PKT_BUF_SIZE) + +#define NL_BATCH_RX_BUFSIZE NL_RCV_PKT_BUF_SIZE + static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"}, {RTM_DELROUTE, "RTM_DELROUTE"}, {RTM_GETROUTE, "RTM_GETROUTE"}, @@ -151,6 +159,62 @@ extern uint32_t nl_rcvbufsize; extern struct zebra_privs_t zserv_privs; +DEFINE_MTYPE_STATIC(ZEBRA, NL_BUF, "Zebra Netlink buffers") + +size_t nl_batch_tx_bufsize; +char *nl_batch_tx_buf; + +char nl_batch_rx_buf[NL_BATCH_RX_BUFSIZE]; + +_Atomic uint32_t nl_batch_bufsize = NL_DEFAULT_BATCH_BUFSIZE; +_Atomic uint32_t nl_batch_send_threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD; + +struct nl_batch { + void *buf; + size_t bufsiz; + size_t limit; + + void *buf_head; + size_t curlen; + size_t msgcnt; + + const struct zebra_dplane_info *zns; + + struct dplane_ctx_q ctx_list; + + /* + * Pointer to the queue of completed contexts outbound back + * towards the dataplane module. + */ + struct dplane_ctx_q *ctx_out_q; +}; + +int netlink_config_write_helper(struct vty *vty) +{ + uint32_t size = + atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed); + uint32_t threshold = atomic_load_explicit(&nl_batch_send_threshold, + memory_order_relaxed); + + if (size != NL_DEFAULT_BATCH_BUFSIZE + || threshold != NL_DEFAULT_BATCH_SEND_THRESHOLD) + vty_out(vty, "zebra kernel netlink batch-tx-buf %u %u\n", size, + threshold); + + return 0; +} + +void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, bool set) +{ + if (!set) { + size = NL_DEFAULT_BATCH_BUFSIZE; + threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD; + } + + atomic_store_explicit(&nl_batch_bufsize, size, memory_order_relaxed); + atomic_store_explicit(&nl_batch_send_threshold, threshold, + memory_order_relaxed); +} int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup) { @@ -327,86 +391,6 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return 0; } -#if defined(HANDLE_NETLINK_FUZZING) -/* Using globals here to avoid adding function parameters */ - -/* Keep distinct filenames for netlink fuzzy collection */ -static unsigned int netlink_file_counter = 1; - -/* File name to read fuzzed netlink from */ -static char netlink_fuzz_file[MAXPATHLEN] = ""; - -/* Flag for whether to read from file or not */ -bool netlink_read; - -/** - * netlink_read_init() - Starts the message parser - * @fname: Filename to read. - */ -void netlink_read_init(const char *fname) -{ - struct zebra_dplane_info dp_info; - - snprintf(netlink_fuzz_file, MAXPATHLEN, "%s", fname); - /* Creating this fake socket for testing purposes */ - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); - - /* Capture key info from zns struct */ - zebra_dplane_info_from_zns(&dp_info, zns, false); - - netlink_parse_info(netlink_information_fetch, &zns->netlink, - &dp_info, 1, 0); -} - -/** - * netlink_write_incoming() - Writes all data received from netlink to a file - * @buf: Data from netlink. - * @size: Size of data. - * @counter: Counter for keeping filenames distinct. - */ -static void netlink_write_incoming(const char *buf, const unsigned int size, - unsigned int counter) -{ - char fname[MAXPATHLEN]; - FILE *f; - - snprintf(fname, MAXPATHLEN, "%s/%s_%u", frr_vtydir, "netlink", counter); - frr_with_privs(&zserv_privs) { - f = fopen(fname, "w"); - } - if (f) { - fwrite(buf, 1, size, f); - fclose(f); - } -} - -/** - * netlink_read_file() - Reads netlink data from file - * @buf: Netlink buffer being overwritten. - * @fname: File name to read from. - * - * Return: Size of file. - */ -static long netlink_read_file(char *buf, const char *fname) -{ - FILE *f; - long file_bytes = -1; - - frr_with_privs(&zserv_privs) { - f = fopen(fname, "r"); - } - if (f) { - fseek(f, 0, SEEK_END); - file_bytes = ftell(f); - rewind(f); - fread(buf, NL_RCV_PKT_BUF_SIZE, 1, f); - fclose(f); - } - return file_bytes; -} - -#endif /* HANDLE_NETLINK_FUZZING */ - static int kernel_read(struct thread *thread) { struct zebra_ns *zns = (struct zebra_ns *)THREAD_ARG(thread); @@ -757,18 +741,7 @@ static int netlink_recv_msg(const struct nlsock *nl, struct msghdr msg, msg.msg_iovlen = 1; do { -#if defined(HANDLE_NETLINK_FUZZING) - /* Check if reading and filename is set */ - if (netlink_read && '\0' != netlink_fuzz_file[0]) { - zlog_debug("Reading netlink fuzz file"); - status = netlink_read_file(buf, netlink_fuzz_file); - ((struct sockaddr_nl *)msg.msg_name)->nl_pid = 0; - } else { - status = recvmsg(nl->sock, &msg, 0); - } -#else status = recvmsg(nl->sock, &msg, 0); -#endif /* HANDLE_NETLINK_FUZZING */ } while (status == -1 && errno == EINTR); if (status == -1) { @@ -800,13 +773,6 @@ static int netlink_recv_msg(const struct nlsock *nl, struct msghdr msg, zlog_hexdump(buf, status); } -#if defined(HANDLE_NETLINK_FUZZING) - if (!netlink_read) { - zlog_debug("Writing incoming netlink message"); - netlink_write_incoming(buf, status, netlink_file_counter++); - } -#endif /* HANDLE_NETLINK_FUZZING */ - return status; } @@ -1008,9 +974,10 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), * startup -> Are we reading in under startup conditions * This is passed through eventually to filter. */ -int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), - struct nlmsghdr *n, - const struct zebra_dplane_info *dp_info, int startup) +static int +netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), + struct nlmsghdr *n, const struct zebra_dplane_info *dp_info, + int startup) { const struct nlsock *nl; @@ -1080,6 +1047,331 @@ int netlink_request(struct nlsock *nl, void *req) return 0; } +static int nl_batch_read_resp(struct nl_batch *bth) +{ + struct nlmsghdr *h; + struct sockaddr_nl snl; + struct msghdr msg; + int status, seq; + const struct nlsock *nl; + struct zebra_dplane_ctx *ctx; + bool ignore_msg; + + nl = &(bth->zns->nls); + + msg.msg_name = (void *)&snl; + msg.msg_namelen = sizeof(snl); + + /* + * The responses are not batched, so we need to read and process one + * message at a time. + */ + while (true) { + status = netlink_recv_msg(nl, msg, nl_batch_rx_buf, + sizeof(nl_batch_rx_buf)); + if (status == -1 || status == 0) + return status; + + h = (struct nlmsghdr *)nl_batch_rx_buf; + ignore_msg = false; + seq = h->nlmsg_seq; + /* + * Find the corresponding context object. Received responses are + * in the same order as requests we sent, so we can simply + * iterate over the context list and match responses with + * requests at same time. + */ + while (true) { + ctx = dplane_ctx_dequeue(&(bth->ctx_list)); + if (ctx == NULL) + break; + + dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx); + + /* We have found corresponding context object. */ + if (dplane_ctx_get_ns(ctx)->nls.seq == seq) + break; + + /* + * 'update' context objects take two consecutive + * sequence numbers. + */ + if (dplane_ctx_is_update(ctx) + && dplane_ctx_get_ns(ctx)->nls.seq + 1 == seq) { + /* + * This is the situation where we get a response + * to a message that should be ignored. + */ + ignore_msg = true; + break; + } + } + + if (ignore_msg) + continue; + + /* + * We received a message with the sequence number that isn't + * associated with any dplane context object. + */ + if (ctx == NULL) { + zlog_debug( + "%s: skipping unassociated response, seq number %d NS %u", + __func__, h->nlmsg_seq, bth->zns->ns_id); + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + int err = netlink_parse_error(nl, h, bth->zns, 0); + + if (err == -1) + dplane_ctx_set_status( + ctx, ZEBRA_DPLANE_REQUEST_FAILURE); + + zlog_debug("%s: netlink error message seq=%d ", + __func__, h->nlmsg_seq); + continue; + } + + /* + * If we get here then we did not receive neither the ack nor + * the error and instead received some other message in an + * unexpected way. + */ + zlog_debug("%s: ignoring message type 0x%04x(%s) NS %u", + __func__, h->nlmsg_type, + nl_msg_type_to_str(h->nlmsg_type), bth->zns->ns_id); + } + + return 0; +} + +static void nl_batch_reset(struct nl_batch *bth) +{ + bth->buf_head = bth->buf; + bth->curlen = 0; + bth->msgcnt = 0; + bth->zns = NULL; + + TAILQ_INIT(&(bth->ctx_list)); +} + +static void nl_batch_init(struct nl_batch *bth, struct dplane_ctx_q *ctx_out_q) +{ + /* + * If the size of the buffer has changed, free and then allocate a new + * one. + */ + size_t bufsize = + atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed); + if (bufsize != nl_batch_tx_bufsize) { + if (nl_batch_tx_buf) + XFREE(MTYPE_NL_BUF, nl_batch_tx_buf); + + nl_batch_tx_buf = XCALLOC(MTYPE_NL_BUF, bufsize); + nl_batch_tx_bufsize = bufsize; + } + + bth->buf = nl_batch_tx_buf; + bth->bufsiz = bufsize; + bth->limit = atomic_load_explicit(&nl_batch_send_threshold, + memory_order_relaxed); + + bth->ctx_out_q = ctx_out_q; + + nl_batch_reset(bth); +} + +static void nl_batch_send(struct nl_batch *bth) +{ + struct zebra_dplane_ctx *ctx; + bool err = false; + + if (bth->curlen != 0 && bth->zns != NULL) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s, batch size=%zu, msg cnt=%zu", + __func__, bth->zns->nls.name, bth->curlen, + bth->msgcnt); + + if (netlink_send_msg(&(bth->zns->nls), bth->buf, bth->curlen) + == -1) + err = true; + + if (!err) { + if (nl_batch_read_resp(bth) == -1) + err = true; + } + } + + /* Move remaining contexts to the outbound queue. */ + while (true) { + ctx = dplane_ctx_dequeue(&(bth->ctx_list)); + if (ctx == NULL) + break; + + if (err) + dplane_ctx_set_status(ctx, + ZEBRA_DPLANE_REQUEST_FAILURE); + + dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx); + } + + nl_batch_reset(bth); +} + +enum netlink_msg_status netlink_batch_add_msg( + struct nl_batch *bth, struct zebra_dplane_ctx *ctx, + ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t), + bool ignore_res) +{ + int seq; + ssize_t size; + struct nlmsghdr *msgh; + + size = (*msg_encoder)(ctx, bth->buf_head, bth->bufsiz - bth->curlen); + + /* + * If there was an error while encoding the message (other than buffer + * overflow) then return an error. + */ + if (size < 0) + return FRR_NETLINK_ERROR; + + /* + * If the message doesn't fit entirely in the buffer then send the batch + * and retry. + */ + if (size == 0) { + nl_batch_send(bth); + size = (*msg_encoder)(ctx, bth->buf_head, + bth->bufsiz - bth->curlen); + /* + * If the message doesn't fit in the empty buffer then just + * return an error. + */ + if (size <= 0) + return FRR_NETLINK_ERROR; + } + + seq = dplane_ctx_get_ns(ctx)->nls.seq; + if (ignore_res) + seq++; + + msgh = (struct nlmsghdr *)bth->buf_head; + msgh->nlmsg_seq = seq; + msgh->nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; + + bth->zns = dplane_ctx_get_ns(ctx); + bth->buf_head = ((char *)bth->buf_head) + size; + bth->curlen += size; + bth->msgcnt++; + + return FRR_NETLINK_QUEUED; +} + +static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + if (dplane_ctx_is_skip_kernel(ctx)) + return FRR_NETLINK_SUCCESS; + + switch (dplane_ctx_get_op(ctx)) { + + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + return netlink_put_route_update_msg(bth, ctx); + + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + return netlink_put_nexthop_update_msg(bth, ctx); + + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + return netlink_put_lsp_update_msg(bth, ctx); + + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + return netlink_put_pw_update_msg(bth, ctx); + + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + return netlink_put_address_update_msg(bth, ctx); + + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + return netlink_put_mac_update_msg(bth, ctx); + + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + return netlink_put_neigh_update_msg(bth, ctx); + + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + return netlink_put_rule_update_msg(bth, ctx); + + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: + return FRR_NETLINK_SUCCESS; + + case DPLANE_OP_NONE: + return FRR_NETLINK_ERROR; + } + + return FRR_NETLINK_ERROR; +} + +void kernel_update_multi(struct dplane_ctx_q *ctx_list) +{ + struct nl_batch batch; + struct zebra_dplane_ctx *ctx; + struct dplane_ctx_q handled_list; + enum netlink_msg_status res; + + TAILQ_INIT(&handled_list); + nl_batch_init(&batch, &handled_list); + + while (true) { + ctx = dplane_ctx_dequeue(ctx_list); + if (ctx == NULL) + break; + + if (batch.zns != NULL + && batch.zns->ns_id != dplane_ctx_get_ns(ctx)->ns_id) + nl_batch_send(&batch); + + /* + * Assume all messages will succeed and then mark only the ones + * that failed. + */ + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); + + res = nl_put_msg(&batch, ctx); + + dplane_ctx_enqueue_tail(&(batch.ctx_list), ctx); + if (res == FRR_NETLINK_ERROR) + dplane_ctx_set_status(ctx, + ZEBRA_DPLANE_REQUEST_FAILURE); + + if (batch.curlen > batch.limit) + nl_batch_send(&batch); + } + + nl_batch_send(&batch); + + TAILQ_INIT(ctx_list); + dplane_ctx_list_append(ctx_list, &handled_list); +} + /* Exported interface function. This function simply calls netlink_socket (). */ void kernel_init(struct zebra_ns *zns) @@ -1161,6 +1453,15 @@ void kernel_init(struct zebra_ns *zns) if (ret < 0) zlog_notice("Registration for extended dp ACK failed : %d %s", errno, safe_strerror(errno)); + + /* + * Trim off the payload of the original netlink message in the + * acknowledgment. This option is available since Linux 4.2, so if + * setsockopt fails, ignore the error. + */ + one = 1; + ret = setsockopt(zns->netlink_dplane.sock, SOL_NETLINK, NETLINK_CAP_ACK, + &one, sizeof(one)); #endif /* Register kernel socket. */ @@ -1177,8 +1478,11 @@ void kernel_init(struct zebra_ns *zns) zns->netlink_dplane.name, safe_strerror(errno), errno); /* Set receive buffer size if it's set from command line */ - if (nl_rcvbufsize) + if (nl_rcvbufsize) { netlink_recvbuf(&zns->netlink, nl_rcvbufsize); + netlink_recvbuf(&zns->netlink_cmd, nl_rcvbufsize); + netlink_recvbuf(&zns->netlink_dplane, nl_rcvbufsize); + } netlink_install_filter(zns->netlink.sock, zns->netlink_cmd.snl.nl_pid, diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index bd8159faf3..696f9be4f6 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -86,10 +86,6 @@ extern const char *nl_rtproto_to_str(uint8_t rtproto); extern const char *nl_family_to_str(uint8_t family); extern const char *nl_rttype_to_str(uint8_t rttype); -#if defined(HANDLE_NETLINK_FUZZING) -extern bool netlink_read; -extern void netlink_read_init(const char *fname); -#endif /* HANDLE_NETLINK_FUZZING */ extern int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), const struct nlsock *nl, const struct zebra_dplane_info *dp_info, @@ -98,13 +94,49 @@ extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup); extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, int startup); -/* Version with 'info' struct only */ -int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), - struct nlmsghdr *n, - const struct zebra_dplane_info *dp_info, int startup); - extern int netlink_request(struct nlsock *nl, void *req); +enum netlink_msg_status { + FRR_NETLINK_SUCCESS, + FRR_NETLINK_ERROR, + FRR_NETLINK_QUEUED, +}; + +struct nl_batch; + +/* + * netlink_batch_add_msg - add message to the netlink batch using dplane + * context object. + * + * @ctx: Dataplane context + * @msg_encoder: A function that encodes dplane context object into + * netlink message. Should take dplane context object, + * pointer to a buffer and buffer's length as parameters + * and should return -1 on error, 0 on buffer overflow or + * size of the encoded message. + * @ignore_res: Whether the result of this message should be ignored. + * This should be used in some 'update' cases where we + * need to send two messages for one context object. + * + * Return: Status of the message. + */ +extern enum netlink_msg_status netlink_batch_add_msg( + struct nl_batch *bth, struct zebra_dplane_ctx *ctx, + ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t), + bool ignore_res); + +/* + * Vty/cli apis + */ +extern int netlink_config_write_helper(struct vty *vty); + +/* + * Configure size of the batch buffer and sending threshold. If 'unset', reset + * to default value. + */ +extern void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, + bool set); + #endif /* HAVE_NETLINK */ #ifdef __cplusplus diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 873d221149..02963651a0 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1136,7 +1136,7 @@ void rtm_read(struct rt_msghdr *rtm) if (rtm->rtm_type == RTM_CHANGE) rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, NULL, 0, RT_TABLE_MAIN, 0, - 0, true); + 0, true, false); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) rib_add(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, @@ -1145,7 +1145,7 @@ void rtm_read(struct rt_msghdr *rtm) else rib_delete(afi, SAFI_UNICAST, VRF_DEFAULT, ZEBRA_ROUTE_KERNEL, 0, zebra_flags, &p, NULL, &nh, 0, RT_TABLE_MAIN, 0, - 0, true); + 0, true, false); } /* Interface function for the kernel routing table updates. Support @@ -1464,4 +1464,99 @@ void kernel_terminate(struct zebra_ns *zns, bool complete) return; } +void kernel_update_multi(struct dplane_ctx_q *ctx_list) +{ + struct zebra_dplane_ctx *ctx; + struct dplane_ctx_q handled_list; + enum zebra_dplane_result res; + + TAILQ_INIT(&handled_list); + + while (true) { + ctx = dplane_ctx_dequeue(ctx_list); + if (ctx == NULL) + break; + + /* + * A previous provider plugin may have asked to skip the + * kernel update. + */ + if (dplane_ctx_is_skip_kernel(ctx)) { + res = ZEBRA_DPLANE_REQUEST_SUCCESS; + goto skip_one; + } + + switch (dplane_ctx_get_op(ctx)) { + + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + res = kernel_route_update(ctx); + break; + + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + res = kernel_nexthop_update(ctx); + break; + + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + res = kernel_lsp_update(ctx); + break; + + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + res = kernel_pw_update(ctx); + break; + + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + res = kernel_address_update_ctx(ctx); + break; + + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + res = kernel_mac_update_ctx(ctx); + break; + + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + res = kernel_neigh_update_ctx(ctx); + break; + + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + res = kernel_pbr_rule_update(ctx); + break; + + /* Ignore 'notifications' - no-op */ + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: + res = ZEBRA_DPLANE_REQUEST_SUCCESS; + break; + + default: + res = ZEBRA_DPLANE_REQUEST_FAILURE; + break; + } + + skip_one: + dplane_ctx_set_status(ctx, res); + + dplane_ctx_enqueue_tail(&handled_list, ctx); + } + + TAILQ_INIT(ctx_list); + dplane_ctx_list_append(ctx_list, &handled_list); +} + #endif /* !HAVE_NETLINK */ diff --git a/zebra/label_manager.c b/zebra/label_manager.c index 93736e672a..d312a661f3 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -240,7 +240,7 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance, if (lmc->proto != NO_PROTO) return NULL; - if (end < lmc->end) { + if (end <= lmc->end) { last_node = node; break; } diff --git a/zebra/main.c b/zebra/main.c index 92e94c2a2a..cfc45567d7 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -57,10 +57,7 @@ #include "zebra/zebra_routemap.h" #include "zebra/zebra_nb.h" #include "zebra/zebra_opaque.h" - -#if defined(HANDLE_NETLINK_FUZZING) -#include "zebra/kernel_netlink.h" -#endif /* HANDLE_NETLINK_FUZZING */ +#include "zebra/zebra_srte.h" #define ZEBRA_PTM_SUPPORT @@ -283,12 +280,6 @@ int main(int argc, char **argv) char *vrf_default_name_configured = NULL; struct sockaddr_storage dummy; socklen_t dummylen; -#if defined(HANDLE_ZAPI_FUZZING) - char *zapi_fuzzing = NULL; -#endif /* HANDLE_ZAPI_FUZZING */ -#if defined(HANDLE_NETLINK_FUZZING) - char *netlink_fuzzing = NULL; -#endif /* HANDLE_NETLINK_FUZZING */ graceful_restart = 0; vrf_configure_backend(VRF_BACKEND_VRF_LITE); @@ -300,12 +291,6 @@ int main(int argc, char **argv) #ifdef HAVE_NETLINK "s:n" #endif -#if defined(HANDLE_ZAPI_FUZZING) - "c:" -#endif /* HANDLE_ZAPI_FUZZING */ -#if defined(HANDLE_NETLINK_FUZZING) - "w:" -#endif /* HANDLE_NETLINK_FUZZING */ , longopts, " -b, --batch Runs in batch mode\n" @@ -320,12 +305,6 @@ int main(int argc, char **argv) " -s, --nl-bufsize Set netlink receive buffer size\n" " --v6-rr-semantics Use v6 RR semantics\n" #endif /* HAVE_NETLINK */ -#if defined(HANDLE_ZAPI_FUZZING) - " -c <file> Bypass normal startup and use this file for testing of zapi\n" -#endif /* HANDLE_ZAPI_FUZZING */ -#if defined(HANDLE_NETLINK_FUZZING) - " -w <file> Bypass normal startup and use this file for testing of netlink input\n" -#endif /* HANDLE_NETLINK_FUZZING */ ); while (1) { @@ -387,21 +366,6 @@ int main(int argc, char **argv) v6_rr_semantics = true; break; #endif /* HAVE_NETLINK */ -#if defined(HANDLE_ZAPI_FUZZING) - case 'c': - zapi_fuzzing = optarg; - break; -#endif /* HANDLE_ZAPI_FUZZING */ -#if defined(HANDLE_NETLINK_FUZZING) - case 'w': - netlink_fuzzing = optarg; - /* This ensures we are aren't writing any of the - * startup netlink messages that happen when we - * just want to read. - */ - netlink_read = true; - break; -#endif /* HANDLE_NETLINK_FUZZING */ default: frr_help_exit(1); break; @@ -438,9 +402,10 @@ int main(int argc, char **argv) zebra_pw_vty_init(); zebra_pbr_init(); zebra_opaque_init(); + zebra_srte_init(); -/* For debug purpose. */ -/* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ + /* For debug purpose. */ + /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ /* Process the configuration file. Among other configuration * directives we can meet those installing static routes. Such @@ -487,20 +452,6 @@ int main(int argc, char **argv) /* Error init */ zebra_error_init(); -#if defined(HANDLE_ZAPI_FUZZING) - if (zapi_fuzzing) { - zserv_read_file(zapi_fuzzing); - exit(0); - } -#endif /* HANDLE_ZAPI_FUZZING */ -#if defined(HANDLE_NETLINK_FUZZING) - if (netlink_fuzzing) { - netlink_read_init(netlink_fuzzing); - exit(0); - } -#endif /* HANDLE_NETLINK_FUZZING */ - - frr_run(zrouter.master); /* Not reached... */ diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 4d6346151a..c0f89e6afe 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -200,8 +200,8 @@ void redistribute_update(const struct prefix *p, const struct prefix *src_p, if (IS_ZEBRA_DEBUG_RIB) { zlog_debug( - "%u:%s: Redist update re %p (%s), old %p (%s)", - re->vrf_id, prefix2str(p, buf, sizeof(buf)), + "(%u:%u):%s: Redist update re %p (%s), old %p (%s)", + re->vrf_id, re->table, prefix2str(p, buf, sizeof(buf)), re, zebra_route_string(re->type), prev_re, prev_re ? zebra_route_string(prev_re->type) : "None"); } @@ -224,12 +224,12 @@ void redistribute_update(const struct prefix *p, const struct prefix *src_p, if (zebra_redistribute_check(re, client, p, afi)) { if (IS_ZEBRA_DEBUG_RIB) { zlog_debug( - "%s: client %s %s(%u), type=%d, distance=%d, metric=%d", - __func__, - zebra_route_string(client->proto), - prefix2str(p, buf, sizeof(buf)), - re->vrf_id, re->type, - re->distance, re->metric); + "%s: client %s %s(%u:%u), type=%d, distance=%d, metric=%d", + __func__, + zebra_route_string(client->proto), + prefix2str(p, buf, sizeof(buf)), + re->vrf_id, re->table, re->type, + re->distance, re->metric); } zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, p, src_p, re); @@ -727,7 +727,7 @@ int zebra_del_import_table_entry(struct zebra_vrf *zvrf, struct route_node *rn, rib_delete(afi, SAFI_UNICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_TABLE, re->table, re->flags, &p, NULL, re->nhe->nhg.nexthop, re->nhe_id, zvrf->table_id, re->metric, re->distance, - false); + false, false); return 0; } diff --git a/zebra/rib.h b/zebra/rib.h index b9f4e56905..be680a112f 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -379,7 +379,7 @@ extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, uint32_t nhe_id, uint32_t table_id, uint32_t metric, - uint8_t distance, bool fromkernel); + uint8_t distance, bool fromkernel, bool connected_down); extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, union g_addr *addr, diff --git a/zebra/router-id.c b/zebra/router-id.c index ba9e721614..7e81f29827 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -59,9 +59,6 @@ static struct connected *router_id_find_node(struct list *l, static int router_id_bad_address(struct connected *ifc) { - if (ifc->address->family != AF_INET) - return 1; - /* non-redistributable addresses shouldn't be used for RIDs either */ if (!zebra_check_addr(ifc->address)) return 1; @@ -69,40 +66,82 @@ static int router_id_bad_address(struct connected *ifc) return 0; } -void router_id_get(struct prefix *p, struct zebra_vrf *zvrf) +static bool router_id_v6_is_any(struct prefix *p) +{ + return memcmp(&p->u.prefix6, &in6addr_any, sizeof(struct in6_addr)) + == 0; +} + +int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) { struct listnode *node; struct connected *c; - - p->u.prefix4.s_addr = INADDR_ANY; - p->family = AF_INET; - p->prefixlen = 32; - - if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) - p->u.prefix4.s_addr = zvrf->rid_user_assigned.u.prefix4.s_addr; - else if (!list_isempty(zvrf->rid_lo_sorted_list)) { - node = listtail(zvrf->rid_lo_sorted_list); - c = listgetdata(node); - p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; - } else if (!list_isempty(zvrf->rid_all_sorted_list)) { - node = listtail(zvrf->rid_all_sorted_list); - c = listgetdata(node); - p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; + struct in6_addr *addr = NULL; + + switch (afi) { + case AFI_IP: + p->u.prefix4.s_addr = INADDR_ANY; + p->family = AF_INET; + p->prefixlen = 32; + if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) + p->u.prefix4.s_addr = + zvrf->rid_user_assigned.u.prefix4.s_addr; + else if (!list_isempty(zvrf->rid_lo_sorted_list)) { + node = listtail(zvrf->rid_lo_sorted_list); + c = listgetdata(node); + p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; + } else if (!list_isempty(zvrf->rid_all_sorted_list)) { + node = listtail(zvrf->rid_all_sorted_list); + c = listgetdata(node); + p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; + } + return 0; + case AFI_IP6: + p->u.prefix6 = in6addr_any; + p->family = AF_INET6; + p->prefixlen = 128; + if (!router_id_v6_is_any(&zvrf->rid6_user_assigned)) + addr = &zvrf->rid6_user_assigned.u.prefix6; + else if (!list_isempty(zvrf->rid6_lo_sorted_list)) { + node = listtail(zvrf->rid6_lo_sorted_list); + c = listgetdata(node); + addr = &c->address->u.prefix6; + } else if (!list_isempty(zvrf->rid6_all_sorted_list)) { + node = listtail(zvrf->rid6_all_sorted_list); + c = listgetdata(node); + addr = &c->address->u.prefix6; + } + if (addr) + memcpy(&p->u.prefix6, addr, sizeof(struct in6_addr)); + return 0; + default: + return -1; } } -static void router_id_set(struct prefix *p, struct zebra_vrf *zvrf) +static int router_id_set(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) { struct prefix p2; struct listnode *node; struct zserv *client; - zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr; + switch (afi) { + case AFI_IP: + zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr; + break; + case AFI_IP6: + zvrf->rid6_user_assigned.u.prefix6 = p->u.prefix6; + break; + default: + return -1; + } - router_id_get(&p2, zvrf); + router_id_get(afi, &p2, zvrf); for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) - zsend_router_id_update(client, &p2, zvrf->vrf->vrf_id); + zsend_router_id_update(client, afi, &p2, zvrf->vrf->vrf_id); + + return 0; } void router_id_add_address(struct connected *ifc) @@ -113,27 +152,42 @@ void router_id_add_address(struct connected *ifc) struct prefix after; struct zserv *client; struct zebra_vrf *zvrf = vrf_info_get(ifc->ifp->vrf_id); + afi_t afi; + struct list *rid_lo; + struct list *rid_all; if (router_id_bad_address(ifc)) return; - router_id_get(&before, zvrf); + switch (ifc->address->family) { + case AF_INET: + afi = AFI_IP; + rid_lo = zvrf->rid_lo_sorted_list; + rid_all = zvrf->rid_all_sorted_list; + break; + case AF_INET6: + afi = AFI_IP6; + rid_lo = zvrf->rid6_lo_sorted_list; + rid_all = zvrf->rid6_all_sorted_list; + break; + default: + return; + } - if (if_is_loopback(ifc->ifp)) - l = zvrf->rid_lo_sorted_list; - else - l = zvrf->rid_all_sorted_list; + router_id_get(afi, &before, zvrf); + + l = if_is_loopback(ifc->ifp) ? rid_lo : rid_all; if (!router_id_find_node(l, ifc)) listnode_add_sort(l, ifc); - router_id_get(&after, zvrf); + router_id_get(afi, &after, zvrf); if (prefix_same(&before, &after)) return; for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) - zsend_router_id_update(client, &after, zvrf_id(zvrf)); + zsend_router_id_update(client, afi, &after, zvrf_id(zvrf)); } void router_id_del_address(struct connected *ifc) @@ -145,27 +199,45 @@ void router_id_del_address(struct connected *ifc) struct listnode *node; struct zserv *client; struct zebra_vrf *zvrf = vrf_info_get(ifc->ifp->vrf_id); + afi_t afi; + struct list *rid_lo; + struct list *rid_all; if (router_id_bad_address(ifc)) return; - router_id_get(&before, zvrf); + switch (ifc->address->family) { + case AF_INET: + afi = AFI_IP; + rid_lo = zvrf->rid_lo_sorted_list; + rid_all = zvrf->rid_all_sorted_list; + break; + case AF_INET6: + afi = AFI_IP6; + rid_lo = zvrf->rid6_lo_sorted_list; + rid_all = zvrf->rid6_all_sorted_list; + break; + default: + return; + } + + router_id_get(afi, &before, zvrf); if (if_is_loopback(ifc->ifp)) - l = zvrf->rid_lo_sorted_list; + l = rid_lo; else - l = zvrf->rid_all_sorted_list; + l = rid_all; if ((c = router_id_find_node(l, ifc))) listnode_delete(l, c); - router_id_get(&after, zvrf); + router_id_get(afi, &after, zvrf); if (prefix_same(&before, &after)) return; for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) - zsend_router_id_update(client, &after, zvrf_id(zvrf)); + zsend_router_id_update(client, afi, &after, zvrf_id(zvrf)); } void router_id_write(struct vty *vty, struct zebra_vrf *zvrf) @@ -178,70 +250,147 @@ void router_id_write(struct vty *vty, struct zebra_vrf *zvrf) snprintf(space, sizeof(space), "%s", " "); if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) { - vty_out(vty, "%srouter-id %s\n", space, - inet_ntoa(zvrf->rid_user_assigned.u.prefix4)); + vty_out(vty, "%sip router-id %pI4\n", space, + &zvrf->rid_user_assigned.u.prefix4); + } + if (!router_id_v6_is_any(&zvrf->rid6_user_assigned)) { + vty_out(vty, "%sipv6 router-id %pI6\n", space, + &zvrf->rid_user_assigned.u.prefix6); } } -DEFUN (router_id, - router_id_cmd, - "router-id A.B.C.D vrf NAME", +DEFUN (ip_router_id, + ip_router_id_cmd, + "ip router-id A.B.C.D vrf NAME", + IP_STR "Manually set the router-id\n" - "IP address to use for router-id\n" VRF_CMD_HELP_STR) + "IP address to use for router-id\n" + VRF_CMD_HELP_STR) { - int idx_ipv4 = 1; - int idx_name = 3; + int idx = 0; + struct prefix rid; + vrf_id_t vrf_id; struct zebra_vrf *zvrf; - struct prefix rid; - vrf_id_t vrf_id = VRF_DEFAULT; + argv_find(argv, argc, "A.B.C.D", &idx); - rid.u.prefix4.s_addr = inet_addr(argv[idx_ipv4]->arg); - if (!rid.u.prefix4.s_addr) + if (!inet_pton(AF_INET, argv[idx]->arg, &rid.u.prefix4)) return CMD_WARNING_CONFIG_FAILED; rid.prefixlen = 32; rid.family = AF_INET; - if (argc > 2) - VRF_GET_ID(vrf_id, argv[idx_name]->arg, false); + argv_find(argv, argc, "NAME", &idx); + VRF_GET_ID(vrf_id, argv[idx]->arg, false); zvrf = vrf_info_lookup(vrf_id); - router_id_set(&rid, zvrf); + router_id_set(AFI_IP, &rid, zvrf); return CMD_SUCCESS; } -DEFUN (router_id_in_vrf, - router_id_in_vrf_cmd, - "router-id A.B.C.D", +ALIAS (ip_router_id, + router_id_cmd, + "router-id A.B.C.D vrf NAME", + "Manually set the router-id\n" + "IP address to use for router-id\n" + VRF_CMD_HELP_STR); + +DEFUN (ipv6_router_id, + ipv6_router_id_cmd, + "ipv6 router-id X:X::X:X vrf NAME", + IPV6_STR + "Manually set the router-id\n" + "IPv6 address to use for router-id\n" + VRF_CMD_HELP_STR) +{ + int idx = 0; + struct prefix rid; + vrf_id_t vrf_id; + struct zebra_vrf *zvrf; + + argv_find(argv, argc, "X:X::X:X", &idx); + + if (!inet_pton(AF_INET6, argv[idx]->arg, &rid.u.prefix6)) + return CMD_WARNING_CONFIG_FAILED; + + rid.prefixlen = 128; + rid.family = AF_INET6; + + argv_find(argv, argc, "NAME", &idx); + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP6, &rid, zvrf); + + return CMD_SUCCESS; +} + + +DEFUN (ip_router_id_in_vrf, + ip_router_id_in_vrf_cmd, + "ip router-id A.B.C.D", + IP_STR "Manuall set the router-id\n" "IP address to use for router-id\n") { ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); - int idx_ipv4 = 1; + int idx = 0; struct prefix rid; - rid.u.prefix4.s_addr = inet_addr(argv[idx_ipv4]->arg); - if (!rid.u.prefix4.s_addr) + argv_find(argv, argc, "A.B.C.D", &idx); + + if (!inet_pton(AF_INET, argv[idx]->arg, &rid.u.prefix4)) return CMD_WARNING_CONFIG_FAILED; rid.prefixlen = 32; rid.family = AF_INET; - router_id_set(&rid, zvrf); + router_id_set(AFI_IP, &rid, zvrf); return CMD_SUCCESS; } -DEFUN (no_router_id, - no_router_id_cmd, - "no router-id [A.B.C.D vrf NAME]", +ALIAS (ip_router_id_in_vrf, + router_id_in_vrf_cmd, + "router-id A.B.C.D", + "Manually set the router-id\n" + "IP address to use for router-id\n"); + +DEFUN (ipv6_router_id_in_vrf, + ipv6_router_id_in_vrf_cmd, + "ipv6 router-id X:X::X:X", + IP6_STR + "Manuall set the IPv6 router-id\n" + "IPV6 address to use for router-id\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + int idx = 0; + struct prefix rid; + + argv_find(argv, argc, "X:X::X:X", &idx); + + if (!inet_pton(AF_INET6, argv[idx]->arg, &rid.u.prefix6)) + return CMD_WARNING_CONFIG_FAILED; + + rid.prefixlen = 128; + rid.family = AF_INET6; + + router_id_set(AFI_IP6, &rid, zvrf); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_router_id, + no_ip_router_id_cmd, + "no ip router-id [A.B.C.D vrf NAME]", NO_STR + IP_STR "Remove the manually configured router-id\n" - "IP address to use for router-id\n" VRF_CMD_HELP_STR) + "IP address to use for router-id\n" + VRF_CMD_HELP_STR) { - int idx_name = 4; + int idx = 0; struct prefix rid; vrf_id_t vrf_id = VRF_DEFAULT; struct zebra_vrf *zvrf; @@ -250,19 +399,54 @@ DEFUN (no_router_id, rid.prefixlen = 0; rid.family = AF_INET; - if (argc > 3) - VRF_GET_ID(vrf_id, argv[idx_name]->arg, false); + if (argv_find(argv, argc, "NAME", &idx)) + VRF_GET_ID(vrf_id, argv[idx]->arg, false); - zvrf = vrf_info_get(vrf_id); - router_id_set(&rid, zvrf); + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP, &rid, zvrf); return CMD_SUCCESS; } -DEFUN (no_router_id_in_vrf, - no_router_id_in_vrf_cmd, - "no router-id [A.B.C.D]", +ALIAS (no_ip_router_id, + no_router_id_cmd, + "no router-id [A.B.C.D vrf NAME]", + NO_STR + "Remove the manually configured router-id\n" + "IP address to use for router-id\n" + VRF_CMD_HELP_STR); + +DEFUN (no_ipv6_router_id, + no_ipv6_router_id_cmd, + "no ipv6 router-id [X:X::X:X vrf NAME]", + NO_STR + IPV6_STR + "Remove the manually configured IPv6 router-id\n" + "IPv6 address to use for router-id\n" + VRF_CMD_HELP_STR) +{ + int idx = 0; + struct prefix rid; + vrf_id_t vrf_id = VRF_DEFAULT; + struct zebra_vrf *zvrf; + + memset(&rid, 0, sizeof(rid)); + rid.family = AF_INET; + + if (argv_find(argv, argc, "NAME", &idx)) + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP6, &rid, zvrf); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_router_id_in_vrf, + no_ip_router_id_in_vrf_cmd, + "no ip router-id [A.B.C.D]", NO_STR + IP_STR "Remove the manually configured router-id\n" "IP address to use for router-id\n") { @@ -274,40 +458,81 @@ DEFUN (no_router_id_in_vrf, rid.prefixlen = 0; rid.family = AF_INET; - router_id_set(&rid, zvrf); + router_id_set(AFI_IP, &rid, zvrf); return CMD_SUCCESS; } -DEFUN (show_router_id, - show_router_id_cmd, - "show router-id [vrf NAME]", +ALIAS (no_ip_router_id_in_vrf, + no_router_id_in_vrf_cmd, + "no router-id [A.B.C.D]", + NO_STR + "Remove the manually configured router-id\n" + "IP address to use for router-id\n"); + +DEFUN (no_ipv6_router_id_in_vrf, + no_ipv6_router_id_in_vrf_cmd, + "no ipv6 router-id [X:X::X:X]", + NO_STR + IP6_STR + "Remove the manually configured IPv6 router-id\n" + "IPv6 address to use for router-id\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + + struct prefix rid; + + memset(&rid, 0, sizeof(rid)); + rid.family = AF_INET; + + router_id_set(AFI_IP6, &rid, zvrf); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_router_id, + show_ip_router_id_cmd, + "show [ip|ipv6] router-id [vrf NAME]", SHOW_STR + IP_STR + IPV6_STR "Show the configured router-id\n" VRF_CMD_HELP_STR) { - int idx_name = 3; + int idx = 0; + vrf_id_t vrf_id = VRF_DEFAULT; + struct zebra_vrf *zvrf; + const char *vrf_name = "default"; + char addr_name[INET6_ADDRSTRLEN]; + int is_ipv6 = 0; - vrf_id_t vrf_id = VRF_DEFAULT; - struct zebra_vrf *zvrf; + is_ipv6 = argv_find(argv, argc, "ipv6", &idx); - if (argc > 2) - VRF_GET_ID(vrf_id, argv[idx_name]->arg, false); + if (argv_find(argv, argc, "NAME", &idx)) { + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + vrf_name = argv[idx]->arg; + } - zvrf = vrf_info_get(vrf_id); + zvrf = vrf_info_get(vrf_id); - if ((zvrf != NULL) && (zvrf->rid_user_assigned.u.prefix4.s_addr)) { - vty_out(vty, "zebra:\n"); - if (vrf_id == VRF_DEFAULT) - vty_out(vty, " router-id %s vrf default\n", - inet_ntoa(zvrf->rid_user_assigned.u.prefix4)); - else - vty_out(vty, " router-id %s vrf %s\n", - inet_ntoa(zvrf->rid_user_assigned.u.prefix4), - argv[idx_name]->arg); - } + if (zvrf != NULL) { + if (is_ipv6) { + if (router_id_v6_is_any(&zvrf->rid6_user_assigned)) + return CMD_SUCCESS; + inet_ntop(AF_INET6, &zvrf->rid6_user_assigned.u.prefix6, + addr_name, sizeof(addr_name)); + } else { + if (zvrf->rid_user_assigned.u.prefix4.s_addr == 0) + return CMD_SUCCESS; + inet_ntop(AF_INET, &zvrf->rid_user_assigned.u.prefix4, + addr_name, sizeof(addr_name)); + } + + vty_out(vty, "zebra:\n"); + vty_out(vty, " router-id %s vrf %s\n", addr_name, vrf_name); + } - return CMD_SUCCESS; + return CMD_SUCCESS; } static int router_id_cmp(void *a, void *b) @@ -319,30 +544,62 @@ static int router_id_cmp(void *a, void *b) &ifb->address->u.prefix4.s_addr); } +static int router_id_v6_cmp(void *a, void *b) +{ + const struct connected *ifa = (const struct connected *)a; + const struct connected *ifb = (const struct connected *)b; + + return IPV6_ADDR_CMP(&ifa->address->u.prefix6, + &ifb->address->u.prefix6); +} + void router_id_cmd_init(void) { + install_element(CONFIG_NODE, &ip_router_id_cmd); install_element(CONFIG_NODE, &router_id_cmd); + install_element(CONFIG_NODE, &ipv6_router_id_cmd); + install_element(CONFIG_NODE, &no_ip_router_id_cmd); install_element(CONFIG_NODE, &no_router_id_cmd); + install_element(CONFIG_NODE, &ip_router_id_in_vrf_cmd); + install_element(VRF_NODE, &ip_router_id_in_vrf_cmd); install_element(CONFIG_NODE, &router_id_in_vrf_cmd); install_element(VRF_NODE, &router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &ipv6_router_id_in_vrf_cmd); + install_element(VRF_NODE, &ipv6_router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &no_ipv6_router_id_cmd); + install_element(CONFIG_NODE, &no_ip_router_id_in_vrf_cmd); + install_element(VRF_NODE, &no_ip_router_id_in_vrf_cmd); install_element(CONFIG_NODE, &no_router_id_in_vrf_cmd); install_element(VRF_NODE, &no_router_id_in_vrf_cmd); - install_element(VIEW_NODE, &show_router_id_cmd); + install_element(CONFIG_NODE, &no_ipv6_router_id_in_vrf_cmd); + install_element(VRF_NODE, &no_ipv6_router_id_in_vrf_cmd); + install_element(VIEW_NODE, &show_ip_router_id_cmd); } void router_id_init(struct zebra_vrf *zvrf) { zvrf->rid_all_sorted_list = &zvrf->_rid_all_sorted_list; zvrf->rid_lo_sorted_list = &zvrf->_rid_lo_sorted_list; + zvrf->rid6_all_sorted_list = &zvrf->_rid6_all_sorted_list; + zvrf->rid6_lo_sorted_list = &zvrf->_rid6_lo_sorted_list; memset(zvrf->rid_all_sorted_list, 0, sizeof(zvrf->_rid_all_sorted_list)); memset(zvrf->rid_lo_sorted_list, 0, sizeof(zvrf->_rid_lo_sorted_list)); memset(&zvrf->rid_user_assigned, 0, sizeof(zvrf->rid_user_assigned)); + memset(zvrf->rid6_all_sorted_list, 0, + sizeof(zvrf->_rid6_all_sorted_list)); + memset(zvrf->rid6_lo_sorted_list, 0, + sizeof(zvrf->_rid6_lo_sorted_list)); + memset(&zvrf->rid6_user_assigned, 0, sizeof(zvrf->rid6_user_assigned)); zvrf->rid_all_sorted_list->cmp = router_id_cmp; zvrf->rid_lo_sorted_list->cmp = router_id_cmp; + zvrf->rid6_all_sorted_list->cmp = router_id_v6_cmp; + zvrf->rid6_lo_sorted_list->cmp = router_id_v6_cmp; zvrf->rid_user_assigned.family = AF_INET; zvrf->rid_user_assigned.prefixlen = 32; + zvrf->rid6_user_assigned.family = AF_INET6; + zvrf->rid6_user_assigned.prefixlen = 128; } diff --git a/zebra/router-id.h b/zebra/router-id.h index c69c321fda..4a35f6605b 100644 --- a/zebra/router-id.h +++ b/zebra/router-id.h @@ -39,7 +39,7 @@ extern void router_id_del_address(struct connected *c); extern void router_id_init(struct zebra_vrf *zvrf); extern void router_id_cmd_init(void); extern void router_id_write(struct vty *vty, struct zebra_vrf *zvrf); -extern void router_id_get(struct prefix *p, struct zebra_vrf *zvrf); +extern int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf); #ifdef __cplusplus } diff --git a/zebra/rt.h b/zebra/rt.h index 143e16b3ea..48f1df2868 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -40,7 +40,7 @@ extern "C" { #define RSYSTEM_ROUTE(type) \ ((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT) - +#ifndef HAVE_NETLINK /* * Update or delete a route, nexthop, LSP, pseudowire, or vxlan MAC from the * kernel, using info from a dataplane context. @@ -63,6 +63,11 @@ enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx); enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx); +extern enum zebra_dplane_result +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_interface_set_master(struct interface *master, @@ -97,6 +102,11 @@ extern int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt, struct nh_grp *nh_ids); extern int kernel_del_mac_nhg(uint32_t nhg_id); +/* + * Message batching interface. + */ +extern void kernel_update_multi(struct dplane_ctx_q *ctx_list); + #ifdef __cplusplus } #endif diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 4daef42d7a..50b1a62d86 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -147,6 +147,8 @@ static uint8_t neigh_flags_to_netlink(uint8_t dplane_flags) flags |= NTF_EXT_LEARNED; if (dplane_flags & DPLANE_NTF_ROUTER) flags |= NTF_ROUTER; + if (dplane_flags & DPLANE_NTF_USE) + flags |= NTF_USE; return flags; } @@ -166,6 +168,8 @@ static uint16_t neigh_state_to_netlink(uint16_t dplane_state) state |= NUD_NOARP; if (dplane_state & DPLANE_NUD_PROBE) state |= NUD_PROBE; + if (dplane_state & DPLANE_NUD_INCOMPLETE) + state |= NUD_INCOMPLETE; return state; } @@ -179,7 +183,8 @@ static inline bool is_selfroute(int proto) || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP) || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL) || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP) - || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)) { + || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC) + || (proto == RTPROT_SRTE)) { return true; } @@ -229,6 +234,9 @@ static inline int zebra2proto(int proto) case ZEBRA_ROUTE_OPENFABRIC: proto = RTPROT_OPENFABRIC; break; + case ZEBRA_ROUTE_SRTE: + proto = RTPROT_SRTE; + break; case ZEBRA_ROUTE_TABLE: case ZEBRA_ROUTE_NHG: proto = RTPROT_ZEBRA; @@ -294,6 +302,9 @@ static inline int proto2zebra(int proto, int family, bool is_nexthop) case RTPROT_OPENFABRIC: proto = ZEBRA_ROUTE_OPENFABRIC; break; + case RTPROT_SRTE: + proto = ZEBRA_ROUTE_SRTE; + break; case RTPROT_ZEBRA: if (is_nexthop) { proto = ZEBRA_ROUTE_NHG; @@ -826,7 +837,7 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, if (nhe_id) { rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, &src_p, NULL, nhe_id, table, metric, - distance, true); + distance, true, false); } else { if (!tb[RTA_MULTIPATH]) { struct nexthop nh; @@ -836,13 +847,13 @@ static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id, gate, afi, vrf_id); rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, &src_p, &nh, 0, table, - metric, distance, true); + metric, distance, true, false); } else { /* XXX: need to compare the entire list of * nexthops here for NLM_F_APPEND stupidity */ rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p, &src_p, NULL, 0, table, - metric, distance, true); + metric, distance, true, false); } } } @@ -2223,19 +2234,11 @@ nexthop_done: return NLMSG_ALIGN(req->n.nlmsg_len); } -/** - * kernel_nexthop_update() - Update/delete a nexthop from the kernel - * - * @ctx: Dataplane context - * - * Return: Dataplane result flag - */ -enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) +static ssize_t netlink_nexthop_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { enum dplane_op_e op; int cmd = 0; - int ret = 0; - char buf[NL_PKT_BUF_SIZE]; op = dplane_ctx_get_op(ctx); if (op == DPLANE_OP_NH_INSTALL || op == DPLANE_OP_NH_UPDATE) @@ -2246,33 +2249,43 @@ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) flog_err(EC_ZEBRA_NHG_FIB_UPDATE, "Context received for kernel nexthop update with incorrect OP code (%u)", op); - return ZEBRA_DPLANE_REQUEST_FAILURE; + return -1; } + return netlink_nexthop_msg_encode(cmd, ctx, buf, buflen); +} + +enum netlink_msg_status +netlink_put_nexthop_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ /* Nothing to do if the kernel doesn't support nexthop objects */ if (!kernel_nexthops_supported()) - return ZEBRA_DPLANE_REQUEST_SUCCESS; + return FRR_NETLINK_SUCCESS; - if (netlink_nexthop_msg_encode(cmd, ctx, buf, sizeof(buf)) > 0) - ret = netlink_talk_info(netlink_talk_filter, (void *)&buf, - dplane_ctx_get_ns(ctx), 0); - else - ret = 0; + return netlink_batch_add_msg(bth, ctx, netlink_nexthop_msg_encoder, + false); +} - return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS - : ZEBRA_DPLANE_REQUEST_FAILURE); +static ssize_t netlink_newroute_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_route_multipath_msg_encode(RTM_NEWROUTE, ctx, buf, + buflen, false, false); } -/* - * Update or delete a prefix from the kernel, - * using info from a dataplane context. - */ -enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) +static ssize_t netlink_delroute_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_route_multipath_msg_encode(RTM_DELROUTE, ctx, buf, + buflen, false, false); +} + +enum netlink_msg_status +netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) { - int cmd, ret; + int cmd; const struct prefix *p = dplane_ctx_get_dest(ctx); - struct nexthop *nexthop; - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) { cmd = RTM_DELROUTE; @@ -2282,7 +2295,6 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) if (p->family == AF_INET || v6_rr_semantics) { /* Single 'replace' operation */ - cmd = RTM_NEWROUTE; /* * With route replace semantics in place @@ -2292,17 +2304,11 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) * route should cause us to withdraw from * the kernel the old non-system route */ - if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)) && - !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) { - if (netlink_route_multipath_msg_encode( - RTM_DELROUTE, ctx, nl_pkt, - sizeof(nl_pkt), false, false) - > 0) - netlink_talk_info( - netlink_talk_filter, - (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); - } + if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)) + && !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) + netlink_batch_add_msg( + bth, ctx, netlink_delroute_msg_encoder, + true); } else { /* * So v6 route replace semantics are not in @@ -2316,51 +2322,24 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) * of the route delete. If that happens yeah we're * screwed. */ - if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) { - if (netlink_route_multipath_msg_encode( - RTM_DELROUTE, ctx, nl_pkt, - sizeof(nl_pkt), false, false) - > 0) - netlink_talk_info( - netlink_talk_filter, - (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); - } - cmd = RTM_NEWROUTE; + if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) + netlink_batch_add_msg( + bth, ctx, netlink_delroute_msg_encoder, + true); } - } else { - return ZEBRA_DPLANE_REQUEST_FAILURE; - } - - if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) { - if (netlink_route_multipath_msg_encode( - cmd, ctx, nl_pkt, sizeof(nl_pkt), false, false) - > 0) - ret = netlink_talk_info(netlink_talk_filter, - (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); - else - ret = -1; - + cmd = RTM_NEWROUTE; } else - ret = 0; - if ((cmd == RTM_NEWROUTE) && (ret == 0)) { - /* Update installed nexthops to signal which have been - * installed. - */ - for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; + return FRR_NETLINK_ERROR; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - } - } + if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) + return FRR_NETLINK_SUCCESS; - return (ret == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); + return netlink_batch_add_msg(bth, ctx, + cmd == RTM_NEWROUTE + ? netlink_newroute_msg_encoder + : netlink_delroute_msg_encoder, + false); } /** @@ -2776,23 +2755,16 @@ static ssize_t netlink_neigh_update_msg_encode( * Add remote VTEP to the flood list for this VxLAN interface (VNI). This * is done by adding an FDB entry with a MAC of 00:00:00:00:00:00. */ -static int netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, - int cmd) +static ssize_t +netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd, + void *buf, size_t buflen) { struct ethaddr dst_mac = {.octet = {0}}; - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; - - if (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*/, - nl_pkt, sizeof(nl_pkt)) - <= 0) - return -1; - return netlink_talk_info(netlink_talk_filter, - (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); + 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*/, buf, buflen); } #ifndef NDA_RTA @@ -3141,9 +3113,8 @@ int netlink_macfdb_read_specific_mac(struct zebra_ns *zns, /* * Netlink-specific handler for MAC updates using dataplane context object. */ -ssize_t -netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data, - size_t datalen) +ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data, + size_t datalen) { struct ipaddr vtep_ip; vlanid_t vid; @@ -3619,15 +3590,14 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id) /* * Utility neighbor-update function, using info from dplane context. */ -static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, - int cmd) +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; uint8_t flags; uint16_t state; uint8_t family; - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; ip = dplane_ctx_neigh_get_ipaddr(ctx); mac = dplane_ctx_neigh_get_mac(ctx); @@ -3652,60 +3622,56 @@ static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, flags, state); } - if (netlink_neigh_update_msg_encode(ctx, cmd, mac, ip, true, family, - RTN_UNICAST, flags, state, - 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, - nl_pkt, sizeof(nl_pkt)) - <= 0) - return -1; - - return netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); + return netlink_neigh_update_msg_encode( + ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state, + 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, buf, buflen); } -/* - * Update MAC, using dataplane context object. - */ -enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx) +static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; - ssize_t rv; - - rv = netlink_macfdb_update_ctx(ctx, nl_pkt, sizeof(nl_pkt)); - if (rv <= 0) - return ZEBRA_DPLANE_REQUEST_FAILURE; - - rv = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); - - return rv == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE; -} - -enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx) -{ - int ret = -1; + ssize_t ret; switch (dplane_ctx_get_op(ctx)) { case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: - ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH); + case DPLANE_OP_NEIGH_DISCOVER: + ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen); break; case DPLANE_OP_NEIGH_DELETE: - ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH); + ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH, buf, buflen); break; case DPLANE_OP_VTEP_ADD: - ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH); + ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH, buf, + buflen); break; case DPLANE_OP_VTEP_DELETE: - ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH); + ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf, + buflen); break; default: - break; + ret = -1; } - return (ret == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); + return ret; +} + +/* + * Update MAC, using dataplane context object. + */ + +enum netlink_msg_status netlink_put_mac_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + return netlink_batch_add_msg(bth, ctx, netlink_macfdb_update_ctx, + false); +} + +enum netlink_msg_status +netlink_put_neigh_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) +{ + return netlink_batch_add_msg(bth, ctx, netlink_neigh_msg_encoder, + false); } /* diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 429ff0bf85..e1bb844785 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -60,6 +60,7 @@ extern "C" { #define RTPROT_PBR 195 #define RTPROT_ZSTATIC 196 #define RTPROT_OPENFABRIC 197 +#define RTPROT_SRTE 198 void rt_netlink_init(void); @@ -73,7 +74,7 @@ extern ssize_t netlink_route_multipath_msg_encode(int cmd, uint8_t *data, size_t datalen, bool fpm, bool force_nhg); extern ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, - uint8_t *data, size_t datalen); + void *data, size_t datalen); extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int netlink_route_read(struct zebra_ns *zns); @@ -100,6 +101,23 @@ extern int netlink_neigh_read_specific_ip(struct ipaddr *ip, struct interface *vlan_if); extern vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id); +struct nl_batch; +extern enum netlink_msg_status +netlink_put_route_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_nexthop_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_mac_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_neigh_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_lsp_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_pw_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); + #ifdef __cplusplus } #endif diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 0271dc7f41..a2e15cbd2b 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -358,20 +358,6 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) } } /* Elevated privs */ - if (RSYSTEM_ROUTE(type) - && dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) { - struct nexthop *nexthop; - - for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - } - } - return res; } diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 648e9eabe1..3a3baab4ca 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -78,6 +78,8 @@ netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx, char buf1[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; + if (buflen < sizeof(*req)) + return 0; memset(req, 0, sizeof(*req)); family = PREFIX_FAMILY(src_ip); bytelen = (family == AF_INET ? 4 : 16); @@ -148,53 +150,55 @@ netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx, return NLMSG_ALIGN(req->n.nlmsg_len); } -/* Install or uninstall specified rule for a specific interface. - * Form netlink message and ship it. - */ -static int netlink_rule_update_internal( - int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm, - uint32_t priority, uint32_t table, const struct prefix *src_ip, - const struct prefix *dst_ip, uint32_t fwmark, uint8_t dsfield) +static ssize_t netlink_rule_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, + size_t buflen) { - char buf[NL_PKT_BUF_SIZE]; + int cmd = RTM_NEWRULE; + + if (dplane_ctx_get_op(ctx) == DPLANE_OP_RULE_DELETE) + cmd = RTM_DELRULE; - netlink_rule_msg_encode(cmd, ctx, filter_bm, priority, table, src_ip, - dst_ip, fwmark, dsfield, buf, sizeof(buf)); - return netlink_talk_info(netlink_talk_filter, (void *)&buf, - dplane_ctx_get_ns(ctx), 0); + return netlink_rule_msg_encode( + cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx), + dplane_ctx_rule_get_priority(ctx), + dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx), + dplane_ctx_rule_get_dst_ip(ctx), + dplane_ctx_rule_get_fwmark(ctx), + dplane_ctx_rule_get_dsfield(ctx), buf, buflen); +} + +static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_rule_msg_encode( + RTM_DELRULE, ctx, dplane_ctx_rule_get_old_filter_bm(ctx), + dplane_ctx_rule_get_old_priority(ctx), + dplane_ctx_rule_get_old_table(ctx), + dplane_ctx_rule_get_old_src_ip(ctx), + dplane_ctx_rule_get_old_dst_ip(ctx), + dplane_ctx_rule_get_old_fwmark(ctx), + dplane_ctx_rule_get_old_dsfield(ctx), buf, buflen); } + /* Public functions */ -/* - * Add, update or delete a rule from the - * kernel, using info from a dataplane context. - */ -enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx) +enum netlink_msg_status +netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) { enum dplane_op_e op; - int cmd; - int ret; + enum netlink_msg_status ret; op = dplane_ctx_get_op(ctx); - if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE) - cmd = RTM_NEWRULE; - else if (op == DPLANE_OP_RULE_DELETE) - cmd = RTM_DELRULE; - else { + if (!(op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE + || op == DPLANE_OP_RULE_DELETE)) { flog_err( EC_ZEBRA_PBR_RULE_UPDATE, "Context received for kernel rule update with incorrect OP code (%u)", op); - return ZEBRA_DPLANE_REQUEST_FAILURE; + return FRR_NETLINK_ERROR; } - ret = netlink_rule_update_internal( - cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx), - dplane_ctx_rule_get_priority(ctx), - dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx), - dplane_ctx_rule_get_dst_ip(ctx), - dplane_ctx_rule_get_fwmark(ctx), - dplane_ctx_rule_get_dsfield(ctx)); + ret = netlink_batch_add_msg(bth, ctx, netlink_rule_msg_encoder, false); /** * Delete the old one. @@ -202,19 +206,10 @@ enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx) * Don't care about this result right? */ if (op == DPLANE_OP_RULE_UPDATE) - netlink_rule_update_internal( - RTM_DELRULE, ctx, - dplane_ctx_rule_get_old_filter_bm(ctx), - dplane_ctx_rule_get_old_priority(ctx), - dplane_ctx_rule_get_old_table(ctx), - dplane_ctx_rule_get_old_src_ip(ctx), - dplane_ctx_rule_get_old_dst_ip(ctx), - dplane_ctx_rule_get_old_fwmark(ctx), - dplane_ctx_rule_get_old_dsfield(ctx)); - - - return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS - : ZEBRA_DPLANE_REQUEST_FAILURE); + netlink_batch_add_msg(bth, ctx, netlink_oldrule_msg_encoder, + true); + + return ret; } /* diff --git a/zebra/rule_netlink.h b/zebra/rule_netlink.h index 8c4741dc06..cf4d978e78 100644 --- a/zebra/rule_netlink.h +++ b/zebra/rule_netlink.h @@ -40,6 +40,9 @@ extern int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); */ extern int netlink_rules_read(struct zebra_ns *zns); +extern enum netlink_msg_status +netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); + #ifdef __cplusplus } #endif diff --git a/zebra/subdir.am b/zebra/subdir.am index c552ca513e..a6ef1537c0 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -80,6 +80,9 @@ zebra_zebra_SOURCES = \ zebra/zebra_errors.c \ zebra/zebra_gr.c \ zebra/zebra_l2.c \ + zebra/zebra_evpn.c \ + zebra/zebra_evpn_mac.c \ + zebra/zebra_evpn_neigh.c \ zebra/zebra_mlag.c \ zebra/zebra_mlag_vty.c \ zebra/zebra_memory.c \ @@ -106,6 +109,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_router.c \ zebra/zebra_rnh.c \ zebra/zebra_routemap.c \ + zebra/zebra_srte.c \ zebra/zebra_vrf.c \ zebra/zebra_vty.c \ zebra/zebra_vxlan.c \ @@ -146,6 +150,10 @@ noinst_HEADERS += \ zebra/zapi_msg.h \ zebra/zebra_dplane.h \ zebra/zebra_errors.h \ + zebra/zebra_evpn.h \ + zebra/zebra_evpn_mac.h \ + zebra/zebra_evpn_neigh.h \ + zebra/zebra_evpn_vxlan.h \ zebra/zebra_fpm_private.h \ zebra/zebra_l2.h \ zebra/zebra_memory.h \ @@ -167,6 +175,7 @@ noinst_HEADERS += \ zebra/zebra_rnh.h \ zebra/zebra_routemap.h \ zebra/zebra_router.h \ + zebra/zebra_srte.h \ zebra/zebra_vrf.h \ zebra/zebra_vxlan.h \ zebra/zebra_vxlan_private.h \ diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 0a459b4d0a..4b31b46bce 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -60,6 +60,7 @@ #include "zebra/zebra_mlag.h" #include "zebra/connected.h" #include "zebra/zebra_opaque.h" +#include "zebra/zebra_srte.h" /* Encoding helpers -------------------------------------------------------- */ @@ -907,17 +908,18 @@ void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable, zserv_send_message(client, s); } -/* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */ -int zsend_router_id_update(struct zserv *client, struct prefix *p, +/* 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) { int blen; + struct stream *s; /* Check this client need interface information. */ - if (!vrf_bitmap_check(client->ridinfo, vrf_id)) + if (!vrf_bitmap_check(client->ridinfo[afi], vrf_id)) return 0; - struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + s = stream_new(ZEBRA_MAX_PACKET_SIZ); /* Message type. */ zclient_create_header(s, ZEBRA_ROUTER_ID_UPDATE, vrf_id); @@ -1076,7 +1078,8 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS) s = msg; - client->nh_reg_time = monotime(NULL); + if (!client->nh_reg_time) + client->nh_reg_time = monotime(NULL); while (l < hdr->length) { STREAM_GETC(s, flags); @@ -1585,16 +1588,17 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) return; } + vrf_id = zvrf_id(zvrf); + if (IS_ZEBRA_DEBUG_RECV) { char buf_prefix[PREFIX_STRLEN]; prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); - zlog_debug("%s: p=%s, msg flags=0x%x, flags=0x%x", - __func__, buf_prefix, (int)api.message, api.flags); + zlog_debug("%s: p=(%u:%u)%s, msg flags=0x%x, flags=0x%x", + __func__, vrf_id, api.tableid, buf_prefix, (int)api.message, api.flags); } /* Allocate new route. */ - vrf_id = zvrf_id(zvrf); re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); re->type = api.type; re->instance = api.instance; @@ -1650,6 +1654,11 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) return; } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); + nexthop->srte_color = api_nh->srte_color; + } + /* MPLS labels for BGP-LU or Segment Routing */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX @@ -1729,6 +1738,11 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) nexthop->backup_num = 0; } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); + nexthop->srte_color = api_nh->srte_color; + } + /* MPLS labels for BGP-LU or Segment Routing */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX @@ -1865,9 +1879,18 @@ static void zread_route_del(ZAPI_HANDLER_ARGS) else table_id = zvrf->table_id; + if (IS_ZEBRA_DEBUG_RECV) { + char buf_prefix[PREFIX_STRLEN]; + + prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); + zlog_debug("%s: p=(%u:%u)%s, msg flags=0x%x, flags=0x%x", + __func__, zvrf_id(zvrf), table_id, buf_prefix, + (int)api.message, api.flags); + } + rib_delete(afi, api.safi, zvrf_id(zvrf), api.type, api.instance, api.flags, &api.prefix, src_p, NULL, 0, table_id, api.metric, - api.distance, false); + api.distance, false, false); /* Stats */ switch (api.prefix.family) { @@ -1897,20 +1920,48 @@ stream_failure: /* Register zebra server router-id information. Send current router-id */ static void zread_router_id_add(ZAPI_HANDLER_ARGS) { + afi_t afi; + struct prefix p; + STREAM_GETW(msg, afi); + + if (afi <= AFI_UNSPEC || afi >= AFI_MAX) { + zlog_warn( + "Invalid AFI %u while registering for router ID notifications", + afi); + goto stream_failure; + } + /* Router-id information is needed. */ - vrf_bitmap_set(client->ridinfo, zvrf_id(zvrf)); + vrf_bitmap_set(client->ridinfo[afi], zvrf_id(zvrf)); - router_id_get(&p, zvrf); + router_id_get(afi, &p, zvrf); - zsend_router_id_update(client, &p, zvrf_id(zvrf)); + zsend_router_id_update(client, afi, &p, zvrf_id(zvrf)); + +stream_failure: + return; } /* Unregister zebra server router-id information. */ static void zread_router_id_delete(ZAPI_HANDLER_ARGS) { - vrf_bitmap_unset(client->ridinfo, zvrf_id(zvrf)); + afi_t afi; + + STREAM_GETW(msg, afi); + + if (afi <= AFI_UNSPEC || afi >= AFI_MAX) { + zlog_warn( + "Invalid AFI %u while unregistering from router ID notifications", + afi); + goto stream_failure; + } + + vrf_bitmap_unset(client->ridinfo[afi], zvrf_id(zvrf)); + +stream_failure: + return; } static void zsend_capabilities(struct zserv *client, struct zebra_vrf *zvrf) @@ -1999,8 +2050,8 @@ static void zread_vrf_unregister(ZAPI_HANDLER_ARGS) for (i = 0; i < ZEBRA_ROUTE_MAX; i++) 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->ridinfo, zvrf_id(zvrf)); } /* @@ -2180,6 +2231,107 @@ static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS) mpls_zapi_labels_process(true, zvrf, &zl); } +static void zread_sr_policy_set(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_sr_policy zp; + struct zapi_srte_tunnel *zt; + struct zebra_sr_policy *policy; + + /* Get input stream. */ + s = msg; + if (zapi_sr_policy_decode(s, &zp) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_sr_policy sent", + __PRETTY_FUNCTION__); + return; + } + zt = &zp.segment_list; + if (zt->label_num < 1) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug( + "%s: SR-TE tunnel must contain at least one label", + __PRETTY_FUNCTION__); + return; + } + + if (!mpls_enabled) + return; + + policy = zebra_sr_policy_find(zp.color, &zp.endpoint); + if (!policy) + policy = zebra_sr_policy_add(zp.color, &zp.endpoint, zp.name); + /* TODO: per-VRF list of SR-TE policies. */ + policy->zvrf = zvrf; + + zebra_sr_policy_validate(policy, &zp.segment_list); +} + +static void zread_sr_policy_delete(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_sr_policy zp; + struct zebra_sr_policy *policy; + + /* Get input stream. */ + s = msg; + if (zapi_sr_policy_decode(s, &zp) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_sr_policy sent", + __PRETTY_FUNCTION__); + return; + } + + if (!mpls_enabled) + return; + + policy = zebra_sr_policy_find(zp.color, &zp.endpoint); + if (!policy) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to find SR-TE policy", + __PRETTY_FUNCTION__); + return; + } + + zebra_sr_policy_del(policy); +} + +int zsend_sr_policy_notify_status(uint32_t color, struct ipaddr *endpoint, + char *name, int status) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_SRTE, 0); + if (!client) { + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug( + "Not notifying pathd about policy %s" + " status change to %d", + name, status); + return 0; + } + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug( + "Notifying pathd about policy %s status change" + " to %d", + name, status); + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + stream_reset(s); + + zclient_create_header(s, ZEBRA_SR_POLICY_NOTIFY_STATUS, VRF_DEFAULT); + stream_putl(s, color); + stream_put_ipaddr(s, endpoint); + stream_write(s, name, SRTE_POLICY_NAME_MAX_LENGTH); + stream_putl(s, status); + + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + /* Send response to a table manager connect request to client */ static void zread_table_manager_connect(struct zserv *client, struct stream *msg, vrf_id_t vrf_id) @@ -2689,6 +2841,7 @@ static inline void zread_ipset(ZAPI_HANDLER_ARGS) zpi.vrf_id = zvrf->vrf->vrf_id; STREAM_GETL(s, zpi.unique); STREAM_GETL(s, zpi.type); + STREAM_GETC(s, zpi.family); STREAM_GET(&zpi.ipset_name, s, ZEBRA_IPSET_NAME_SIZE); if (hdr->command == ZEBRA_IPSET_CREATE) @@ -2799,6 +2952,7 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS) STREAM_GETL(s, zpi->action); STREAM_GETL(s, zpi->fwmark); STREAM_GET(&zpi->ipset_name, s, ZEBRA_IPSET_NAME_SIZE); + STREAM_GETC(s, zpi->family); STREAM_GETW(s, zpi->pkt_len_min); STREAM_GETW(s, zpi->pkt_len_max); STREAM_GETW(s, zpi->tcp_flags); @@ -2806,6 +2960,7 @@ static inline void zread_iptable(ZAPI_HANDLER_ARGS) STREAM_GETC(s, zpi->dscp_value); STREAM_GETC(s, zpi->fragment); STREAM_GETC(s, zpi->protocol); + STREAM_GETW(s, zpi->flow_label); STREAM_GETL(s, zpi->nb_interface); zebra_pbr_iptable_update_interfacelist(s, zpi); @@ -2820,6 +2975,41 @@ stream_failure: return; } +static inline void zread_neigh_discover(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + ifindex_t ifindex; + struct interface *ifp; + struct prefix p; + struct ipaddr ip; + + s = msg; + + STREAM_GETL(s, ifindex); + + ifp = if_lookup_by_index_per_ns(zvrf->zns, ifindex); + if (!ifp) { + zlog_debug("Failed to lookup ifindex: %u", ifindex); + return; + } + + STREAM_GETC(s, p.family); + STREAM_GETC(s, p.prefixlen); + STREAM_GET(&p.u.prefix, s, prefix_blen(&p)); + + if (p.family == AF_INET) + SET_IPADDR_V4(&ip); + else + SET_IPADDR_V6(&ip); + + memcpy(&ip.ip.addr, &p.u.prefix, prefix_blen(&p)); + + dplane_neigh_discover(ifp, &ip); + +stream_failure: + return; +} + static void zsend_error_msg(struct zserv *client, enum zebra_error_types error, struct zmsghdr *bad_hdr) { @@ -2879,6 +3069,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_BFD_CLIENT_REGISTER] = zebra_ptm_bfd_client_register, [ZEBRA_INTERFACE_ENABLE_RADV] = zebra_interface_radv_enable, [ZEBRA_INTERFACE_DISABLE_RADV] = zebra_interface_radv_disable, + [ZEBRA_SR_POLICY_SET] = zread_sr_policy_set, + [ZEBRA_SR_POLICY_DELETE] = zread_sr_policy_delete, [ZEBRA_MPLS_LABELS_ADD] = zread_mpls_labels_add, [ZEBRA_MPLS_LABELS_DELETE] = zread_mpls_labels_delete, [ZEBRA_MPLS_LABELS_REPLACE] = zread_mpls_labels_replace, @@ -2921,31 +3113,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_MLAG_CLIENT_REGISTER] = zebra_mlag_client_register, [ZEBRA_MLAG_CLIENT_UNREGISTER] = zebra_mlag_client_unregister, [ZEBRA_MLAG_FORWARD_MSG] = zebra_mlag_forward_client_msg, - [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities -}; - -#if defined(HANDLE_ZAPI_FUZZING) -extern struct zebra_privs_t zserv_privs; - -static void zserv_write_incoming(struct stream *orig, uint16_t command) -{ - char fname[MAXPATHLEN]; - struct stream *copy; - int fd = -1; - - copy = stream_dup(orig); - stream_set_getp(copy, 0); - - snprintf(fname, MAXPATHLEN, "%s/%u", frr_vtydir, command); - - frr_with_privs(&zserv_privs) { - fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, 0644); - } - stream_flush(copy, fd); - close(fd); - stream_free(copy); -} -#endif + [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities, + [ZEBRA_NEIGH_DISCOVER] = zread_neigh_discover}; /* * Process a batch of zapi messages. @@ -2973,13 +3142,10 @@ void zserv_handle_commands(struct zserv *client, struct stream_fifo *fifo) zapi_parse_header(msg, &hdr); - if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV + && IS_ZEBRA_DEBUG_DETAIL) zserv_log_message(NULL, msg, &hdr); -#if defined(HANDLE_ZAPI_FUZZING) - zserv_write_incoming(msg, hdr.command); -#endif - hdr.length -= ZEBRA_HEADER_SIZE; /* Before checking for a handler function, check for diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index eb50e3c410..e7c755c2bf 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -68,8 +68,8 @@ extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct prefix *src_p, const struct route_entry *re); -extern int zsend_router_id_update(struct zserv *zclient, struct prefix *p, - vrf_id_t vrf_id); +extern int zsend_router_id_update(struct zserv *zclient, afi_t afi, + struct prefix *p, vrf_id_t vrf_id); extern int zsend_interface_vrf_update(struct zserv *zclient, struct interface *ifp, vrf_id_t vrf_id); extern int zsend_interface_link_params(struct zserv *zclient, @@ -100,7 +100,9 @@ extern int zsend_assign_label_chunk_response(struct zserv *client, extern int zsend_label_manager_connect_response(struct zserv *client, vrf_id_t vrf_id, unsigned short result); - +extern int zsend_sr_policy_notify_status(uint32_t color, + struct ipaddr *endpoint, char *name, + int status); #ifdef __cplusplus } diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 5dcf76db15..9771235717 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -185,7 +185,7 @@ struct dplane_mac_info { }; /* - * EVPN neighbor info for the dataplane + * Neighbor info for the dataplane */ struct dplane_neigh_info { struct ipaddr ip_addr; @@ -607,6 +607,7 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_RULE_ADD: case DPLANE_OP_RULE_DELETE: case DPLANE_OP_RULE_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_NONE: break; } @@ -839,6 +840,10 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_RULE_UPDATE: ret = "RULE_UPDATE"; break; + + case DPLANE_OP_NEIGH_DISCOVER: + ret = "NEIGH_DISCOVER"; + break; } return ret; @@ -858,8 +863,6 @@ const char *dplane_res2str(enum zebra_dplane_result res) case ZEBRA_DPLANE_REQUEST_SUCCESS: ret = "SUCCESS"; break; - case ZEBRA_DPLANE_REQUEST_PENDING: - ret = "PENDING"; } return ret; @@ -1908,10 +1911,11 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, * If its a delete we only use the prefix anyway, so this only * matters for INSTALL/UPDATE. */ - if (((op == DPLANE_OP_ROUTE_INSTALL) - || (op == DPLANE_OP_ROUTE_UPDATE)) - && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) - && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) { + if (zebra_nhg_kernel_nexthops_enabled() + && (((op == DPLANE_OP_ROUTE_INSTALL) + || (op == DPLANE_OP_ROUTE_UPDATE)) + && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED) + && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED))) { ret = ENOENT; goto done; } @@ -1979,6 +1983,7 @@ int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, * it probably won't require two messages */ dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_NH_UPDATE)); + ctx->zd_is_update = (op == DPLANE_OP_NH_UPDATE); ret = AOK; @@ -2001,6 +2006,7 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, /* Capture namespace info */ dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT), (op == DPLANE_OP_LSP_UPDATE)); + ctx->zd_is_update = (op == DPLANE_OP_LSP_UPDATE); memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp)); @@ -2222,6 +2228,7 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx, dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT), op == DPLANE_OP_RULE_UPDATE); + ctx->zd_is_update = (op == DPLANE_OP_RULE_UPDATE); ctx->zd_vrf_id = new_rule->vrf_id; memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname)); @@ -2346,11 +2353,8 @@ dplane_route_update_internal(struct route_node *rn, if (ret == AOK) result = ZEBRA_DPLANE_REQUEST_QUEUED; else { - if (ret == ENOENT) - result = ZEBRA_DPLANE_REQUEST_SUCCESS; - else - atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, - 1, memory_order_relaxed); + atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1, + memory_order_relaxed); if (ctx) dplane_ctx_free(&ctx); } @@ -3138,25 +3142,6 @@ enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp, } /* - * Enqueue evpn neighbor update for the dataplane. - */ -enum zebra_dplane_result dplane_rem_neigh_update(const struct interface *ifp, - const struct ipaddr *ip, - const struct ethaddr *mac) -{ - enum zebra_dplane_result result; - uint32_t update_flags = 0; - - update_flags |= DPLANE_NEIGH_REMOTE; - - result = neigh_update_internal(DPLANE_OP_NEIGH_UPDATE, - ifp, mac, ip, 0, DPLANE_NUD_PROBE, - update_flags); - - return result; -} - -/* * Enqueue evpn neighbor delete for the dataplane. */ enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp, @@ -3222,8 +3207,19 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, return result; } +enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, + const struct ipaddr *ip) +{ + enum zebra_dplane_result result; + + result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, ip, + DPLANE_NTF_USE, DPLANE_NUD_INCOMPLETE, 0); + + return result; +} + /* - * Common helper api for evpn neighbor updates + * Common helper api for neighbor updates */ static enum zebra_dplane_result neigh_update_internal(enum dplane_op_e op, @@ -3242,9 +3238,8 @@ neigh_update_internal(enum dplane_op_e op, char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN]; zlog_debug("init neigh ctx %s: ifp %s, mac %s, ip %s", - dplane_op2str(op), + dplane_op2str(op), ifp->name, prefix_mac2str(mac, buf1, sizeof(buf1)), - ifp->name, ipaddr2str(ip, buf2, sizeof(buf2))); } @@ -3445,7 +3440,9 @@ int dplane_show_provs_helper(struct vty *vty, bool detailed) out_max = atomic_load_explicit(&prov->dp_out_max, memory_order_relaxed); - vty_out(vty, "%s (%u): in: %"PRIu64", q_max: %"PRIu64", out: %"PRIu64", q_max: %"PRIu64"\n", + vty_out(vty, + "%s (%u): in: %" PRIu64 ", q_max: %" PRIu64 + ", out: %" PRIu64 ", q_max: %" PRIu64 "\n", prov->dp_name, prov->dp_id, in, in_max, out, out_max); DPLANE_LOCK(); @@ -3717,149 +3714,100 @@ void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx) * Kernel dataplane provider */ -/* - * Handler for kernel LSP updates - */ -static enum zebra_dplane_result -kernel_dplane_lsp_update(struct zebra_dplane_ctx *ctx) -{ - return kernel_lsp_update(ctx); -} - -/* - * Handler for kernel pseudowire updates - */ -static enum zebra_dplane_result -kernel_dplane_pw_update(struct zebra_dplane_ctx *ctx) +static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) { - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) - zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u", - dplane_ctx_get_ifname(ctx), - dplane_op2str(ctx->zd_op), - dplane_ctx_get_pw_af(ctx), - dplane_ctx_get_pw_local_label(ctx), - dplane_ctx_get_pw_remote_label(ctx)); + char buf[PREFIX_STRLEN]; - return kernel_pw_update(ctx); -} - -/* - * Handler for kernel route updates - */ -static enum zebra_dplane_result -kernel_dplane_route_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - char dest_str[PREFIX_STRLEN]; + switch (dplane_ctx_get_op(ctx)) { - prefix2str(dplane_ctx_get_dest(ctx), - dest_str, sizeof(dest_str)); + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + prefix2str(dplane_ctx_get_dest(ctx), buf, sizeof(buf)); zlog_debug("%u:%s Dplane route update ctx %p op %s", - dplane_ctx_get_vrf(ctx), dest_str, - ctx, dplane_op2str(dplane_ctx_get_op(ctx))); - } - - return kernel_route_update(ctx); -} - -/* - * Handler for kernel-facing interface address updates - */ -static enum zebra_dplane_result -kernel_dplane_address_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - char dest_str[PREFIX_STRLEN]; - - prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str, - sizeof(dest_str)); - - zlog_debug("Dplane intf %s, idx %u, addr %s", - dplane_op2str(dplane_ctx_get_op(ctx)), - dplane_ctx_get_ifindex(ctx), dest_str); - } - - return kernel_address_update_ctx(ctx); -} + dplane_ctx_get_vrf(ctx), buf, ctx, + dplane_op2str(dplane_ctx_get_op(ctx))); + break; -/** - * kernel_dplane_nexthop_update() - Handler for kernel nexthop updates - * - * @ctx: Dataplane context - * - * Return: Dataplane result flag - */ -static enum zebra_dplane_result -kernel_dplane_nexthop_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: zlog_debug("ID (%u) Dplane nexthop update ctx %p op %s", dplane_ctx_get_nhe_id(ctx), ctx, dplane_op2str(dplane_ctx_get_op(ctx))); - } + break; - return kernel_nexthop_update(ctx); -} + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + break; -/* - * Handler for kernel-facing EVPN MAC address updates - */ -static enum zebra_dplane_result -kernel_dplane_mac_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - char buf[ETHER_ADDR_STRLEN]; + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u", + dplane_ctx_get_ifname(ctx), + dplane_op2str(ctx->zd_op), dplane_ctx_get_pw_af(ctx), + dplane_ctx_get_pw_local_label(ctx), + dplane_ctx_get_pw_remote_label(ctx)); + break; + + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + prefix2str(dplane_ctx_get_intf_addr(ctx), buf, sizeof(buf)); + + zlog_debug("Dplane intf %s, idx %u, addr %s", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifindex(ctx), buf); + break; + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: prefix_mac2str(dplane_ctx_mac_get_addr(ctx), buf, sizeof(buf)); zlog_debug("Dplane %s, mac %s, ifindex %u", dplane_op2str(dplane_ctx_get_op(ctx)), buf, dplane_ctx_get_ifindex(ctx)); - } - - return kernel_mac_update_ctx(ctx); -} - -/* - * Handler for kernel-facing EVPN neighbor updates - */ -static enum zebra_dplane_result -kernel_dplane_neigh_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - char buf[PREFIX_STRLEN]; + break; + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf, sizeof(buf)); zlog_debug("Dplane %s, ip %s, ifindex %u", dplane_op2str(dplane_ctx_get_op(ctx)), buf, dplane_ctx_get_ifindex(ctx)); - } - - return kernel_neigh_update_ctx(ctx); -} + break; -/* - * Handler for kernel PBR rule updates - */ -static enum zebra_dplane_result -kernel_dplane_rule_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: zlog_debug("Dplane rule update op %s, if %s(%u), ctx %p", dplane_op2str(dplane_ctx_get_op(ctx)), dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx), ctx); + break; - return kernel_pbr_rule_update(ctx); + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: + + case DPLANE_OP_NONE: + break; + } } -static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx, - enum zebra_dplane_result res) +static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) { + enum zebra_dplane_result res = dplane_ctx_get_status(ctx); + switch (dplane_ctx_get_op(ctx)) { case DPLANE_OP_ROUTE_INSTALL: @@ -3868,6 +3816,27 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx, if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1, memory_order_relaxed); + + if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) + && (res == ZEBRA_DPLANE_REQUEST_SUCCESS)) { + struct nexthop *nexthop; + + /* Update installed nexthops to signal which have been + * installed. + */ + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), + nexthop)) { + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_ACTIVE)) { + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB); + } + } + } break; case DPLANE_OP_NH_INSTALL: @@ -3914,6 +3883,7 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx, case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors, 1, memory_order_relaxed); @@ -3932,11 +3902,14 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx, case DPLANE_OP_SYS_ROUTE_DELETE: case DPLANE_OP_ROUTE_NOTIFY: case DPLANE_OP_LSP_NOTIFY: + break; + case DPLANE_OP_NONE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_other_errors, + 1, memory_order_relaxed); break; } - - dplane_ctx_set_status(ctx, res); } /* @@ -3944,7 +3917,6 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx, */ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) { - enum zebra_dplane_result res; struct zebra_dplane_ctx *ctx, *tctx; struct dplane_ctx_q work_list; int counter, limit; @@ -3958,97 +3930,21 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) dplane_provider_get_name(prov)); for (counter = 0; counter < limit; counter++) { - ctx = dplane_provider_dequeue_in_ctx(prov); if (ctx == NULL) break; - /* A previous provider plugin may have asked to skip the - * kernel update. - */ - if (dplane_ctx_is_skip_kernel(ctx)) { - res = ZEBRA_DPLANE_REQUEST_SUCCESS; - goto skip_one; - } - - /* Dispatch to appropriate kernel-facing apis */ - switch (dplane_ctx_get_op(ctx)) { - - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - res = kernel_dplane_route_update(ctx); - break; - - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: - case DPLANE_OP_NH_DELETE: - res = kernel_dplane_nexthop_update(ctx); - break; - - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_UPDATE: - case DPLANE_OP_LSP_DELETE: - res = kernel_dplane_lsp_update(ctx); - break; - - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - res = kernel_dplane_pw_update(ctx); - break; - - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - res = kernel_dplane_address_update(ctx); - break; - - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - res = kernel_dplane_mac_update(ctx); - break; - - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_VTEP_ADD: - case DPLANE_OP_VTEP_DELETE: - res = kernel_dplane_neigh_update(ctx); - break; - - case DPLANE_OP_RULE_ADD: - case DPLANE_OP_RULE_DELETE: - case DPLANE_OP_RULE_UPDATE: - res = kernel_dplane_rule_update(ctx); - break; - - /* Ignore 'notifications' - no-op */ - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_LSP_NOTIFY: - res = ZEBRA_DPLANE_REQUEST_SUCCESS; - break; - - default: - atomic_fetch_add_explicit( - &zdplane_info.dg_other_errors, 1, - memory_order_relaxed); - - res = ZEBRA_DPLANE_REQUEST_FAILURE; - break; - } - - skip_one: - /* If the request isn't pending, we can handle the result right - * away. - */ - if (res != ZEBRA_DPLANE_REQUEST_PENDING) - kernel_dplane_handle_result(ctx, res); + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + kernel_dplane_log_detail(ctx); TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries); } + kernel_update_multi(&work_list); + TAILQ_FOREACH_SAFE (ctx, &work_list, zd_q_entries, tctx) { + kernel_dplane_handle_result(ctx); + TAILQ_REMOVE(&work_list, ctx, zd_q_entries); dplane_provider_enqueue_out_ctx(prov, ctx); } @@ -4093,7 +3989,6 @@ static int test_dplane_process_func(struct zebra_dplane_provider *prov) limit = dplane_provider_get_work_limit(prov); for (counter = 0; counter < limit; counter++) { - ctx = dplane_provider_dequeue_in_ctx(prov); if (ctx == NULL) break; diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 32032ed77d..5dd789a851 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -90,7 +90,6 @@ enum zebra_dplane_result { ZEBRA_DPLANE_REQUEST_QUEUED, ZEBRA_DPLANE_REQUEST_SUCCESS, ZEBRA_DPLANE_REQUEST_FAILURE, - ZEBRA_DPLANE_REQUEST_PENDING, }; /* @@ -150,6 +149,9 @@ enum dplane_op_e { DPLANE_OP_RULE_ADD, DPLANE_OP_RULE_DELETE, DPLANE_OP_RULE_UPDATE, + + /* Link layer address discovery */ + DPLANE_OP_NEIGH_DISCOVER, }; /* @@ -161,12 +163,14 @@ enum dplane_op_e { /* Neighbor cache flags */ #define DPLANE_NTF_EXT_LEARNED 0x01 #define DPLANE_NTF_ROUTER 0x02 +#define DPLANE_NTF_USE 0x04 /* Neighbor cache states */ #define DPLANE_NUD_REACHABLE 0x01 #define DPLANE_NUD_STALE 0x02 #define DPLANE_NUD_NOARP 0x04 #define DPLANE_NUD_PROBE 0x08 +#define DPLANE_NUD_INCOMPLETE 0x10 /* MAC update flags - dplane_mac_info.update_flags */ #define DPLANE_MAC_REMOTE (1 << 0) @@ -557,9 +561,6 @@ enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp, const struct ethaddr *mac, bool set_router, bool set_static, bool set_inactive); -enum zebra_dplane_result dplane_rem_neigh_update(const struct interface *ifp, - const struct ipaddr *ip, - const struct ethaddr *mac); enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp, const struct ipaddr *ip); @@ -573,6 +574,12 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, const struct in_addr *ip, vni_t vni); +/* + * Enqueue a neighbour discovery request for the dataplane. + */ +enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, + const struct ipaddr *ip); + /* Forward ref of zebra_pbr_rule */ struct zebra_pbr_rule; diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c new file mode 100644 index 0000000000..73df93258e --- /dev/null +++ b/zebra/zebra_evpn.c @@ -0,0 +1,1448 @@ +/* + * Zebra EVPN for VxLAN code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * 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 FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include <zebra.h> + +#include "hash.h" +#include "if.h" +#include "jhash.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "stream.h" +#include "table.h" +#include "vlan.h" +#include "vxlan.h" +#ifdef GNU_LINUX +#include <linux/neighbour.h> +#endif + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#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" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_vxlan.h" +#include "zebra/zebra_router.h" + +DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN, "VNI hash"); +DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN_VTEP, "VNI remote VTEP"); + +/* PMSI strings. */ +#define VXLAN_FLOOD_STR_NO_INFO "-" +#define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO +static const struct message zvtep_flood_str[] = { + {VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO}, + {VXLAN_FLOOD_PIM_SM, "PIM-SM"}, + {VXLAN_FLOOD_HEAD_END_REPL, "HER"}, + {0} +}; + +int advertise_gw_macip_enabled(zebra_evpn_t *zevpn) +{ + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + if (zvrf && zvrf->advertise_gw_macip) + return 1; + + if (zevpn && zevpn->advertise_gw_macip) + return 1; + + return 0; +} + +int advertise_svi_macip_enabled(zebra_evpn_t *zevpn) +{ + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + if (zvrf && zvrf->advertise_svi_macip) + return 1; + + if (zevpn && zevpn->advertise_svi_macip) + return 1; + + return 0; +} + +/* + * Print a specific EVPN entry. + */ +void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) +{ + struct vty *vty; + zebra_vtep_t *zvtep; + uint32_t num_macs; + uint32_t num_neigh; + json_object *json = NULL; + json_object *json_vtep_list = NULL; + json_object *json_ip_str = NULL; + + vty = ctxt[0]; + json = ctxt[1]; + + if (json == NULL) { + vty_out(vty, "VNI: %u\n", zevpn->vni); + vty_out(vty, " Type: %s\n", "L2"); + vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zevpn->vrf_id)); + } else { + json_object_int_add(json, "vni", zevpn->vni); + json_object_string_add(json, "type", "L2"); + json_object_string_add(json, "vrf", + vrf_id_to_name(zevpn->vrf_id)); + } + + if (!zevpn->vxlan_if) { // unexpected + if (json == NULL) + vty_out(vty, " VxLAN interface: unknown\n"); + return; + } + num_macs = num_valid_macs(zevpn); + num_neigh = hashcount(zevpn->neigh_table); + if (json == NULL) { + vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name); + vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex); + vty_out(vty, " Local VTEP IP: %s\n", + inet_ntoa(zevpn->local_vtep_ip)); + vty_out(vty, " Mcast group: %s\n", + inet_ntoa(zevpn->mcast_grp)); + } else { + json_object_string_add(json, "vxlanInterface", + zevpn->vxlan_if->name); + json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex); + json_object_string_add(json, "vtepIp", + inet_ntoa(zevpn->local_vtep_ip)); + json_object_string_add(json, "mcastGroup", + inet_ntoa(zevpn->mcast_grp)); + json_object_string_add(json, "advertiseGatewayMacip", + zevpn->advertise_gw_macip ? "Yes" : "No"); + json_object_int_add(json, "numMacs", num_macs); + json_object_int_add(json, "numArpNd", num_neigh); + } + if (!zevpn->vteps) { + if (json == NULL) + vty_out(vty, " No remote VTEPs known for this VNI\n"); + } else { + if (json == NULL) + vty_out(vty, " Remote VTEPs for this VNI:\n"); + else + json_vtep_list = json_object_new_array(); + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + const char *flood_str = lookup_msg(zvtep_flood_str, + zvtep->flood_control, + VXLAN_FLOOD_STR_DEFAULT); + + if (json == NULL) { + vty_out(vty, " %s flood: %s\n", + inet_ntoa(zvtep->vtep_ip), + flood_str); + } else { + json_ip_str = json_object_new_string( + inet_ntoa(zvtep->vtep_ip)); + json_object_array_add(json_vtep_list, + json_ip_str); + } + } + if (json) + json_object_object_add(json, "numRemoteVteps", + json_vtep_list); + } + if (json == NULL) { + vty_out(vty, + " Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, + " Number of ARPs (IPv4 and IPv6, local and remote) " + "known for this VNI: %u\n", + num_neigh); + vty_out(vty, " Advertise-gw-macip: %s\n", + zevpn->advertise_gw_macip ? "Yes" : "No"); + } +} + +/* + * Print an EVPN hash entry - called for display of all VNIs. + */ +void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[]) +{ + struct vty *vty; + zebra_evpn_t *zevpn; + zebra_vtep_t *zvtep; + uint32_t num_vteps = 0; + uint32_t num_macs = 0; + uint32_t num_neigh = 0; + json_object *json = NULL; + json_object *json_evpn = NULL; + json_object *json_ip_str = NULL; + json_object *json_vtep_list = NULL; + + vty = ctxt[0]; + json = ctxt[1]; + + zevpn = (zebra_evpn_t *)bucket->data; + + zvtep = zevpn->vteps; + while (zvtep) { + num_vteps++; + zvtep = zvtep->next; + } + + num_macs = num_valid_macs(zevpn); + num_neigh = hashcount(zevpn->neigh_table); + if (json == NULL) + vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n", + zevpn->vni, "L2", + zevpn->vxlan_if ? zevpn->vxlan_if->name : "unknown", + num_macs, num_neigh, num_vteps, + vrf_id_to_name(zevpn->vrf_id)); + else { + char vni_str[VNI_STR_LEN]; + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "vni", zevpn->vni); + json_object_string_add(json_evpn, "type", "L2"); + json_object_string_add(json_evpn, "vxlanIf", + zevpn->vxlan_if ? zevpn->vxlan_if->name + : "unknown"); + json_object_int_add(json_evpn, "numMacs", num_macs); + json_object_int_add(json_evpn, "numArpNd", num_neigh); + json_object_int_add(json_evpn, "numRemoteVteps", num_vteps); + json_object_string_add(json_evpn, "tenantVrf", + vrf_id_to_name(zevpn->vrf_id)); + if (num_vteps) { + json_vtep_list = json_object_new_array(); + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + json_ip_str = json_object_new_string( + inet_ntoa(zvtep->vtep_ip)); + json_object_array_add(json_vtep_list, + json_ip_str); + } + json_object_object_add(json_evpn, "remoteVteps", + json_vtep_list); + } + json_object_object_add(json, vni_str, json_evpn); + } +} + +/* + * Print an EVPN hash entry in detail - called for display of all EVPNs. + */ +void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data) +{ + struct vty *vty; + zebra_evpn_t *zevpn; + json_object *json_array = NULL; + bool use_json = false; + struct zebra_evpn_show *zes = data; + + vty = zes->vty; + json_array = zes->json; + use_json = zes->use_json; + + zevpn = (zebra_evpn_t *)bucket->data; + + zebra_vxlan_print_vni(vty, zes->zvrf, zevpn->vni, use_json, json_array); + + if (!use_json) + vty_out(vty, "\n"); +} + +int zebra_evpn_del_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn) +{ + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) + continue; + + if (c->address->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), + sizeof(struct in_addr)); + } else if (c->address->family == AF_INET6) { + ip.ipa_type = IPADDR_V6; + memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), + sizeof(struct in6_addr)); + } else { + continue; + } + + zebra_evpn_gw_macip_del(ifp, zevpn, &ip); + } + + return 0; +} + +int zebra_evpn_add_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn) +{ + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) + continue; + + if (c->address->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), + sizeof(struct in_addr)); + } else if (c->address->family == AF_INET6) { + ip.ipa_type = IPADDR_V6; + memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), + sizeof(struct in6_addr)); + } else { + continue; + } + + zebra_evpn_gw_macip_add(ifp, zevpn, &macaddr, &ip); + } + return 0; +} + +static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, + uint16_t cmd) +{ + struct zserv *client = NULL; + struct stream *s = NULL; + char buf[PREFIX_STRLEN]; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, cmd, vrf_id); + stream_put(s, p, sizeof(struct prefix)); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send ip prefix %s %s on vrf %s", + prefix2str(p, buf, sizeof(buf)), + (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL", + vrf_id_to_name(vrf_id)); + + if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) + client->prefixadd_cnt++; + else + client->prefixdel_cnt++; + + return zserv_send_message(client, s); +} + +int zebra_evpn_advertise_subnet(zebra_evpn_t *zevpn, struct interface *ifp, + int advertise) +{ + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct prefix p; + + memcpy(&p, c->address, sizeof(struct prefix)); + + /* skip link local address */ + if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) + continue; + + apply_mask(&p); + if (advertise) + ip_prefix_send_to_client(ifp->vrf_id, &p, + ZEBRA_IP_PREFIX_ROUTE_ADD); + else + ip_prefix_send_to_client(ifp->vrf_id, &p, + ZEBRA_IP_PREFIX_ROUTE_DEL); + } + return 0; +} + +/* + * zebra_evpn_gw_macip_add_to_client + */ +int zebra_evpn_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ethaddr *macaddr, struct ipaddr *ip) +{ + zebra_mac_t *mac = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + zif = zevpn->vxlan_if->info; + if (!zif) + return -1; + + vxl = &zif->l2info.vxl; + + if (zebra_evpn_mac_gw_macip_add(ifp, zevpn, ip, &mac, macaddr, + vxl->access_vlan) + != 0) + return -1; + + return zebra_evpn_neigh_gw_macip_add(ifp, zevpn, ip, mac); +} + +/* + * zebra_evpn_gw_macip_del_from_client + */ +int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; + + /* If the neigh entry is not present nothing to do*/ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + /* mac entry should be present */ + mac = zebra_evpn_mac_lookup(zevpn, &n->emac); + if (!mac) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("MAC %s doesn't exist for neigh %s on VNI %u", + prefix_mac2str(&n->emac, + buf1, sizeof(buf1)), + ipaddr2str(ip, buf2, sizeof(buf2)), + zevpn->vni); + return -1; + } + + /* If the entry is not local nothing to do*/ + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) + return -1; + + /* only need to delete the entry from bgp if we sent it before */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", + ifp->vrf_id, ifp->name, ifp->ifindex, zevpn->vni, + prefix_mac2str(&(n->emac), buf1, sizeof(buf1)), + ipaddr2str(ip, buf2, sizeof(buf2))); + + /* Remove neighbor from BGP. */ + zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac, + n->flags, ZEBRA_NEIGH_ACTIVE, + false /*force*/); + + /* Delete this neighbor entry. */ + zebra_evpn_neigh_del(zevpn, n); + + /* see if the mac needs to be deleted as well*/ + if (mac) + zebra_evpn_deref_ip2mac(zevpn, mac); + + return 0; +} + +void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *vrr_if = NULL; + struct interface *ifp; + + /* Add primary SVI MAC*/ + zevpn = (zebra_evpn_t *)bucket->data; + + /* Global (Zvrf) advertise-default-gw is disabled, + * but zevpn advertise-default-gw is enabled + */ + if (zevpn->advertise_gw_macip) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("VNI: %u GW-MACIP enabled, retain gw-macip", + zevpn->vni); + return; + } + + ifp = zevpn->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + + zl2_info = zif->l2info.vxl; + + vlan_if = + zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + if (!vlan_if) + return; + + /* Del primary MAC-IP */ + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); + + /* Del VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zebra_evpn_del_macip_for_intf(vrr_if, zevpn); + + return; +} + +void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *vrr_if = NULL; + struct interface *ifp = NULL; + + zevpn = (zebra_evpn_t *)bucket->data; + + ifp = zevpn->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + zl2_info = zif->l2info.vxl; + + vlan_if = + zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + if (!vlan_if) + return; + + /* Add primary SVI MAC-IP */ + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); + + if (advertise_gw_macip_enabled(zevpn)) { + /* Add VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zebra_evpn_add_macip_for_intf(vrr_if, zevpn); + } + + return; +} + +void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *ifp; + + /* Add primary SVI MAC*/ + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) + return; + + /* Global(vrf) advertise-svi-ip disabled, but zevpn advertise-svi-ip + * enabled + */ + if (zevpn->advertise_svi_macip) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("VNI: %u SVI-MACIP enabled, retain svi-macip", + zevpn->vni); + return; + } + + ifp = zevpn->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + + zl2_info = zif->l2info.vxl; + + vlan_if = + zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + if (!vlan_if) + return; + + /* Del primary MAC-IP */ + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); + + return; +} + +/* + * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC + * notifications, to see if they are of interest. + */ +zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, + struct interface *br_if, vlanid_t vid) +{ + struct zebra_ns *zns; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + struct zebra_l2info_vxlan *vxl = NULL; + uint8_t bridge_vlan_aware; + zebra_evpn_t *zevpn; + int found = 0; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + bridge_vlan_aware = br->vlan_aware; + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!bridge_vlan_aware || vxl->access_vlan == vid) { + found = 1; + break; + } + } + + if (!found) + return NULL; + + zevpn = zebra_evpn_lookup(vxl->vni); + return zevpn; +} + +/* + * Map SVI and associated bridge to an EVPN. This is invoked upon getting + * neighbor notifications, to see if they are of interest. + */ +zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, + struct interface *br_if) +{ + struct zebra_ns *zns; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + struct zebra_l2info_vxlan *vxl = NULL; + uint8_t bridge_vlan_aware; + vlanid_t vid = 0; + zebra_evpn_t *zevpn; + int found = 0; + + if (!br_if) + return NULL; + + /* Make sure the linked interface is a bridge. */ + if (!IS_ZEBRA_IF_BRIDGE(br_if)) + return NULL; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + bridge_vlan_aware = br->vlan_aware; + if (bridge_vlan_aware) { + struct zebra_l2info_vlan *vl; + + if (!IS_ZEBRA_IF_VLAN(ifp)) + return NULL; + + zif = ifp->info; + assert(zif); + vl = &zif->l2info.vl; + vid = vl->vid; + } + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!bridge_vlan_aware || vxl->access_vlan == vid) { + found = 1; + break; + } + } + + if (!found) + return NULL; + + zevpn = zebra_evpn_lookup(vxl->vni); + return zevpn; +} + +/* Map to MAC-VLAN interface corresponding to specified SVI interface. + */ +struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, + struct interface *svi_if) +{ + struct zebra_ns *zns; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + int found = 0; + + /* Defensive check, caller expected to invoke only with valid bridge. */ + if (!br_if) + return NULL; + + if (!svi_if) { + zlog_debug("svi_if is not passed."); + return NULL; + } + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + + /* Identify corresponding VLAN interface. */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) + continue; + zif = tmp_if->info; + + if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) + continue; + + if (zif->link == svi_if) { + found = 1; + break; + } + } + + return found ? tmp_if : NULL; +} + +/* + * Install MAC hash entry - called upon access VLAN change. + */ +void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_mac_t *mac; + struct mac_walk_ctx *wctx = ctxt; + + mac = (zebra_mac_t *)bucket->data; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) + zebra_evpn_rem_mac_install(wctx->zevpn, mac, false); +} + +/* + * Read and populate local MACs and neighbors corresponding to this EVPN. + */ +void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) +{ + struct zebra_ns *zns; + struct zebra_if *zif; + struct interface *vlan_if; + struct zebra_l2info_vxlan *vxl; + struct interface *vrr_if; + + zif = ifp->info; + vxl = &zif->l2info.vxl; + zns = zebra_ns_lookup(NS_DEFAULT); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u", + ifp->name, ifp->ifindex, zevpn->vni, + zif->brslave_info.bridge_ifindex); + + macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if); + vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); + if (vlan_if) { + + /* Add SVI MAC-IP */ + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); + + /* Add VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zebra_evpn_add_macip_for_intf(vrr_if, zevpn); + + neigh_read_for_vlan(zns, vlan_if); + } +} + +/* + * Hash function for EVPN. + */ +unsigned int zebra_evpn_hash_keymake(const void *p) +{ + const zebra_evpn_t *zevpn = p; + + return (jhash_1word(zevpn->vni, 0)); +} + +/* + * Compare 2 evpn hash entries. + */ +bool zebra_evpn_hash_cmp(const void *p1, const void *p2) +{ + const zebra_evpn_t *zevpn1 = p1; + const zebra_evpn_t *zevpn2 = p2; + + return (zevpn1->vni == zevpn2->vni); +} + +int zebra_evpn_list_cmp(void *p1, void *p2) +{ + const zebra_evpn_t *zevpn1 = p1; + const zebra_evpn_t *zevpn2 = p2; + + if (zevpn1->vni == zevpn2->vni) + return 0; + return (zevpn1->vni < zevpn2->vni) ? -1 : 1; +} + +/* + * Callback to allocate VNI hash entry. + */ +void *zebra_evpn_alloc(void *p) +{ + const zebra_evpn_t *tmp_vni = p; + zebra_evpn_t *zevpn; + + zevpn = XCALLOC(MTYPE_ZEVPN, sizeof(zebra_evpn_t)); + zevpn->vni = tmp_vni->vni; + return ((void *)zevpn); +} + +/* + * Look up EVPN hash entry. + */ +zebra_evpn_t *zebra_evpn_lookup(vni_t vni) +{ + struct zebra_vrf *zvrf; + zebra_evpn_t tmp_vni; + zebra_evpn_t *zevpn = NULL; + + zvrf = zebra_vrf_get_evpn(); + assert(zvrf); + memset(&tmp_vni, 0, sizeof(zebra_evpn_t)); + tmp_vni.vni = vni; + zevpn = hash_lookup(zvrf->evpn_table, &tmp_vni); + + return zevpn; +} + +/* + * Add EVPN hash entry. + */ +zebra_evpn_t *zebra_evpn_add(vni_t vni) +{ + struct zebra_vrf *zvrf; + zebra_evpn_t tmp_zevpn; + zebra_evpn_t *zevpn = NULL; + + zvrf = zebra_vrf_get_evpn(); + assert(zvrf); + memset(&tmp_zevpn, 0, sizeof(zebra_evpn_t)); + tmp_zevpn.vni = vni; + zevpn = hash_get(zvrf->evpn_table, &tmp_zevpn, zebra_evpn_alloc); + assert(zevpn); + + zebra_evpn_evpn_es_init(zevpn); + + /* Create hash table for MAC */ + zevpn->mac_table = zebra_mac_db_create("Zebra EVPN MAC Table"); + + /* Create hash table for neighbors */ + zevpn->neigh_table = zebra_neigh_db_create("Zebra EVPN Neighbor Table"); + + return zevpn; +} + +/* + * Delete EVPN hash entry. + */ +int zebra_evpn_del(zebra_evpn_t *zevpn) +{ + struct zebra_vrf *zvrf; + zebra_evpn_t *tmp_zevpn; + + zvrf = zebra_vrf_get_evpn(); + assert(zvrf); + + /* Free the neighbor hash table. */ + hash_free(zevpn->neigh_table); + zevpn->neigh_table = NULL; + + /* Free the MAC hash table. */ + hash_free(zevpn->mac_table); + zevpn->mac_table = NULL; + + zebra_evpn_evpn_es_cleanup(zevpn); + + /* Free the EVPN hash entry and allocated memory. */ + tmp_zevpn = hash_release(zvrf->evpn_table, zevpn); + XFREE(MTYPE_ZEVPN, tmp_zevpn); + + return 0; +} + +/* + * Inform BGP about local EVPN addition. + */ +int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) +{ + struct zserv *client; + struct stream *s; + int rc; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id()); + stream_putl(s, zevpn->vni); + stream_put_in_addr(s, &zevpn->local_vtep_ip); + stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ + stream_put_in_addr(s, &zevpn->mcast_grp); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send EVPN_ADD %u %s tenant vrf %s to %s", zevpn->vni, + inet_ntoa(zevpn->local_vtep_ip), + vrf_id_to_name(zevpn->vrf_id), + zebra_route_string(client->proto)); + + client->vniadd_cnt++; + rc = zserv_send_message(client, s); + + if (!(zevpn->flags & ZEVPN_READY_FOR_BGP)) { + zevpn->flags |= ZEVPN_READY_FOR_BGP; + /* once the EVPN is sent the ES-EVIs can also be replayed + * to BGP + */ + zebra_evpn_update_all_es(zevpn); + } + return rc; +} + +/* + * Inform BGP about local EVPN deletion. + */ +int zebra_evpn_send_del_to_client(zebra_evpn_t *zevpn) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + if (zevpn->flags & ZEVPN_READY_FOR_BGP) { + zevpn->flags &= ~ZEVPN_READY_FOR_BGP; + /* the ES-EVIs must be removed from BGP before the EVPN is */ + zebra_evpn_update_all_es(zevpn); + } + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + stream_reset(s); + + zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id()); + stream_putl(s, zevpn->vni); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send EVPN_DEL %u to %s", zevpn->vni, + zebra_route_string(client->proto)); + + client->vnidel_cnt++; + return zserv_send_message(client, s); +} + +/* + * See if remote VTEP matches with prefix. + */ +static int zebra_evpn_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep) +{ + return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip)); +} + +/* + * Locate remote VTEP in EVPN hash table. + */ +zebra_vtep_t *zebra_evpn_vtep_find(zebra_evpn_t *zevpn, struct in_addr *vtep_ip) +{ + zebra_vtep_t *zvtep; + + if (!zevpn) + return NULL; + + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + if (zebra_evpn_vtep_match(vtep_ip, zvtep)) + break; + } + + return zvtep; +} + +/* + * Add remote VTEP to EVPN hash table. + */ +zebra_vtep_t *zebra_evpn_vtep_add(zebra_evpn_t *zevpn, struct in_addr *vtep_ip, + int flood_control) + +{ + zebra_vtep_t *zvtep; + + zvtep = XCALLOC(MTYPE_ZEVPN_VTEP, sizeof(zebra_vtep_t)); + + zvtep->vtep_ip = *vtep_ip; + zvtep->flood_control = flood_control; + + if (zevpn->vteps) + zevpn->vteps->prev = zvtep; + zvtep->next = zevpn->vteps; + zevpn->vteps = zvtep; + + return zvtep; +} + +/* + * Remove remote VTEP from EVPN hash table. + */ +int zebra_evpn_vtep_del(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep) +{ + if (zvtep->next) + zvtep->next->prev = zvtep->prev; + if (zvtep->prev) + zvtep->prev->next = zvtep->next; + else + zevpn->vteps = zvtep->next; + + zvtep->prev = zvtep->next = NULL; + XFREE(MTYPE_ZEVPN_VTEP, zvtep); + + return 0; +} + +/* + * Delete all remote VTEPs for this EVPN (upon VNI delete). Also + * uninstall from kernel if asked to. + */ +int zebra_evpn_vtep_del_all(zebra_evpn_t *zevpn, int uninstall) +{ + zebra_vtep_t *zvtep, *zvtep_next; + + if (!zevpn) + return -1; + + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep_next) { + zvtep_next = zvtep->next; + if (uninstall) + zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip); + zebra_evpn_vtep_del(zevpn, zvtep); + } + + return 0; +} + +/* + * Install remote VTEP into the kernel if the remote VTEP has asked + * for head-end-replication. + */ +int zebra_evpn_vtep_install(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep) +{ + if (is_vxlan_flooding_head_end() && + (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) { + if (ZEBRA_DPLANE_REQUEST_FAILURE == + dplane_vtep_add(zevpn->vxlan_if, + &zvtep->vtep_ip, zevpn->vni)) + return -1; + } + + return 0; +} + +/* + * Uninstall remote VTEP from the kernel. + */ +int zebra_evpn_vtep_uninstall(zebra_evpn_t *zevpn, struct in_addr *vtep_ip) +{ + if (!zevpn->vxlan_if) { + zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", + zevpn->vni, zevpn); + return -1; + } + + if (ZEBRA_DPLANE_REQUEST_FAILURE == + dplane_vtep_delete(zevpn->vxlan_if, vtep_ip, zevpn->vni)) + return -1; + + return 0; +} + +/* + * Install or uninstall flood entries in the kernel corresponding to + * remote VTEPs. This is invoked upon change to BUM handling. + */ +void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket, + void *zvrf) +{ + zebra_evpn_t *zevpn; + zebra_vtep_t *zvtep; + + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) + return; + + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + if (is_vxlan_flooding_head_end()) + zebra_evpn_vtep_install(zevpn, zvtep); + else + zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip); + } +} + +/* + * Cleanup EVPN/VTEP and update kernel + */ +void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg) +{ + zebra_evpn_t *zevpn = NULL; + + zevpn = (zebra_evpn_t *)bucket->data; + + /* Free up all neighbors and MACs, if any. */ + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zebra_evpn_vtep_del_all(zevpn, 1); + + /* Delete the hash entry. */ + zebra_evpn_del(zevpn); +} + +static void +zebra_evpn_process_sync_macip_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi) +{ + struct sync_mac_ip_ctx ctx; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool sticky; + bool remote_gw; + zebra_neigh_t *n = NULL; + + sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); + remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + /* if sticky or remote-gw ignore updates from the peer */ + if (sticky || remote_gw) { + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH + || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "Ignore sync-macip vni %u mac %s%s%s%s%s", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + sticky ? " sticky" : "", + remote_gw ? " remote_gw" : ""); + return; + } + + if (ipa_len) { + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); + if (n + && !zebra_evpn_neigh_is_bgp_seq_ok(zevpn, n, macaddr, seq)) + return; + } + + memset(&ctx, 0, sizeof(ctx)); + ctx.mac = zebra_evpn_proc_sync_mac_update( + zevpn, macaddr, ipa_len, ipaddr, flags, seq, esi, &ctx); + if (ctx.ignore_macip || !ctx.mac || !ipa_len) + return; + + zebra_evpn_proc_sync_neigh_update(zevpn, n, ipa_len, ipaddr, flags, seq, + esi, &ctx); +} + +/************************** remote mac-ip handling **************************/ +/* Process a remote MACIP add from BGP. */ +void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, + struct in_addr vtep_ip, esi_t *esi) +{ + zebra_evpn_t *zevpn; + zebra_vtep_t *zvtep; + zebra_mac_t *mac = NULL; + struct interface *ifp = NULL; + struct zebra_if *zif = NULL; + struct zebra_vrf *zvrf; + + /* Locate EVPN hash entry - expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_warn("Unknown VNI %u upon remote MACIP ADD", vni); + return; + } + + ifp = zevpn->vxlan_if; + if (ifp) + zif = ifp->info; + if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) { + zlog_warn( + "Ignoring remote MACIP ADD VNI %u, invalid interface state or info", + vni); + return; + } + + /* Type-2 routes from another PE can be interpreted as remote or + * SYNC based on the destination ES - + * SYNC - if ES is local + * REMOTE - if ES is not local + */ + if (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) { + zebra_evpn_process_sync_macip_add(zevpn, macaddr, ipa_len, + ipaddr, flags, seq, esi); + return; + } + + /* The remote VTEP specified should normally exist, but it is + * possible that when peering comes up, peer may advertise MACIP + * routes before advertising type-3 routes. + */ + if (vtep_ip.s_addr) { + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); + if (!zvtep) { + zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip, + VXLAN_FLOOD_DISABLED); + if (!zvtep) { + flog_err( + EC_ZEBRA_VTEP_ADD_FAILED, + "Failed to add remote VTEP, VNI %u zevpn %p upon remote MACIP ADD", + vni, zevpn); + return; + } + + zebra_evpn_vtep_install(zevpn, zvtep); + } + } + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + if (!zvrf) + return; + + + if (process_mac_remote_macip_add(zevpn, zvrf, macaddr, ipa_len, ipaddr, + &mac, vtep_ip, flags, seq, esi) + != 0) + return; + + process_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac, vtep_ip, flags, + seq); +} + +/* Process a remote MACIP delete from BGP. */ +void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + struct in_addr vtep_ip) +{ + zebra_evpn_t *zevpn; + zebra_mac_t *mac = NULL; + zebra_neigh_t *n = NULL; + struct interface *ifp = NULL; + struct zebra_if *zif = NULL; + struct zebra_ns *zns; + struct zebra_l2info_vxlan *vxl; + struct zebra_vrf *zvrf; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + + /* Locate EVPN hash entry - expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni); + return; + } + + ifp = zevpn->vxlan_if; + if (ifp) + zif = ifp->info; + if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignoring remote MACIP DEL VNI %u, invalid interface state or info", + vni); + return; + } + zns = zebra_ns_lookup(NS_DEFAULT); + vxl = &zif->l2info.vxl; + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (ipa_len) + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); + + if (n && !mac) { + zlog_warn( + "Failed to locate MAC %s for neigh %s VNI %u upon remote MACIP DEL", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ipaddr, buf1, sizeof(buf1)), vni); + return; + } + + /* If the remote mac or neighbor doesn't exist there is nothing + * more to do. Otherwise, uninstall the entry and then remove it. + */ + if (!mac && !n) + return; + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + + /* Ignore the delete if this mac is a gateway mac-ip */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) { + zlog_warn( + "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", + vni, prefix_mac2str(macaddr, buf, sizeof(buf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) : ""); + return; + } + + /* Uninstall remote neighbor or MAC. */ + if (n) + zebra_evpn_neigh_remote_uninstall(zevpn, zvrf, n, mac, ipaddr); + else { + /* DAD: when MAC is freeze state as remote learn event, + * remote mac-ip delete event is received will result in freeze + * entry removal, first fetch kernel for the same entry present + * as LOCAL and reachable, avoid deleting this entry instead + * use kerenel local entry to update during unfreeze time. + */ + if (zvrf->dad_freeze + && CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE) + && CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: MAC %s (flags 0x%x) is remote and duplicate, read kernel for local entry", + __func__, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + mac->flags); + macfdb_read_specific_mac(zns, zif->brslave_info.br_if, + macaddr, vxl->access_vlan); + } + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + if (!ipa_len) + zebra_evpn_sync_mac_del(mac); + } else if (CHECK_FLAG(mac->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_mac_del(zevpn, mac); + } + } +} + +/************************** EVPN BGP config management ************************/ +void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + + zevpn = (zebra_evpn_t *)bucket->data; + zevpn->advertise_gw_macip = 0; + zevpn->advertise_svi_macip = 0; + zevpn->advertise_subnet = 0; + + zebra_evpn_neigh_del_all(zevpn, 1, 0, + DEL_REMOTE_NEIGH | DEL_REMOTE_NEIGH_FROM_VTEP); + zebra_evpn_mac_del_all(zevpn, 1, 0, + DEL_REMOTE_MAC | DEL_REMOTE_MAC_FROM_VTEP); + zebra_evpn_vtep_del_all(zevpn, 1); +} diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h new file mode 100644 index 0000000000..3b6a5b21e8 --- /dev/null +++ b/zebra/zebra_evpn.h @@ -0,0 +1,208 @@ +/* + * Zebra EVPN Data structures and definitions + * These are "internal" to this function. + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * Copyright (C) 2020 Volta Networks. + * + * 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 FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_EVPN_H +#define _ZEBRA_EVPN_H + +#include <zebra.h> + +#include "if.h" +#include "linklist.h" +#include "bitfield.h" + +#include "zebra/zebra_l2.h" +#include "zebra/interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct zebra_evpn_t_ zebra_evpn_t; +typedef struct zebra_vtep_t_ zebra_vtep_t; + +RB_HEAD(zebra_es_evi_rb_head, zebra_evpn_es_evi); +RB_PROTOTYPE(zebra_es_evi_rb_head, zebra_evpn_es_evi, rb_node, + zebra_es_evi_rb_cmp); + +/* Private Structure to pass callback data for hash iterator */ +struct zebra_evpn_show { + struct vty *vty; + json_object *json; + struct zebra_vrf *zvrf; + bool use_json; +}; + +/* + * VTEP info + * + * Right now, this just has each remote VTEP's IP address. + */ +struct zebra_vtep_t_ { + /* Remote IP. */ + /* NOTE: Can only be IPv4 right now. */ + struct in_addr vtep_ip; + /* Flood mode (one of enum vxlan_flood_control) based on the PMSI + * tunnel type advertised by the remote VTEP + */ + int flood_control; + + /* Links. */ + struct zebra_vtep_t_ *next; + struct zebra_vtep_t_ *prev; +}; + +/* + * VNI hash table + * + * Contains information pertaining to a VNI: + * - the list of remote VTEPs (with this VNI) + */ +struct zebra_evpn_t_ { + /* VNI - key */ + vni_t vni; + + /* ES flags */ + uint32_t flags; +#define ZEVPN_READY_FOR_BGP (1 << 0) /* ready to be sent to BGP */ + + /* Flag for advertising gw macip */ + uint8_t advertise_gw_macip; + + /* Flag for advertising svi macip */ + uint8_t advertise_svi_macip; + + /* Flag for advertising gw macip */ + uint8_t advertise_subnet; + + /* Corresponding VxLAN interface. */ + struct interface *vxlan_if; + + /* List of remote VTEPs */ + zebra_vtep_t *vteps; + + /* Local IP */ + struct in_addr local_vtep_ip; + + /* PIM-SM MDT group for BUM flooding */ + struct in_addr mcast_grp; + + /* tenant VRF, if any */ + vrf_id_t vrf_id; + + /* List of local or remote MAC */ + struct hash *mac_table; + + /* List of local or remote neighbors (MAC+IP) */ + struct hash *neigh_table; + + /* RB tree of ES-EVIs */ + struct zebra_es_evi_rb_head es_evi_rb_tree; + + /* List of local ESs */ + struct list *local_es_evi_list; +}; + +struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); + +static inline struct interface *zevpn_map_to_svi(zebra_evpn_t *zevpn) +{ + struct interface *ifp; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + + ifp = zevpn->vxlan_if; + if (!ifp) + return NULL; + zif = ifp->info; + if (!zif) + return NULL; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return NULL; + zl2_info = zif->l2info.vxl; + return zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); +} + +int advertise_gw_macip_enabled(zebra_evpn_t *zevpn); +int advertise_svi_macip_enabled(zebra_evpn_t *zevpn); +void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt); +void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[]); +void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data); +int zebra_evpn_add_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn); +int zebra_evpn_del_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn); +int zebra_evpn_advertise_subnet(zebra_evpn_t *zevpn, struct interface *ifp, + int advertise); +int zebra_evpn_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ethaddr *macaddr, struct ipaddr *ip); +int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip); +void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt); +void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt); +void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt); +zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, + struct interface *br_if, vlanid_t vid); +zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, + struct interface *br_if); +struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, + struct interface *svi_if); +void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp); +unsigned int zebra_evpn_hash_keymake(const void *p); +bool zebra_evpn_hash_cmp(const void *p1, const void *p2); +int zebra_evpn_list_cmp(void *p1, void *p2); +void *zebra_evpn_alloc(void *p); +zebra_evpn_t *zebra_evpn_lookup(vni_t vni); +zebra_evpn_t *zebra_evpn_add(vni_t vni); +int zebra_evpn_del(zebra_evpn_t *zevpn); +int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn); +int zebra_evpn_send_del_to_client(zebra_evpn_t *zevpn); +zebra_vtep_t *zebra_evpn_vtep_find(zebra_evpn_t *zevpn, + struct in_addr *vtep_ip); +zebra_vtep_t *zebra_evpn_vtep_add(zebra_evpn_t *zevpn, struct in_addr *vtep_ip, + int flood_control); +int zebra_evpn_vtep_del(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep); +int zebra_evpn_vtep_del_all(zebra_evpn_t *zevpn, int uninstall); +int zebra_evpn_vtep_install(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep); +int zebra_evpn_vtep_uninstall(zebra_evpn_t *zevpn, struct in_addr *vtep_ip); +void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket, + void *zvrf); +void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg); +void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, + struct in_addr vtep_ip, esi_t *esi); +void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + struct in_addr vtep_ip); +void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt); + +#ifdef __cplusplus +} +#endif + +#endif /*_ZEBRA_EVPN_H */ diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c new file mode 100644 index 0000000000..b9cc02a276 --- /dev/null +++ b/zebra/zebra_evpn_mac.c @@ -0,0 +1,2231 @@ +/* + * Zebra EVPN for VxLAN code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * 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 FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#include "hash.h" +#include "interface.h" +#include "jhash.h" +#include "memory.h" +#include "prefix.h" +#include "vlan.h" +#include "json.h" + +#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" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" + +DEFINE_MTYPE_STATIC(ZEBRA, MAC, "EVPN MAC"); + +/* + * Return number of valid MACs in an EVPN's MAC hash table - all + * remote MACs and non-internal (auto) local MACs count. + */ +uint32_t num_valid_macs(zebra_evpn_t *zevpn) +{ + unsigned int i; + uint32_t num_macs = 0; + struct hash *hash; + struct hash_bucket *hb; + zebra_mac_t *mac; + + hash = zevpn->mac_table; + if (!hash) + return num_macs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + mac = (zebra_mac_t *)hb->data; + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + || CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + || !CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) + num_macs++; + } + } + + return num_macs; +} + +uint32_t num_dup_detected_macs(zebra_evpn_t *zevpn) +{ + unsigned int i; + uint32_t num_macs = 0; + struct hash *hash; + struct hash_bucket *hb; + zebra_mac_t *mac; + + hash = zevpn->mac_table; + if (!hash) + return num_macs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + mac = (zebra_mac_t *)hb->data; + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + num_macs++; + } + } + + return num_macs; +} + +/* + * Install remote MAC into the forwarding plane. + */ +int zebra_evpn_rem_mac_install(zebra_evpn_t *zevpn, zebra_mac_t *mac, + bool was_static) +{ + const struct zebra_if *zif, *br_zif; + const struct zebra_l2info_vxlan *vxl; + bool sticky; + enum zebra_dplane_result res; + const struct interface *br_ifp; + vlanid_t vid; + uint32_t nhg_id; + struct in_addr vtep_ip; + + if (!(mac->flags & ZEBRA_MAC_REMOTE)) + return 0; + + zif = zevpn->vxlan_if->info; + if (!zif) + return -1; + + br_ifp = zif->brslave_info.br_if; + if (br_ifp == NULL) + return -1; + + vxl = &zif->l2info.vxl; + + sticky = !!CHECK_FLAG(mac->flags, + (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)); + + /* If nexthop group for the FDB entry is inactive (not programmed in + * the dataplane) the MAC entry cannot be installed + */ + if (mac->es) { + if (!(mac->es->flags & ZEBRA_EVPNES_NHG_ACTIVE)) + return -1; + nhg_id = mac->es->nhg_id; + vtep_ip.s_addr = 0; + } else { + nhg_id = 0; + vtep_ip = mac->fwd_info.r_vtep_ip; + } + + br_zif = (const struct zebra_if *)(br_ifp->info); + + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) + vid = vxl->access_vlan; + else + vid = 0; + + res = dplane_rem_mac_add(zevpn->vxlan_if, br_ifp, vid, &mac->macaddr, + vtep_ip, sticky, nhg_id, was_static); + if (res != ZEBRA_DPLANE_REQUEST_FAILURE) + return 0; + else + return -1; +} + +/* + * Uninstall remote MAC from the forwarding plane. + */ +int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + const struct zebra_if *zif, *br_zif; + const struct zebra_l2info_vxlan *vxl; + struct in_addr vtep_ip; + const struct interface *ifp, *br_ifp; + vlanid_t vid; + enum zebra_dplane_result res; + + if (!(mac->flags & ZEBRA_MAC_REMOTE)) + return 0; + + if (!zevpn->vxlan_if) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "VNI %u hash %p couldn't be uninstalled - no intf", + zevpn->vni, zevpn); + return -1; + } + + zif = zevpn->vxlan_if->info; + if (!zif) + return -1; + + br_ifp = zif->brslave_info.br_if; + if (br_ifp == NULL) + return -1; + + vxl = &zif->l2info.vxl; + + br_zif = (const struct zebra_if *)br_ifp->info; + + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) + vid = vxl->access_vlan; + else + vid = 0; + + ifp = zevpn->vxlan_if; + vtep_ip = mac->fwd_info.r_vtep_ip; + + res = dplane_rem_mac_del(ifp, br_ifp, vid, &mac->macaddr, vtep_ip); + if (res != ZEBRA_DPLANE_REQUEST_FAILURE) + return 0; + else + return -1; +} + +/* + * Decrement neighbor refcount of MAC; uninstall and free it if + * appropriate. + */ +void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) + return; + + /* If all remote neighbors referencing a remote MAC go away, + * we need to uninstall the MAC. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + && remote_neigh_count(mac) == 0) { + zebra_evpn_rem_mac_uninstall(zevpn, mac); + zebra_evpn_es_mac_deref_entry(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + } + + /* If no neighbors, delete the MAC. */ + if (list_isempty(mac->neigh_list)) + zebra_evpn_mac_del(zevpn, mac); +} + +static void zebra_evpn_mac_get_access_info(zebra_mac_t *mac, + struct interface **ifpP, + vlanid_t *vid) +{ + /* if the mac is associated with an ES we must get the access + * info from the ES + */ + if (mac->es) { + struct zebra_if *zif; + + /* get the access port from the es */ + *ifpP = mac->es->zif ? mac->es->zif->ifp : NULL; + /* get the vlan from the EVPN */ + if (mac->zevpn->vxlan_if) { + zif = mac->zevpn->vxlan_if->info; + *vid = zif->l2info.vxl.access_vlan; + } else { + *vid = 0; + } + } else { + struct zebra_ns *zns; + + *vid = mac->fwd_info.local.vid; + zns = zebra_ns_lookup(NS_DEFAULT); + *ifpP = if_lookup_by_index_per_ns(zns, + mac->fwd_info.local.ifindex); + } +} + +static int zebra_evpn_dad_mac_auto_recovery_exp(struct thread *t) +{ + struct zebra_vrf *zvrf = NULL; + zebra_mac_t *mac = NULL; + zebra_evpn_t *zevpn = NULL; + struct listnode *node = NULL; + zebra_neigh_t *nbr = NULL; + char buf[ETHER_ADDR_STRLEN]; + + mac = THREAD_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + zvrf = vrf_info_lookup(mac->zevpn->vrf_id); + if (!zvrf) + return 0; + + zevpn = zebra_evpn_lookup(mac->zevpn->vni); + if (!zevpn) + return 0; + + mac = zebra_evpn_mac_lookup(zevpn, &mac->macaddr); + if (!mac) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired", + __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, mac->dad_count, listcount(mac->neigh_list)); + + /* Remove all IPs as duplicate associcated with this MAC */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + ZEBRA_NEIGH_SET_INACTIVE(nbr); + else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) + zebra_evpn_rem_neigh_install( + zevpn, nbr, false /*was_static*/); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->dad_dup_detect_time = 0; + } + + UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + mac->dad_count = 0; + mac->detect_start_time.tv_sec = 0; + mac->detect_start_time.tv_usec = 0; + mac->dad_dup_detect_time = 0; + mac->dad_mac_auto_recovery_timer = NULL; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* Inform to BGP */ + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr, + mac->flags, mac->loc_seq, + mac->es)) + return -1; + + /* Process all neighbors associated with this MAC. */ + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + 0 /*es_change*/); + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); + + /* Install the entry. */ + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); + } + + return 0; +} + +static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, + zebra_mac_t *mac, + struct in_addr vtep_ip, + bool do_dad, bool *is_dup_detect, + bool is_local) +{ + zebra_neigh_t *nbr; + struct listnode *node = NULL; + struct timeval elapsed = {0, 0}; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + bool reset_params = false; + + if (!(zvrf->dup_addr_detect && do_dad)) + return; + + /* MAC is detected as duplicate, + * Local MAC event -> hold on advertising to BGP. + * Remote MAC event -> hold on installing it. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u", + __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, mac->dad_count, + zvrf->dad_freeze_time); + + /* For duplicate MAC do not update + * client but update neigh due to + * this MAC update. + */ + if (zvrf->dad_freeze) + *is_dup_detect = true; + + return; + } + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + */ + monotime_since(&mac->detect_start_time, &elapsed); + reset_params = (elapsed.tv_sec > zvrf->dad_time); + if (is_local && !reset_params) { + /* RFC-7432: A PE/VTEP that detects a MAC mobility + * event via LOCAL learning starts an M-second timer. + * + * NOTE: This is the START of the probe with count is + * 0 during LOCAL learn event. + * (mac->dad_count == 0 || elapsed.tv_sec >= zvrf->dad_time) + */ + reset_params = !mac->dad_count; + } + + if (reset_params) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u", + __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, mac->dad_count); + + mac->dad_count = 0; + /* Start dup. addr detection (DAD) start time, + * ONLY during LOCAL learn. + */ + if (is_local) + monotime(&mac->detect_start_time); + + } else if (!is_local) { + /* For REMOTE MAC, increment detection count + * ONLY while in probe window, once window passed, + * next local learn event should trigger DAD. + */ + mac->dad_count++; + } + + /* For LOCAL MAC learn event, once count is reset above via either + * initial/start detection time or passed the probe time, the count + * needs to be incremented. + */ + if (is_local) + mac->dad_count++; + + if (mac->dad_count >= zvrf->dad_max_moves) { + flog_warn(EC_ZEBRA_DUP_MAC_DETECTED, + "VNI %u: MAC %s detected as duplicate during %s VTEP %s", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + is_local ? "local update, last" : + "remote update, from", inet_ntoa(vtep_ip)); + + SET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + + /* Capture Duplicate detection time */ + mac->dad_dup_detect_time = monotime(NULL); + + /* Mark all IPs/Neighs as duplicate + * associcated with this MAC + */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + + /* Ony Mark IPs which are Local */ + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + continue; + + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + + nbr->dad_dup_detect_time = monotime(NULL); + + flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, + buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + is_local ? "local" : "remote"); + } + + /* Start auto recovery timer for this MAC */ + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + if (zvrf->dad_freeze && zvrf->dad_freeze_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start", + __func__, + prefix_mac2str(&mac->macaddr, buf, + sizeof(buf)), + mac->flags, zvrf->dad_freeze_time); + + thread_add_timer(zrouter.master, + zebra_evpn_dad_mac_auto_recovery_exp, + mac, zvrf->dad_freeze_time, + &mac->dad_mac_auto_recovery_timer); + } + + /* In case of local update, do not inform to client (BGPd), + * upd_neigh for neigh sequence change. + */ + if (zvrf->dad_freeze) + *is_dup_detect = true; + } +} + +/* + * Print a specific MAC entry. + */ +void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) +{ + struct vty *vty; + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct zebra_vrf *zvrf; + struct timeval detect_start_time = {0, 0}; + char timebuf[MONOTIME_STRLEN]; + char thread_buf[THREAD_TIMER_STRLEN]; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return; + + vty = (struct vty *)ctxt; + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + if (json) { + json_object *json_mac = json_object_new_object(); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *ifp; + vlanid_t vid; + + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + json_object_string_add(json_mac, "type", "local"); + if (ifp) { + json_object_string_add(json_mac, "intf", + ifp->name); + json_object_int_add(json_mac, "ifindex", + ifp->ifindex); + } + if (vid) + json_object_int_add(json_mac, "vlan", vid); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + json_object_string_add(json_mac, "type", "remote"); + json_object_string_add( + json_mac, "remoteVtep", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) + json_object_string_add(json_mac, "type", "auto"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) + json_object_boolean_true_add(json_mac, "stickyMac"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) + json_object_boolean_true_add(json_mac, + "defaultGateway"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) + json_object_boolean_true_add(json_mac, + "remoteGatewayMac"); + + json_object_int_add(json_mac, "localSequence", mac->loc_seq); + json_object_int_add(json_mac, "remoteSequence", mac->rem_seq); + + json_object_int_add(json_mac, "detectionCount", mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, "isDuplicate"); + else + json_object_boolean_false_add(json_mac, "isDuplicate"); + + json_object_int_add(json_mac, "syncNeighCount", + mac->sync_neigh_cnt); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) + json_object_boolean_true_add(json_mac, "localInactive"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY)) + json_object_boolean_true_add(json_mac, "peerProxy"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + json_object_boolean_true_add(json_mac, "peerActive"); + if (mac->hold_timer) + json_object_string_add( + json_mac, "peerActiveHold", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + mac->hold_timer)); + if (mac->es) + json_object_string_add(json_mac, "esi", + mac->es->esi_str); + /* print all the associated neigh */ + if (!listcount(mac->neigh_list)) + json_object_string_add(json_mac, "neighbors", "none"); + else { + json_object *json_active_nbrs = json_object_new_array(); + json_object *json_inactive_nbrs = + json_object_new_array(); + json_object *json_nbrs = json_object_new_object(); + + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) + json_object_array_add( + json_active_nbrs, + json_object_new_string( + ipaddr2str( + &n->ip, buf2, + sizeof(buf2)))); + else + json_object_array_add( + json_inactive_nbrs, + json_object_new_string( + ipaddr2str( + &n->ip, buf2, + sizeof(buf2)))); + } + + json_object_object_add(json_nbrs, "active", + json_active_nbrs); + json_object_object_add(json_nbrs, "inactive", + json_inactive_nbrs); + json_object_object_add(json_mac, "neighbors", + json_nbrs); + } + + json_object_object_add(json, buf1, json_mac); + } else { + vty_out(vty, "MAC: %s\n", buf1); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *ifp; + vlanid_t vid; + + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + + if (mac->es) + vty_out(vty, " ESI: %s\n", mac->es->esi_str); + + if (ifp) + vty_out(vty, " Intf: %s(%u)", ifp->name, + ifp->ifindex); + else + vty_out(vty, " Intf: -"); + vty_out(vty, " VLAN: %u", vid); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + if (mac->es) + vty_out(vty, " Remote ES: %s", + mac->es->esi_str); + else + vty_out(vty, " Remote VTEP: %s", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { + vty_out(vty, " Auto Mac "); + } + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) + vty_out(vty, " Sticky Mac "); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) + vty_out(vty, " Default-gateway Mac "); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) + vty_out(vty, " Remote-gateway Mac "); + + vty_out(vty, "\n"); + vty_out(vty, " Sync-info: neigh#: %u", mac->sync_neigh_cnt); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) + vty_out(vty, " local-inactive"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY)) + vty_out(vty, " peer-proxy"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + vty_out(vty, " peer-active"); + if (mac->hold_timer) + vty_out(vty, " (ht: %s)", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + mac->hold_timer)); + vty_out(vty, "\n"); + vty_out(vty, " Local Seq: %u Remote Seq: %u", mac->loc_seq, + mac->rem_seq); + vty_out(vty, "\n"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + vty_out(vty, " Duplicate, detected at %s", + time_to_string(mac->dad_dup_detect_time, + timebuf)); + } else if (mac->dad_count) { + monotime_since(&mac->detect_start_time, + &detect_start_time); + if (detect_start_time.tv_sec <= zvrf->dad_time) { + time_to_string(mac->detect_start_time.tv_sec, + timebuf); + vty_out(vty, + " Duplicate detection started at %s, detection count %u\n", + timebuf, mac->dad_count); + } + } + + /* print all the associated neigh */ + vty_out(vty, " Neighbors:\n"); + if (!listcount(mac->neigh_list)) + vty_out(vty, " No Neighbors\n"); + else { + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { + vty_out(vty, " %s %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2)), + (IS_ZEBRA_NEIGH_ACTIVE(n) + ? "Active" + : "Inactive")); + } + } + + vty_out(vty, "\n"); + } +} + +static char *zebra_evpn_print_mac_flags(zebra_mac_t *mac, char *flags_buf, + uint32_t flags_buf_sz) +{ + snprintf(flags_buf, flags_buf_sz, "%s%s%s%s", + mac->sync_neigh_cnt ? + "N" : "", + (mac->flags & ZEBRA_MAC_ES_PEER_ACTIVE) ? + "P" : "", + (mac->flags & ZEBRA_MAC_ES_PEER_PROXY) ? + "X" : "", + (mac->flags & ZEBRA_MAC_LOCAL_INACTIVE) ? + "I" : ""); + + return flags_buf; +} + +/* + * Print MAC hash entry - called for display of all MACs. + */ +void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_mac_hdr = NULL, *json_mac = NULL; + zebra_mac_t *mac; + char buf1[ETHER_ADDR_STRLEN]; + struct mac_walk_ctx *wctx = ctxt; + char flags_buf[6]; + + vty = wctx->vty; + json_mac_hdr = wctx->json; + mac = (zebra_mac_t *)bucket->data; + + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + if (json_mac_hdr) + json_mac = json_object_new_object(); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *ifp; + vlanid_t vid; + + if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) + return; + + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + if (json_mac_hdr == NULL) { + vty_out(vty, "%-17s %-6s %-5s %-30s", buf1, "local", + zebra_evpn_print_mac_flags(mac, flags_buf, + sizeof(flags_buf)), + ifp ? ifp->name : "-"); + } else { + json_object_string_add(json_mac, "type", "local"); + if (ifp) + json_object_string_add(json_mac, "intf", + ifp->name); + } + if (vid) { + if (json_mac_hdr == NULL) + vty_out(vty, " %-5u", vid); + else + json_object_int_add(json_mac, "vlan", vid); + } else /* No vid? fill out the space */ + if (json_mac_hdr == NULL) + vty_out(vty, " %-5s", ""); + if (json_mac_hdr == NULL) { + vty_out(vty, " %u/%u", mac->loc_seq, mac->rem_seq); + vty_out(vty, "\n"); + } else { + json_object_int_add(json_mac, "localSequence", + mac->loc_seq); + json_object_int_add(json_mac, "remoteSequence", + mac->rem_seq); + json_object_int_add(json_mac, "detectionCount", + mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, + "isDuplicate"); + else + json_object_boolean_false_add(json_mac, + "isDuplicate"); + json_object_object_add(json_mac_hdr, buf1, json_mac); + } + + wctx->count++; + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + + if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) + && !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, + &wctx->r_vtep_ip)) + return; + + if (json_mac_hdr == NULL) { + if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) + && (wctx->count == 0)) { + vty_out(vty, "\nVNI %u\n\n", wctx->zevpn->vni); + vty_out(vty, "%-17s %-6s %-5s%-30s %-5s %s\n", + "MAC", "Type", "Flags", + "Intf/Remote ES/VTEP", "VLAN", + "Seq #'s"); + } + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %u/%u\n", buf1, + "remote", + zebra_evpn_print_mac_flags(mac, flags_buf, + sizeof(flags_buf)), + mac->es ? mac->es->esi_str + : inet_ntoa(mac->fwd_info.r_vtep_ip), + "", mac->loc_seq, mac->rem_seq); + } else { + json_object_string_add(json_mac, "type", "remote"); + json_object_string_add( + json_mac, "remoteVtep", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + json_object_object_add(json_mac_hdr, buf1, json_mac); + json_object_int_add(json_mac, "localSequence", + mac->loc_seq); + json_object_int_add(json_mac, "remoteSequence", + mac->rem_seq); + json_object_int_add(json_mac, "detectionCount", + mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, + "isDuplicate"); + else + json_object_boolean_false_add(json_mac, + "isDuplicate"); + } + + wctx->count++; + } +} + +/* + * Print MAC hash entry in detail - called for display of all MACs. + */ +void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_mac_hdr = NULL; + zebra_mac_t *mac; + struct mac_walk_ctx *wctx = ctxt; + char buf1[ETHER_ADDR_STRLEN]; + + vty = wctx->vty; + json_mac_hdr = wctx->json; + mac = (zebra_mac_t *)bucket->data; + if (!mac) + return; + + wctx->count++; + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + zebra_evpn_print_mac(mac, vty, json_mac_hdr); +} + +/* + * Inform BGP about local MACIP. + */ +int zebra_evpn_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, + struct ipaddr *ip, uint8_t flags, + uint32_t seq, int state, + struct zebra_evpn_es *es, uint16_t cmd) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + int ipa_len; + struct zserv *client = NULL; + struct stream *s = NULL; + esi_t *esi = es ? &es->esi : zero_esi; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, cmd, zebra_vrf_get_evpn_id()); + stream_putl(s, vni); + stream_put(s, macaddr->octet, ETH_ALEN); + if (ip) { + ipa_len = 0; + if (IS_IPADDR_V4(ip)) + ipa_len = IPV4_MAX_BYTELEN; + else if (IS_IPADDR_V6(ip)) + ipa_len = IPV6_MAX_BYTELEN; + + stream_putl(s, ipa_len); /* IP address length */ + if (ipa_len) + stream_put(s, &ip->ip.addr, ipa_len); /* IP address */ + } else + stream_putl(s, 0); /* Just MAC. */ + + if (cmd == ZEBRA_MACIP_ADD) { + stream_putc(s, flags); /* sticky mac/gateway mac */ + stream_putl(s, seq); /* sequence number */ + stream_put(s, esi, sizeof(esi_t)); + } else { + stream_putl(s, state); /* state - active/inactive */ + } + + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Send MACIP %s f 0x%x MAC %s IP %s seq %u L2-VNI %u ESI %s to %s", + (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni, + es ? es->esi_str : "-", + zebra_route_string(client->proto)); + + if (cmd == ZEBRA_MACIP_ADD) + client->macipadd_cnt++; + else + client->macipdel_cnt++; + + return zserv_send_message(client, s); +} + +static unsigned int mac_hash_keymake(const void *p) +{ + const zebra_mac_t *pmac = p; + const void *pnt = (void *)pmac->macaddr.octet; + + return jhash(pnt, ETH_ALEN, 0xa5a5a55a); +} + +/* + * Compare two MAC addresses. + */ +static bool mac_cmp(const void *p1, const void *p2) +{ + const zebra_mac_t *pmac1 = p1; + const zebra_mac_t *pmac2 = p2; + + if (pmac1 == NULL && pmac2 == NULL) + return true; + + if (pmac1 == NULL || pmac2 == NULL) + return false; + + return (memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETH_ALEN) + == 0); +} + +/* + * Callback to allocate MAC hash entry. + */ +static void *zebra_evpn_mac_alloc(void *p) +{ + const zebra_mac_t *tmp_mac = p; + zebra_mac_t *mac; + + mac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); + *mac = *tmp_mac; + + return ((void *)mac); +} + +/* + * Add MAC entry. + */ +zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr) +{ + zebra_mac_t tmp_mac; + zebra_mac_t *mac = NULL; + + memset(&tmp_mac, 0, sizeof(zebra_mac_t)); + memcpy(&tmp_mac.macaddr, macaddr, ETH_ALEN); + mac = hash_get(zevpn->mac_table, &tmp_mac, zebra_evpn_mac_alloc); + assert(mac); + + mac->zevpn = zevpn; + mac->dad_mac_auto_recovery_timer = NULL; + + mac->neigh_list = list_new(); + mac->neigh_list->cmp = neigh_list_cmp; + + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) { + char buf[ETHER_ADDR_STRLEN]; + + zlog_debug("%s: MAC %s flags 0x%x", __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags); + } + return mac; +} + +/* + * Delete MAC entry. + */ +int zebra_evpn_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + zebra_mac_t *tmp_mac; + + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) { + char buf[ETHER_ADDR_STRLEN]; + + zlog_debug("%s: MAC %s flags 0x%x", __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags); + } + + /* force de-ref any ES entry linked to the MAC */ + zebra_evpn_es_mac_deref_entry(mac); + + /* Cancel proxy hold timer */ + zebra_evpn_mac_stop_hold_timer(mac); + + /* Cancel auto recovery */ + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + + list_delete(&mac->neigh_list); + + /* Free the VNI hash entry and allocated memory. */ + tmp_mac = hash_release(zevpn->mac_table, mac); + XFREE(MTYPE_MAC, tmp_mac); + + return 0; +} + +static bool zebra_evpn_check_mac_del_from_db(struct mac_walk_ctx *wctx, + zebra_mac_t *mac) +{ + if ((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_LOCAL)) + return true; + else if ((wctx->flags & DEL_REMOTE_MAC) + && (mac->flags & ZEBRA_MAC_REMOTE)) + return true; + else if ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) + && (mac->flags & ZEBRA_MAC_REMOTE) + && IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip)) + return true; + else if ((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_AUTO) + && !listcount(mac->neigh_list)) { + if (IS_ZEBRA_DEBUG_VXLAN) { + char buf[ETHER_ADDR_STRLEN]; + + zlog_debug( + "%s: Del MAC %s flags 0x%x", __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags); + } + wctx->uninstall = 0; + + return true; + } + + return false; +} + +/* + * Free MAC hash entry (callback) + */ +static void zebra_evpn_mac_del_hash_entry(struct hash_bucket *bucket, void *arg) +{ + struct mac_walk_ctx *wctx = arg; + zebra_mac_t *mac = bucket->data; + + if (zebra_evpn_check_mac_del_from_db(wctx, mac)) { + if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) { + zebra_evpn_mac_send_del_to_client(wctx->zevpn->vni, + &mac->macaddr, + mac->flags, false); + } + if (wctx->uninstall) { + if (zebra_evpn_mac_is_static(mac)) + zebra_evpn_sync_mac_dp_install( + mac, false /* set_inactive */, + true /* force_clear_static */, + __func__); + + if (mac->flags & ZEBRA_MAC_REMOTE) + zebra_evpn_rem_mac_uninstall(wctx->zevpn, mac); + } + + zebra_evpn_mac_del(wctx->zevpn, mac); + } + + return; +} + +/* + * Delete all MAC entries for this EVPN. + */ +void zebra_evpn_mac_del_all(zebra_evpn_t *zevpn, int uninstall, int upd_client, + uint32_t flags) +{ + struct mac_walk_ctx wctx; + + if (!zevpn->mac_table) + return; + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.zevpn = zevpn; + wctx.uninstall = uninstall; + wctx.upd_client = upd_client; + wctx.flags = flags; + + hash_iterate(zevpn->mac_table, zebra_evpn_mac_del_hash_entry, &wctx); +} + +/* + * Look up MAC hash entry. + */ +zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevpn, struct ethaddr *mac) +{ + zebra_mac_t tmp; + zebra_mac_t *pmac; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.macaddr, mac, ETH_ALEN); + pmac = hash_lookup(zevpn->mac_table, &tmp); + + return pmac; +} + +/* + * Inform BGP about local MAC addition. + */ +int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t mac_flags, uint32_t seq, + struct zebra_evpn_es *es) +{ + uint8_t flags = 0; + + if (CHECK_FLAG(mac_flags, ZEBRA_MAC_LOCAL_INACTIVE)) { + /* host reachability has not been verified locally */ + + /* if no ES peer is claiming reachability we can't advertise the + * entry + */ + if (!CHECK_FLAG(mac_flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + return 0; + + /* ES peers are claiming reachability; we will + * advertise the entry but with a proxy flag + */ + SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT); + } + + if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); + if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + + return zebra_evpn_macip_send_msg_to_client(vni, macaddr, NULL, flags, + seq, ZEBRA_NEIGH_ACTIVE, es, + ZEBRA_MACIP_ADD); +} + +/* + * Inform BGP about local MAC deletion. + */ +int zebra_evpn_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t flags, bool force) +{ + if (!force) { + if (CHECK_FLAG(flags, ZEBRA_MAC_LOCAL_INACTIVE) + && !CHECK_FLAG(flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + /* the host was not advertised - nothing to delete */ + return 0; + } + + return zebra_evpn_macip_send_msg_to_client( + vni, macaddr, NULL, 0 /* flags */, 0 /* seq */, + ZEBRA_NEIGH_ACTIVE, NULL, ZEBRA_MACIP_DEL); +} + +/* + * wrapper to create a MAC hash table + */ +struct hash *zebra_mac_db_create(const char *desc) +{ + return hash_create(mac_hash_keymake, mac_cmp, desc); +} + +/* program sync mac flags in the dataplane */ +void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, + bool force_clear_static, const char *caller) +{ + char macbuf[ETHER_ADDR_STRLEN]; + struct interface *ifp; + bool sticky; + bool set_static; + zebra_evpn_t *zevpn = mac->zevpn; + vlanid_t vid; + struct zebra_if *zif; + struct interface *br_ifp; + + /* get the access vlan from the vxlan_device */ + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + + if (!ifp) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no access-port", + caller, zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags, + set_inactive ? "inactive " : ""); + return; + } + + zif = ifp->info; + br_ifp = zif->brslave_info.br_if; + if (!br_ifp) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no br", + caller, zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags, + set_inactive ? "inactive " : ""); + return; + } + + sticky = !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY); + if (force_clear_static) + set_static = false; + else + set_static = zebra_evpn_mac_is_static(mac); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "dp-install sync-mac vni %u mac %s es %s 0x%x %s%s", + zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags, + set_static ? "static " : "", + set_inactive ? "inactive " : ""); + + dplane_local_mac_add(ifp, br_ifp, vid, &mac->macaddr, sticky, + set_static, set_inactive); +} + +void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready, + bool new_bgp_ready) +{ + if (new_bgp_ready) + zebra_evpn_mac_send_add_to_client(mac->zevpn->vni, + &mac->macaddr, mac->flags, + mac->loc_seq, mac->es); + else if (old_bgp_ready) + zebra_evpn_mac_send_del_to_client(mac->zevpn->vni, + &mac->macaddr, mac->flags, + true /* force */); +} + +/* MAC hold timer is used to age out peer-active flag. + * + * During this wait time we expect the dataplane component or an + * external neighmgr daemon to probe existing hosts to independently + * establish their presence on the ES. + */ +static int zebra_evpn_mac_hold_exp_cb(struct thread *t) +{ + zebra_mac_t *mac; + bool old_bgp_ready; + bool new_bgp_ready; + bool old_static; + bool new_static; + char macbuf[ETHER_ADDR_STRLEN]; + + mac = THREAD_ARG(t); + /* the purpose of the hold timer is to age out the peer-active + * flag + */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + return 0; + + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + old_static = zebra_evpn_mac_is_static(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE); + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + new_static = zebra_evpn_mac_is_static(mac); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac vni %u mac %s es %s 0x%x hold expired", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags); + + /* re-program the local mac in the dataplane if the mac is no + * longer static + */ + if (old_static != new_static) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* inform bgp if needed */ + if (old_bgp_ready != new_bgp_ready) + zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready, + new_bgp_ready); + + return 0; +} + +static inline void zebra_evpn_mac_start_hold_timer(zebra_mac_t *mac) +{ + char macbuf[ETHER_ADDR_STRLEN]; + + if (mac->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac vni %u mac %s es %s 0x%x hold started", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags); + thread_add_timer(zrouter.master, zebra_evpn_mac_hold_exp_cb, mac, + zmh_info->mac_hold_time, &mac->hold_timer); +} + +void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac) +{ + char macbuf[ETHER_ADDR_STRLEN]; + + if (!mac->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac vni %u mac %s es %s 0x%x hold stopped", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags); + THREAD_OFF(mac->hold_timer); +} + +void zebra_evpn_sync_mac_del(zebra_mac_t *mac) +{ + char macbuf[ETHER_ADDR_STRLEN]; + bool old_static; + bool new_static; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac del vni %u mac %s es %s seq %d f 0x%x", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->loc_seq, + mac->flags); + old_static = zebra_evpn_mac_is_static(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + zebra_evpn_mac_start_hold_timer(mac); + new_static = zebra_evpn_mac_is_static(mac); + + if (old_static != new_static) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); +} + +static inline bool zebra_evpn_mac_is_bgp_seq_ok(zebra_evpn_t *zevpn, + zebra_mac_t *mac, uint32_t seq, + uint16_t ipa_len, + struct ipaddr *ipaddr) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + uint32_t tmp_seq; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + tmp_seq = mac->loc_seq; + else + tmp_seq = mac->rem_seq; + + if (seq < tmp_seq) { + /* if the mac was never advertised to bgp we must accept + * whatever sequence number bgp sends + * XXX - check with Vivek + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + && !zebra_evpn_mac_is_ready_for_bgp(mac->flags)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-macip accept vni %u mac %s%s%s lower seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + tmp_seq, mac->flags); + return true; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-macip ignore vni %u mac %s%s%s as existing has higher seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + tmp_seq, mac->flags); + return false; + } + + return true; +} + +zebra_mac_t * +zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx) +{ + zebra_mac_t *mac; + bool inform_bgp = false; + bool inform_dataplane = false; + bool seq_change = false; + bool es_change = false; + uint32_t tmp_seq; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_local = false; + bool old_bgp_ready; + bool new_bgp_ready; + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + /* if it is a new local path we need to inform both + * the control protocol and the data-plane + */ + inform_bgp = true; + inform_dataplane = true; + ctx->mac_created = true; + ctx->mac_inactive = true; + + /* create the MAC and associate it with the dest ES */ + mac = zebra_evpn_mac_add(zevpn, macaddr); + zebra_evpn_es_mac_ref(mac, esi); + + /* local mac activated by an ES peer */ + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + /* if mac-only route setup peer flags */ + if (!ipa_len) { + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + SET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY); + else + SET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE); + } + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + old_bgp_ready = false; + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + } else { + uint32_t old_flags; + uint32_t new_flags; + bool old_static; + bool new_static; + bool sticky; + bool remote_gw; + + old_flags = mac->flags; + sticky = !!CHECK_FLAG(old_flags, ZEBRA_MAC_STICKY); + remote_gw = !!CHECK_FLAG(old_flags, ZEBRA_MAC_REMOTE_DEF_GW); + if (sticky || remote_gw) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "Ignore sync-macip vni %u mac %s%s%s%s%s", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, + sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + sticky ? " sticky" : "", + remote_gw ? " remote_gw" : ""); + ctx->ignore_macip = true; + return NULL; + } + if (!zebra_evpn_mac_is_bgp_seq_ok(zevpn, mac, seq, ipa_len, + ipaddr)) { + ctx->ignore_macip = true; + return NULL; + } + + old_local = !!CHECK_FLAG(old_flags, ZEBRA_MAC_LOCAL); + old_static = zebra_evpn_mac_is_static(mac); + + /* re-build the mac flags */ + new_flags = 0; + SET_FLAG(new_flags, ZEBRA_MAC_LOCAL); + /* retain old local activity flag */ + if (old_flags & ZEBRA_MAC_LOCAL) { + new_flags |= (old_flags & ZEBRA_MAC_LOCAL_INACTIVE); + } else { + new_flags |= ZEBRA_MAC_LOCAL_INACTIVE; + ctx->mac_inactive = true; + } + if (ipa_len) { + /* if mac-ip route do NOT update the peer flags + * i.e. retain only flags as is + */ + new_flags |= (old_flags & ZEBRA_MAC_ALL_PEER_FLAGS); + } else { + /* if mac-only route update peer flags */ + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) { + SET_FLAG(new_flags, ZEBRA_MAC_ES_PEER_PROXY); + /* if the mac was peer-active previously we + * need to keep the flag and start the + * holdtimer on it. the peer-active flag is + * cleared on holdtimer expiry. + */ + if (CHECK_FLAG(old_flags, + ZEBRA_MAC_ES_PEER_ACTIVE)) { + SET_FLAG(new_flags, + ZEBRA_MAC_ES_PEER_ACTIVE); + zebra_evpn_mac_start_hold_timer(mac); + } + } else { + SET_FLAG(new_flags, ZEBRA_MAC_ES_PEER_ACTIVE); + /* stop hold timer if a peer has verified + * reachability + */ + zebra_evpn_mac_stop_hold_timer(mac); + } + } + mac->rem_seq = 0; + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + mac->flags = new_flags; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC && (old_flags != new_flags)) + zlog_debug( + "sync-mac vni %u mac %s old_f 0x%x new_f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + old_flags, mac->flags); + + /* update es */ + es_change = zebra_evpn_es_mac_ref(mac, esi); + /* if mac dest change - inform both sides */ + if (es_change) { + inform_bgp = true; + inform_dataplane = true; + ctx->mac_inactive = true; + } + /* if peer-flag is being set notify dataplane that the + * entry must not be expired because of local inactivity + */ + new_static = zebra_evpn_mac_is_static(mac); + if (old_static != new_static) + inform_dataplane = true; + + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(old_flags); + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + if (old_bgp_ready != new_bgp_ready) + inform_bgp = true; + } + + + /* update sequence number; if that results in a new local sequence + * inform bgp + */ + tmp_seq = MAX(mac->loc_seq, seq); + if (tmp_seq != mac->loc_seq) { + mac->loc_seq = tmp_seq; + seq_change = true; + inform_bgp = true; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("sync-mac %s vni %u mac %s es %s seq %d f 0x%x%s%s", + ctx->mac_created ? "created" : "updated", zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->loc_seq, + mac->flags, inform_bgp ? " inform_bgp" : "", + inform_dataplane ? " inform_dp" : ""); + + if (inform_bgp) + zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready, + new_bgp_ready); + + /* neighs using the mac may need to be re-sent to + * bgp with updated info + */ + if (seq_change || es_change || !old_local) + zebra_evpn_process_neigh_on_local_mac_change( + zevpn, mac, seq_change, es_change); + + if (inform_dataplane) { + if (ipa_len) + /* if the mac is being created as a part of MAC-IP + * route wait for the neigh to be updated or + * created before programming the mac + */ + ctx->mac_dp_update_deferred = true; + else + /* program the local mac in the kernel. when the ES + * change we need to force the dataplane to reset + * the activity as we are yet to establish activity + * locally + */ + zebra_evpn_sync_mac_dp_install( + mac, ctx->mac_inactive, + false /* force_clear_static */, __func__); + } + + return mac; +} + +/* update local fowarding info. return true if a dest-ES change + * is detected + */ +static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac, + struct interface *ifp, + vlanid_t vid) +{ + struct zebra_if *zif = ifp->info; + bool es_change; + + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + + es_change = zebra_evpn_es_mac_ref_entry(mac, zif->es_info.es); + + if (!mac->es) { + /* if es is set fwd_info is not-relevant/taped-out */ + mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.vid = vid; + } + + return es_change; +} + +/* Notify Local MACs to the clienti, skips GW MAC */ +static void zebra_evpn_send_mac_hash_entry_to_client(struct hash_bucket *bucket, + void *arg) +{ + struct mac_walk_ctx *wctx = arg; + zebra_mac_t *zmac = bucket->data; + + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_DEF_GW)) + return; + + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) + zebra_evpn_mac_send_add_to_client(wctx->zevpn->vni, + &zmac->macaddr, zmac->flags, + zmac->loc_seq, zmac->es); +} + +/* Iterator to Notify Local MACs of a EVPN */ +void zebra_evpn_send_mac_list_to_client(zebra_evpn_t *zevpn) +{ + struct mac_walk_ctx wctx; + + if (!zevpn->mac_table) + return; + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.zevpn = zevpn; + + hash_iterate(zevpn->mac_table, zebra_evpn_send_mac_hash_entry_to_client, + &wctx); +} + +void zebra_evpn_rem_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + zebra_evpn_process_neigh_on_remote_mac_del(zevpn, mac); + /* the remote sequence number in the auto mac entry + * needs to be reset to 0 as the mac entry may have + * been removed on all VTEPs (including + * the originating one) + */ + mac->rem_seq = 0; + + /* If all remote neighbors referencing a remote MAC + * go away, we need to uninstall the MAC. + */ + if (remote_neigh_count(mac) == 0) { + zebra_evpn_rem_mac_uninstall(zevpn, mac); + zebra_evpn_es_mac_deref_entry(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + } + + if (list_isempty(mac->neigh_list)) + zebra_evpn_mac_del(zevpn, mac); + else + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); +} + +/* Print Duplicate MAC */ +void zebra_evpn_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_mac_t *mac; + + mac = (zebra_mac_t *)bucket->data; + if (!mac) + return; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + zebra_evpn_print_mac_hash(bucket, ctxt); +} + +/* Print Duplicate MAC in detail */ +void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_mac_t *mac; + + mac = (zebra_mac_t *)bucket->data; + if (!mac) + return; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + zebra_evpn_print_mac_hash_detail(bucket, ctxt); +} + +int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ethaddr *macaddr, uint16_t ipa_len, + struct ipaddr *ipaddr, zebra_mac_t **macp, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq, esi_t *esi) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + uint32_t tmp_seq; + bool sticky; + bool remote_gw; + int update_mac = 0; + bool do_dad = false; + bool is_dup_detect = false; + esi_t *old_esi; + bool old_static = false; + zebra_mac_t *mac; + + sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); + remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + + /* Ignore if the mac is already present as a gateway mac */ + if (mac && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW) + && CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignore remote MACIP ADD VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", + zevpn->vni, + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) + : ""); + return -1; + } + + old_esi = (mac && mac->es) ? &mac->es->esi : zero_esi; + + /* check if the remote MAC is unknown or has a change. + * If so, that needs to be updated first. Note that client could + * install MAC and MACIP separately or just install the latter. + */ + if (!mac || !CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + || sticky != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) + || remote_gw != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW) + || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip) + || memcmp(old_esi, esi, sizeof(esi_t)) || seq != mac->rem_seq) + update_mac = 1; + + if (update_mac) { + if (!mac) { + mac = zebra_evpn_mac_add(zevpn, macaddr); + if (!mac) { + zlog_warn( + "Failed to add MAC %s VNI %u Remote VTEP %s", + prefix_mac2str(macaddr, buf, + sizeof(buf)), + zevpn->vni, inet_ntoa(vtep_ip)); + return -1; + } + + zebra_evpn_es_mac_ref(mac, esi); + + /* Is this MAC created for a MACIP? */ + if (ipa_len) + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + } else { + zebra_evpn_es_mac_ref(mac, esi); + + /* When host moves but changes its (MAC,IP) + * binding, BGP may install a MACIP entry that + * corresponds to "older" location of the host + * in transient situations (because {IP1,M1} + * is a different route from {IP1,M2}). Check + * the sequence number and ignore this update + * if appropriate. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + tmp_seq = mac->loc_seq; + else + tmp_seq = mac->rem_seq; + + if (seq < tmp_seq) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing MAC has higher seq %u flags 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str( + ipaddr, buf1, + sizeof(buf1)) + : "", + tmp_seq, mac->flags); + return -1; + } + } + + /* Check MAC's curent state is local (this is the case + * where MAC has moved from L->R) and check previous + * detection started via local learning. + * RFC-7432: A PE/VTEP that detects a MAC mobility + * event via local learning starts an M-second timer. + * + * VTEP-IP or seq. change alone is not considered + * for dup. detection. + * + * MAC is already marked duplicate set dad, then + * is_dup_detect will be set to not install the entry. + */ + if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + && mac->dad_count) + || CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + do_dad = true; + + /* Remove local MAC from BGP. */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* force drop the sync flags */ + old_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac->remote vni %u mac %s es %s seq %d f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + mac->es ? mac->es->esi_str : "-", + mac->loc_seq, mac->flags); + zebra_evpn_mac_clear_sync_info(mac); + zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, + mac->flags, + false /* force */); + } + + /* Set "auto" and "remote" forwarding info. */ + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + mac->fwd_info.r_vtep_ip = vtep_ip; + + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + + if (remote_gw) + SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); + + zebra_evpn_dup_addr_detect_for_mac( + zvrf, mac, mac->fwd_info.r_vtep_ip, do_dad, + &is_dup_detect, false); + + if (!is_dup_detect) { + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); + /* Install the entry. */ + zebra_evpn_rem_mac_install(zevpn, mac, old_static); + } + } + + /* Update seq number. */ + mac->rem_seq = seq; + + /* If there is no IP, return after clearing AUTO flag of MAC. */ + if (!ipa_len) { + UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + return -1; + } + *macp = mac; + return 0; +} + +int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, + struct interface *ifp, + struct ethaddr *macaddr, vlanid_t vid, + bool sticky, bool local_inactive, + bool dp_static) +{ + zebra_mac_t *mac; + char buf[ETHER_ADDR_STRLEN]; + bool mac_sticky = false; + bool inform_client = false; + bool upd_neigh = false; + bool is_dup_detect = false; + struct in_addr vtep_ip = {.s_addr = 0}; + bool es_change = false; + bool new_bgp_ready; + /* assume inactive if not present or if not local */ + bool old_local_inactive = true; + bool old_bgp_ready = false; + bool inform_dataplane = false; + bool new_static = false; + + assert(ifp); + /* Check if we need to create or update or it is a NO-OP. */ + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u%s", + sticky ? "sticky " : "", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vid, zevpn->vni, + local_inactive ? " local-inactive" : ""); + + mac = zebra_evpn_mac_add(zevpn, macaddr); + if (!mac) { + flog_err( + EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add MAC %s intf %s(%u) VID %u VNI %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vid, zevpn->vni); + return -1; + } + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + es_change = zebra_evpn_local_mac_update_fwd_info(mac, ifp, vid); + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + inform_client = true; + } else { + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u %scurFlags 0x%x", + sticky ? "sticky " : "", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vid, zevpn->vni, + local_inactive ? "local-inactive " : "", + mac->flags); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *old_ifp; + vlanid_t old_vid; + bool old_static; + + zebra_evpn_mac_get_access_info(mac, &old_ifp, &old_vid); + old_bgp_ready = + zebra_evpn_mac_is_ready_for_bgp(mac->flags); + old_local_inactive = + !!(mac->flags & ZEBRA_MAC_LOCAL_INACTIVE); + old_static = zebra_evpn_mac_is_static(mac); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) + mac_sticky = true; + + /* + * Update any changes and if changes are relevant to + * BGP, note it. + */ + if (mac_sticky == sticky && old_ifp == ifp + && old_vid == vid + && old_local_inactive == local_inactive + && dp_static == old_static) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + " Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u%s, " + "entry exists and has not changed ", + sticky ? "sticky " : "", + prefix_mac2str(macaddr, buf, + sizeof(buf)), + ifp->name, ifp->ifindex, vid, + zevpn->vni, + local_inactive + ? " local_inactive" + : ""); + return 0; + } + if (mac_sticky != sticky) { + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + else + UNSET_FLAG(mac->flags, + ZEBRA_MAC_STICKY); + inform_client = true; + } + + es_change = zebra_evpn_local_mac_update_fwd_info( + mac, ifp, vid); + /* If an es_change is detected we need to advertise + * the route with a sequence that is one + * greater. This is need to indicate a mac-move + * to the ES peers + */ + if (es_change) { + mac->loc_seq = mac->loc_seq + 1; + /* force drop the peer/sync info as it is + * simply no longer relevant + */ + if (CHECK_FLAG(mac->flags, + ZEBRA_MAC_ALL_PEER_FLAGS)) { + zebra_evpn_mac_clear_sync_info(mac); + new_static = + zebra_evpn_mac_is_static(mac); + /* if we clear peer-flags we + * also need to notify the dataplane + * to drop the static flag + */ + if (old_static != new_static) + inform_dataplane = true; + } + } + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + || CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { + bool do_dad = false; + + /* + * MAC has either moved or was "internally" created due + * to a neighbor learn and is now actually learnt. If + * it was learnt as a remote sticky MAC, this is an + * operator error. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) { + flog_warn( + EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT, + "MAC %s already learnt as remote sticky MAC behind VTEP %s VNI %u", + prefix_mac2str(macaddr, buf, + sizeof(buf)), + inet_ntoa(mac->fwd_info.r_vtep_ip), + zevpn->vni); + return 0; + } + + /* If an actual move, compute MAC's seq number */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + mac->loc_seq = + MAX(mac->rem_seq + 1, mac->loc_seq); + vtep_ip = mac->fwd_info.r_vtep_ip; + /* Trigger DAD for remote MAC */ + do_dad = true; + } + + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + es_change = zebra_evpn_local_mac_update_fwd_info( + mac, ifp, vid); + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + /* + * We have to inform BGP of this MAC as well as process + * all neighbors. + */ + inform_client = true; + upd_neigh = true; + + zebra_evpn_dup_addr_detect_for_mac( + zvrf, mac, vtep_ip, do_dad, &is_dup_detect, + true); + if (is_dup_detect) { + inform_client = false; + upd_neigh = false; + } + } + } + + /* if the dataplane thinks the entry is sync but it is + * not sync in zebra we need to re-install to fixup + */ + if (dp_static) { + new_static = zebra_evpn_mac_is_static(mac); + if (!new_static) + inform_dataplane = true; + } + + if (local_inactive) + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + /* if local-activity has changed we need update bgp + * even if bgp already knows about the mac + */ + if ((old_local_inactive != local_inactive) + || (new_bgp_ready != old_bgp_ready)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "local mac vni %u mac %s es %s seq %d f 0x%x%s", + zevpn->vni, + prefix_mac2str(macaddr, buf, sizeof(buf)), + mac->es ? mac->es->esi_str : "", mac->loc_seq, + mac->flags, + local_inactive ? " local-inactive" : ""); + inform_client = true; + } + + if (es_change) { + inform_client = true; + upd_neigh = true; + } + + /* Inform dataplane if required. */ + if (inform_dataplane) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* Inform BGP if required. */ + if (inform_client) + zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready, + new_bgp_ready); + + /* Process all neighbors associated with this MAC, if required. */ + if (upd_neigh) + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + es_change); + + return 0; +} + +int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + struct interface *ifp) +{ + zebra_mac_t *mac; + char buf[ETHER_ADDR_STRLEN]; + bool old_bgp_ready; + bool new_bgp_ready; + /* If entry doesn't exist, nothing to do. */ + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) + return 0; + + /* Is it a local entry? */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "DEL MAC %s intf %s(%u) VID %u -> VNI %u seq %u flags 0x%x nbr count %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, + ifp->ifindex, mac->fwd_info.local.vid, zevpn->vni, + mac->loc_seq, mac->flags, listcount(mac->neigh_list)); + + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + if (zebra_evpn_mac_is_static(mac)) { + /* this is a synced entry and can only be removed when the + * es-peers stop advertising it. + */ + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "re-add sync-mac vni %u mac %s es %s seq %d f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, buf, sizeof(buf)), + mac->es ? mac->es->esi_str : "-", mac->loc_seq, + mac->flags); + + /* inform-bgp about change in local-activity if any */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) { + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + new_bgp_ready = + zebra_evpn_mac_is_ready_for_bgp(mac->flags); + zebra_evpn_mac_send_add_del_to_client( + mac, old_bgp_ready, new_bgp_ready); + } + + /* re-install the entry in the kernel */ + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + return 0; + } + + /* Update all the neigh entries associated with this mac */ + zebra_evpn_process_neigh_on_local_mac_del(zevpn, mac); + + /* Remove MAC from BGP. */ + zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, mac->flags, + false /* force */); + + zebra_evpn_es_mac_deref_entry(mac); + + /* + * If there are no neigh associated with the mac delete the mac + * else mark it as AUTO for forward reference + */ + if (!listcount(mac->neigh_list)) { + zebra_evpn_mac_del(zevpn, mac); + } else { + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + } + + return 0; +} + +int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t **macp, + struct ethaddr *macaddr, vlanid_t vlan_id) +{ + char buf[ETHER_ADDR_STRLEN]; + zebra_mac_t *mac; + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + mac = zebra_evpn_mac_add(zevpn, macaddr); + if (!mac) { + flog_err(EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add MAC %s intf %s(%u) VID %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vlan_id); + return -1; + } + } + + /* Set "local" forwarding info. */ + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.vid = vlan_id; + + *macp = mac; + + return 0; +} diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h new file mode 100644 index 0000000000..39aaf1fb30 --- /dev/null +++ b/zebra/zebra_evpn_mac.h @@ -0,0 +1,263 @@ +/* + * Zebra EVPN MAC Data structures and definitions + * These are "internal" to this function. + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * Copyright (C) 2020 Volta Networks. + * + * 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 FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_EVPN_MAC_H +#define _ZEBRA_EVPN_MAC_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct zebra_mac_t_ zebra_mac_t; + +struct host_rb_entry { + RB_ENTRY(host_rb_entry) hl_entry; + + struct prefix p; +}; + +RB_HEAD(host_rb_tree_entry, host_rb_entry); +RB_PROTOTYPE(host_rb_tree_entry, host_rb_entry, hl_entry, + host_rb_entry_compare); +/* + * MAC hash table. + * + * This table contains the MAC addresses pertaining to this VNI. + * This includes local MACs learnt on an attached VLAN that maps + * to this VNI as well as remote MACs learnt and installed by BGP. + * Local MACs will be known either on a VLAN sub-interface or + * on (port, VLAN); however, it is sufficient for zebra to maintain + * against the VNI i.e., it does not need to retain the local "port" + * information. The correct VNI will be obtained as zebra maintains + * the mapping (of VLAN to VNI). + */ +struct zebra_mac_t_ { + /* MAC address. */ + struct ethaddr macaddr; + + uint32_t flags; +#define ZEBRA_MAC_LOCAL 0x01 +#define ZEBRA_MAC_REMOTE 0x02 +#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */ +#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */ +#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */ +#define ZEBRA_MAC_DEF_GW 0x20 +/* remote VTEP advertised MAC as default GW */ +#define ZEBRA_MAC_REMOTE_DEF_GW 0x40 +#define ZEBRA_MAC_DUPLICATE 0x80 +#define ZEBRA_MAC_FPM_SENT 0x100 /* whether or not this entry was sent. */ +/* MAC is locally active on an ethernet segment peer */ +#define ZEBRA_MAC_ES_PEER_ACTIVE 0x200 +/* MAC has been proxy-advertised by peers. This means we need to + * keep the entry for forwarding but cannot advertise it + */ +#define ZEBRA_MAC_ES_PEER_PROXY 0x400 +/* We have not been able to independently establish that the host is + * local connected but one or more ES peers claims it is. + * We will maintain the entry for forwarding purposes and continue + * to advertise it as locally attached but with a "proxy" flag + */ +#define ZEBRA_MAC_LOCAL_INACTIVE 0x800 + +#define ZEBRA_MAC_ALL_LOCAL_FLAGS (ZEBRA_MAC_LOCAL | ZEBRA_MAC_LOCAL_INACTIVE) +#define ZEBRA_MAC_ALL_PEER_FLAGS \ + (ZEBRA_MAC_ES_PEER_PROXY | ZEBRA_MAC_ES_PEER_ACTIVE) + + /* back pointer to zevpn */ + zebra_evpn_t *zevpn; + + /* Local or remote info. */ + union { + struct { + ifindex_t ifindex; + vlanid_t vid; + } local; + + struct in_addr r_vtep_ip; + } fwd_info; + + /* Local or remote ES */ + struct zebra_evpn_es *es; + /* memory used to link the mac to the es */ + struct listnode es_listnode; + + /* Mobility sequence numbers associated with this entry. */ + uint32_t rem_seq; + uint32_t loc_seq; + + /* List of neigh associated with this mac */ + struct list *neigh_list; + + /* list of hosts pointing to this remote RMAC */ + struct host_rb_tree_entry host_rb; + + /* Duplicate mac detection */ + uint32_t dad_count; + + struct thread *dad_mac_auto_recovery_timer; + + struct timeval detect_start_time; + + time_t dad_dup_detect_time; + + /* used for ageing out the PEER_ACTIVE flag */ + struct thread *hold_timer; + + /* number of neigh entries (using this mac) that have + * ZEBRA_MAC_ES_PEER_ACTIVE or ZEBRA_NEIGH_ES_PEER_PROXY + */ + uint32_t sync_neigh_cnt; +}; + +/* + * Context for MAC hash walk - used by callbacks. + */ +struct mac_walk_ctx { + zebra_evpn_t *zevpn; /* EVPN hash */ + struct zebra_vrf *zvrf; /* VRF - for client notification. */ + int uninstall; /* uninstall from kernel? */ + int upd_client; /* uninstall from client? */ + + uint32_t flags; +#define DEL_LOCAL_MAC 0x1 +#define DEL_REMOTE_MAC 0x2 +#define DEL_ALL_MAC (DEL_LOCAL_MAC | DEL_REMOTE_MAC) +#define DEL_REMOTE_MAC_FROM_VTEP 0x4 +#define SHOW_REMOTE_MAC_FROM_VTEP 0x8 + + struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */ + + struct vty *vty; /* Used by VTY handlers */ + uint32_t count; /* Used by VTY handlers */ + struct json_object *json; /* Used for JSON Output */ + bool print_dup; /* Used to print dup addr list */ +}; + +struct rmac_walk_ctx { + struct vty *vty; + struct json_object *json; +}; + +/* temporary datastruct to pass info between the mac-update and + * neigh-update while handling mac-ip routes + */ +struct sync_mac_ip_ctx { + bool ignore_macip; + bool mac_created; + bool mac_inactive; + bool mac_dp_update_deferred; + zebra_mac_t *mac; +}; + +/**************************** SYNC MAC handling *****************************/ +/**************************** SYNC MAC handling *****************************/ +/* if the mac has been added of a mac-route from the peer + * or if it is being referenced by a neigh added by the + * peer we cannot let it age out i.e. we set the static bit + * in the dataplane + */ +static inline bool zebra_evpn_mac_is_static(zebra_mac_t *mac) +{ + return ((mac->flags & ZEBRA_MAC_ALL_PEER_FLAGS) || mac->sync_neigh_cnt); +} + +/* mac needs to be locally active or active on an ES peer */ +static inline bool zebra_evpn_mac_is_ready_for_bgp(uint32_t flags) +{ + return (flags & ZEBRA_MAC_LOCAL) + && (!(flags & ZEBRA_MAC_LOCAL_INACTIVE) + || (flags & ZEBRA_MAC_ES_PEER_ACTIVE)); +} + +void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac); + +static inline void zebra_evpn_mac_clear_sync_info(zebra_mac_t *mac) +{ + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_PEER_FLAGS); + zebra_evpn_mac_stop_hold_timer(mac); +} + +struct hash *zebra_mac_db_create(const char *desc); +uint32_t num_valid_macs(zebra_evpn_t *zevi); +uint32_t num_dup_detected_macs(zebra_evpn_t *zevi); +int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevi, zebra_mac_t *mac); +int zebra_evpn_rem_mac_install(zebra_evpn_t *zevi, zebra_mac_t *mac, + bool was_static); +void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevi, zebra_mac_t *mac); +zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevi, struct ethaddr *mac); +zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevi, struct ethaddr *macaddr); +int zebra_evpn_mac_del(zebra_evpn_t *zevi, zebra_mac_t *mac); +int zebra_evpn_macip_send_msg_to_client(uint32_t id, struct ethaddr *macaddr, + struct ipaddr *ip, uint8_t flags, + uint32_t seq, int state, + struct zebra_evpn_es *es, uint16_t cmd); +void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json); +void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, + bool force_clear_static, + const char *caller); +void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready, + bool new_bgp_ready); + +void zebra_evpn_mac_del_all(zebra_evpn_t *zevi, int uninstall, int upd_client, + uint32_t flags); +int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t mac_flags, uint32_t seq, + struct zebra_evpn_es *es); +int zebra_evpn_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t flags, bool force); +void zebra_evpn_send_mac_list_to_client(zebra_evpn_t *zevi); +zebra_mac_t * +zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevi, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx); +void zebra_evpn_sync_mac_del(zebra_mac_t *mac); +void zebra_evpn_rem_mac_del(zebra_evpn_t *zevi, zebra_mac_t *mac); +void zebra_evpn_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket, + void *ctxt); +int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ethaddr *macaddr, uint16_t ipa_len, + struct ipaddr *ipaddr, zebra_mac_t **macp, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq, esi_t *esi); + +int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, + struct interface *ifp, + struct ethaddr *macaddr, vlanid_t vid, + bool sticky, bool local_inactive, + bool dp_static); +int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + struct interface *ifp); +int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t **macp, + struct ethaddr *macaddr, vlanid_t vlan_id); + +#ifdef __cplusplus +} +#endif + +#endif /*_ZEBRA_EVPN_MAC_H */ diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index fae36ec6fa..3c99ce29c3 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -44,6 +44,8 @@ #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" #include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_router.h" #include "zebra/zebra_evpn_mh.h" @@ -55,9 +57,9 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZES_EVI, "ES info per-EVI"); DEFINE_MTYPE_STATIC(ZEBRA, ZMH_INFO, "MH global info"); DEFINE_MTYPE_STATIC(ZEBRA, ZES_VTEP, "VTEP attached to the ES"); -static void zebra_evpn_es_get_one_base_vni(void); +static void zebra_evpn_es_get_one_base_evpn(void); static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, - zebra_vni_t *vni, bool add); + zebra_evpn_t *zevpn, bool add); static void zebra_evpn_local_es_del(struct zebra_evpn_es *es); static int zebra_evpn_local_es_update(struct zebra_if *zif, uint32_t lid, struct ethaddr *sysmac); @@ -67,7 +69,7 @@ esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; /*****************************************************************************/ /* Ethernet Segment to EVI association - * 1. The ES-EVI entry is maintained as a RB tree per L2-VNI - * (zebra_vni_t.es_evi_rb_tree). + * (zebra_evpn_t.es_evi_rb_tree). * 2. Each local ES-EVI entry is sent to BGP which advertises it as an * EAD-EVI (Type-1 EVPN) route * 3. Local ES-EVI setup is re-evaluated on the following triggers - @@ -81,7 +83,7 @@ esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; * is then sent to zebra which allocates a NHG for it. */ -/* compare ES-IDs for the ES-EVI RB tree maintained per-VNI */ +/* compare ES-IDs for the ES-EVI RB tree maintained per-EVPN */ static int zebra_es_evi_rb_cmp(const struct zebra_evpn_es_evi *es_evi1, const struct zebra_evpn_es_evi *es_evi2) { @@ -94,17 +96,17 @@ RB_GENERATE(zebra_es_evi_rb_head, zebra_evpn_es_evi, * tables. */ static struct zebra_evpn_es_evi *zebra_evpn_es_evi_new(struct zebra_evpn_es *es, - zebra_vni_t *zvni) + zebra_evpn_t *zevpn) { struct zebra_evpn_es_evi *es_evi; es_evi = XCALLOC(MTYPE_ZES_EVI, sizeof(struct zebra_evpn_es_evi)); es_evi->es = es; - es_evi->zvni = zvni; + es_evi->zevpn = zevpn; - /* insert into the VNI-ESI rb tree */ - if (RB_INSERT(zebra_es_evi_rb_head, &zvni->es_evi_rb_tree, es_evi)) { + /* insert into the EVPN-ESI rb tree */ + if (RB_INSERT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, es_evi)) { XFREE(MTYPE_ZES_EVI, es_evi); return NULL; } @@ -115,15 +117,15 @@ static struct zebra_evpn_es_evi *zebra_evpn_es_evi_new(struct zebra_evpn_es *es, if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("es %s evi %d new", - es_evi->es->esi_str, es_evi->zvni->vni); + es_evi->es->esi_str, es_evi->zevpn->vni); return es_evi; } -/* returns TRUE if the VNI is ready to be sent to BGP */ -static inline bool zebra_evpn_vni_send_to_client_ok(zebra_vni_t *zvni) +/* returns TRUE if the EVPN is ready to be sent to BGP */ +static inline bool zebra_evpn_send_to_client_ok(zebra_evpn_t *zevpn) { - return !!(zvni->flags & ZVNI_READY_FOR_BGP); + return !!(zevpn->flags & ZEVPN_READY_FOR_BGP); } /* Evaluate if the es_evi is ready to be sent BGP - @@ -142,7 +144,7 @@ static void zebra_evpn_es_evi_re_eval_send_to_client( /* ES and L2-VNI have to be individually ready for BGP */ if ((es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL) && (es_evi->es->flags & ZEBRA_EVPNES_READY_FOR_BGP) && - zebra_evpn_vni_send_to_client_ok(es_evi->zvni)) + zebra_evpn_send_to_client_ok(es_evi->zevpn)) es_evi->flags |= ZEBRA_EVPNES_EVI_READY_FOR_BGP; else es_evi->flags &= ~ZEBRA_EVPNES_EVI_READY_FOR_BGP; @@ -153,10 +155,10 @@ static void zebra_evpn_es_evi_re_eval_send_to_client( return; if (new_ready) - zebra_evpn_es_evi_send_to_client(es_evi->es, es_evi->zvni, + zebra_evpn_es_evi_send_to_client(es_evi->es, es_evi->zevpn, true /* add */); else - zebra_evpn_es_evi_send_to_client(es_evi->es, es_evi->zvni, + zebra_evpn_es_evi_send_to_client(es_evi->es, es_evi->zevpn, false /* add */); } @@ -166,17 +168,17 @@ static void zebra_evpn_es_evi_re_eval_send_to_client( static void zebra_evpn_es_evi_free(struct zebra_evpn_es_evi *es_evi) { struct zebra_evpn_es *es = es_evi->es; - zebra_vni_t *zvni = es_evi->zvni; + zebra_evpn_t *zevpn = es_evi->zevpn; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("es %s evi %d free", - es_evi->es->esi_str, es_evi->zvni->vni); + es_evi->es->esi_str, es_evi->zevpn->vni); /* remove from the ES's VNI list */ list_delete_node(es->es_evi_list, &es_evi->es_listnode); /* remove from the VNI-ESI rb tree */ - RB_REMOVE(zebra_es_evi_rb_head, &zvni->es_evi_rb_tree, es_evi); + RB_REMOVE(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, es_evi); /* remove from the VNI-ESI rb tree */ XFREE(MTYPE_ZES_EVI, es_evi); @@ -184,13 +186,13 @@ static void zebra_evpn_es_evi_free(struct zebra_evpn_es_evi *es_evi) /* find the ES-EVI in the per-L2-VNI RB tree */ static struct zebra_evpn_es_evi *zebra_evpn_es_evi_find( - struct zebra_evpn_es *es, zebra_vni_t *zvni) + struct zebra_evpn_es *es, zebra_evpn_t *zevpn) { struct zebra_evpn_es_evi es_evi; es_evi.es = es; - return RB_FIND(zebra_es_evi_rb_head, &zvni->es_evi_rb_tree, &es_evi); + return RB_FIND(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, &es_evi); } /* Tell BGP about an ES-EVI deletion and then delete it */ @@ -201,50 +203,50 @@ static void zebra_evpn_local_es_evi_do_del(struct zebra_evpn_es_evi *es_evi) if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("local es %s evi %d del", - es_evi->es->esi_str, es_evi->zvni->vni); + es_evi->es->esi_str, es_evi->zevpn->vni); if (es_evi->flags & ZEBRA_EVPNES_EVI_READY_FOR_BGP) { /* send a del only if add was sent for it earlier */ zebra_evpn_es_evi_send_to_client(es_evi->es, - es_evi->zvni, false /* add */); + es_evi->zevpn, false /* add */); } - /* delete it from the VNI's local list */ - list_delete_node(es_evi->zvni->local_es_evi_list, + /* delete it from the EVPN's local list */ + list_delete_node(es_evi->zevpn->local_es_evi_list, &es_evi->l2vni_listnode); es_evi->flags &= ~ZEBRA_EVPNES_EVI_LOCAL; zebra_evpn_es_evi_free(es_evi); } static void zebra_evpn_local_es_evi_del(struct zebra_evpn_es *es, - zebra_vni_t *zvni) + zebra_evpn_t *zevpn) { struct zebra_evpn_es_evi *es_evi; - es_evi = zebra_evpn_es_evi_find(es, zvni); + es_evi = zebra_evpn_es_evi_find(es, zevpn); if (es_evi) zebra_evpn_local_es_evi_do_del(es_evi); } /* Create an ES-EVI if it doesn't already exist and tell BGP */ static void zebra_evpn_local_es_evi_add(struct zebra_evpn_es *es, - zebra_vni_t *zvni) + zebra_evpn_t *zevpn) { struct zebra_evpn_es_evi *es_evi; - es_evi = zebra_evpn_es_evi_find(es, zvni); + es_evi = zebra_evpn_es_evi_find(es, zevpn); if (!es_evi) { - es_evi = zebra_evpn_es_evi_new(es, zvni); + es_evi = zebra_evpn_es_evi_new(es, zevpn); if (!es_evi) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("local es %s evi %d add", - es_evi->es->esi_str, es_evi->zvni->vni); + es_evi->es->esi_str, es_evi->zevpn->vni); es_evi->flags |= ZEBRA_EVPNES_EVI_LOCAL; - /* add to the VNI's local list */ + /* add to the EVPN's local list */ listnode_init(&es_evi->l2vni_listnode, es_evi); - listnode_add(zvni->local_es_evi_list, &es_evi->l2vni_listnode); + listnode_add(zevpn->local_es_evi_list, &es_evi->l2vni_listnode); zebra_evpn_es_evi_re_eval_send_to_client(es_evi); } @@ -263,7 +265,7 @@ static void zebra_evpn_es_evi_show_entry(struct vty *vty, strlcat(type_str, "L", sizeof(type_str)); vty_out(vty, "%-8d %-30s %-4s\n", - es_evi->zvni->vni, es_evi->es->esi_str, + es_evi->zevpn->vni, es_evi->es->esi_str, type_str); } } @@ -281,7 +283,7 @@ static void zebra_evpn_es_evi_show_entry_detail(struct vty *vty, strlcat(type_str, "L", sizeof(type_str)); vty_out(vty, "VNI %d ESI: %s\n", - es_evi->zvni->vni, es_evi->es->esi_str); + es_evi->zevpn->vni, es_evi->es->esi_str); vty_out(vty, " Type: %s\n", type_str); vty_out(vty, " Ready for BGP: %s\n", (es_evi->flags & @@ -291,12 +293,12 @@ static void zebra_evpn_es_evi_show_entry_detail(struct vty *vty, } } -static void zebra_evpn_es_evi_show_one_vni(zebra_vni_t *zvni, +static void zebra_evpn_es_evi_show_one_evpn(zebra_evpn_t *zevpn, struct vty *vty, json_object *json, int detail) { struct zebra_evpn_es_evi *es_evi; - RB_FOREACH(es_evi, zebra_es_evi_rb_head, &zvni->es_evi_rb_tree) { + RB_FOREACH(es_evi, zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree) { if (detail) zebra_evpn_es_evi_show_entry_detail(vty, es_evi, json); else @@ -310,13 +312,13 @@ struct evpn_mh_show_ctx { int detail; }; -static void zebra_evpn_es_evi_show_one_vni_hash_cb(struct hash_bucket *bucket, +static void zebra_evpn_es_evi_show_one_evpn_hash_cb(struct hash_bucket *bucket, void *ctxt) { - zebra_vni_t *zvni = (zebra_vni_t *)bucket->data; + zebra_evpn_t *zevpn = (zebra_evpn_t *)bucket->data; struct evpn_mh_show_ctx *wctx = (struct evpn_mh_show_ctx *)ctxt; - zebra_evpn_es_evi_show_one_vni(zvni, wctx->vty, + zebra_evpn_es_evi_show_one_evpn(zevpn, wctx->vty, wctx->json, wctx->detail); } @@ -338,17 +340,17 @@ void zebra_evpn_es_evi_show(struct vty *vty, bool uj, int detail) vty_out(vty, "%-8s %-30s %-4s\n", "VNI", "ESI", "Type"); } /* Display all L2-VNIs */ - hash_iterate(zvrf->vni_table, zebra_evpn_es_evi_show_one_vni_hash_cb, + hash_iterate(zvrf->evpn_table, zebra_evpn_es_evi_show_one_evpn_hash_cb, &wctx); } void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj, vni_t vni, int detail) { json_object *json = NULL; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; - zvni = zvni_lookup(vni); - if (zvni) { + zevpn = zebra_evpn_lookup(vni); + if (zevpn) { if (!detail && !json) { vty_out(vty, "Type: L local, R remote\n"); vty_out(vty, "%-8s %-30s %-4s\n", "VNI", "ESI", "Type"); @@ -357,52 +359,52 @@ void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj, vni_t vni, int detail) if (!uj) vty_out(vty, "VNI %d doesn't exist\n", vni); } - zebra_evpn_es_evi_show_one_vni(zvni, vty, json, detail); + zebra_evpn_es_evi_show_one_evpn(zevpn, vty, json, detail); } /* Initialize the ES tables maintained per-L2_VNI */ -void zebra_evpn_vni_es_init(zebra_vni_t *zvni) +void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn) { /* Initialize the ES-EVI RB tree */ - RB_INIT(zebra_es_evi_rb_head, &zvni->es_evi_rb_tree); + RB_INIT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree); /* Initialize the local and remote ES lists maintained for quick * walks by type */ - zvni->local_es_evi_list = list_new(); - listset_app_node_mem(zvni->local_es_evi_list); + zevpn->local_es_evi_list = list_new(); + listset_app_node_mem(zevpn->local_es_evi_list); } -/* Cleanup the ES info maintained per-L2_VNI */ -void zebra_evpn_vni_es_cleanup(zebra_vni_t *zvni) +/* Cleanup the ES info maintained per- EVPN */ +void zebra_evpn_evpn_es_cleanup(zebra_evpn_t *zevpn) { struct zebra_evpn_es_evi *es_evi; struct zebra_evpn_es_evi *es_evi_next; RB_FOREACH_SAFE(es_evi, zebra_es_evi_rb_head, - &zvni->es_evi_rb_tree, es_evi_next) { + &zevpn->es_evi_rb_tree, es_evi_next) { zebra_evpn_local_es_evi_do_del(es_evi); } - list_delete(&zvni->local_es_evi_list); - zebra_evpn_es_clear_base_vni(zvni); + list_delete(&zevpn->local_es_evi_list); + zebra_evpn_es_clear_base_evpn(zevpn); } /* called when the oper state or bridge membership changes for the * vxlan device */ -void zebra_evpn_vni_update_all_es(zebra_vni_t *zvni) +void zebra_evpn_update_all_es(zebra_evpn_t *zevpn) { struct zebra_evpn_es_evi *es_evi; struct listnode *node; - /* the VNI is now elgible as a base for EVPN-MH */ - if (zebra_evpn_vni_send_to_client_ok(zvni)) - zebra_evpn_es_set_base_vni(zvni); + /* the EVPN is now elgible as a base for EVPN-MH */ + if (zebra_evpn_send_to_client_ok(zevpn)) + zebra_evpn_es_set_base_evpn(zevpn); else - zebra_evpn_es_clear_base_vni(zvni); + zebra_evpn_es_clear_base_evpn(zevpn); - for (ALL_LIST_ELEMENTS_RO(zvni->local_es_evi_list, node, es_evi)) + for (ALL_LIST_ELEMENTS_RO(zevpn->local_es_evi_list, node, es_evi)) zebra_evpn_es_evi_re_eval_send_to_client(es_evi); } @@ -514,24 +516,24 @@ static void zebra_evpn_acc_bd_free_on_deref(struct zebra_evpn_access_bd *acc_bd) } /* called when a EVPN-L2VNI is set or cleared against a BD */ -static void zebra_evpn_acc_bd_vni_set(struct zebra_evpn_access_bd *acc_bd, - zebra_vni_t *zvni, zebra_vni_t *old_zvni) +static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd, + zebra_evpn_t *zevpn, zebra_evpn_t *old_zevpn) { struct zebra_if *zif; struct listnode *node; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("access vlan %d l2-vni %u set", - acc_bd->vid, zvni ? zvni->vni : 0); + acc_bd->vid, zevpn ? zevpn->vni : 0); for (ALL_LIST_ELEMENTS_RO(acc_bd->mbr_zifs, node, zif)) { if (!zif->es_info.es) continue; - if (zvni) - zebra_evpn_local_es_evi_add(zif->es_info.es, zvni); - else if (old_zvni) - zebra_evpn_local_es_evi_del(zif->es_info.es, old_zvni); + if (zevpn) + zebra_evpn_local_es_evi_add(zif->es_info.es, zevpn); + else if (old_zevpn) + zebra_evpn_local_es_evi_del(zif->es_info.es, old_zevpn); } } @@ -540,7 +542,7 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif) { struct zebra_evpn_access_bd *acc_bd; struct zebra_if *old_vxlan_zif; - zebra_vni_t *old_zvni; + zebra_evpn_t *old_zevpn; if (!vid) return; @@ -554,20 +556,20 @@ void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif) if (vxlan_zif == old_vxlan_zif) return; - old_zvni = acc_bd->zvni; - acc_bd->zvni = zvni_lookup(vxlan_zif->l2info.vxl.vni); - if (acc_bd->zvni == old_zvni) + old_zevpn = acc_bd->zevpn; + acc_bd->zevpn = zebra_evpn_lookup(vxlan_zif->l2info.vxl.vni); + if (acc_bd->zevpn == old_zevpn) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("access vlan %d vni %u ref", acc_bd->vid, vxlan_zif->l2info.vxl.vni); - if (old_zvni) - zebra_evpn_acc_bd_vni_set(acc_bd, NULL, old_zvni); + if (old_zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn); - if (acc_bd->zvni) - zebra_evpn_acc_bd_vni_set(acc_bd, acc_bd->zvni, NULL); + if (acc_bd->zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, acc_bd->zevpn, NULL); } /* handle VLAN->VxLAN_IF deref */ @@ -590,18 +592,18 @@ void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif) zlog_debug("access vlan %d vni %u deref", acc_bd->vid, vxlan_zif->l2info.vxl.vni); - if (acc_bd->zvni) - zebra_evpn_acc_bd_vni_set(acc_bd, NULL, acc_bd->zvni); + if (acc_bd->zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, acc_bd->zevpn); - acc_bd->zvni = NULL; + acc_bd->zevpn = NULL; acc_bd->vxlan_zif = NULL; /* if there are no other references the access_bd can be freed */ zebra_evpn_acc_bd_free_on_deref(acc_bd); } -/* handle EVPN L2VNI add/del */ -void zebra_evpn_vxl_vni_set(struct zebra_if *zif, zebra_vni_t *zvni, +/* handle EVPN add/del */ +void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, zebra_evpn_t *zevpn, bool set) { struct zebra_l2info_vxlan *vxl; @@ -617,16 +619,16 @@ void zebra_evpn_vxl_vni_set(struct zebra_if *zif, zebra_vni_t *zvni, return; if (set) { - zebra_evpn_es_set_base_vni(zvni); - if (acc_bd->zvni != zvni) { - acc_bd->zvni = zvni; - zebra_evpn_acc_bd_vni_set(acc_bd, zvni, NULL); + zebra_evpn_es_set_base_evpn(zevpn); + if (acc_bd->zevpn != zevpn) { + acc_bd->zevpn = zevpn; + zebra_evpn_acc_bd_evpn_set(acc_bd, zevpn, NULL); } } else { - if (acc_bd->zvni) { - zebra_vni_t *old_zvni = acc_bd->zvni; - acc_bd->zvni = NULL; - zebra_evpn_acc_bd_vni_set(acc_bd, NULL, old_zvni); + if (acc_bd->zevpn) { + zebra_evpn_t *old_zevpn = acc_bd->zevpn; + acc_bd->zevpn = NULL; + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn); } } } @@ -651,8 +653,8 @@ void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif) vid, zif->ifp->name); listnode_add(acc_bd->mbr_zifs, zif); - if (acc_bd->zvni && zif->es_info.es) - zebra_evpn_local_es_evi_add(zif->es_info.es, acc_bd->zvni); + if (acc_bd->zevpn && zif->es_info.es) + zebra_evpn_local_es_evi_add(zif->es_info.es, acc_bd->zevpn); } /* handle deletion of VLAN members */ @@ -678,8 +680,8 @@ void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif) list_delete_node(acc_bd->mbr_zifs, node); - if (acc_bd->zvni && zif->es_info.es) - zebra_evpn_local_es_evi_del(zif->es_info.es, acc_bd->zvni); + if (acc_bd->zevpn && zif->es_info.es) + zebra_evpn_local_es_evi_del(zif->es_info.es, acc_bd->zevpn); /* if there are no other references the access_bd can be freed */ zebra_evpn_acc_bd_free_on_deref(acc_bd); @@ -699,7 +701,7 @@ static void zebra_evpn_acc_vl_show_entry_detail(struct vty *vty, acc_bd->vxlan_zif ? acc_bd->vxlan_zif->ifp->name : "-"); vty_out(vty, " L2-VNI: %d\n", - acc_bd->zvni ? acc_bd->zvni->vni : 0); + acc_bd->zevpn ? acc_bd->zevpn->vni : 0); vty_out(vty, " Member Count: %d\n", listcount(acc_bd->mbr_zifs)); vty_out(vty, " Members: \n"); @@ -717,7 +719,7 @@ static void zebra_evpn_acc_vl_show_entry(struct vty *vty, acc_bd->vid, acc_bd->vxlan_zif ? acc_bd->vxlan_zif->ifp->name : "-", - acc_bd->zvni ? acc_bd->zvni->vni : 0, + acc_bd->zevpn ? acc_bd->zevpn->vni : 0, listcount(acc_bd->mbr_zifs)); } @@ -961,7 +963,7 @@ static void zebra_evpn_nh_del(struct zebra_evpn_es_vtep *es_vtep) /* A list of remote VTEPs is maintained for each ES. This list includes - * 1. VTEPs for which we have imported the ESR i.e. ES-peers * 2. VTEPs that have an "active" ES-EVI VTEP i.e. EAD-per-ES and EAD-per-EVI - * have been imported into one or more VNIs + * have been imported into one or more EVPNs */ static int zebra_evpn_es_vtep_cmp(void *p1, void *p2) { @@ -1257,11 +1259,11 @@ void zebra_evpn_es_send_all_to_client(bool add) if (add) zebra_evpn_es_evi_send_to_client( - es, es_evi->zvni, + es, es_evi->zevpn, true /* add */); else zebra_evpn_es_evi_send_to_client( - es, es_evi->zvni, + es, es_evi->zevpn, false /* add */); } if (!add) @@ -1283,8 +1285,8 @@ static void zebra_evpn_es_setup_evis(struct zebra_evpn_es *es) bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { acc_bd = zebra_evpn_acc_vl_find(vid); - if (acc_bd->zvni) - zebra_evpn_local_es_evi_add(es, acc_bd->zvni); + if (acc_bd->zevpn) + zebra_evpn_local_es_evi_add(es, acc_bd->zevpn); } } @@ -1296,9 +1298,9 @@ static void zebra_evpn_es_local_mac_update(struct zebra_evpn_es *es, for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) { if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) { - zebra_vxlan_sync_mac_dp_install(mac, - false /* set_inactive */, - force_clear_static, __func__); + zebra_evpn_sync_mac_dp_install( + mac, false /* set_inactive */, + force_clear_static, __func__); } } } @@ -1328,8 +1330,8 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, /* setup base-vni if one doesn't already exist; the ES will get sent * to BGP as a part of that process */ - if (!zmh_info->es_base_vni) - zebra_evpn_es_get_one_base_vni(); + if (!zmh_info->es_base_evpn) + zebra_evpn_es_get_one_base_evpn(); else /* send notification to bgp */ zebra_evpn_es_re_eval_send_to_client(es, @@ -1354,7 +1356,8 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es *es) if (!(es->flags & ZEBRA_EVPNES_LOCAL)) return; - es->flags &= ~ZEBRA_EVPNES_LOCAL; + es->flags &= ~(ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_READY_FOR_BGP); + /* if there any local macs referring to the ES as dest we * need to clear the static reference on them */ @@ -1611,9 +1614,13 @@ bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi) es = zebra_evpn_es_find(esi); if (!es) { - es = zebra_evpn_es_new(esi); - if (IS_ZEBRA_DEBUG_EVPN_MH_ES) - zlog_debug("auto es %s add on mac ref", es->esi_str); + /* If non-zero esi implicitly create a new ES */ + if (memcmp(esi, zero_esi, sizeof(esi_t))) { + es = zebra_evpn_es_new(esi); + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("auto es %s add on mac ref", + es->esi_str); + } } return zebra_evpn_es_mac_ref_entry(mac, es); @@ -1621,7 +1628,7 @@ bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi) /* Inform BGP about local ES-EVI add or del */ static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, - zebra_vni_t *zvni, bool add) + zebra_evpn_t *zevpn, bool add) { struct zserv *client; struct stream *s; @@ -1637,7 +1644,7 @@ static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, add ? ZEBRA_LOCAL_ES_EVI_ADD : ZEBRA_LOCAL_ES_EVI_DEL, zebra_vrf_get_evpn_id()); stream_put(s, &es->esi, sizeof(esi_t)); - stream_putl(s, zvni->vni); + stream_putl(s, zevpn->vni); /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); @@ -1645,7 +1652,7 @@ static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("send %s local es %s evi %u to %s", add ? "add" : "del", - es->esi_str, zvni->vni, + es->esi_str, zevpn->vni, zebra_route_string(client->proto)); client->local_es_add_cnt++; @@ -1979,39 +1986,39 @@ DEFPY(zebra_evpn_es_id, * necessary */ /* called when a new vni is added or becomes oper up or becomes a bridge port */ -void zebra_evpn_es_set_base_vni(zebra_vni_t *zvni) +void zebra_evpn_es_set_base_evpn(zebra_evpn_t *zevpn) { struct listnode *node; struct zebra_evpn_es *es; - if (zmh_info->es_base_vni) { - if (zmh_info->es_base_vni != zvni) { - /* unrelated VNI; ignore it */ + if (zmh_info->es_base_evpn) { + if (zmh_info->es_base_evpn != zevpn) { + /* unrelated EVPN; ignore it */ return; } /* check if the local vtep-ip has changed */ } else { - /* check if the VNI can be used as base VNI */ - if (!zebra_evpn_vni_send_to_client_ok(zvni)) + /* check if the EVPN can be used as base EVPN */ + if (!zebra_evpn_send_to_client_ok(zevpn)) return; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("es base vni set to %d", - zvni->vni); - zmh_info->es_base_vni = zvni; + zevpn->vni); + zmh_info->es_base_evpn = zevpn; } /* update local VTEP-IP */ if (zmh_info->es_originator_ip.s_addr == - zmh_info->es_base_vni->local_vtep_ip.s_addr) + zmh_info->es_base_evpn->local_vtep_ip.s_addr) return; zmh_info->es_originator_ip.s_addr = - zmh_info->es_base_vni->local_vtep_ip.s_addr; + zmh_info->es_base_evpn->local_vtep_ip.s_addr; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("es originator ip set to %s", - inet_ntoa(zmh_info->es_base_vni->local_vtep_ip)); + inet_ntoa(zmh_info->es_base_evpn->local_vtep_ip)); /* if originator ip changes we need to update bgp */ for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, node, es)) { @@ -2026,20 +2033,20 @@ void zebra_evpn_es_set_base_vni(zebra_vni_t *zvni) /* called when a vni is removed or becomes oper down or is removed from a * bridge */ -void zebra_evpn_es_clear_base_vni(zebra_vni_t *zvni) +void zebra_evpn_es_clear_base_evpn(zebra_evpn_t *zevpn) { struct listnode *node; struct zebra_evpn_es *es; - if (zmh_info->es_base_vni != zvni) + if (zmh_info->es_base_evpn != zevpn) return; - zmh_info->es_base_vni = NULL; - /* lost current base VNI; try to find a new one */ - zebra_evpn_es_get_one_base_vni(); + zmh_info->es_base_evpn = NULL; + /* lost current base EVPN; try to find a new one */ + zebra_evpn_es_get_one_base_evpn(); - /* couldn't locate an eligible base vni */ - if (!zmh_info->es_base_vni && zmh_info->es_originator_ip.s_addr) { + /* couldn't locate an eligible base evpn */ + if (!zmh_info->es_base_evpn && zmh_info->es_originator_ip.s_addr) { if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("es originator ip cleared"); @@ -2053,27 +2060,27 @@ void zebra_evpn_es_clear_base_vni(zebra_vni_t *zvni) } /* Locate an "eligible" L2-VNI to follow */ -static int zebra_evpn_es_get_one_base_vni_cb(struct hash_bucket *b, void *data) +static int zebra_evpn_es_get_one_base_evpn_cb(struct hash_bucket *b, void *data) { - zebra_vni_t *zvni = b->data; + zebra_evpn_t *zevpn = b->data; - zebra_evpn_es_set_base_vni(zvni); + zebra_evpn_es_set_base_evpn(zevpn); - if (zmh_info->es_base_vni) + if (zmh_info->es_base_evpn) return HASHWALK_ABORT; return HASHWALK_CONTINUE; } -/* locate a base_vni to follow for the purposes of common params like +/* locate a base_evpn to follow for the purposes of common params like * originator IP */ -static void zebra_evpn_es_get_one_base_vni(void) +static void zebra_evpn_es_get_one_base_evpn(void) { struct zebra_vrf *zvrf; zvrf = zebra_vrf_get_evpn(); - hash_walk(zvrf->vni_table, zebra_evpn_es_get_one_base_vni_cb, NULL); + hash_walk(zvrf->evpn_table, zebra_evpn_es_get_one_base_evpn_cb, NULL); } /*****************************************************************************/ diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 46c25a04bc..ed62677e3b 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -79,8 +79,8 @@ RB_HEAD(zebra_es_rb_head, zebra_evpn_es); RB_PROTOTYPE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp); /* ES per-EVI info - * - ES-EVIs are maintained per-VNI (vni->es_evi_rb_tree) - * - Local ES-EVIs are linked to per-VNI list for quick access + * - ES-EVIs are maintained per-EVPN (vni->es_evi_rb_tree) + * - Local ES-EVIs are linked to per-EVPN list for quick access * - Although some infrastucture is present for remote ES-EVIs, currently * BGP does NOT send remote ES-EVIs to zebra. This may change in the * future (but must be changed thoughtfully and only if needed as ES-EVI @@ -88,7 +88,7 @@ RB_PROTOTYPE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp); */ struct zebra_evpn_es_evi { struct zebra_evpn_es *es; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; /* ES-EVI flags */ uint32_t flags; @@ -97,11 +97,11 @@ struct zebra_evpn_es_evi { #define ZEBRA_EVPNES_EVI_READY_FOR_BGP (1 << 1) /* ready to be sent to BGP */ /* memory used for adding the es_evi to - * es_evi->zvni->es_evi_rb_tree + * es_evi->zevpn->es_evi_rb_tree */ RB_ENTRY(zebra_evpn_es_evi) rb_node; /* memory used for linking the es_evi to - * es_evi->zvni->local_es_evi_list + * es_evi->zevpn->local_es_evi_list */ struct listnode l2vni_listnode; /* memory used for linking the es_evi to @@ -135,8 +135,8 @@ struct zebra_evpn_access_bd { struct zebra_if *vxlan_zif; /* vxlan device */ /* list of members associated with the BD i.e. (potential) ESs */ struct list *mbr_zifs; - /* presence of zvni activates the EVI on all the ESs in mbr_zifs */ - zebra_vni_t *zvni; + /* presence of zevpn activates the EVI on all the ESs in mbr_zifs */ + zebra_evpn_t *zevpn; }; /* multihoming information stored in zrouter */ @@ -155,7 +155,7 @@ struct zebra_evpn_mh_info { * XXX: once single vxlan device model becomes available this will * not be necessary */ - zebra_vni_t *es_base_vni; + zebra_evpn_t *es_base_evpn; struct in_addr es_originator_ip; /* L2 NH and NHG ids - @@ -198,12 +198,12 @@ extern void zebra_evpn_mh_terminate(void); extern bool zebra_evpn_is_if_es_capable(struct zebra_if *zif); extern void zebra_evpn_if_init(struct zebra_if *zif); extern void zebra_evpn_if_cleanup(struct zebra_if *zif); -extern void zebra_evpn_vni_es_init(zebra_vni_t *zvni); -extern void zebra_evpn_vni_es_cleanup(zebra_vni_t *zvni); -extern void zebra_evpn_vxl_vni_set(struct zebra_if *zif, zebra_vni_t *zvni, +extern void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn); +extern void zebra_evpn_evpn_es_cleanup(zebra_evpn_t *zevpn); +extern void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, zebra_evpn_t *zevpn, bool set); -extern void zebra_evpn_es_set_base_vni(zebra_vni_t *zvni); -extern void zebra_evpn_es_clear_base_vni(zebra_vni_t *zvni); +extern void zebra_evpn_es_set_base_evpn(zebra_evpn_t *zevpn); +extern void zebra_evpn_es_clear_base_evpn(zebra_evpn_t *zevpn); extern void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif); extern void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif); extern void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif); @@ -213,7 +213,7 @@ extern void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up); extern void zebra_evpn_es_show(struct vty *vty, bool uj); extern void zebra_evpn_es_show_detail(struct vty *vty, bool uj); extern void zebra_evpn_es_show_esi(struct vty *vty, bool uj, esi_t *esi); -extern void zebra_evpn_vni_update_all_es(zebra_vni_t *zvni); +extern void zebra_evpn_update_all_es(zebra_evpn_t *zevpn); extern void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS); extern void zebra_evpn_es_evi_show(struct vty *vty, bool uj, int detail); extern void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj, diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c new file mode 100644 index 0000000000..492052b1b2 --- /dev/null +++ b/zebra/zebra_evpn_neigh.c @@ -0,0 +1,2453 @@ +/* + * Zebra EVPN Neighbor code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * 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 FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#include "hash.h" +#include "interface.h" +#include "jhash.h" +#include "memory.h" +#include "prefix.h" +#include "vlan.h" +#include "json.h" + +#include "zebra/zserv.h" +#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" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_evpn_mac.h" + +DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "EVI Neighbor"); + +/* + * Make hash key for neighbors. + */ +static unsigned int neigh_hash_keymake(const void *p) +{ + const zebra_neigh_t *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); +} + +/* + * Compare two neighbor hash structures. + */ +static bool neigh_cmp(const void *p1, const void *p2) +{ + const zebra_neigh_t *n1 = p1; + const zebra_neigh_t *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); +} + +int neigh_list_cmp(void *p1, void *p2) +{ + const zebra_neigh_t *n1 = p1; + const zebra_neigh_t *n2 = p2; + + return memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)); +} + +struct hash *zebra_neigh_db_create(const char *desc) +{ + return hash_create(neigh_hash_keymake, neigh_cmp, desc); +} + +uint32_t num_dup_detected_neighs(zebra_evpn_t *zevpn) +{ + unsigned int i; + uint32_t num_neighs = 0; + struct hash *hash; + struct hash_bucket *hb; + zebra_neigh_t *nbr; + + hash = zevpn->neigh_table; + if (!hash) + return num_neighs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + nbr = (zebra_neigh_t *)hb->data; + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + num_neighs++; + } + } + + return num_neighs; +} + +/* + * Helper function to determine maximum width of neighbor IP address for + * display - just because we're dealing with IPv6 addresses that can + * widely vary. + */ +void zebra_evpn_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt) +{ + zebra_neigh_t *n; + char buf[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + int width; + + n = (zebra_neigh_t *)bucket->data; + + ipaddr2str(&n->ip, buf, sizeof(buf)); + width = strlen(buf); + if (width > wctx->addr_width) + wctx->addr_width = width; +} + +/* + * Count of remote neighbors referencing this MAC. + */ +int remote_neigh_count(zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + int count = 0; + + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + count++; + } + + return count; +} + +/* + * Install remote neighbor into the kernel. + */ +int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n, + bool was_static) +{ + struct interface *vlan_if; + int flags; + int ret = 0; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE)) + return 0; + + vlan_if = zevpn_map_to_svi(zevpn); + if (!vlan_if) + return -1; + + flags = DPLANE_NTF_EXT_LEARNED; + if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) + flags |= DPLANE_NTF_ROUTER; + ZEBRA_NEIGH_SET_ACTIVE(n); + + dplane_rem_neigh_add(vlan_if, &n->ip, &n->emac, flags, was_static); + + return ret; +} + +/* + * Install neighbor hash entry - called upon access VLAN change. + */ +void zebra_evpn_install_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_neigh_t *n; + struct neigh_walk_ctx *wctx = ctxt; + + n = (zebra_neigh_t *)bucket->data; + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + zebra_evpn_rem_neigh_install(wctx->zevpn, n, + false /*was_static*/); +} + +/* + * Callback to allocate neighbor hash entry. + */ +static void *zebra_evpn_neigh_alloc(void *p) +{ + const zebra_neigh_t *tmp_n = p; + zebra_neigh_t *n; + + n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); + *n = *tmp_n; + + return ((void *)n); +} + +static void zebra_evpn_local_neigh_ref_mac(zebra_neigh_t *n, + struct ethaddr *macaddr, + zebra_mac_t *mac, + bool send_mac_update) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_static; + bool new_static; + + memcpy(&n->emac, macaddr, ETH_ALEN); + n->mac = mac; + + /* Link to new MAC */ + if (!mac) + return; + + listnode_add_sort(mac->neigh_list, n); + if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) { + old_static = zebra_evpn_mac_is_static(mac); + ++mac->sync_neigh_cnt; + new_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh ref mac vni %u ip %s mac %s ref %d", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + mac->sync_neigh_cnt); + if ((old_static != new_static) && send_mac_update) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install( + mac, false /*set_inactive*/, + false /*force_clear_static*/, __func__); + } +} + +/* sync-path that is active on an ES peer */ +static void zebra_evpn_sync_neigh_dp_install(zebra_neigh_t *n, + bool set_inactive, + bool force_clear_static, + const char *caller) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + struct zebra_ns *zns; + struct interface *ifp; + bool set_static; + bool set_router; + + zns = zebra_ns_lookup(NS_DEFAULT); + ifp = if_lookup_by_index_per_ns(zns, n->ifindex); + if (!ifp) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "%s: dp-install sync-neigh vni %u ip %s mac %s if %d f 0x%x skipped", + caller, n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + n->ifindex, n->flags); + return; + } + + if (force_clear_static) + set_static = false; + else + set_static = zebra_evpn_neigh_is_static(n); + + set_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* XXX - this will change post integration with the new kernel */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + set_inactive = true; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "%s: dp-install sync-neigh vni %u ip %s mac %s if %s(%d) f 0x%x%s%s%s", + caller, n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + ifp->name, n->ifindex, n->flags, + set_router ? " router" : "", + set_static ? " static" : "", + set_inactive ? " inactive" : ""); + dplane_local_neigh_add(ifp, &n->ip, &n->emac, set_router, set_static, + set_inactive); +} + +/* + * Inform BGP about local neighbor addition. + */ +int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, + zebra_mac_t *zmac, uint32_t neigh_flags, + uint32_t seq) +{ + uint8_t flags = 0; + + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) { + /* host reachability has not been verified locally */ + + /* if no ES peer is claiming reachability we can't advertise + * the entry + */ + if (!CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + return 0; + + /* ES peers are claiming reachability; we will + * advertise the entry but with a proxy flag + */ + SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT); + } + + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + /* Set router flag (R-bit) based on local neigh entry add */ + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ROUTER_FLAG)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_SVI_IP)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP); + + return zebra_evpn_macip_send_msg_to_client( + vni, macaddr, ip, flags, seq, ZEBRA_NEIGH_ACTIVE, + zmac ? zmac->es : NULL, ZEBRA_MACIP_ADD); +} + +/* + * Inform BGP about local neighbor deletion. + */ +int zebra_evpn_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, uint32_t flags, + int state, bool force) +{ + if (!force) { + if (CHECK_FLAG(flags, ZEBRA_NEIGH_LOCAL_INACTIVE) + && !CHECK_FLAG(flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + /* the neigh was not advertised - nothing to delete */ + return 0; + } + + return zebra_evpn_macip_send_msg_to_client( + vni, macaddr, ip, flags, 0, state, NULL, ZEBRA_MACIP_DEL); +} + +static void zebra_evpn_neigh_send_add_del_to_client(zebra_neigh_t *n, + bool old_bgp_ready, + bool new_bgp_ready) +{ + if (new_bgp_ready) + zebra_evpn_neigh_send_add_to_client(n->zevpn->vni, &n->ip, + &n->emac, n->mac, n->flags, + n->loc_seq); + else if (old_bgp_ready) + zebra_evpn_neigh_send_del_to_client(n->zevpn->vni, &n->ip, + &n->emac, n->flags, + n->state, true /*force*/); +} + +/* if the static flag associated with the neigh changes we need + * to update the sync-neigh references against the MAC + * and inform the dataplane about the static flag changes. + */ +void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static, + bool new_n_static, bool defer_n_dp, + bool defer_mac_dp, const char *caller) +{ + zebra_mac_t *mac = n->mac; + bool old_mac_static; + bool new_mac_static; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (old_n_static == new_n_static) + return; + + /* update the neigh sync references in the dataplane. if + * the neigh is in the middle of updates the caller can + * request for a defer + */ + if (!defer_n_dp) + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + if (!mac) + return; + + /* update the mac sync ref cnt */ + old_mac_static = zebra_evpn_mac_is_static(mac); + if (new_n_static) { + ++mac->sync_neigh_cnt; + } else if (old_n_static) { + if (mac->sync_neigh_cnt) + --mac->sync_neigh_cnt; + } + new_mac_static = zebra_evpn_mac_is_static(mac); + + /* update the mac sync references in the dataplane */ + if ((old_mac_static != new_mac_static) && !defer_mac_dp) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh ref-chg vni %u ip %s mac %s f 0x%x %d%s%s%s%s by %s", + n->zevpn->vni, ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags, mac->sync_neigh_cnt, + old_n_static ? " old_n_static" : "", + new_n_static ? " new_n_static" : "", + old_mac_static ? " old_mac_static" : "", + new_mac_static ? " new_mac_static" : "", caller); +} + +/* Neigh hold timer is used to age out peer-active flag. + * + * During this wait time we expect the dataplane component or an + * external neighmgr daemon to probe existing hosts to independently + * establish their presence on the ES. + */ +static int zebra_evpn_neigh_hold_exp_cb(struct thread *t) +{ + zebra_neigh_t *n; + bool old_bgp_ready; + bool new_bgp_ready; + bool old_n_static; + bool new_n_static; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + n = THREAD_ARG(t); + /* the purpose of the hold timer is to age out the peer-active + * flag + */ + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + return 0; + + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + new_n_static = zebra_evpn_neigh_is_static(n); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold expired", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + + /* re-program the local neigh in the dataplane if the neigh is no + * longer static + */ + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, false /*defer_n_dp*/, + false /*defer_mac_dp*/, __func__); + + /* inform bgp if needed */ + if (old_bgp_ready != new_bgp_ready) + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + return 0; +} + +static inline void zebra_evpn_neigh_start_hold_timer(zebra_neigh_t *n) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (n->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold start", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + thread_add_timer(zrouter.master, zebra_evpn_neigh_hold_exp_cb, n, + zmh_info->neigh_hold_time, &n->hold_timer); +} + +static void zebra_evpn_local_neigh_deref_mac(zebra_neigh_t *n, + bool send_mac_update) +{ + zebra_mac_t *mac = n->mac; + zebra_evpn_t *zevpn = n->zevpn; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_static; + bool new_static; + + n->mac = NULL; + if (!mac) + return; + + if ((n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) && mac->sync_neigh_cnt) { + old_static = zebra_evpn_mac_is_static(mac); + --mac->sync_neigh_cnt; + new_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh deref mac vni %u ip %s mac %s ref %d", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + mac->sync_neigh_cnt); + if ((old_static != new_static) && send_mac_update) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install( + mac, false /* set_inactive */, + false /* force_clear_static */, __func__); + } + + listnode_delete(mac->neigh_list, n); + zebra_evpn_deref_ip2mac(zevpn, mac); +} + +bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n, + struct ethaddr *macaddr, uint32_t seq) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + uint32_t tmp_seq; + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) + tmp_seq = n->loc_seq; + else + tmp_seq = n->rem_seq; + + if (seq < tmp_seq) { + /* if the neigh was never advertised to bgp we must accept + * whatever sequence number bgp sends + * XXX - check with Vivek + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) + && !zebra_evpn_neigh_is_ready_for_bgp(n)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-macip accept vni %u mac %s IP %s lower seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, + sizeof(macbuf)), + ipaddr2str(&n->ip, ipbuf, + sizeof(ipbuf)), + tmp_seq, n->flags); + return true; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-macip ignore vni %u mac %s IP %s as existing has higher seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + tmp_seq, n->flags); + return false; + } + + return true; +} + +/* + * Add neighbor entry. + */ +static zebra_neigh_t *zebra_evpn_neigh_add(zebra_evpn_t *zevpn, + struct ipaddr *ip, + struct ethaddr *mac, + zebra_mac_t *zmac, uint32_t n_flags) +{ + zebra_neigh_t tmp_n; + zebra_neigh_t *n = NULL; + + memset(&tmp_n, 0, sizeof(zebra_neigh_t)); + memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); + n = hash_get(zevpn->neigh_table, &tmp_n, zebra_evpn_neigh_alloc); + assert(n); + + n->state = ZEBRA_NEIGH_INACTIVE; + n->zevpn = zevpn; + n->dad_ip_auto_recovery_timer = NULL; + n->flags = n_flags; + + if (!zmac) + zmac = zebra_evpn_mac_lookup(zevpn, mac); + zebra_evpn_local_neigh_ref_mac(n, mac, zmac, + false /* send_mac_update */); + + return n; +} + +/* + * Delete neighbor entry. + */ +int zebra_evpn_neigh_del(zebra_evpn_t *zevpn, zebra_neigh_t *n) +{ + zebra_neigh_t *tmp_n; + + if (n->mac) + listnode_delete(n->mac->neigh_list, n); + + /* Cancel auto recovery */ + THREAD_OFF(n->dad_ip_auto_recovery_timer); + + /* Free the VNI hash entry and allocated memory. */ + tmp_n = hash_release(zevpn->neigh_table, n); + XFREE(MTYPE_NEIGH, tmp_n); + + return 0; +} + +void zebra_evpn_sync_neigh_del(zebra_neigh_t *n) +{ + bool old_n_static; + bool new_n_static; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh del vni %u ip %s mac %s f 0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + zebra_evpn_neigh_start_hold_timer(n); + new_n_static = zebra_evpn_neigh_is_static(n); + + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, false /*defer-dp*/, + false /*defer_mac_dp*/, __func__); +} + +zebra_neigh_t * +zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx) +{ + struct interface *ifp = NULL; + bool is_router; + zebra_mac_t *mac = ctx->mac; + uint32_t tmp_seq; + bool old_router = false; + bool old_bgp_ready = false; + bool new_bgp_ready; + bool inform_dataplane = false; + bool inform_bgp = false; + bool old_mac_static; + bool new_mac_static; + bool set_dp_inactive = false; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool created; + ifindex_t ifindex = 0; + + /* locate l3-svi */ + ifp = zevpn_map_to_svi(zevpn); + if (ifp) + ifindex = ifp->ifindex; + + is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + old_mac_static = zebra_evpn_mac_is_static(mac); + + if (!n) { + uint32_t n_flags = 0; + + /* New neighbor - create */ + SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL); + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_PROXY); + else + SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr, mac, + n_flags); + n->ifindex = ifindex; + ZEBRA_NEIGH_SET_ACTIVE(n); + + created = true; + inform_dataplane = true; + inform_bgp = true; + set_dp_inactive = true; + } else { + bool mac_change; + uint32_t old_flags = n->flags; + bool old_n_static; + bool new_n_static; + + created = false; + old_n_static = zebra_evpn_neigh_is_static(n); + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + old_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + mac_change = !!memcmp(&n->emac, &mac->macaddr, ETH_ALEN); + + /* deref and clear old info */ + if (mac_change) { + if (old_bgp_ready) { + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + old_bgp_ready = false; + } + if (n->mac) + zebra_evpn_local_neigh_deref_mac( + n, false /*send_mac_update*/); + } + /* clear old fwd info */ + n->rem_seq = 0; + n->r_vtep_ip.s_addr = 0; + + /* setup new flags */ + n->flags = 0; + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + /* retain activity flag if the neigh was + * previously local + */ + if (old_flags & ZEBRA_NEIGH_LOCAL) { + n->flags |= (old_flags & ZEBRA_NEIGH_LOCAL_INACTIVE); + } else { + inform_dataplane = true; + set_dp_inactive = true; + n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE; + } + + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + else + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + /* if the neigh was peer-active previously we + * need to keep the flag and start the + * holdtimer on it. the peer-active flag is + * cleared on holdtimer expiry. + */ + if (CHECK_FLAG(old_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + zebra_evpn_neigh_start_hold_timer(n); + } + } else { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + /* stop hold timer if a peer has verified + * reachability + */ + zebra_evpn_neigh_stop_hold_timer(n); + } + ZEBRA_NEIGH_SET_ACTIVE(n); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH && (old_flags != n->flags)) + zlog_debug( + "sync-neigh vni %u ip %s mac %s old_f 0x%x new_f 0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + old_flags, n->flags); + + new_n_static = zebra_evpn_neigh_is_static(n); + if (mac_change) { + set_dp_inactive = true; + n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE; + inform_dataplane = true; + zebra_evpn_local_neigh_ref_mac( + n, &mac->macaddr, mac, + false /*send_mac_update*/); + } else if (old_n_static != new_n_static) { + inform_dataplane = true; + /* if static flags have changed without a mac change + * we need to create the correct sync-refs against + * the existing mac + */ + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, + true /*defer_dp*/, true /*defer_mac_dp*/, + __func__); + } + + /* Update the forwarding info. */ + if (n->ifindex != ifindex) { + n->ifindex = ifindex; + inform_dataplane = true; + } + } + + /* update the neigh seq. we don't bother with the mac seq as + * sync_mac_update already took care of that + */ + tmp_seq = MAX(n->loc_seq, seq); + if (tmp_seq != n->loc_seq) { + n->loc_seq = tmp_seq; + inform_bgp = true; + } + + /* Mark Router flag (R-bit) */ + if (is_router) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + if (old_router != is_router) + inform_dataplane = true; + + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + if (old_bgp_ready != new_bgp_ready) + inform_bgp = true; + + new_mac_static = zebra_evpn_mac_is_static(mac); + if ((old_mac_static != new_mac_static) || ctx->mac_dp_update_deferred) + zebra_evpn_sync_mac_dp_install(mac, ctx->mac_inactive, + false /* force_clear_static */, + __func__); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh %s vni %u ip %s mac %s if %s(%d) seq %d f 0x%x%s%s", + created ? "created" : "updated", n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + ifp ? ifp->name : "", ifindex, n->loc_seq, n->flags, + inform_bgp ? " inform_bgp" : "", + inform_dataplane ? " inform_dp" : ""); + + if (inform_dataplane) + zebra_evpn_sync_neigh_dp_install(n, set_dp_inactive, + false /* force_clear_static */, + __func__); + + if (inform_bgp) + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + return n; +} + +/* + * Uninstall remote neighbor from the kernel. + */ +static int zebra_evpn_neigh_uninstall(zebra_evpn_t *zevpn, zebra_neigh_t *n) +{ + struct interface *vlan_if; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE)) + return 0; + + vlan_if = zevpn_map_to_svi(zevpn); + if (!vlan_if) + return -1; + + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + + dplane_rem_neigh_delete(vlan_if, &n->ip); + + return 0; +} + +/* + * Free neighbor hash entry (callback) + */ +static void zebra_evpn_neigh_del_hash_entry(struct hash_bucket *bucket, + void *arg) +{ + struct neigh_walk_ctx *wctx = arg; + zebra_neigh_t *n = bucket->data; + + if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL)) + || ((wctx->flags & DEL_REMOTE_NEIGH) + && (n->flags & ZEBRA_NEIGH_REMOTE)) + || ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP) + && (n->flags & ZEBRA_NEIGH_REMOTE) + && IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))) { + if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL)) + zebra_evpn_neigh_send_del_to_client( + wctx->zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + + if (wctx->uninstall) { + if (zebra_evpn_neigh_is_static(n)) + zebra_evpn_sync_neigh_dp_install( + n, false /* set_inactive */, + true /* force_clear_static */, + __func__); + if ((n->flags & ZEBRA_NEIGH_REMOTE)) + zebra_evpn_neigh_uninstall(wctx->zevpn, n); + } + + zebra_evpn_neigh_del(wctx->zevpn, n); + } + + return; +} + +/* + * Delete all neighbor entries for this EVPN. + */ +void zebra_evpn_neigh_del_all(zebra_evpn_t *zevpn, int uninstall, + int upd_client, uint32_t flags) +{ + struct neigh_walk_ctx wctx; + + if (!zevpn->neigh_table) + return; + + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zevpn = zevpn; + wctx.uninstall = uninstall; + wctx.upd_client = upd_client; + wctx.flags = flags; + + hash_iterate(zevpn->neigh_table, zebra_evpn_neigh_del_hash_entry, + &wctx); +} + +/* + * Look up neighbor hash entry. + */ +zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn, struct ipaddr *ip) +{ + zebra_neigh_t tmp; + zebra_neigh_t *n; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); + n = hash_lookup(zevpn->neigh_table, &tmp); + + return n; +} + +/* + * Process all neighbors associated with a MAC upon the MAC being learnt + * locally or undergoing any other change (such as sequence number). + */ +void zebra_evpn_process_neigh_on_local_mac_change(zebra_evpn_t *zevpn, + zebra_mac_t *zmac, + bool seq_change, + bool es_change) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + struct zebra_vrf *zvrf = NULL; + char buf[ETHER_ADDR_STRLEN]; + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on local MAC %s %s, VNI %u", + prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), + seq_change ? "CHANGE" : "ADD", zevpn->vni); + + /* Walk all neighbors and mark any inactive local neighbors as + * active and/or update sequence number upon a move, and inform BGP. + * The action for remote neighbors is TBD. + * NOTE: We can't simply uninstall remote neighbors as the kernel may + * accidentally end up deleting a just-learnt local neighbor. + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change + || es_change) { + ZEBRA_NEIGH_SET_ACTIVE(n); + n->loc_seq = zmac->loc_seq; + if (!(zvrf->dup_addr_detect && zvrf->dad_freeze + && !!CHECK_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE))) + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, &n->ip, &n->emac, + n->mac, n->flags, n->loc_seq); + } + } + } +} + +/* + * Process all neighbors associated with a local MAC upon the MAC being + * deleted. + */ +void zebra_evpn_process_neigh_on_local_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf[ETHER_ADDR_STRLEN]; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on local MAC %s DEL, VNI %u", + prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), + zevpn->vni); + + /* Walk all local neighbors and mark as inactive and inform + * BGP, if needed. + * TBD: There is currently no handling for remote neighbors. We + * don't expect them to exist, if they do, do we install the MAC + * as a remote MAC and the neighbor as remote? + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) { + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + ZEBRA_NEIGH_ACTIVE, false /*force*/); + } + } + } +} + +/* + * Process all neighbors associated with a MAC upon the MAC being remotely + * learnt. + */ +void zebra_evpn_process_neigh_on_remote_mac_add(zebra_evpn_t *zevpn, + zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf[ETHER_ADDR_STRLEN]; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on remote MAC %s ADD, VNI %u", + prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), + zevpn->vni); + + /* Walk all local neighbors and mark as inactive and inform + * BGP, if needed. + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) { + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + ZEBRA_NEIGH_ACTIVE, false /* force */); + } + } + } +} + +/* + * Process all neighbors associated with a remote MAC upon the MAC being + * deleted. + */ +void zebra_evpn_process_neigh_on_remote_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac) +{ + /* NOTE: Currently a NO-OP. */ +} + +static inline void zebra_evpn_local_neigh_update_log( + const char *pfx, zebra_neigh_t *n, bool is_router, bool local_inactive, + bool old_bgp_ready, bool new_bgp_ready, bool inform_dataplane, + bool inform_bgp, const char *sfx) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (!IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + return; + + zlog_debug("%s neigh vni %u ip %s mac %s f 0x%x%s%s%s%s%s%s %s", pfx, + n->zevpn->vni, ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), n->flags, + is_router ? " router" : "", + local_inactive ? " local-inactive" : "", + old_bgp_ready ? " old_bgp_ready" : "", + new_bgp_ready ? " new_bgp_ready" : "", + inform_dataplane ? " inform_dp" : "", + inform_bgp ? " inform_bgp" : "", sfx); +} + +/* As part Duplicate Address Detection (DAD) for IP mobility + * MAC binding changes, ensure to inherit duplicate flag + * from MAC. + */ +static int zebra_evpn_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, + zebra_mac_t *old_zmac, + zebra_mac_t *new_zmac, + zebra_neigh_t *nbr) +{ + bool is_old_mac_dup = false; + bool is_new_mac_dup = false; + + if (!zvrf->dup_addr_detect) + return 0; + /* Check old or new MAC is detected as duplicate + * mark this neigh as duplicate + */ + if (old_zmac) + is_old_mac_dup = + CHECK_FLAG(old_zmac->flags, ZEBRA_MAC_DUPLICATE); + if (new_zmac) + is_new_mac_dup = + CHECK_FLAG(new_zmac->flags, ZEBRA_MAC_DUPLICATE); + /* Old and/or new MAC can be in duplicate state, + * based on that IP/Neigh Inherits the flag. + * If New MAC is marked duplicate, inherit to the IP. + * If old MAC is duplicate but new MAC is not, clear + * duplicate flag for IP and reset detection params + * and let IP DAD retrigger. + */ + if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + /* Capture Duplicate detection time */ + nbr->dad_dup_detect_time = monotime(NULL); + /* Mark neigh inactive */ + ZEBRA_NEIGH_SET_INACTIVE(nbr); + + return 1; + } else if (is_old_mac_dup && !is_new_mac_dup) { + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + } + return 0; +} + +static int zebra_evpn_dad_ip_auto_recovery_exp(struct thread *t) +{ + struct zebra_vrf *zvrf = NULL; + zebra_neigh_t *nbr = NULL; + zebra_evpn_t *zevpn = NULL; + char buf1[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + + nbr = THREAD_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + zvrf = vrf_info_lookup(nbr->zevpn->vrf_id); + if (!zvrf) + return 0; + + zevpn = zebra_evpn_lookup(nbr->zevpn->vni); + if (!zevpn) + return 0; + + nbr = zebra_evpn_neigh_lookup(zevpn, &nbr->ip); + if (!nbr) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired", + __func__, + prefix_mac2str(&nbr->emac, buf2, sizeof(buf2)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), nbr->flags, + nbr->dad_count, zevpn->vni); + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + nbr->dad_ip_auto_recovery_timer = NULL; + ZEBRA_NEIGH_SET_ACTIVE(nbr); + + /* Send to BGP */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_neigh_send_add_to_client(zevpn->vni, &nbr->ip, + &nbr->emac, nbr->mac, + nbr->flags, nbr->loc_seq); + } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); + } + + return 0; +} + +static void +zebra_evpn_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, zebra_neigh_t *nbr, + struct in_addr vtep_ip, bool do_dad, + bool *is_dup_detect, bool is_local) +{ + + struct timeval elapsed = {0, 0}; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + bool reset_params = false; + + if (!zvrf->dup_addr_detect) + return; + + /* IP is detected as duplicate or inherit dup + * state, hold on to install as remote entry + * only if freeze is enabled. + */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u", + __func__, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + nbr->flags, nbr->dad_count, + zvrf->dad_freeze_time); + + if (zvrf->dad_freeze) + *is_dup_detect = true; + + /* warn-only action, neigh will be installed. + * freeze action, it wil not be installed. + */ + return; + } + + if (!do_dad) + return; + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + * During remote mac add, count should already be 1 + * via local learning. + */ + monotime_since(&nbr->detect_start_time, &elapsed); + reset_params = (elapsed.tv_sec > zvrf->dad_time); + + if (is_local && !reset_params) { + /* RFC-7432: A PE/VTEP that detects a MAC mobility + * event via LOCAL learning starts an M-second timer. + * + * NOTE: This is the START of the probe with count is + * 0 during LOCAL learn event. + */ + reset_params = !nbr->dad_count; + } + + if (reset_params) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u", + __func__, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + nbr->flags, nbr->dad_count); + /* Reset learn count but do not start detection + * during REMOTE learn event. + */ + nbr->dad_count = 0; + /* Start dup. addr detection (DAD) start time, + * ONLY during LOCAL learn. + */ + if (is_local) + monotime(&nbr->detect_start_time); + + } else if (!is_local) { + /* For REMOTE IP/Neigh, increment detection count + * ONLY while in probe window, once window passed, + * next local learn event should trigger DAD. + */ + nbr->dad_count++; + } + + /* For LOCAL IP/Neigh learn event, once count is reset above via either + * initial/start detection time or passed the probe time, the count + * needs to be incremented. + */ + if (is_local) + nbr->dad_count++; + + if (nbr->dad_count >= zvrf->dad_max_moves) { + flog_warn( + EC_ZEBRA_DUP_IP_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %s", + nbr->zevpn->vni, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + is_local ? "local update, last" : "remote update, from", + inet_ntoa(vtep_ip)); + + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + + /* Capture Duplicate detection time */ + nbr->dad_dup_detect_time = monotime(NULL); + + /* Start auto recovery timer for this IP */ + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + if (zvrf->dad_freeze && zvrf->dad_freeze_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start", + __func__, + prefix_mac2str(&nbr->emac, buf, + sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, + sizeof(buf1)), + nbr->flags, zvrf->dad_freeze_time); + + thread_add_timer(zrouter.master, + zebra_evpn_dad_ip_auto_recovery_exp, + nbr, zvrf->dad_freeze_time, + &nbr->dad_ip_auto_recovery_timer); + } + if (zvrf->dad_freeze) + *is_dup_detect = true; + } +} + +int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + bool is_router, bool local_inactive, + bool dp_static) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct zebra_vrf *zvrf; + zebra_neigh_t *n = NULL; + zebra_mac_t *zmac = NULL, *old_zmac = NULL; + uint32_t old_mac_seq = 0, mac_new_seq = 0; + bool upd_mac_seq = false; + bool neigh_mac_change = false; + bool neigh_on_hold = false; + bool neigh_was_remote = false; + bool do_dad = false; + struct in_addr vtep_ip = {.s_addr = 0}; + bool inform_dataplane = false; + bool created = false; + bool new_static = false; + bool old_bgp_ready = false; + bool new_bgp_ready; + + /* Check if the MAC exists. */ + zmac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!zmac) { + /* create a dummy MAC if the MAC is not already present */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("AUTO MAC %s created for neigh %s on VNI %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), + zevpn->vni); + + zmac = zebra_evpn_mac_add(zevpn, macaddr); + if (!zmac) { + zlog_debug("Failed to add MAC %s VNI %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + zevpn->vni); + return -1; + } + + memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info)); + memset(&zmac->flags, 0, sizeof(uint32_t)); + SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO); + } else { + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { + /* + * We don't change the MAC to local upon a neighbor + * learn event, we wait for the explicit local MAC + * learn. However, we have to compute its sequence + * number in preparation for when it actually turns + * local. + */ + upd_mac_seq = true; + } + } + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + if (!zvrf) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug(" Unable to find vrf for: %d", + zevpn->vxlan_if->vrf_id); + return -1; + } + + /* Check if the neighbor exists. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) { + /* New neighbor - create */ + n = zebra_evpn_neigh_add(zevpn, ip, macaddr, zmac, 0); + if (!n) { + flog_err( + EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", + ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, zevpn->vni); + return -1; + } + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; + created = true; + } else { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + bool mac_different; + bool cur_is_router; + bool old_local_inactive; + + old_local_inactive = !!CHECK_FLAG( + n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + + /* Note any changes and see if of interest to BGP. */ + mac_different = !!memcmp(&n->emac, macaddr, ETH_ALEN); + cur_is_router = + !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + new_static = zebra_evpn_neigh_is_static(n); + if (!mac_different && is_router == cur_is_router + && old_local_inactive == local_inactive + && dp_static != new_static) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + " Ignoring entry mac is the same and is_router == cur_is_router"); + n->ifindex = ifp->ifindex; + return 0; + } + + old_zmac = n->mac; + if (!mac_different) { + /* XXX - cleanup this code duplication */ + bool is_neigh_freezed = false; + + /* Only the router flag has changed. */ + if (is_router) + SET_FLAG(n->flags, + ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, + ZEBRA_NEIGH_ROUTER_FLAG); + + if (local_inactive) + SET_FLAG(n->flags, + ZEBRA_NEIGH_LOCAL_INACTIVE); + else + UNSET_FLAG(n->flags, + ZEBRA_NEIGH_LOCAL_INACTIVE); + new_bgp_ready = + zebra_evpn_neigh_is_ready_for_bgp(n); + + /* Neigh is in freeze state and freeze action + * is enabled, do not send update to client. + */ + is_neigh_freezed = + (zvrf->dup_addr_detect + && zvrf->dad_freeze + && CHECK_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE)); + + zebra_evpn_local_neigh_update_log( + "local", n, is_router, local_inactive, + old_bgp_ready, new_bgp_ready, false, + false, "flag-update"); + + /* if the neigh can no longer be advertised + * remove it from bgp + */ + if (!is_neigh_freezed) { + zebra_evpn_neigh_send_add_del_to_client( + n, old_bgp_ready, + new_bgp_ready); + } else { + if (IS_ZEBRA_DEBUG_VXLAN + && IS_ZEBRA_NEIGH_ACTIVE(n)) + zlog_debug( + " Neighbor active and frozen"); + } + return 0; + } + + /* The MAC has changed, need to issue a delete + * first as this means a different MACIP route. + * Also, need to do some unlinking/relinking. + * We also need to update the MAC's sequence number + * in different situations. + */ + if (old_bgp_ready) { + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + old_bgp_ready = false; + } + if (old_zmac) { + old_mac_seq = CHECK_FLAG(old_zmac->flags, + ZEBRA_MAC_REMOTE) + ? old_zmac->rem_seq + : old_zmac->loc_seq; + neigh_mac_change = upd_mac_seq = true; + zebra_evpn_local_neigh_deref_mac( + n, true /* send_mac_update */); + } + + /* if mac changes abandon peer flags and tell + * dataplane to clear the static flag + */ + if (zebra_evpn_neigh_clear_sync_info(n)) + inform_dataplane = true; + /* Update the forwarding info. */ + n->ifindex = ifp->ifindex; + + /* Link to new MAC */ + zebra_evpn_local_neigh_ref_mac( + n, macaddr, zmac, true /* send_mac_update */); + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + /* + * Neighbor has moved from remote to local. Its + * MAC could have also changed as part of the move. + */ + if (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) + != 0) { + old_zmac = n->mac; + if (old_zmac) { + old_mac_seq = + CHECK_FLAG(old_zmac->flags, + ZEBRA_MAC_REMOTE) + ? old_zmac->rem_seq + : old_zmac->loc_seq; + neigh_mac_change = upd_mac_seq = true; + zebra_evpn_local_neigh_deref_mac( + n, true /* send_update */); + } + + /* Link to new MAC */ + zebra_evpn_local_neigh_ref_mac( + n, macaddr, zmac, true /*send_update*/); + } + /* Based on Mobility event Scenario-B from the + * draft, neigh's previous state was remote treat this + * event for DAD. + */ + neigh_was_remote = true; + vtep_ip = n->r_vtep_ip; + /* Mark appropriately */ + UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + n->r_vtep_ip.s_addr = INADDR_ANY; + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; + } + } + + /* If MAC was previously remote, or the neighbor had a different + * MAC earlier, recompute the sequence number. + */ + if (upd_mac_seq) { + uint32_t seq1, seq2; + + seq1 = CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE) + ? zmac->rem_seq + 1 + : zmac->loc_seq; + seq2 = neigh_mac_change ? old_mac_seq + 1 : 0; + mac_new_seq = zmac->loc_seq < MAX(seq1, seq2) ? MAX(seq1, seq2) + : zmac->loc_seq; + } + + if (local_inactive) + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + /* Mark Router flag (R-bit) */ + if (is_router) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* if the dataplane thinks that this is a sync entry but + * zebra doesn't we need to re-concile the diff + * by re-installing the dataplane entry + */ + if (dp_static) { + new_static = zebra_evpn_neigh_is_static(n); + if (!new_static) + inform_dataplane = true; + } + + /* Check old and/or new MAC detected as duplicate mark + * the neigh as duplicate + */ + if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) { + flog_warn( + EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC", + zevpn->vni, prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + } + + /* For IP Duplicate Address Detection (DAD) is trigger, + * when the event is extended mobility based on scenario-B + * from the draft, IP/Neigh's MAC binding changed and + * neigh's previous state was remote. + */ + if (neigh_mac_change && neigh_was_remote) + do_dad = true; + + zebra_evpn_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad, + &neigh_on_hold, true); + + if (inform_dataplane) + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* Before we program this in BGP, we need to check if MAC is locally + * learnt. If not, force neighbor to be inactive and reset its seq. + */ + if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) { + zebra_evpn_local_neigh_update_log( + "local", n, is_router, local_inactive, false, false, + inform_dataplane, false, "auto-mac"); + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zmac->loc_seq = mac_new_seq; + return 0; + } + + zebra_evpn_local_neigh_update_log("local", n, is_router, local_inactive, + false, false, inform_dataplane, true, + created ? "created" : "updated"); + + /* If the MAC's sequence number has changed, inform the MAC and all + * neighbors associated with the MAC to BGP, else just inform this + * neighbor. + */ + if (upd_mac_seq && zmac->loc_seq != mac_new_seq) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Seq changed for MAC %s VNI %u - old %u new %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + zevpn->vni, zmac->loc_seq, mac_new_seq); + zmac->loc_seq = mac_new_seq; + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, macaddr, + zmac->flags, + zmac->loc_seq, zmac->es)) + return -1; + zebra_evpn_process_neigh_on_local_mac_change(zevpn, zmac, 1, + 0 /*es_change*/); + return 0; + } + + n->loc_seq = zmac->loc_seq; + + if (!neigh_on_hold) { + ZEBRA_NEIGH_SET_ACTIVE(n); + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + } else { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug(" Neighbor on hold not sending"); + } + return 0; +} + +int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + uint16_t state) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *zmac = NULL; + + /* If the neighbor is unknown, there is no further action. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + /* If a remote entry, see if it needs to be refreshed */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { +#ifdef GNU_LINUX + if (state & NUD_STALE) + zebra_evpn_rem_neigh_install(zevpn, n, + false /*was_static*/); +#endif + } else { + /* We got a "remote" neighbor notification for an entry + * we think is local. This can happen in a multihoming + * scenario - but only if the MAC is already "remote". + * Just mark our entry as "remote". + */ + zmac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { + zlog_debug( + "Ignore remote neigh %s (MAC %s) on L2-VNI %u - MAC unknown or local", + ipaddr2str(&n->ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, buf, sizeof(buf)), + zevpn->vni); + return -1; + } + + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS); + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + ZEBRA_NEIGH_SET_ACTIVE(n); + n->r_vtep_ip = zmac->fwd_info.r_vtep_ip; + } + + return 0; +} + +/* Notify Neighbor entries to the Client, skips the GW entry */ +static void +zebra_evpn_send_neigh_hash_entry_to_client(struct hash_bucket *bucket, + void *arg) +{ + struct mac_walk_ctx *wctx = arg; + zebra_neigh_t *zn = bucket->data; + zebra_mac_t *zmac = NULL; + + if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_DEF_GW)) + return; + + if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_LOCAL) + && IS_ZEBRA_NEIGH_ACTIVE(zn)) { + zmac = zebra_evpn_mac_lookup(wctx->zevpn, &zn->emac); + if (!zmac) + return; + + zebra_evpn_neigh_send_add_to_client(wctx->zevpn->vni, &zn->ip, + &zn->emac, zn->mac, + zn->flags, zn->loc_seq); + } +} + +/* Iterator of a specific EVPN */ +void zebra_evpn_send_neigh_to_client(zebra_evpn_t *zevpn) +{ + struct neigh_walk_ctx wctx; + + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zevpn = zevpn; + + hash_iterate(zevpn->neigh_table, + zebra_evpn_send_neigh_hash_entry_to_client, &wctx); +} + +void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct neigh_walk_ctx *wctx = ctxt; + zebra_neigh_t *nbr; + zebra_evpn_t *zevpn; + char buf[INET6_ADDRSTRLEN]; + + nbr = (zebra_neigh_t *)bucket->data; + if (!nbr) + return; + + zevpn = wctx->zevpn; + + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + return; + + if (IS_ZEBRA_DEBUG_VXLAN) { + ipaddr2str(&nbr->ip, buf, sizeof(buf)); + zlog_debug("%s: clear neigh %s dup state, flags 0x%x seq %u", + __func__, buf, nbr->flags, nbr->loc_seq); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_neigh_send_add_to_client(zevpn->vni, &nbr->ip, + &nbr->emac, nbr->mac, + nbr->flags, nbr->loc_seq); + } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); + } +} + +/* + * Print a specific neighbor entry. + */ +void zebra_evpn_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) +{ + struct vty *vty; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + const char *type_str; + const char *state_str; + bool flags_present = false; + struct zebra_vrf *zvrf = NULL; + struct timeval detect_start_time = {0, 0}; + char timebuf[MONOTIME_STRLEN]; + char thread_buf[THREAD_TIMER_STRLEN]; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return; + + ipaddr2str(&n->ip, buf2, sizeof(buf2)); + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); + type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ? "local" : "remote"; + state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; + vty = (struct vty *)ctxt; + if (json == NULL) { + bool sync_info = false; + + vty_out(vty, "IP: %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + vty_out(vty, " Type: %s\n", type_str); + vty_out(vty, " State: %s\n", state_str); + vty_out(vty, " MAC: %s\n", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + vty_out(vty, " Sync-info:"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) { + vty_out(vty, " local-inactive"); + sync_info = true; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) { + vty_out(vty, " peer-proxy"); + sync_info = true; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) { + vty_out(vty, " peer-active"); + sync_info = true; + } + if (n->hold_timer) { + vty_out(vty, " (ht: %s)", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + n->hold_timer)); + sync_info = true; + } + if (!sync_info) + vty_out(vty, " -"); + vty_out(vty, "\n"); + } else { + json_object_string_add(json, "ip", buf2); + json_object_string_add(json, "type", type_str); + json_object_string_add(json, "state", state_str); + json_object_string_add(json, "mac", buf1); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + json_object_boolean_true_add(json, "localInactive"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) + json_object_boolean_true_add(json, "peerProxy"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + json_object_boolean_true_add(json, "peerActive"); + if (n->hold_timer) + json_object_string_add( + json, "peerActiveHold", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + n->hold_timer)); + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if (n->mac->es) { + if (json) + json_object_string_add(json, "remoteEs", + n->mac->es->esi_str); + else + vty_out(vty, " Remote ES: %s\n", + n->mac->es->esi_str); + } else { + if (json) + json_object_string_add(json, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + else + vty_out(vty, " Remote VTEP: %s\n", + inet_ntoa(n->r_vtep_ip)); + } + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) { + if (!json) { + vty_out(vty, " Flags: Default-gateway"); + flags_present = true; + } else + json_object_boolean_true_add(json, "defaultGateway"); + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) { + if (!json) { + vty_out(vty, + flags_present ? " ,Router" : " Flags: Router"); + flags_present = true; + } + } + if (json == NULL) { + if (flags_present) + vty_out(vty, "\n"); + vty_out(vty, " Local Seq: %u Remote Seq: %u\n", n->loc_seq, + n->rem_seq); + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) { + vty_out(vty, " Duplicate, detected at %s", + time_to_string(n->dad_dup_detect_time, + timebuf)); + } else if (n->dad_count) { + monotime_since(&n->detect_start_time, + &detect_start_time); + if (detect_start_time.tv_sec <= zvrf->dad_time) { + time_to_string(n->detect_start_time.tv_sec, + timebuf); + vty_out(vty, + " Duplicate detection started at %s, detection count %u\n", + timebuf, n->dad_count); + } + } + } else { + json_object_int_add(json, "localSequence", n->loc_seq); + json_object_int_add(json, "remoteSequence", n->rem_seq); + json_object_int_add(json, "detectionCount", n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json, "isDuplicate"); + else + json_object_boolean_false_add(json, "isDuplicate"); + } +} + +void zebra_evpn_print_neigh_hdr(struct vty *vty, struct neigh_walk_ctx *wctx) +{ + vty_out(vty, "Flags: I=local-inactive, P=peer-active, X=peer-proxy\n"); + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %s\n", -wctx->addr_width, + "Neighbor", "Type", "Flags", "State", "MAC", "Remote ES/VTEP", + "Seq #'s"); +} + +static char *zebra_evpn_print_neigh_flags(zebra_neigh_t *n, char *flags_buf, + uint32_t flags_buf_sz) +{ + snprintf(flags_buf, flags_buf_sz, "%s%s%s", + (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE) ? + "P" : "", + (n->flags & ZEBRA_NEIGH_ES_PEER_PROXY) ? + "X" : "", + (n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE) ? + "I" : ""); + + return flags_buf; +} + +/* + * Print neighbor hash entry - called for display of all neighbors. + */ +void zebra_evpn_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_evpn = NULL, *json_row = NULL; + zebra_neigh_t *n; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + const char *state_str; + char flags_buf[6]; + + vty = wctx->vty; + json_evpn = wctx->json; + n = (zebra_neigh_t *)bucket->data; + + if (json_evpn) + json_row = json_object_new_object(); + + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); + ipaddr2str(&n->ip, buf2, sizeof(buf2)); + state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + return; + + if (json_evpn == NULL) { + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n", + -wctx->addr_width, buf2, "local", + zebra_evpn_print_neigh_flags(n, flags_buf, + sizeof(flags_buf)), state_str, buf1, + "", n->loc_seq, n->rem_seq); + } else { + json_object_string_add(json_row, "type", "local"); + json_object_string_add(json_row, "state", state_str); + json_object_string_add(json_row, "mac", buf1); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) + json_object_boolean_true_add(json_row, + "defaultGateway"); + json_object_int_add(json_row, "localSequence", + n->loc_seq); + json_object_int_add(json_row, "remoteSequence", + n->rem_seq); + json_object_int_add(json_row, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json_row, + "isDuplicate"); + else + json_object_boolean_false_add(json_row, + "isDuplicate"); + } + wctx->count++; + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + && !IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) + return; + + if (json_evpn == NULL) { + if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + && (wctx->count == 0)) + zebra_evpn_print_neigh_hdr(vty, wctx); + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n", + -wctx->addr_width, buf2, "remote", + zebra_evpn_print_neigh_flags(n, flags_buf, + sizeof(flags_buf)), state_str, buf1, + n->mac->es ? n->mac->es->esi_str + : inet_ntoa(n->r_vtep_ip), + n->loc_seq, n->rem_seq); + } else { + json_object_string_add(json_row, "type", "remote"); + json_object_string_add(json_row, "state", state_str); + json_object_string_add(json_row, "mac", buf1); + if (n->mac->es) + json_object_string_add(json_row, "remoteEs", + n->mac->es->esi_str); + else + json_object_string_add(json_row, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) + json_object_boolean_true_add(json_row, + "defaultGateway"); + json_object_int_add(json_row, "localSequence", + n->loc_seq); + json_object_int_add(json_row, "remoteSequence", + n->rem_seq); + json_object_int_add(json_row, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json_row, + "isDuplicate"); + else + json_object_boolean_false_add(json_row, + "isDuplicate"); + } + wctx->count++; + } + + if (json_evpn) + json_object_object_add(json_evpn, buf2, json_row); +} + +/* + * Print neighbor hash entry in detail - called for display of all neighbors. + */ +void zebra_evpn_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_evpn = NULL, *json_row = NULL; + zebra_neigh_t *n; + char buf[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + + vty = wctx->vty; + json_evpn = wctx->json; + n = (zebra_neigh_t *)bucket->data; + if (!n) + return; + + ipaddr2str(&n->ip, buf, sizeof(buf)); + if (json_evpn) + json_row = json_object_new_object(); + + zebra_evpn_print_neigh(n, vty, json_row); + + if (json_evpn) + json_object_object_add(json_evpn, buf, json_row); +} + +void zebra_evpn_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_neigh_t *nbr; + + nbr = (zebra_neigh_t *)bucket->data; + if (!nbr) + return; + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + zebra_evpn_print_neigh_hash(bucket, ctxt); +} + +void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_neigh_t *nbr; + + nbr = (zebra_neigh_t *)bucket->data; + if (!nbr) + return; + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + zebra_evpn_print_neigh_hash_detail(bucket, ctxt); +} + +void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ipaddr *ipaddr, zebra_mac_t *mac, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq) +{ + zebra_neigh_t *n; + int update_neigh = 0; + uint32_t tmp_seq; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + zebra_mac_t *old_mac = NULL; + bool old_static = false; + bool do_dad = false; + bool is_dup_detect = false; + bool is_router; + + assert(mac); + is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + + /* Check if the remote neighbor itself is unknown or has a + * change. If so, create or update and then install the entry. + */ + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); + if (!n || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) + || is_router != !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG) + || (memcmp(&n->emac, &mac->macaddr, sizeof(struct ethaddr)) != 0) + || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip) || seq != n->rem_seq) + update_neigh = 1; + + if (update_neigh) { + if (!n) { + n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr, + mac, 0); + if (!n) { + zlog_warn( + "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s", + ipaddr2str(ipaddr, buf1, sizeof(buf1)), + prefix_mac2str(&mac->macaddr, buf, + sizeof(buf)), + zevpn->vni, inet_ntoa(vtep_ip)); + return; + } + + } else { + const char *n_type; + + /* When host moves but changes its (MAC,IP) + * binding, BGP may install a MACIP entry that + * corresponds to "older" location of the host + * in transient situations (because {IP1,M1} + * is a different route from {IP1,M2}). Check + * the sequence number and ignore this update + * if appropriate. + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + tmp_seq = n->loc_seq; + n_type = "local"; + } else { + tmp_seq = n->rem_seq; + n_type = "remote"; + } + if (seq < tmp_seq) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s Neigh has higher seq %u", + zevpn->vni, + prefix_mac2str(&mac->macaddr, + buf, + sizeof(buf)), + " IP ", + ipaddr2str(ipaddr, buf1, + sizeof(buf1)), + n_type, tmp_seq); + return; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + old_static = zebra_evpn_neigh_is_static(n); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync->remote neigh vni %u ip %s mac %s seq %d f0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, buf1, + sizeof(buf1)), + prefix_mac2str(&n->emac, buf, + sizeof(buf)), + seq, n->flags); + zebra_evpn_neigh_clear_sync_info(n); + if (IS_ZEBRA_NEIGH_ACTIVE(n)) + zebra_evpn_mac_send_del_to_client( + zevpn->vni, &mac->macaddr, + mac->flags, false /*force*/); + } + if (memcmp(&n->emac, &mac->macaddr, + sizeof(struct ethaddr)) + != 0) { + /* update neigh list for macs */ + old_mac = + zebra_evpn_mac_lookup(zevpn, &n->emac); + if (old_mac) { + listnode_delete(old_mac->neigh_list, n); + n->mac = NULL; + zebra_evpn_deref_ip2mac(zevpn, old_mac); + } + n->mac = mac; + listnode_add_sort(mac->neigh_list, n); + memcpy(&n->emac, &mac->macaddr, ETH_ALEN); + + /* Check Neigh's curent state is local + * (this is the case where neigh/host has moved + * from L->R) and check previous detction + * started via local learning. + * + * RFC-7432: A PE/VTEP that detects a MAC + * mobilit event via local learning starts + * an M-second timer. + * VTEP-IP or seq. change along is not + * considered for dup. detection. + * + * Mobilty event scenario-B IP-MAC binding + * changed. + */ + if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + && n->dad_count) + do_dad = true; + } + } + + /* Set "remote" forwarding info. */ + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS); + n->r_vtep_ip = vtep_ip; + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + + /* Set router flag (R-bit) to this Neighbor entry */ + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG)) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* Check old or new MAC detected as duplicate, + * inherit duplicate flag to this neigh. + */ + if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, old_mac, mac, n)) { + flog_warn( + EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC", + zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ipaddr2str(&n->ip, buf1, sizeof(buf1))); + } + + /* Check duplicate address detection for IP */ + zebra_evpn_dup_addr_detect_for_neigh( + zvrf, n, n->r_vtep_ip, do_dad, &is_dup_detect, false); + /* Install the entry. */ + if (!is_dup_detect) + zebra_evpn_rem_neigh_install(zevpn, n, old_static); + } + + /* Update seq number. */ + n->rem_seq = seq; +} + +int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t *mac) +{ + zebra_neigh_t *n; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + assert(mac); + + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) { + n = zebra_evpn_neigh_add(zevpn, ip, &mac->macaddr, mac, 0); + if (!n) { + flog_err( + EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", + ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, zevpn->vni); + return -1; + } + } + + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + ZEBRA_NEIGH_SET_ACTIVE(n); + memcpy(&n->emac, &mac->macaddr, ETH_ALEN); + n->ifindex = ifp->ifindex; + + /* Only advertise in BGP if the knob is enabled */ + if (advertise_gw_macip_enabled(zevpn)) { + + SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); + SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW); + /* Set Router flag (R-bit) */ + if (ip->ipa_type == IPADDR_V6) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x", + ifp->name, ifp->ifindex, zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); + + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq); + } else if (advertise_svi_macip_enabled(zevpn)) { + + SET_FLAG(n->flags, ZEBRA_NEIGH_SVI_IP); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "SVI %s(%u) L2-VNI %u, sending SVI MAC %s IP %s add to BGP with flags 0x%x", + ifp->name, ifp->ifindex, zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); + + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq); + } + + return 0; +} + +void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn, + struct zebra_vrf *zvrf, zebra_neigh_t *n, + zebra_mac_t *mac, struct ipaddr *ipaddr) +{ + char buf1[INET6_ADDRSTRLEN]; + + if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE) + && CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) + && (memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN) == 0)) { + struct interface *vlan_if; + + vlan_if = zevpn_map_to_svi(zevpn); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry", + __func__, + ipaddr2str(ipaddr, buf1, sizeof(buf1)), + n->flags, vlan_if ? vlan_if->name : "Unknown"); + if (vlan_if) + neigh_read_specific_ip(ipaddr, vlan_if); + } + + /* When the MAC changes for an IP, it is possible the + * client may update the new MAC before trying to delete the + * "old" neighbor (as these are two different MACIP routes). + * Do the delete only if the MAC matches. + */ + if (!memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_sync_neigh_del(n); + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_neigh_uninstall(zevpn, n); + zebra_evpn_neigh_del(zevpn, n); + zebra_evpn_deref_ip2mac(zevpn, mac); + } + } +} + +int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip) +{ + zebra_neigh_t *n; + zebra_mac_t *zmac; + bool old_bgp_ready; + bool new_bgp_ready; + char buf[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + struct zebra_vrf *zvrf; + + /* If entry doesn't exist, nothing to do. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + zmac = zebra_evpn_mac_lookup(zevpn, &n->emac); + if (!zmac) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Trying to del a neigh %s without a mac %s on VNI %u", + ipaddr2str(ip, buf, sizeof(buf)), + prefix_mac2str(&n->emac, buf2, sizeof(buf2)), + zevpn->vni); + + return 0; + } + + /* If it is a remote entry, the kernel has aged this out or someone has + * deleted it, it needs to be re-installed as Quagga is the owner. + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, n, false /*was_static*/); + return 0; + } + + /* if this is a sync entry it cannot be dropped re-install it in + * the dataplane + */ + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + if (zebra_evpn_neigh_is_static(n)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("re-add sync neigh vni %u ip %s mac %s 0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, buf, sizeof(buf)), + prefix_mac2str(&n->emac, buf2, sizeof(buf2)), + n->flags); + + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + /* inform-bgp about change in local-activity if any */ + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + /* re-install the entry in the kernel */ + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + return 0; + } + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + if (!zvrf) { + zlog_debug("%s: VNI %u vrf lookup failed.", __func__, + zevpn->vni); + return -1; + } + + /* In case of feeze action, if local neigh is in duplicate state, + * Mark the Neigh as inactive before sending delete request to BGPd, + * If BGPd has remote entry, it will re-install + */ + if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + ZEBRA_NEIGH_SET_INACTIVE(n); + + /* Remove neighbor from BGP. */ + zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac, + n->flags, n->state, + false /* force */); + + /* Delete this neighbor entry. */ + zebra_evpn_neigh_del(zevpn, n); + + /* see if the AUTO mac needs to be deleted */ + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) + && !listcount(zmac->neigh_list)) + zebra_evpn_mac_del(zevpn, zmac); + + return 0; +} diff --git a/zebra/zebra_evpn_neigh.h b/zebra/zebra_evpn_neigh.h new file mode 100644 index 0000000000..4b98266c86 --- /dev/null +++ b/zebra/zebra_evpn_neigh.h @@ -0,0 +1,294 @@ +/* + * Zebra EVPN Neighbor Data structures and definitions + * These are "internal" to this function. + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * Copyright (C) 2020 Volta Networks. + * + * 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 FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_EVPN_NEIGH_H +#define _ZEBRA_EVPN_NEIGH_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct zebra_neigh_t_ zebra_neigh_t; + +#define IS_ZEBRA_NEIGH_ACTIVE(n) (n->state == ZEBRA_NEIGH_ACTIVE) + +#define IS_ZEBRA_NEIGH_INACTIVE(n) (n->state == ZEBRA_NEIGH_INACTIVE) + +#define ZEBRA_NEIGH_SET_ACTIVE(n) n->state = ZEBRA_NEIGH_ACTIVE + +#define ZEBRA_NEIGH_SET_INACTIVE(n) n->state = ZEBRA_NEIGH_INACTIVE + +/* + * Neighbor hash table. + * + * This table contains the neighbors (IP to MAC bindings) pertaining to + * this VNI. This includes local neighbors learnt on the attached VLAN + * device that maps to this VNI as well as remote neighbors learnt and + * installed by BGP. + * Local neighbors will be known against the VLAN device (SVI); however, + * it is sufficient for zebra to maintain against the VNI. The correct + * VNI will be obtained as zebra maintains the mapping (of VLAN to VNI). + */ +struct zebra_neigh_t_ { + /* IP address. */ + struct ipaddr ip; + + /* MAC address. */ + struct ethaddr emac; + + /* Back pointer to MAC. Only applicable to hosts in a L2-VNI. */ + zebra_mac_t *mac; + + /* Underlying interface. */ + ifindex_t ifindex; + + zebra_evpn_t *zevpn; + + uint32_t flags; +#define ZEBRA_NEIGH_LOCAL 0x01 +#define ZEBRA_NEIGH_REMOTE 0x02 +#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */ +#define ZEBRA_NEIGH_DEF_GW 0x08 +#define ZEBRA_NEIGH_ROUTER_FLAG 0x10 +#define ZEBRA_NEIGH_DUPLICATE 0x20 +#define ZEBRA_NEIGH_SVI_IP 0x40 +/* rxed from an ES peer */ +#define ZEBRA_NEIGH_ES_PEER_ACTIVE 0x80 +/* rxed from an ES peer as a proxy advertisement */ +#define ZEBRA_NEIGH_ES_PEER_PROXY 0x100 +/* We have not been able to independently establish that the host + * is local connected + */ +#define ZEBRA_NEIGH_LOCAL_INACTIVE 0x200 +#define ZEBRA_NEIGH_ALL_LOCAL_FLAGS \ + (ZEBRA_NEIGH_LOCAL | ZEBRA_NEIGH_LOCAL_INACTIVE) +#define ZEBRA_NEIGH_ALL_PEER_FLAGS \ + (ZEBRA_NEIGH_ES_PEER_PROXY | ZEBRA_NEIGH_ES_PEER_ACTIVE) + + enum zebra_neigh_state state; + + /* Remote VTEP IP - applicable only for remote neighbors. */ + struct in_addr r_vtep_ip; + + /* + * Mobility sequence numbers associated with this entry. The rem_seq + * represents the sequence number from the client (BGP) for the most + * recent add or update of this entry while the loc_seq represents + * the sequence number informed (or to be informed) by zebra to BGP + * for this entry. + */ + uint32_t rem_seq; + uint32_t loc_seq; + + /* list of hosts pointing to this remote NH entry */ + struct host_rb_tree_entry host_rb; + + /* Duplicate ip detection */ + uint32_t dad_count; + + struct thread *dad_ip_auto_recovery_timer; + + struct timeval detect_start_time; + + time_t dad_dup_detect_time; + + /* used for ageing out the PEER_ACTIVE flag */ + struct thread *hold_timer; +}; + +/* + * Context for neighbor hash walk - used by callbacks. + */ +struct neigh_walk_ctx { + zebra_evpn_t *zevpn; /* VNI hash */ + struct zebra_vrf *zvrf; /* VRF - for client notification. */ + int uninstall; /* uninstall from kernel? */ + int upd_client; /* uninstall from client? */ + + uint32_t flags; +#define DEL_LOCAL_NEIGH 0x1 +#define DEL_REMOTE_NEIGH 0x2 +#define DEL_ALL_NEIGH (DEL_LOCAL_NEIGH | DEL_REMOTE_NEIGH) +#define DEL_REMOTE_NEIGH_FROM_VTEP 0x4 +#define SHOW_REMOTE_NEIGH_FROM_VTEP 0x8 + + struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */ + + struct vty *vty; /* Used by VTY handlers */ + uint32_t count; /* Used by VTY handlers */ + uint8_t addr_width; /* Used by VTY handlers */ + struct json_object *json; /* Used for JSON Output */ +}; + +/**************************** SYNC neigh handling **************************/ +static inline bool zebra_evpn_neigh_is_static(zebra_neigh_t *neigh) +{ + return !!(neigh->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS); +} + +static inline bool zebra_evpn_neigh_is_ready_for_bgp(zebra_neigh_t *n) +{ + bool mac_ready; + bool neigh_ready; + + mac_ready = !!(n->mac->flags & ZEBRA_MAC_LOCAL); + neigh_ready = + ((n->flags & ZEBRA_NEIGH_LOCAL) && IS_ZEBRA_NEIGH_ACTIVE(n) + && (!(n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE) + || (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE))) + ? true + : false; + + return mac_ready && neigh_ready; +} + +static inline void zebra_evpn_neigh_stop_hold_timer(zebra_neigh_t *n) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (!n->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold stop", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + THREAD_OFF(n->hold_timer); +} + +void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static, + bool new_n_static, bool defer_n_dp, + bool defer_mac_dp, const char *caller); + +static inline bool zebra_evpn_neigh_clear_sync_info(zebra_neigh_t *n) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_n_static = false; + bool new_n_static = false; + + if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x clear", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + n->flags); + + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_PEER_FLAGS); + new_n_static = zebra_evpn_neigh_is_static(n); + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, + true /*defer_dp)*/, false /*defer_mac_dp*/, + __func__); + } + zebra_evpn_neigh_stop_hold_timer(n); + + /* if the neigh static flag changed inform that a dp + * re-install maybe needed + */ + return old_n_static != new_n_static; +} + +int remote_neigh_count(zebra_mac_t *zmac); + +int neigh_list_cmp(void *p1, void *p2); +struct hash *zebra_neigh_db_create(const char *desc); +uint32_t num_dup_detected_neighs(zebra_evpn_t *zevpn); +void zebra_evpn_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt); +int remote_neigh_count(zebra_mac_t *zmac); +int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n, + bool was_static); +void zebra_evpn_install_neigh_hash(struct hash_bucket *bucket, void *ctxt); +int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, + zebra_mac_t *zmac, uint32_t neigh_flags, + uint32_t seq); +int zebra_evpn_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, uint32_t flags, + int state, bool force); +bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n, + struct ethaddr *macaddr, uint32_t seq); +int zebra_evpn_neigh_del(zebra_evpn_t *zevpn, zebra_neigh_t *n); +void zebra_evpn_sync_neigh_del(zebra_neigh_t *n); +zebra_neigh_t * +zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx); +void zebra_evpn_neigh_del_all(zebra_evpn_t *zevpn, int uninstall, + int upd_client, uint32_t flags); +zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn, struct ipaddr *ip); + +int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n, + bool was_static); +void zebra_evpn_process_neigh_on_remote_mac_add(zebra_evpn_t *zevpn, + zebra_mac_t *zmac); +void zebra_evpn_process_neigh_on_local_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac); +void zebra_evpn_process_neigh_on_local_mac_change(zebra_evpn_t *zevpn, + zebra_mac_t *zmac, + bool seq_change, + bool es_change); +void zebra_evpn_process_neigh_on_remote_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac); +int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + bool is_router, bool local_inactive, + bool dp_static); +int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + uint16_t state); +void zebra_evpn_send_neigh_to_client(zebra_evpn_t *zevpn); +void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); +void zebra_evpn_print_neigh_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_neigh_hdr(struct vty *vty, struct neigh_walk_ctx *wctx); +void zebra_evpn_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket, + void *ctxt); +void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ipaddr *ipaddr, zebra_mac_t *mac, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq); +int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t *mac); +void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn, + struct zebra_vrf *zvrf, zebra_neigh_t *n, + zebra_mac_t *mac, struct ipaddr *ipaddr); +int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip); + + +#ifdef __cplusplus +} +#endif + +#endif /*_ZEBRA_EVPN_NEIGH_H */ diff --git a/zebra/zebra_evpn_vxlan.h b/zebra/zebra_evpn_vxlan.h new file mode 100644 index 0000000000..bf8904d492 --- /dev/null +++ b/zebra/zebra_evpn_vxlan.h @@ -0,0 +1,71 @@ +/* + * Zebra EVPN for VxLAN code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * 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 FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* Get the VRR interface for SVI if any */ +static inline struct interface * +zebra_get_vrr_intf_for_svi(struct interface *ifp) +{ + struct zebra_vrf *zvrf = NULL; + struct interface *tmp_if = NULL; + struct zebra_if *zif = NULL; + + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + FOR_ALL_INTERFACES (zvrf->vrf, tmp_if) { + zif = tmp_if->info; + if (!zif) + continue; + + if (!IS_ZEBRA_IF_MACVLAN(tmp_if)) + continue; + + if (zif->link == ifp) + return tmp_if; + } + + return NULL; +} + +/* EVPN<=>vxlan_zif association */ +static inline void zevpn_vxlan_if_set(zebra_evpn_t *zevpn, + struct interface *ifp, bool set) +{ + struct zebra_if *zif; + + if (set) { + if (zevpn->vxlan_if == ifp) + return; + zevpn->vxlan_if = ifp; + } else { + if (!zevpn->vxlan_if) + return; + zevpn->vxlan_if = NULL; + } + + if (ifp) + zif = ifp->info; + else + zif = NULL; + + zebra_evpn_vxl_evpn_set(zif, zevpn, set); +} diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 1ac7bc7a2a..03b8c8de1f 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -47,6 +47,7 @@ #include "zebra/zebra_memory.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_mpls.h" +#include "zebra/zebra_srte.h" #include "zebra/zebra_errors.h" DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object") @@ -955,9 +956,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) case ZEBRA_DPLANE_REQUEST_SUCCESS: zvrf->lsp_installs++; break; - /* Should never happen */ - case ZEBRA_DPLANE_REQUEST_PENDING: - break; } } } else { @@ -983,10 +981,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) case ZEBRA_DPLANE_REQUEST_SUCCESS: zvrf->lsp_removals++; break; - - /* Should never happen */ - case ZEBRA_DPLANE_REQUEST_PENDING: - break; } } else if (CHECK_FLAG(lsp->flags, LSP_FLAG_CHANGED)) { zebra_nhlfe_t *nhlfe; @@ -1030,10 +1024,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) case ZEBRA_DPLANE_REQUEST_SUCCESS: zvrf->lsp_installs++; break; - - /* Should never happen */ - case ZEBRA_DPLANE_REQUEST_PENDING: - break; } } } @@ -1505,6 +1495,7 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) char buf[BUFSIZ]; json_object *json_nhlfe = NULL; json_object *json_backups = NULL; + json_object *json_label_stack; struct nexthop *nexthop = nhlfe->nexthop; int i; @@ -1512,6 +1503,14 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) json_object_string_add(json_nhlfe, "type", nhlfe_type2str(nhlfe->type)); json_object_int_add(json_nhlfe, "outLabel", nexthop->nh_label->label[0]); + + json_label_stack = json_object_new_array(); + json_object_object_add(json_nhlfe, "outLabelStack", json_label_stack); + for (i = 0; i < nexthop->nh_label->num_labels; i++) + json_object_array_add( + json_label_stack, + json_object_new_int(nexthop->nh_label->label[i])); + json_object_int_add(json_nhlfe, "distance", nhlfe->distance); if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) @@ -1916,14 +1915,13 @@ static int mpls_processq_init(void) } -/* Public functions */ - /* * Process LSP update results from zebra dataplane. */ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) { struct zebra_vrf *zvrf; + mpls_label_t label; zebra_ile_t tmp_ile; struct hash *lsp_table; zebra_lsp_t *lsp; @@ -1931,6 +1929,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) struct nexthop *nexthop; enum dplane_op_e op; enum zebra_dplane_result status; + enum zebra_sr_policy_update_label_mode update_mode; op = dplane_ctx_get_op(ctx); status = dplane_ctx_get_status(ctx); @@ -1941,6 +1940,8 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) dplane_ctx_get_in_label(ctx), dplane_res2str(status)); + label = dplane_ctx_get_in_label(ctx); + switch (op) { case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: @@ -1951,7 +1952,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) lsp_table = zvrf->lsp_table; - tmp_ile.in_label = dplane_ctx_get_in_label(ctx); + tmp_ile.in_label = label; lsp = hash_lookup(lsp_table, &tmp_ile); if (lsp == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) @@ -1985,13 +1986,21 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) } } + update_mode = (op == DPLANE_OP_LSP_INSTALL) + ? ZEBRA_SR_POLICY_LABEL_CREATED + : ZEBRA_SR_POLICY_LABEL_UPDATED; + zebra_sr_policy_label_update(label, update_mode); break; case DPLANE_OP_LSP_DELETE: - if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) + if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) { flog_warn(EC_ZEBRA_LSP_DELETE_FAILURE, "LSP Deletion Failure: in-label %u", dplane_ctx_get_in_label(ctx)); + break; + } + zebra_sr_policy_label_update(label, + ZEBRA_SR_POLICY_LABEL_REMOVED); break; default: @@ -3419,6 +3428,21 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, return 0; } +zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return NULL; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + return hash_lookup(lsp_table, &tmp_ile); +} + /* * Uninstall a particular NHLFE in the forwarding table. If this is * the only NHLFE, the entire LSP forwarding entry has to be deleted. diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 07a8efeb8b..c0e58c44e3 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -33,6 +33,7 @@ #include "mpls.h" #include "zebra/zserv.h" #include "zebra/zebra_vrf.h" +#include "hook.h" #ifdef __cplusplus extern "C" { @@ -326,6 +327,11 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, const union g_addr *gate, ifindex_t ifindex); /* + * Lookup LSP by its input label. + */ +zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label); + +/* * Uninstall a particular NHLFE in the forwarding table. If this is * the only NHLFE, the entire LSP forwarding entry has to be deleted. */ @@ -461,6 +467,7 @@ static inline uint8_t lsp_distance(enum lsp_types_t type) case ZEBRA_LSP_SHARP: case ZEBRA_LSP_OSPF_SR: case ZEBRA_LSP_ISIS_SR: + case ZEBRA_LSP_SRTE: return 150; } @@ -492,6 +499,8 @@ static inline enum lsp_types_t lsp_type_from_re_type(int re_type) return ZEBRA_LSP_ISIS_SR; case ZEBRA_ROUTE_SHARP: return ZEBRA_LSP_SHARP; + case ZEBRA_ROUTE_SRTE: + return ZEBRA_LSP_SRTE; default: return ZEBRA_LSP_NONE; } @@ -517,6 +526,8 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type) return ZEBRA_ROUTE_KERNEL; case ZEBRA_LSP_SHARP: return ZEBRA_ROUTE_SHARP; + case ZEBRA_LSP_SRTE: + return ZEBRA_ROUTE_SRTE; } /* @@ -544,6 +555,8 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type) return "SR (IS-IS)"; case ZEBRA_LSP_SHARP: return "SHARP"; + case ZEBRA_LSP_SRTE: + return "SR-TE"; case ZEBRA_LSP_NONE: return "Unknown"; } diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c index c95a021639..3b2279c66c 100644 --- a/zebra/zebra_mpls_netlink.c +++ b/zebra/zebra_mpls_netlink.c @@ -28,13 +28,9 @@ #include "zebra/zebra_mpls.h" #include "zebra/kernel_netlink.h" -/* - * LSP forwarding update using dataplane context information. - */ -enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx) +static ssize_t netlink_lsp_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, + size_t buflen) { - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; - ssize_t ret = -1; int cmd; /* Call to netlink layer based on type of update */ @@ -48,26 +44,21 @@ enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx) if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_MPLS) zlog_debug("LSP in-label %u: update fails, no best NHLFE", dplane_ctx_get_in_label(ctx)); - goto done; + return -1; } cmd = RTM_NEWROUTE; } else /* Invalid op? */ - goto done; - - ret = netlink_mpls_multipath_msg_encode(cmd, ctx, nl_pkt, - sizeof(nl_pkt)); - if (ret <= 0) - return ZEBRA_DPLANE_REQUEST_FAILURE; - - ret = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); + return -1; -done: + return netlink_mpls_multipath_msg_encode(cmd, ctx, buf, buflen); +} - return (ret == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); +enum netlink_msg_status netlink_put_lsp_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + return netlink_batch_add_msg(bth, ctx, netlink_lsp_msg_encoder, false); } /* @@ -75,9 +66,10 @@ done: * but note that the default has been to report 'success' for pw updates * on unsupported platforms. */ -enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx) +enum netlink_msg_status netlink_put_pw_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) { - return ZEBRA_DPLANE_REQUEST_SUCCESS; + return FRR_NETLINK_SUCCESS; } int mpls_kernel_init(void) diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c index a25d08f267..1fc1faff67 100644 --- a/zebra/zebra_nb.c +++ b/zebra/zebra_nb.c @@ -553,6 +553,12 @@ const struct frr_yang_module_info frr_zebra_info = { } }, { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srte-color", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem, + } + }, + { .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry", .cbs = { .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next, diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h index 80aeb02d2d..e68b819767 100644 --- a/zebra/zebra_nb.h +++ b/zebra/zebra_nb.h @@ -275,6 +275,9 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem( struct yang_data * lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem( struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( + struct nb_cb_get_elem_args *args); const void * lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next( struct nb_cb_get_next_args *args); diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c index e4501273bd..b4ed910b4d 100644 --- a/zebra/zebra_nb_config.c +++ b/zebra/zebra_nb_config.c @@ -1204,12 +1204,14 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) switch (args->event) { case NB_EV_PREPARE: case NB_EV_ABORT: - break; + return NB_OK; case NB_EV_VALIDATE: zvrf_evpn = zebra_vrf_get_evpn(); - if (!zvrf_evpn) + if (!zvrf_evpn) { + snprintf(args->errmsg, args->errmsg_len, + "evpn vrf is not present."); return NB_ERR_VALIDATION; - + } vni = yang_dnode_get_uint32(args->dnode, NULL); /* Get vrf info from parent node, reject configuration * if zebra vrf already mapped to different vni id. @@ -1217,10 +1219,16 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) pn_dnode = yang_dnode_get_parent(args->dnode, "vrf"); if (pn_dnode) { vrfname = yang_dnode_get_string(pn_dnode, "./name"); - vrf = vrf_lookup_by_name(vrfname); - zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id); + zvrf = zebra_vrf_lookup_by_name(vrfname); + if (!zvrf) { + snprintf(args->errmsg, args->errmsg_len, + "zebra vrf info not found for vrf:%s.", + vrfname); + return NB_ERR_VALIDATION; + } if (zvrf->l3vni && zvrf->l3vni != vni) { - zlog_debug( + snprintf( + args->errmsg, args->errmsg_len, "vni %u cannot be configured as vni %u is already configured under the vrf", vni, zvrf->l3vni); return NB_ERR_VALIDATION; @@ -1230,10 +1238,8 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) /* Check if this VNI is already present in the system */ zl3vni = zl3vni_lookup(vni); if (zl3vni) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "VNI %u is already configured as L3-VNI", - vni); + snprintf(args->errmsg, args->errmsg_len, + "VNI %u is already configured as L3-VNI", vni); return NB_ERR_VALIDATION; } @@ -1241,7 +1247,7 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); - zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id); + zvrf = zebra_vrf_lookup_by_name(vrf->name); vni = yang_dnode_get_uint32(args->dnode, NULL); /* Note: This covers lib_vrf_zebra_prefix_only_modify() config * along with l3vni config @@ -1252,7 +1258,8 @@ int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) pfx_only ? 1 : 0, 1) != 0) { if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( + snprintf( + args->errmsg, args->errmsg_len, "vrf vni %u mapping failed with error: %s", vni, err); return NB_ERR; @@ -1279,17 +1286,21 @@ int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args) case NB_EV_PREPARE: case NB_EV_ABORT: case NB_EV_VALIDATE: - break; + return NB_OK; case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); - zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id); + zvrf = zebra_vrf_lookup_by_name(vrf->name); vni = yang_dnode_get_uint32(args->dnode, NULL); if (!zl3vni_lookup(vni)) return NB_OK; - if (zvrf->l3vni != vni) + if (zvrf->l3vni != vni) { + snprintf(args->errmsg, args->errmsg_len, + "vrf %s has different vni %u mapped", + vrf->name, zvrf->l3vni); return NB_ERR; + } if (is_l3vni_for_prefix_routes_only(zvrf->l3vni)) filter = 1; diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c index 19ad3769eb..21c89f64ed 100644 --- a/zebra/zebra_nb_state.c +++ b/zebra/zebra_nb_state.c @@ -808,6 +808,22 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem( /* * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srte-color + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE)) + return yang_data_new_uint32(args->xpath, nexthop->srte_color); + + return NULL; +} + +/* + * XPath: * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry */ const void * diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 43bf745896..b8faaa43fd 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -28,6 +28,7 @@ #include "lib/mpls.h" #include "lib/jhash.h" #include "lib/debug.h" +#include "lib/lib_errors.h" #include "zebra/connected.h" #include "zebra/debug.h" @@ -36,6 +37,7 @@ #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" #include "zebra_errors.h" @@ -1620,7 +1622,8 @@ void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe) } static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, - struct nexthop *nexthop) + struct nexthop *nexthop, + struct zebra_sr_policy *policy) { struct nexthop *resolved_hop; uint8_t num_labels = 0; @@ -1684,7 +1687,21 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; /* Copy labels of the resolved route and the parent resolving to it */ - if (newhop->nh_label) { + if (policy) { + int i = 0; + + /* + * Don't push the first SID if the corresponding action in the + * LFIB is POP. + */ + if (!newhop->nh_label || !newhop->nh_label->num_labels + || newhop->nh_label->label[0] == MPLS_LABEL_IMPLICIT_NULL) + i = 1; + + for (; i < policy->segment_list.label_num; i++) + labels[num_labels++] = policy->segment_list.labels[i]; + label_type = policy->segment_list.type; + } else if (newhop->nh_label) { for (i = 0; i < newhop->nh_label->num_labels; i++) { /* Be a bit picky about overrunning the local array */ if (num_labels >= MPLS_MAX_LABELS) { @@ -1771,11 +1788,13 @@ static int nexthop_active(afi_t afi, struct route_entry *re, struct route_node *rn; struct route_entry *match = NULL; int resolved; + zebra_nhlfe_t *nhlfe; struct nexthop *newhop; struct interface *ifp; rib_dest_t *dest; struct zebra_vrf *zvrf; - struct in_addr ipv4; + struct in_addr local_ipv4; + struct in_addr *ipv4; if ((nexthop->type == NEXTHOP_TYPE_IPV4) || nexthop->type == NEXTHOP_TYPE_IPV6) @@ -1839,6 +1858,51 @@ static int nexthop_active(afi_t afi, struct route_entry *re, /* Validation for ipv4 mapped ipv6 nexthop. */ if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { afi = AFI_IP; + ipv4 = &local_ipv4; + ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, ipv4); + } else { + ipv4 = &nexthop->gate.ipv4; + } + + if (nexthop->srte_color) { + struct ipaddr endpoint = {0}; + struct zebra_sr_policy *policy; + + switch (afi) { + case AFI_IP: + endpoint.ipa_type = IPADDR_V4; + endpoint.ipaddr_v4 = *ipv4; + break; + case AFI_IP6: + endpoint.ipa_type = IPADDR_V6; + endpoint.ipaddr_v6 = nexthop->gate.ipv6; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown address-family: %u", __func__, + afi); + exit(1); + } + + policy = zebra_sr_policy_find(nexthop->srte_color, &endpoint); + if (policy && policy->status == ZEBRA_SR_POLICY_UP) { + resolved = 0; + frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, + nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_DELETED)) + continue; + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, nhlfe->nexthop, + nexthop, policy); + resolved = 1; + } + if (resolved) + return 1; + } } /* Make lookup prefix. */ @@ -1847,12 +1911,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, case AFI_IP: p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; - if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { - ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, &ipv4); - p.u.prefix4 = ipv4; - } else { - p.u.prefix4 = nexthop->gate.ipv4; - } + p.u.prefix4 = *ipv4; break; case AFI_IP6: p.family = AF_INET6; @@ -1978,7 +2037,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re, SET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, nexthop); + nexthop_set_resolved(afi, newhop, nexthop, + NULL); resolved = 1; } @@ -2001,7 +2061,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re, SET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, nexthop); + nexthop_set_resolved(afi, newhop, nexthop, + NULL); resolved = 1; } done_with_match: @@ -2602,6 +2663,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_RULE_ADD: case DPLANE_OP_RULE_DELETE: case DPLANE_OP_RULE_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_NONE: break; } diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 1bc8d893bc..95d241c59f 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -83,6 +83,27 @@ const struct message icmp_typecode_str[] = { {0} }; +const struct message icmpv6_typecode_str[] = { + { 128 << 8, "echo-request"}, + { 129 << 8, "echo-reply"}, + { 1 << 8, "no-route"}, + { (1 << 8) + 1, "communication-prohibited"}, + { (1 << 8) + 3, "address-unreachable"}, + { (1 << 8) + 4, "port-unreachable"}, + { (2 << 8), "packet-too-big"}, + { 3 << 0, "ttl-zero-during-transit"}, + { (3 << 8) + 1, "ttl-zero-during-reassembly"}, + { 4 << 0, "bad-header"}, + { (4 << 0) + 1, "unknown-header-type"}, + { (4 << 0) + 2, "unknown-option"}, + { 133 << 8, "router-solicitation"}, + { 134 << 8, "router-advertisement"}, + { 135 << 8, "neighbor-solicitation"}, + { 136 << 8, "neighbor-advertisement"}, + { 137 << 8, "redirect"}, + {0} +}; + /* definitions */ static const struct message tcp_value_str[] = { {TCP_HEADER_FIN, "FIN"}, @@ -251,6 +272,8 @@ uint32_t zebra_pbr_ipset_hash_key(const void *arg) uint32_t *pnt = (uint32_t *)&ipset->ipset_name; uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de); + key = jhash_1word(ipset->family, key); + return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, key); } @@ -267,6 +290,8 @@ bool zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2) return false; if (r1->vrf_id != r2->vrf_id) return false; + if (r1->family != r2->family) + return false; if (strncmp(r1->ipset_name, r2->ipset_name, ZEBRA_IPSET_NAME_SIZE)) @@ -376,6 +401,8 @@ uint32_t zebra_pbr_iptable_hash_key(const void *arg) key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, 0x63ab42de); key = jhash_1word(iptable->fwmark, key); + key = jhash_1word(iptable->family, key); + key = jhash_1word(iptable->flow_label, key); key = jhash_1word(iptable->pkt_len_min, key); key = jhash_1word(iptable->pkt_len_max, key); key = jhash_1word(iptable->tcp_flags, key); @@ -411,6 +438,10 @@ bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) if (strncmp(r1->ipset_name, r2->ipset_name, ZEBRA_IPSET_NAME_SIZE)) return false; + if (r1->family != r2->family) + return false; + if (r1->flow_label != r2->flow_label) + return false; if (r1->pkt_len_min != r2->pkt_len_min) return false; if (r1->pkt_len_max != r2->pkt_len_max) @@ -876,7 +907,8 @@ static const char *zebra_pbr_prefix2str(union prefixconstptr pu, const struct prefix *p = pu.p; char buf[PREFIX2STR_BUFFER]; - if (p->family == AF_INET && p->prefixlen == IPV4_MAX_PREFIXLEN) { + if ((p->family == AF_INET && p->prefixlen == IPV4_MAX_PREFIXLEN) || + (p->family == AF_INET6 && p->prefixlen == IPV6_MAX_PREFIXLEN)) { snprintf(str, size, "%s", inet_ntop(p->family, &p->u.prefix, buf, PREFIX2STR_BUFFER)); return str; @@ -889,6 +921,9 @@ static void zebra_pbr_display_icmp(struct vty *vty, { char decoded_str[20]; uint16_t port; + struct zebra_pbr_ipset *zpi; + + zpi = zpie->backpointer; /* range icmp type */ if (zpie->src_port_max || zpie->dst_port_max) { @@ -901,8 +936,10 @@ static void zebra_pbr_display_icmp(struct vty *vty, memset(decoded_str, 0, sizeof(decoded_str)); snprintf(decoded_str, sizeof(decoded_str), "%u/%u", zpie->src_port_min, zpie->dst_port_min); - vty_out(vty, ":icmp:%s", - lookup_msg(icmp_typecode_str, + vty_out(vty, ":%s:%s", + zpi->family == AF_INET6 ? "ipv6-icmp" : "icmp", + lookup_msg(zpi->family == AF_INET6 ? + icmpv6_typecode_str : icmp_typecode_str, port, decoded_str)); } } @@ -1012,8 +1049,9 @@ static int zebra_pbr_show_ipset_walkcb(struct hash_bucket *bucket, void *arg) struct vty *vty = uniqueipset->vty; struct zebra_ns *zns = uniqueipset->zns; - vty_out(vty, "IPset %s type %s\n", zpi->ipset_name, - zebra_pbr_ipset_type2str(zpi->type)); + vty_out(vty, "IPset %s type %s family %s\n", zpi->ipset_name, + zebra_pbr_ipset_type2str(zpi->type), + family2str(zpi->family)); unique.vty = vty; unique.zpi = zpi; unique.zns = zns; @@ -1058,9 +1096,9 @@ void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname) vty_out(vty, "No IPset %s found\n", ipsetname); return; } - vty_out(vty, "IPset %s type %s\n", ipsetname, - zebra_pbr_ipset_type2str(zpi->type)); - + vty_out(vty, "IPset %s type %s family %s\n", ipsetname, + zebra_pbr_ipset_type2str(zpi->type), + family2str(zpi->family)); unique.vty = vty; unique.zpi = zpi; unique.zns = zns; @@ -1101,7 +1139,9 @@ static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable, int ret; uint64_t pkts = 0, bytes = 0; - vty_out(vty, "IPtable %s action %s (%u)\n", iptable->ipset_name, + vty_out(vty, "IPtable %s family %s action %s (%u)\n", + iptable->ipset_name, + family2str(iptable->family), iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect", iptable->unique); if (iptable->type == IPSET_NET_PORT || @@ -1140,6 +1180,12 @@ static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable, iptable->filter_bm & MATCH_DSCP_INVERSE_SET ? "not" : "", iptable->dscp_value); } + if (iptable->filter_bm & (MATCH_FLOW_LABEL_SET | + MATCH_FLOW_LABEL_INVERSE_SET)) { + vty_out(vty, "\t flowlabel %s %d\n", + iptable->filter_bm & MATCH_FLOW_LABEL_INVERSE_SET ? + "not" : "", iptable->flow_label); + } if (iptable->fragment) { char val_str[10]; diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index dd2d7a190d..e7504a3547 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -79,6 +79,9 @@ struct zebra_pbr_ipset { * but value is an enum ipset_type */ uint32_t type; + + uint8_t family; + char ipset_name[ZEBRA_IPSET_NAME_SIZE]; }; @@ -150,6 +153,9 @@ struct zebra_pbr_iptable { uint8_t protocol; uint32_t nb_interface; + uint16_t flow_label; + + uint8_t family; struct list *interface_name_list; @@ -157,6 +163,7 @@ struct zebra_pbr_iptable { }; extern const struct message icmp_typecode_str[]; +extern const struct message icmpv6_typecode_str[]; const char *zebra_pbr_ipset_type2str(uint32_t type); @@ -172,13 +179,6 @@ void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable); void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable); /* - * Add, update or delete a rule from the - * kernel, using info from a dataplane context. - */ -extern enum zebra_dplane_result -kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx); - -/* * Get to know existing PBR rules in the kernel - typically called at startup. */ extern void kernel_read_pbr_rules(struct zebra_ns *zns); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index ee9d5d76e0..ff30de18a3 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -106,7 +106,8 @@ static const struct { [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 5}, [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 5}, [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 3}, - [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5} + [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5}, + [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, 5}, /* Any new route type added to zebra, should be mirrored here */ /* no entry/default: 150 */ @@ -119,6 +120,7 @@ _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, char buf[SRCDEST2STR_BUFFER + sizeof(" (MRIB)")]; char msgbuf[512]; va_list ap; + uint32_t table = 0; va_start(ap, msgfmt); vsnprintf(msgbuf, sizeof(msgbuf), msgfmt, ap); @@ -126,15 +128,24 @@ _rnode_zlog(const char *_func, vrf_id_t vrf_id, struct route_node *rn, if (rn) { struct rib_table_info *info = srcdest_rnode_table_info(rn); + rib_dest_t *dest = NULL; + struct route_entry *re = NULL; + srcdest_rnode2str(rn, buf, sizeof(buf)); if (info->safi == SAFI_MULTICAST) strlcat(buf, " (MRIB)", sizeof(buf)); + + dest = rib_dest_from_rnode(rn); + if (dest) + re = re_list_first(&dest->routes); + if (re) + table = re->table; } else { snprintf(buf, sizeof(buf), "{(route_node *) NULL}"); } - zlog(priority, "%s: %d:%s: %s", _func, vrf_id, buf, msgbuf); + zlog(priority, "%s: (%u:%u):%s: %s", _func, vrf_id, table, buf, msgbuf); } #define rnode_debug(node, vrf_id, ...) \ @@ -488,17 +499,14 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, srcdest_rnode2str(rn, str, sizeof(str)); flog_err(EC_ZEBRA_DP_INSTALL_FAIL, - "%u:%s: Failed to enqueue dataplane install", - re->vrf_id, str); + "%u:%u:%s: Failed to enqueue dataplane install", + re->vrf_id, re->table, str); break; } case ZEBRA_DPLANE_REQUEST_SUCCESS: if (zvrf) zvrf->installs++; break; - /* Should never happen */ - case ZEBRA_DPLANE_REQUEST_PENDING: - break; } return; @@ -543,9 +551,6 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) if (zvrf) zvrf->removals++; break; - /* Should never happen */ - case ZEBRA_DPLANE_REQUEST_PENDING: - break; } return; @@ -763,9 +768,9 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn, if (IS_ZEBRA_DEBUG_RIB) { char buf[SRCDEST2STR_BUFFER]; srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug("%s(%u):%s: Adding route rn %p, re %p (%s)", - zvrf_name(zvrf), zvrf_id(zvrf), buf, rn, new, - zebra_route_string(new->type)); + zlog_debug("%s(%u:%u):%s: Adding route rn %p, re %p (%s)", + zvrf_name(zvrf), zvrf_id(zvrf), new->table, buf, rn, + new, zebra_route_string(new->type)); } /* If labeled-unicast route, install transit LSP. */ @@ -786,9 +791,9 @@ static void rib_process_del_fib(struct zebra_vrf *zvrf, struct route_node *rn, if (IS_ZEBRA_DEBUG_RIB) { char buf[SRCDEST2STR_BUFFER]; srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug("%s(%u):%s: Deleting route rn %p, re %p (%s)", - zvrf_name(zvrf), zvrf_id(zvrf), buf, rn, old, - zebra_route_string(old->type)); + zlog_debug("%s(%u:%u):%s: Deleting route rn %p, re %p (%s)", + zvrf_name(zvrf), zvrf_id(zvrf), old->table, buf, rn, + old, zebra_route_string(old->type)); } /* If labeled-unicast route, uninstall transit LSP. */ @@ -839,17 +844,17 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, srcdest_rnode2str(rn, buf, sizeof(buf)); if (new != old) zlog_debug( - "%s(%u):%s: Updating route rn %p, re %p (%s) old %p (%s)", + "%s(%u:%u):%s: Updating route rn %p, re %p (%s) old %p (%s)", zvrf_name(zvrf), zvrf_id(zvrf), - buf, rn, new, + new->table, buf, rn, new, zebra_route_string(new->type), old, zebra_route_string(old->type)); else zlog_debug( - "%s(%u):%s: Updating route rn %p, re %p (%s)", + "%s(%u:%u):%s: Updating route rn %p, re %p (%s)", zvrf_name(zvrf), zvrf_id(zvrf), - buf, rn, new, + new->table, buf, rn, new, zebra_route_string(new->type)); } @@ -879,17 +884,17 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf, srcdest_rnode2str(rn, buf, sizeof(buf)); if (new != old) zlog_debug( - "%s(%u):%s: Deleting route rn %p, re %p (%s) old %p (%s) - nexthop inactive", + "%s(%u:%u):%s: Deleting route rn %p, re %p (%s) old %p (%s) - nexthop inactive", zvrf_name(zvrf), zvrf_id(zvrf), - buf, rn, new, + new->table, buf, rn, new, zebra_route_string(new->type), old, zebra_route_string(old->type)); else zlog_debug( - "%s(%u):%s: Deleting route rn %p, re %p (%s) - nexthop inactive", + "%s(%u:%u):%s: Deleting route rn %p, re %p (%s) - nexthop inactive", zvrf_name(zvrf), zvrf_id(zvrf), - buf, rn, new, + new->table, buf, rn, new, zebra_route_string(new->type)); } @@ -1023,24 +1028,29 @@ static void rib_process(struct route_node *rn) if (IS_ZEBRA_DEBUG_RIB) srcdest_rnode2str(rn, buf, sizeof(buf)); - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("%s(%u):%s: Processing rn %p", VRF_LOGNAME(vrf), - vrf_id, buf, rn); - /* * we can have rn's that have a NULL info pointer * (dest). As such let's not let the deref happen * additionally we know RNODE_FOREACH_RE_SAFE * will not iterate so we are ok. */ - if (dest) + if (dest) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + struct route_entry *re = re_list_first(&dest->routes); + + zlog_debug("%s(%u:%u):%s: Processing rn %p", + VRF_LOGNAME(vrf), vrf_id, re->table, buf, + rn); + } + old_fib = dest->selected_fib; + } RNODE_FOREACH_RE_SAFE (rn, re, next) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( - "%s(%u):%s: Examine re %p (%s) status %x flags %x dist %d metric %d", - VRF_LOGNAME(vrf), vrf_id, buf, re, + "%s(%u:%u):%s: Examine re %p (%s) status %x flags %x dist %d metric %d", + VRF_LOGNAME(vrf), vrf_id, re->table, buf, re, zebra_route_string(re->type), re->status, re->flags, re->distance, re->metric); @@ -1136,10 +1146,20 @@ static void rib_process(struct route_node *rn) */ if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + struct route_entry *entry; + + entry = old_selected + ? old_selected + : new_selected + ? new_selected + : old_fib ? old_fib + : new_fib ? new_fib : NULL; + zlog_debug( - "%s(%u):%s: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", - VRF_LOGNAME(vrf), vrf_id, buf, (void *)old_selected, - (void *)new_selected, (void *)old_fib, (void *)new_fib); + "%s(%u:%u):%s: After processing: old_selected %p new_selected %p old_fib %p new_fib %p", + VRF_LOGNAME(vrf), vrf_id, entry ? entry->table : 0, buf, + (void *)old_selected, (void *)new_selected, + (void *)old_fib, (void *)new_fib); } /* Buffer ROUTE_ENTRY_CHANGED here, because it will get cleared if @@ -1461,8 +1481,8 @@ static bool rib_update_re_from_ctx(struct route_entry *re, is_selected = (re == dest->selected_fib); if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("update_from_ctx: %s(%u):%s: %sSELECTED, re %p", - VRF_LOGNAME(vrf), re->vrf_id, dest_str, + zlog_debug("update_from_ctx: %s(%u:%u):%s: %sSELECTED, re %p", + VRF_LOGNAME(vrf), re->vrf_id, re->table, dest_str, (is_selected ? "" : "NOT "), re); /* Update zebra's nexthop FIB flag for each nexthop that was installed. @@ -1488,8 +1508,9 @@ static bool rib_update_re_from_ctx(struct route_entry *re, if (matched) { if (IS_ZEBRA_DEBUG_RIB) zlog_debug( - "%s(%u):%s update_from_ctx(): existing fib nhg, no change", - VRF_LOGNAME(vrf), re->vrf_id, dest_str); + "%s(%u:%u):%s update_from_ctx(): existing fib nhg, no change", + VRF_LOGNAME(vrf), re->vrf_id, re->table, + dest_str); goto check_backups; } else if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG)) { @@ -1498,8 +1519,9 @@ static bool rib_update_re_from_ctx(struct route_entry *re, */ if (IS_ZEBRA_DEBUG_RIB) zlog_debug( - "%s(%u):%s update_from_ctx(): replacing fib nhg", - VRF_LOGNAME(vrf), re->vrf_id, dest_str); + "%s(%u:%u):%s update_from_ctx(): replacing fib nhg", + VRF_LOGNAME(vrf), re->vrf_id, re->table, + dest_str); nexthops_free(re->fib_ng.nexthop); re->fib_ng.nexthop = NULL; @@ -1509,8 +1531,9 @@ static bool rib_update_re_from_ctx(struct route_entry *re, changed_p = true; } else { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug("%s(%u):%s update_from_ctx(): no fib nhg", - VRF_LOGNAME(vrf), re->vrf_id, dest_str); + zlog_debug("%s(%u:%u):%s update_from_ctx(): no fib nhg", + VRF_LOGNAME(vrf), re->vrf_id, re->table, + dest_str); } /* @@ -1537,9 +1560,9 @@ static bool rib_update_re_from_ctx(struct route_entry *re, if (matched) { if (IS_ZEBRA_DEBUG_RIB) zlog_debug( - "%s(%u):%s update_from_ctx(): rib nhg matched, changed '%s'", - VRF_LOGNAME(vrf), re->vrf_id, dest_str, - (changed_p ? "true" : "false")); + "%s(%u:%u):%s update_from_ctx(): rib nhg matched, changed '%s'", + VRF_LOGNAME(vrf), re->vrf_id, re->table, + dest_str, (changed_p ? "true" : "false")); goto check_backups; } @@ -1550,8 +1573,8 @@ no_nexthops: */ if (IS_ZEBRA_DEBUG_RIB) zlog_debug( - "%s(%u):%s update_from_ctx(): changed %s, adding new fib nhg%s", - VRF_LOGNAME(vrf), re->vrf_id, dest_str, + "%s(%u:%u):%s update_from_ctx(): changed %s, adding new fib nhg%s", + VRF_LOGNAME(vrf), re->vrf_id, re->table, dest_str, (changed_p ? "true" : "false"), ctxnhg->nexthop != NULL ? "" : " (empty)"); @@ -1727,9 +1750,10 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) zlog_debug( - "%s(%u):%s Processing dplane result ctx %p, op %s result %s", - VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), dest_str, - ctx, dplane_op2str(op), dplane_res2str(status)); + "%s(%u:%u):%s Processing dplane result ctx %p, op %s result %s", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str, ctx, + dplane_op2str(op), dplane_res2str(status)); /* * Update is a bit of a special case, where we may have both old and new @@ -1779,10 +1803,10 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if (old_re->dplane_sequence != dplane_ctx_get_old_seq(ctx)) { if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) zlog_debug( - "%s(%u):%s Stale dplane result for old_re %p", + "%s(%u:%u):%s Stale dplane result for old_re %p", VRF_LOGNAME(vrf), - dplane_ctx_get_vrf(ctx), dest_str, - old_re); + dplane_ctx_get_vrf(ctx), old_re->table, + dest_str, old_re); } else UNSET_FLAG(old_re->status, ROUTE_ENTRY_QUEUED); } @@ -1820,9 +1844,11 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) if (!fib_changed) { if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) zlog_debug( - "%s(%u):%s no fib change for re", + "%s(%u:%u):%s no fib change for re", VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table( + ctx), dest_str); } @@ -1859,8 +1885,9 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) zsend_route_notify_owner(re, dest_pfx, ZAPI_ROUTE_FAIL_INSTALL); - zlog_warn("%s(%u):%s: Route install failed", + zlog_warn("%s(%u:%u):%s: Route install failed", VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), prefix2str(dest_pfx, dest_str, sizeof(dest_str))); } @@ -1888,8 +1915,9 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_REMOVE_FAIL); - zlog_warn("%s(%u):%s: Route Deletion failure", + zlog_warn("%s(%u:%u):%s: Route Deletion failure", VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), prefix2str(dest_pfx, dest_str, sizeof(dest_str))); } @@ -1986,9 +2014,9 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) if (rn == NULL) { if (debug_p) { zlog_debug( - "Failed to process dplane notification: no routes for %s(%u):%s", + "Failed to process dplane notification: no routes for %s(%u:%u):%s", VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), - dest_str); + dplane_ctx_get_table(ctx), dest_str); } goto done; } @@ -1997,9 +2025,9 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) srcdest_rnode_prefixes(rn, &dest_pfx, &src_pfx); if (debug_p) - zlog_debug("%s(%u):%s Processing dplane notif ctx %p", - VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), dest_str, - ctx); + zlog_debug("%s(%u:%u):%s Processing dplane notif ctx %p", + VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str, ctx); /* * Take a pass through the routes, look for matches with the context @@ -2014,9 +2042,9 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) if (re == NULL) { if (debug_p) zlog_debug( - "%s(%u):%s Unable to process dplane notification: no entry for type %s", + "%s(%u:%u):%s Unable to process dplane notification: no entry for type %s", VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), - dest_str, + dplane_ctx_get_table(ctx), dest_str, zebra_route_string(dplane_ctx_get_type(ctx))); goto done; @@ -2048,18 +2076,20 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); if (debug_p) zlog_debug( - "%s(%u):%s dplane notif, uninstalled type %s route", + "%s(%u:%u):%s dplane notif, uninstalled type %s route", VRF_LOGNAME(vrf), - dplane_ctx_get_vrf(ctx), dest_str, + dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str, zebra_route_string( dplane_ctx_get_type(ctx))); } else { /* At least report on the event. */ if (debug_p) zlog_debug( - "%s(%u):%s dplane notif, but type %s not selected_fib", + "%s(%u:%u):%s dplane notif, but type %s not selected_fib", VRF_LOGNAME(vrf), - dplane_ctx_get_vrf(ctx), dest_str, + dplane_ctx_get_vrf(ctx), + dplane_ctx_get_table(ctx), dest_str, zebra_route_string( dplane_ctx_get_type(ctx))); } @@ -2083,9 +2113,9 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) if (!fib_changed) { if (debug_p) zlog_debug( - "%s(%u):%s dplane notification: rib_update returns FALSE", + "%s(%u:%u):%s dplane notification: rib_update returns FALSE", VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), - dest_str); + dplane_ctx_get_table(ctx), dest_str); } /* @@ -2100,9 +2130,9 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) if (start_count > 0 && end_count > 0) { if (debug_p) zlog_debug( - "%s(%u):%s applied nexthop changes from dplane notification", + "%s(%u:%u):%s applied nexthop changes from dplane notification", VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), - dest_str); + dplane_ctx_get_table(ctx), dest_str); /* Changed nexthops - update kernel/others */ dplane_route_notif_update(rn, re, @@ -2111,9 +2141,9 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) } else if (start_count == 0 && end_count > 0) { if (debug_p) zlog_debug( - "%s(%u):%s installed transition from dplane notification", + "%s(%u:%u):%s installed transition from dplane notification", VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), - dest_str); + dplane_ctx_get_table(ctx), dest_str); /* We expect this to be the selected route, so we want * to tell others about this transition. @@ -2129,9 +2159,9 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) } else if (start_count > 0 && end_count == 0) { if (debug_p) zlog_debug( - "%s(%u):%s un-installed transition from dplane notification", + "%s(%u:%u):%s un-installed transition from dplane notification", VRF_LOGNAME(vrf), dplane_ctx_get_vrf(ctx), - dest_str); + dplane_ctx_get_table(ctx), dest_str); /* Transition from _something_ installed to _nothing_ * installed. @@ -2187,17 +2217,20 @@ static void process_subq_route(struct listnode *lnode, uint8_t qindex) rnode = listgetdata(lnode); dest = rib_dest_from_rnode(rnode); - if (dest) - zvrf = rib_dest_vrf(dest); + assert(dest); + + zvrf = rib_dest_vrf(dest); rib_process(rnode); if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + struct route_entry *re = re_list_first(&dest->routes); char buf[SRCDEST2STR_BUFFER]; srcdest_rnode2str(rnode, buf, sizeof(buf)); - zlog_debug("%s(%u):%s: rn %p dequeued from sub-queue %u", - zvrf_name(zvrf), zvrf_id(zvrf), buf, rnode, qindex); + zlog_debug("%s(%u:%u):%s: rn %p dequeued from sub-queue %u", + zvrf_name(zvrf), zvrf_id(zvrf), re ? re->table : 0, buf, + rnode, qindex); } if (rnode->info) @@ -3008,7 +3041,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, unsigned short instance, int flags, struct prefix *p, struct prefix_ipv6 *src_p, const struct nexthop *nh, uint32_t nhe_id, uint32_t table_id, uint32_t metric, - uint8_t distance, bool fromkernel) + uint8_t distance, bool fromkernel, bool connected_down) { struct route_table *table; struct route_node *rn; @@ -3130,7 +3163,8 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, rn, fib, zebra_route_string(fib->type)); } - if (allow_delete) { + if (allow_delete + || CHECK_FLAG(dest->flags, RIB_ROUTE_ANY_QUEUED)) { UNSET_FLAG(fib->status, ROUTE_ENTRY_INSTALLED); /* Unset flags. */ for (rtnh = fib->nhe->nhg.nexthop; rtnh; @@ -3215,6 +3249,19 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, rib_delnode(rn, same); } + /* + * This is to force an immediate re-eval of this particular + * node via nexthop tracking. Why? Because there are scenarios + * where the interface is flapping and the normal queuing methodology + * will cause down/up events to very very rarely be combined into + * a non-event from nexthop tracking perspective. Leading + * to some fun timing situations with upper level routing protocol + * trying to and failing to install routes during this blip. Especially + * when zebra is under load. + */ + if (connected_down) + zebra_rib_evaluate_rn_nexthops(rn, + zebra_router_get_next_sequence()); route_unlock_node(rn); return; } @@ -3769,6 +3816,7 @@ static int rib_process_dplane_results(struct thread *thread) case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: 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 b151e90a92..cdc00f6026 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -46,6 +46,7 @@ #include "zebra/debug.h" #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" +#include "zebra/zebra_srte.h" #include "zebra/interface.h" #include "zebra/zebra_memory.h" #include "zebra/zebra_errors.h" @@ -57,8 +58,6 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re, static void copy_state(struct rnh *rnh, const struct route_entry *re, struct route_node *rn); static int compare_state(struct route_entry *r1, struct route_entry *r2); -static int send_client(struct rnh *rnh, struct zserv *client, - enum rnh_type type, vrf_id_t vrf_id); static void print_rnh(struct route_node *rn, struct vty *vty); static int zebra_client_cleanup_rnh(struct zserv *client); @@ -305,7 +304,7 @@ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, * We always need to respond with known information, * currently multiple daemons expect this behavior */ - send_client(rnh, client, type, vrf_id); + zebra_send_rnh_update(rnh, client, type, vrf_id, 0); } void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, @@ -531,8 +530,9 @@ static void zebra_rnh_eval_import_check_entry(struct zebra_vrf *zvrf, afi_t afi, } /* state changed, notify clients */ for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { - send_client(rnh, client, - RNH_IMPORT_CHECK_TYPE, zvrf->vrf->vrf_id); + zebra_send_rnh_update(rnh, client, + RNH_IMPORT_CHECK_TYPE, + zvrf->vrf->vrf_id, 0); } } } @@ -594,7 +594,8 @@ static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi, zebra_route_string(client->proto)); } - send_client(rnh, client, RNH_NEXTHOP_TYPE, zvrf->vrf->vrf_id); + zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE, + zvrf->vrf->vrf_id, 0); } if (re) @@ -1107,8 +1108,9 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2) return 0; } -static int send_client(struct rnh *rnh, struct zserv *client, - enum rnh_type type, vrf_id_t vrf_id) +int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, + enum rnh_type type, vrf_id_t vrf_id, + uint32_t srte_color) { struct stream *s = NULL; struct route_entry *re; @@ -1117,6 +1119,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, struct nexthop *nh; struct route_node *rn; int ret; + uint32_t message = 0; int cmd = (type == RNH_IMPORT_CHECK_TYPE) ? ZEBRA_IMPORT_CHECK_UPDATE : ZEBRA_NEXTHOP_UPDATE; @@ -1128,6 +1131,11 @@ static int send_client(struct rnh *rnh, struct zserv *client, zclient_create_header(s, cmd, vrf_id); + /* Message flags. */ + if (srte_color) + SET_FLAG(message, ZAPI_MESSAGE_SRTE); + stream_putl(s, message); + stream_putw(s, rn->p.family); switch (rn->p.family) { case AF_INET: @@ -1144,6 +1152,9 @@ static int send_client(struct rnh *rnh, struct zserv *client, __func__, rn->p.family); goto failure; } + if (srte_color) + stream_putl(s, srte_color); + if (re) { struct zapi_nexthop znh; struct nexthop_group *nhg; @@ -1160,7 +1171,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, for (ALL_NEXTHOPS_PTR(nhg, nh)) if (rnh_nexthop_valid(re, nh)) { zapi_nexthop_from_nexthop(&znh, nh); - ret = zapi_nexthop_encode(s, &znh, 0/*flags*/); + ret = zapi_nexthop_encode(s, &znh, 0, message); if (ret < 0) goto failure; @@ -1173,7 +1184,8 @@ static int send_client(struct rnh *rnh, struct zserv *client, if (rnh_nexthop_valid(re, nh)) { zapi_nexthop_from_nexthop(&znh, nh); ret = zapi_nexthop_encode( - s, &znh, 0 /* flags */); + s, &znh, 0 /* flags */, + 0 /* message */); if (ret < 0) goto failure; diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index e744504920..ba12b1738f 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -50,6 +50,9 @@ extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, extern void zebra_free_rnh(struct rnh *rnh); extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, enum rnh_type type, vrf_id_t vrfid); +extern int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, + enum rnh_type type, vrf_id_t vrf_id, + uint32_t srte_color); extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *, bool *); extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *); extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c new file mode 100644 index 0000000000..d6043534e3 --- /dev/null +++ b/zebra/zebra_srte.c @@ -0,0 +1,378 @@ +/* Zebra SR-TE code + * Copyright (C) 2020 NetDEF, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "lib/zclient.h" +#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" + +DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_SR_POLICY, "SR Policy") + +static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy); + +/* Generate rb-tree of SR Policy instances. */ +static inline int +zebra_sr_policy_instance_compare(const struct zebra_sr_policy *a, + const struct zebra_sr_policy *b) +{ + return sr_policy_compare(&a->endpoint, &b->endpoint, a->color, + b->color); +} +RB_GENERATE(zebra_sr_policy_instance_head, zebra_sr_policy, entry, + zebra_sr_policy_instance_compare) + +struct zebra_sr_policy_instance_head zebra_sr_policy_instances = + RB_INITIALIZER(&zebra_sr_policy_instances); + +struct zebra_sr_policy *zebra_sr_policy_add(uint32_t color, + struct ipaddr *endpoint, char *name) +{ + struct zebra_sr_policy *policy; + + policy = XCALLOC(MTYPE_ZEBRA_SR_POLICY, sizeof(*policy)); + policy->color = color; + policy->endpoint = *endpoint; + strlcpy(policy->name, name, sizeof(policy->name)); + policy->status = ZEBRA_SR_POLICY_DOWN; + RB_INSERT(zebra_sr_policy_instance_head, &zebra_sr_policy_instances, + policy); + + return policy; +} + +void zebra_sr_policy_del(struct zebra_sr_policy *policy) +{ + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_deactivate(policy); + RB_REMOVE(zebra_sr_policy_instance_head, &zebra_sr_policy_instances, + policy); + XFREE(MTYPE_ZEBRA_SR_POLICY, policy); +} + +struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color, + struct ipaddr *endpoint) +{ + struct zebra_sr_policy policy = {}; + + policy.color = color; + policy.endpoint = *endpoint; + return RB_FIND(zebra_sr_policy_instance_head, + &zebra_sr_policy_instances, &policy); +} + +struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name) +{ + struct zebra_sr_policy *policy; + + // TODO: create index for policy names + RB_FOREACH (policy, zebra_sr_policy_instance_head, + &zebra_sr_policy_instances) { + if (strcmp(policy->name, name) == 0) + return policy; + } + + return NULL; +} + +static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, + struct zserv *client) +{ + const zebra_nhlfe_t *nhlfe; + struct stream *s; + uint32_t message = 0; + unsigned long nump = 0; + uint8_t num; + struct zapi_nexthop znh; + int ret; + + /* Get output stream. */ + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, zvrf_id(policy->zvrf)); + + /* Message flags. */ + SET_FLAG(message, ZAPI_MESSAGE_SRTE); + stream_putl(s, message); + + switch (policy->endpoint.ipa_type) { + case IPADDR_V4: + stream_putw(s, AF_INET); + stream_putc(s, IPV4_MAX_BITLEN); + stream_put_in_addr(s, &policy->endpoint.ipaddr_v4); + break; + case IPADDR_V6: + stream_putw(s, AF_INET6); + stream_putc(s, IPV6_MAX_BITLEN); + stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN); + break; + default: + flog_warn(EC_LIB_DEVELOPMENT, + "%s: unknown policy endpoint address family: %u", + __func__, policy->endpoint.ipa_type); + exit(1); + } + stream_putl(s, policy->color); + + num = 0; + frr_each (nhlfe_list_const, &policy->lsp->nhlfe_list, nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + continue; + + if (num == 0) { + stream_putc(s, re_type_from_lsp_type(nhlfe->type)); + stream_putw(s, 0); /* instance - not available */ + stream_putc(s, nhlfe->distance); + stream_putl(s, 0); /* metric - not available */ + nump = stream_get_endp(s); + stream_putc(s, 0); + } + + zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop); + ret = zapi_nexthop_encode(s, &znh, 0, message); + if (ret < 0) + goto failure; + + num++; + } + stream_putc_at(s, nump, num); + stream_putw_at(s, 0, stream_get_endp(s)); + + client->nh_last_upd_time = monotime(NULL); + client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE; + return zserv_send_message(client, s); + +failure: + + stream_free(s); + return -1; +} + +static void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy) +{ + struct rnh *rnh; + struct prefix p = {}; + struct zebra_vrf *zvrf; + struct listnode *node; + struct zserv *client; + + zvrf = policy->zvrf; + switch (policy->endpoint.ipa_type) { + case IPADDR_V4: + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = policy->endpoint.ipaddr_v4; + break; + case IPADDR_V6: + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_BITLEN; + p.u.prefix6 = policy->endpoint.ipaddr_v6; + break; + default: + flog_warn(EC_LIB_DEVELOPMENT, + "%s: unknown policy endpoint address family: %u", + __func__, policy->endpoint.ipa_type); + exit(1); + } + + rnh = zebra_lookup_rnh(&p, zvrf_id(zvrf), RNH_NEXTHOP_TYPE); + if (!rnh) + return; + + for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_notify_update_client(policy, client); + else + /* Fallback to the IGP shortest path. */ + zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE, + zvrf_id(zvrf), policy->color); + } +} + +static void zebra_sr_policy_activate(struct zebra_sr_policy *policy, + zebra_lsp_t *lsp) +{ + policy->status = ZEBRA_SR_POLICY_UP; + policy->lsp = lsp; + (void)zebra_sr_policy_bsid_install(policy); + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_UP); + zebra_sr_policy_notify_update(policy); +} + +static void zebra_sr_policy_update(struct zebra_sr_policy *policy, + zebra_lsp_t *lsp, + struct zapi_srte_tunnel *old_tunnel) +{ + bool bsid_changed; + bool segment_list_changed; + + policy->lsp = lsp; + + bsid_changed = + policy->segment_list.local_label != old_tunnel->local_label; + segment_list_changed = + policy->segment_list.label_num != old_tunnel->label_num + || memcmp(policy->segment_list.labels, old_tunnel->labels, + sizeof(mpls_label_t) + * policy->segment_list.label_num); + + /* Re-install label stack if necessary. */ + if (bsid_changed || segment_list_changed) { + zebra_sr_policy_bsid_uninstall(policy, old_tunnel->local_label); + (void)zebra_sr_policy_bsid_install(policy); + } + + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_UP); + + /* Handle segment-list update. */ + if (segment_list_changed) + zebra_sr_policy_notify_update(policy); +} + +static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy) +{ + policy->status = ZEBRA_SR_POLICY_DOWN; + policy->lsp = NULL; + zebra_sr_policy_bsid_uninstall(policy, + policy->segment_list.local_label); + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_DOWN); + zebra_sr_policy_notify_update(policy); +} + +int zebra_sr_policy_validate(struct zebra_sr_policy *policy, + struct zapi_srte_tunnel *new_tunnel) +{ + struct zapi_srte_tunnel old_tunnel = policy->segment_list; + zebra_lsp_t *lsp; + + if (new_tunnel) + policy->segment_list = *new_tunnel; + + /* Try to resolve the Binding-SID nexthops. */ + lsp = mpls_lsp_find(policy->zvrf, policy->segment_list.labels[0]); + if (!lsp || !lsp->best_nhlfe + || lsp->addr_family != ipaddr_family(&policy->endpoint)) { + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_deactivate(policy); + return -1; + } + + /* First label was resolved successfully. */ + if (policy->status == ZEBRA_SR_POLICY_DOWN) + zebra_sr_policy_activate(policy, lsp); + else + zebra_sr_policy_update(policy, lsp, &old_tunnel); + + return 0; +} + +int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy) +{ + struct zapi_srte_tunnel *zt = &policy->segment_list; + zebra_nhlfe_t *nhlfe; + + if (zt->local_label == MPLS_LABEL_NONE) + return 0; + + frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, nhlfe) { + uint8_t num_out_labels; + mpls_label_t *out_labels; + mpls_label_t null_label = MPLS_LABEL_IMPLICIT_NULL; + + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + continue; + + /* + * Don't push the first SID if the corresponding action in the + * LFIB is POP. + */ + if (!nhlfe->nexthop->nh_label + || !nhlfe->nexthop->nh_label->num_labels + || nhlfe->nexthop->nh_label->label[0] + == MPLS_LABEL_IMPLICIT_NULL) { + if (zt->label_num > 1) { + num_out_labels = zt->label_num - 1; + out_labels = &zt->labels[1]; + } else { + num_out_labels = 1; + out_labels = &null_label; + } + } else { + num_out_labels = zt->label_num; + out_labels = zt->labels; + } + + if (mpls_lsp_install( + policy->zvrf, zt->type, zt->local_label, + num_out_labels, out_labels, nhlfe->nexthop->type, + &nhlfe->nexthop->gate, nhlfe->nexthop->ifindex) + < 0) + return -1; + } + + return 0; +} + +void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy, + mpls_label_t old_bsid) +{ + struct zapi_srte_tunnel *zt = &policy->segment_list; + + mpls_lsp_uninstall_all_vrf(policy->zvrf, zt->type, old_bsid); +} + +int zebra_sr_policy_label_update(mpls_label_t label, + enum zebra_sr_policy_update_label_mode mode) +{ + struct zebra_sr_policy *policy; + + RB_FOREACH (policy, zebra_sr_policy_instance_head, + &zebra_sr_policy_instances) { + mpls_label_t next_hop_label; + + next_hop_label = policy->segment_list.labels[0]; + if (next_hop_label != label) + continue; + + switch (mode) { + case ZEBRA_SR_POLICY_LABEL_CREATED: + case ZEBRA_SR_POLICY_LABEL_UPDATED: + case ZEBRA_SR_POLICY_LABEL_REMOVED: + zebra_sr_policy_validate(policy, NULL); + break; + } + } + + return 0; +} + +void zebra_srte_init(void) +{ +} diff --git a/zebra/zebra_srte.h b/zebra/zebra_srte.h new file mode 100644 index 0000000000..e5239b7b7b --- /dev/null +++ b/zebra/zebra_srte.h @@ -0,0 +1,74 @@ +/* Zebra's client header. + * Copyright (C) 2020 Netdef, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_SRTE_H +#define _ZEBRA_SRTE_H + +#include "zebra/zebra_mpls.h" + +#include "lib/zclient.h" +#include "lib/srte.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum zebra_sr_policy_update_label_mode { + ZEBRA_SR_POLICY_LABEL_CREATED = 1, + ZEBRA_SR_POLICY_LABEL_UPDATED = 2, + ZEBRA_SR_POLICY_LABEL_REMOVED = 3, +}; + +struct zebra_sr_policy { + RB_ENTRY(zebra_sr_policy) entry; + uint32_t color; + struct ipaddr endpoint; + char name[SRTE_POLICY_NAME_MAX_LENGTH]; + enum zebra_sr_policy_status status; + struct zapi_srte_tunnel segment_list; + zebra_lsp_t *lsp; + struct zebra_vrf *zvrf; +}; +RB_HEAD(zebra_sr_policy_instance_head, zebra_sr_policy); +RB_PROTOTYPE(zebra_sr_policy_instance_head, zebra_sr_policy, entry, + zebra_sr_policy_instance_compare) + +extern struct zebra_sr_policy_instance_head zebra_sr_policy_instances; + +struct zebra_sr_policy * +zebra_sr_policy_add(uint32_t color, struct ipaddr *endpoint, char *name); +void zebra_sr_policy_del(struct zebra_sr_policy *policy); +struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color, + struct ipaddr *endpoint); +struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name); +int zebra_sr_policy_validate(struct zebra_sr_policy *policy, + struct zapi_srte_tunnel *new_tunnel); +int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy); +void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy, + mpls_label_t old_bsid); +void zebra_srte_init(void); +int zebra_sr_policy_label_update(mpls_label_t label, + enum zebra_sr_policy_update_label_mode mode); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEBRA_SRTE_H */ diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index d262faa070..910d192317 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -92,6 +92,11 @@ struct zebra_vrf { struct list *rid_all_sorted_list; struct list *rid_lo_sorted_list; struct prefix rid_user_assigned; + struct list _rid6_all_sorted_list; + struct list _rid6_lo_sorted_list; + struct list *rid6_all_sorted_list; + struct list *rid6_lo_sorted_list; + struct prefix rid6_user_assigned; /* * Back pointer to the owning namespace. @@ -125,9 +130,9 @@ struct zebra_vrf { #define MPLS_FLAG_SCHEDULE_LSPS (1 << 0) /* - * VNI hash table (for EVPN). Only in the EVPN instance. + * EVPN hash table. Only in the EVPN instance. */ - struct hash *vni_table; + struct hash *evpn_table; /* * Whether EVPN is enabled or not. Only in the EVPN instance. diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 2ea04eee2e..b6d0b26125 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -57,6 +57,7 @@ #include "zebra/interface.h" #include "northbound_cli.h" #include "zebra/zebra_nb.h" +#include "zebra/kernel_netlink.h" extern int allow_delete; @@ -562,6 +563,10 @@ static void show_route_nexthop_helper(struct vty *vty, if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, sizeof(buf))) vty_out(vty, ", src %s", buf); + /* SR-TE information */ + if (nexthop->srte_color) + vty_out(vty, ", SR-TE color %u", + nexthop->srte_color); } break; case NEXTHOP_TYPE_IPV6: @@ -781,6 +786,9 @@ static void show_nexthop_json_helper(json_object *json_nexthop, json_object_int_add(json_nexthop, "weight", nexthop->weight); + if (nexthop->srte_color) + json_object_int_add(json_nexthop, "srteColor", + nexthop->srte_color); } static void vty_show_ip_route(struct vty *vty, struct route_node *rn, @@ -2434,7 +2442,7 @@ DEFUN (no_vrf_vni_mapping, int filter = 0; ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); - vni_t vni = strtoul(argv[1]->arg, NULL, 10); + vni_t vni = strtoul(argv[2]->arg, NULL, 10); assert(vrf); assert(zvrf); @@ -3390,6 +3398,11 @@ static int config_write_protocol(struct vty *vty) if (!zebra_nhg_kernel_nexthops_enabled()) vty_out(vty, "no zebra nexthop kernel enable\n"); +#ifdef HAVE_NETLINK + /* Include netlink info */ + netlink_config_write_helper(vty); +#endif /* HAVE_NETLINK */ + return 1; } @@ -3712,6 +3725,44 @@ DEFUN_HIDDEN (show_frr, return CMD_SUCCESS; } +#ifdef HAVE_NETLINK +DEFUN_HIDDEN(zebra_kernel_netlink_batch_tx_buf, + zebra_kernel_netlink_batch_tx_buf_cmd, + "zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)", + ZEBRA_STR + "Zebra kernel interface\n" + "Set Netlink parameters\n" + "Set batch buffer size and send threshold\n" + "Size of the buffer\n" + "Send threshold\n") +{ + uint32_t bufsize = 0, threshold = 0; + + bufsize = strtoul(argv[4]->arg, NULL, 10); + threshold = strtoul(argv[5]->arg, NULL, 10); + + netlink_set_batch_buffer_size(bufsize, threshold, true); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf, + no_zebra_kernel_netlink_batch_tx_buf_cmd, + "no zebra kernel netlink batch-tx-buf [(0-1048576)] [(0-1048576)]", + NO_STR ZEBRA_STR + "Zebra kernel interface\n" + "Set Netlink parameters\n" + "Set batch buffer size and send threshold\n" + "Size of the buffer\n" + "Send threshold\n") +{ + netlink_set_batch_buffer_size(0, 0, false); + + return CMD_SUCCESS; +} + +#endif /* HAVE_NETLINK */ + /* IP node for static routes. */ static int zebra_ip_config(struct vty *vty); static struct cmd_node ip_node = { @@ -3849,5 +3900,10 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd); install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd); +#ifdef HAVE_NETLINK + install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd); + install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd); +#endif /* HAVE_NETLINK */ + install_element(VIEW_NODE, &zebra_show_routing_tables_summary_cmd); } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index ff09b48dcf..13a58bc34a 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -49,79 +49,31 @@ #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" #include "zebra/zebra_vxlan_private.h" #include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_vxlan.h" #include "zebra/zebra_router.h" DEFINE_MTYPE_STATIC(ZEBRA, HOST_PREFIX, "host prefix"); -DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash"); DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash"); -DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP"); -DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC"); -DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor"); +DEFINE_MTYPE_STATIC(ZEBRA, L3VNI_MAC, "EVPN L3VNI MAC"); +DEFINE_MTYPE_STATIC(ZEBRA, L3NEIGH, "EVPN Neighbor"); DEFINE_MTYPE_STATIC(ZEBRA, ZVXLAN_SG, "zebra VxLAN multicast group"); DEFINE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni, bool delete, const char *reason), (rmac, zl3vni, delete, reason)) -/* definitions */ -/* PMSI strings. */ -#define VXLAN_FLOOD_STR_NO_INFO "-" -#define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO -static const struct message zvtep_flood_str[] = { - {VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO}, - {VXLAN_FLOOD_PIM_SM, "PIM-SM"}, - {VXLAN_FLOOD_HEAD_END_REPL, "HER"}, - {0} -}; - /* static function declarations */ -static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, - uint16_t cmd); -static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); -static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt); -static void zvni_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt); -static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, - void **args); +static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket, + void **args); static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, json_object *json); static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, json_object *json); -static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json); -static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt); -static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt); -static void zvni_print(zebra_vni_t *zvni, void **ctxt); -static void zvni_print_hash(struct hash_bucket *bucket, void *ctxt[]); - -static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, - struct ipaddr *ip, uint8_t flags, - uint32_t seq, int state, - struct zebra_evpn_es *es, - uint16_t cmd); -static unsigned int neigh_hash_keymake(const void *p); -static void *zvni_neigh_alloc(void *p); -static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, - struct ethaddr *mac, zebra_mac_t *zmac, - uint32_t n_flags); -static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n); -static void zvni_neigh_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags); -static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip); -static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *mac, zebra_mac_t *zmac, - uint32_t flags, uint32_t seq); -static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *mac, - uint32_t flags, int state, bool force); -static int zvni_rem_neigh_install(zebra_vni_t *zvni, - zebra_neigh_t *n, bool was_static); -static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n); -static int zvni_neigh_probe(zebra_vni_t *zvni, zebra_neigh_t *n); -static zebra_vni_t *zvni_from_svi(struct interface *ifp, - struct interface *br_if); -static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); -static struct interface *zvni_map_to_macvlan(struct interface *br_if, - struct interface *svi_if); +static void zevpn_print_mac_hash_all_evpn(struct hash_bucket *bucket, void *ctxt); /* l3-vni next-hop neigh related APIs */ static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, @@ -152,69 +104,7 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni); static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni); static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni); -static unsigned int mac_hash_keymake(const void *p); -static bool mac_cmp(const void *p1, const void *p2); -static void *zvni_mac_alloc(void *p); -static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr); -static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac); -static void zvni_mac_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags); -static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *macaddr); -static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, - uint32_t flags, uint32_t seq, struct zebra_evpn_es *es); -static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr, - uint32_t flags, bool force); -static zebra_vni_t *zvni_map_vlan(struct interface *ifp, - struct interface *br_if, vlanid_t vid); -static int zvni_rem_mac_install(zebra_vni_t *zvni, - zebra_mac_t *mac, bool was_static); -static int zvni_rem_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac); -static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt); - -static unsigned int vni_hash_keymake(const void *p); -static void *zvni_alloc(void *p); -static zebra_vni_t *zvni_add(vni_t vni); -static int zvni_del(zebra_vni_t *zvni); -static int zvni_send_add_to_client(zebra_vni_t *zvni); -static int zvni_send_del_to_client(zebra_vni_t *zvni); -static void zvni_build_hash_table(void); -static int zvni_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep); -static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip); -static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip, - int flood_control); -static int zvni_vtep_del(zebra_vni_t *zvni, zebra_vtep_t *zvtep); -static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall); -static int zvni_vtep_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep); -static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip); -static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni); -static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni); -static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, - struct ethaddr *macaddr, struct ipaddr *ip); -static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, - struct ipaddr *ip); -struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp); -static int advertise_gw_macip_enabled(zebra_vni_t *zvni); -static int advertise_svi_macip_enabled(zebra_vni_t *zvni); -static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, - zebra_mac_t *old_zmac, - zebra_mac_t *new_zmac, - zebra_neigh_t *nbr); -static int remote_neigh_count(zebra_mac_t *zmac); -static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac); -static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t); -static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t); -static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, - zebra_neigh_t *nbr, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local); -static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, - zebra_mac_t *mac, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local); +static void zevpn_build_hash_table(void); static unsigned int zebra_vxlan_sg_hash_key_make(const void *p); static bool zebra_vxlan_sg_hash_eq(const void *p1, const void *p2); static void zebra_vxlan_sg_do_deref(struct zebra_vrf *zvrf, @@ -227,25 +117,6 @@ static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, struct in_addr mcast_grp); static void zebra_vxlan_sg_cleanup(struct hash_bucket *bucket, void *arg); -static void zvni_send_mac_to_client(zebra_vni_t *zvn); -static void zvni_send_neigh_to_client(zebra_vni_t *zvni); -static void zebra_vxlan_rem_mac_del(zebra_vni_t *zvni, - zebra_mac_t *zmac); -static inline void zebra_vxlan_mac_stop_hold_timer(zebra_mac_t *mac); -static inline bool zebra_vxlan_mac_is_static(zebra_mac_t *mac); -static void zebra_vxlan_local_neigh_ref_mac(zebra_neigh_t *n, - struct ethaddr *macaddr, zebra_mac_t *mac, - bool send_mac_update); -static void zebra_vxlan_local_neigh_deref_mac(zebra_neigh_t *n, - bool send_mac_update); -static inline bool zebra_vxlan_neigh_is_ready_for_bgp(zebra_neigh_t *n); -static inline bool zebra_vxlan_neigh_clear_sync_info(zebra_neigh_t *n); -static void zebra_vxlan_sync_neigh_dp_install(zebra_neigh_t *n, - bool set_inactive, bool force_clear_static, const char *caller); -static inline bool zebra_vxlan_neigh_is_static(zebra_neigh_t *neigh); -static void zebra_vxlan_neigh_send_add_del_to_client(zebra_neigh_t *n, - bool old_bgp_ready, bool new_bgp_ready); - /* Private functions */ static int host_rb_entry_compare(const struct host_rb_entry *hle1, const struct host_rb_entry *hle2) @@ -293,761 +164,14 @@ static uint32_t rb_host_count(struct host_rb_tree_entry *hrbe) } /* - * Return number of valid MACs in a VNI's MAC hash table - all - * remote MACs and non-internal (auto) local MACs count. - */ -static uint32_t num_valid_macs(zebra_vni_t *zvni) -{ - unsigned int i; - uint32_t num_macs = 0; - struct hash *hash; - struct hash_bucket *hb; - zebra_mac_t *mac; - - hash = zvni->mac_table; - if (!hash) - return num_macs; - for (i = 0; i < hash->size; i++) { - for (hb = hash->index[i]; hb; hb = hb->next) { - mac = (zebra_mac_t *)hb->data; - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) - || CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) - || !CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) - num_macs++; - } - } - - return num_macs; -} - -static uint32_t num_dup_detected_macs(zebra_vni_t *zvni) -{ - unsigned int i; - uint32_t num_macs = 0; - struct hash *hash; - struct hash_bucket *hb; - zebra_mac_t *mac; - - hash = zvni->mac_table; - if (!hash) - return num_macs; - for (i = 0; i < hash->size; i++) { - for (hb = hash->index[i]; hb; hb = hb->next) { - mac = (zebra_mac_t *)hb->data; - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - num_macs++; - } - } - - return num_macs; -} - -static uint32_t num_dup_detected_neighs(zebra_vni_t *zvni) -{ - unsigned int i; - uint32_t num_neighs = 0; - struct hash *hash; - struct hash_bucket *hb; - zebra_neigh_t *nbr; - - hash = zvni->neigh_table; - if (!hash) - return num_neighs; - for (i = 0; i < hash->size; i++) { - for (hb = hash->index[i]; hb; hb = hb->next) { - nbr = (zebra_neigh_t *)hb->data; - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - num_neighs++; - } - } - - return num_neighs; -} - -static int advertise_gw_macip_enabled(zebra_vni_t *zvni) -{ - struct zebra_vrf *zvrf; - - zvrf = zebra_vrf_get_evpn(); - if (zvrf && zvrf->advertise_gw_macip) - return 1; - - if (zvni && zvni->advertise_gw_macip) - return 1; - - return 0; -} - -static int advertise_svi_macip_enabled(zebra_vni_t *zvni) -{ - struct zebra_vrf *zvrf; - - zvrf = zebra_vrf_get_evpn(); - if (zvrf && zvrf->advertise_svi_macip) - return 1; - - if (zvni && zvni->advertise_svi_macip) - return 1; - - return 0; -} - -/* As part Duplicate Address Detection (DAD) for IP mobility - * MAC binding changes, ensure to inherit duplicate flag - * from MAC. + * Print neighbors for all EVPN. */ -static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, - zebra_mac_t *old_zmac, - zebra_mac_t *new_zmac, - zebra_neigh_t *nbr) -{ - bool is_old_mac_dup = false; - bool is_new_mac_dup = false; - - if (!zvrf->dup_addr_detect) - return 0; - /* Check old or new MAC is detected as duplicate - * mark this neigh as duplicate - */ - if (old_zmac) - is_old_mac_dup = CHECK_FLAG(old_zmac->flags, - ZEBRA_MAC_DUPLICATE); - if (new_zmac) - is_new_mac_dup = CHECK_FLAG(new_zmac->flags, - ZEBRA_MAC_DUPLICATE); - /* Old and/or new MAC can be in duplicate state, - * based on that IP/Neigh Inherits the flag. - * If New MAC is marked duplicate, inherit to the IP. - * If old MAC is duplicate but new MAC is not, clear - * duplicate flag for IP and reset detection params - * and let IP DAD retrigger. - */ - if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { - SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - /* Capture Duplicate detection time */ - nbr->dad_dup_detect_time = monotime(NULL); - /* Mark neigh inactive */ - ZEBRA_NEIGH_SET_INACTIVE(nbr); - - return 1; - } else if (is_old_mac_dup && !is_new_mac_dup) { - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->detect_start_time.tv_usec = 0; - } - return 0; -} - -static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, - zebra_mac_t *mac, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local) -{ - zebra_neigh_t *nbr; - struct listnode *node = NULL; - struct timeval elapsed = {0, 0}; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - bool reset_params = false; - - if (!(zvrf->dup_addr_detect && do_dad)) - return; - - /* MAC is detected as duplicate, - * Local MAC event -> hold on advertising to BGP. - * Remote MAC event -> hold on installing it. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u", - __func__, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - mac->flags, mac->dad_count, - zvrf->dad_freeze_time); - - /* For duplicate MAC do not update - * client but update neigh due to - * this MAC update. - */ - if (zvrf->dad_freeze) - *is_dup_detect = true; - - return; - } - - /* Check if detection time (M-secs) expired. - * Reset learn count and detection start time. - */ - monotime_since(&mac->detect_start_time, &elapsed); - reset_params = (elapsed.tv_sec > zvrf->dad_time); - if (is_local && !reset_params) { - /* RFC-7432: A PE/VTEP that detects a MAC mobility - * event via LOCAL learning starts an M-second timer. - * - * NOTE: This is the START of the probe with count is - * 0 during LOCAL learn event. - * (mac->dad_count == 0 || elapsed.tv_sec >= zvrf->dad_time) - */ - reset_params = !mac->dad_count; - } - - if (reset_params) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u", - __func__, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - mac->flags, mac->dad_count); - - mac->dad_count = 0; - /* Start dup. addr detection (DAD) start time, - * ONLY during LOCAL learn. - */ - if (is_local) - monotime(&mac->detect_start_time); - - } else if (!is_local) { - /* For REMOTE MAC, increment detection count - * ONLY while in probe window, once window passed, - * next local learn event should trigger DAD. - */ - mac->dad_count++; - } - - /* For LOCAL MAC learn event, once count is reset above via either - * initial/start detection time or passed the probe time, the count - * needs to be incremented. - */ - if (is_local) - mac->dad_count++; - - if (mac->dad_count >= zvrf->dad_max_moves) { - flog_warn(EC_ZEBRA_DUP_MAC_DETECTED, - "VNI %u: MAC %s detected as duplicate during %s VTEP %s", - mac->zvni->vni, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - is_local ? "local update, last" : - "remote update, from", inet_ntoa(vtep_ip)); - - SET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); - - /* Capture Duplicate detection time */ - mac->dad_dup_detect_time = monotime(NULL); - - /* Mark all IPs/Neighs as duplicate - * associcated with this MAC - */ - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { - - /* Ony Mark IPs which are Local */ - if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) - continue; - - SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - - nbr->dad_dup_detect_time = monotime(NULL); - - flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC", - mac->zvni->vni, - prefix_mac2str(&mac->macaddr, - buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - is_local ? "local" : "remote"); - } - - /* Start auto recovery timer for this MAC */ - THREAD_OFF(mac->dad_mac_auto_recovery_timer); - if (zvrf->dad_freeze && zvrf->dad_freeze_time) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start", - __func__, - prefix_mac2str(&mac->macaddr, buf, - sizeof(buf)), - mac->flags, zvrf->dad_freeze_time); - - thread_add_timer(zrouter.master, - zebra_vxlan_dad_mac_auto_recovery_exp, - mac, zvrf->dad_freeze_time, - &mac->dad_mac_auto_recovery_timer); - } - - /* In case of local update, do not inform to client (BGPd), - * upd_neigh for neigh sequence change. - */ - if (zvrf->dad_freeze) - *is_dup_detect = true; - } -} - -static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, - zebra_neigh_t *nbr, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local) -{ - - struct timeval elapsed = {0, 0}; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - bool reset_params = false; - - if (!zvrf->dup_addr_detect) - return; - - /* IP is detected as duplicate or inherit dup - * state, hold on to install as remote entry - * only if freeze is enabled. - */ - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u", - __func__, - prefix_mac2str(&nbr->emac, buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - nbr->flags, nbr->dad_count, - zvrf->dad_freeze_time); - - if (zvrf->dad_freeze) - *is_dup_detect = true; - - /* warn-only action, neigh will be installed. - * freeze action, it wil not be installed. - */ - return; - } - - if (!do_dad) - return; - - /* Check if detection time (M-secs) expired. - * Reset learn count and detection start time. - * During remote mac add, count should already be 1 - * via local learning. - */ - monotime_since(&nbr->detect_start_time, &elapsed); - reset_params = (elapsed.tv_sec > zvrf->dad_time); - - if (is_local && !reset_params) { - /* RFC-7432: A PE/VTEP that detects a MAC mobility - * event via LOCAL learning starts an M-second timer. - * - * NOTE: This is the START of the probe with count is - * 0 during LOCAL learn event. - */ - reset_params = !nbr->dad_count; - } - - if (reset_params) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u", - __func__, - prefix_mac2str(&nbr->emac, buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - nbr->flags, nbr->dad_count); - /* Reset learn count but do not start detection - * during REMOTE learn event. - */ - nbr->dad_count = 0; - /* Start dup. addr detection (DAD) start time, - * ONLY during LOCAL learn. - */ - if (is_local) - monotime(&nbr->detect_start_time); - - } else if (!is_local) { - /* For REMOTE IP/Neigh, increment detection count - * ONLY while in probe window, once window passed, - * next local learn event should trigger DAD. - */ - nbr->dad_count++; - } - - /* For LOCAL IP/Neigh learn event, once count is reset above via either - * initial/start detection time or passed the probe time, the count - * needs to be incremented. - */ - if (is_local) - nbr->dad_count++; - - if (nbr->dad_count >= zvrf->dad_max_moves) { - flog_warn(EC_ZEBRA_DUP_IP_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %s", - nbr->zvni->vni, - prefix_mac2str(&nbr->emac, buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - is_local ? "local update, last" : - "remote update, from", - inet_ntoa(vtep_ip)); - - SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - - /* Capture Duplicate detection time */ - nbr->dad_dup_detect_time = monotime(NULL); - - /* Start auto recovery timer for this IP */ - THREAD_OFF(nbr->dad_ip_auto_recovery_timer); - if (zvrf->dad_freeze && zvrf->dad_freeze_time) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start", - __func__, - prefix_mac2str(&nbr->emac, buf, - sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, - sizeof(buf1)), - nbr->flags, zvrf->dad_freeze_time); - - thread_add_timer(zrouter.master, - zebra_vxlan_dad_ip_auto_recovery_exp, - nbr, zvrf->dad_freeze_time, - &nbr->dad_ip_auto_recovery_timer); - } - if (zvrf->dad_freeze) - *is_dup_detect = true; - } -} - -/* - * Helper function to determine maximum width of neighbor IP address for - * display - just because we're dealing with IPv6 addresses that can - * widely vary. - */ -static void zvni_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt) -{ - zebra_neigh_t *n; - char buf[INET6_ADDRSTRLEN]; - struct neigh_walk_ctx *wctx = ctxt; - int width; - - n = (zebra_neigh_t *)bucket->data; - - ipaddr2str(&n->ip, buf, sizeof(buf)); - width = strlen(buf); - if (width > wctx->addr_width) - wctx->addr_width = width; - -} - -/* - * Print a specific neighbor entry. - */ -static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) -{ - struct vty *vty; - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - const char *type_str; - const char *state_str; - bool flags_present = false; - struct zebra_vrf *zvrf = NULL; - struct timeval detect_start_time = {0, 0}; - char timebuf[MONOTIME_STRLEN]; - char thread_buf[THREAD_TIMER_STRLEN]; - - zvrf = zebra_vrf_get_evpn(); - if (!zvrf) - return; - - ipaddr2str(&n->ip, buf2, sizeof(buf2)); - prefix_mac2str(&n->emac, buf1, sizeof(buf1)); - type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ? - "local" : "remote"; - state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; - vty = (struct vty *)ctxt; - if (json == NULL) { - bool sync_info = false; - - vty_out(vty, "IP: %s\n", - ipaddr2str(&n->ip, buf2, sizeof(buf2))); - vty_out(vty, " Type: %s\n", type_str); - vty_out(vty, " State: %s\n", state_str); - vty_out(vty, " MAC: %s\n", - prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - vty_out(vty, " Sync-info:"); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) { - vty_out(vty, " local-inactive"); - sync_info = true; - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) { - vty_out(vty, " peer-proxy"); - sync_info = true; - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) { - vty_out(vty, " peer-active"); - sync_info = true; - } - if (n->hold_timer) { - vty_out(vty, " (ht: %s)", - thread_timer_to_hhmmss( - thread_buf, - sizeof(thread_buf), - n->hold_timer)); - sync_info = true; - } - if (!sync_info) - vty_out(vty, " -"); - vty_out(vty, "\n"); - } else { - json_object_string_add(json, "ip", buf2); - json_object_string_add(json, "type", type_str); - json_object_string_add(json, "state", state_str); - json_object_string_add(json, "mac", buf1); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) - json_object_boolean_true_add(json, - "localInactive"); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) - json_object_boolean_true_add(json, - "peerProxy"); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) - json_object_boolean_true_add(json, - "peerActive"); - if (n->hold_timer) - json_object_string_add(json, "peerActiveHold", - thread_timer_to_hhmmss( - thread_buf, - sizeof(thread_buf), - n->hold_timer)); - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - if (n->mac->es) { - if (json) - json_object_string_add(json, "remoteEs", - n->mac->es->esi_str); - else - vty_out(vty, " Remote ES: %s\n", - n->mac->es->esi_str); - } else { - if (json) - json_object_string_add(json, "remoteVtep", - inet_ntoa(n->r_vtep_ip)); - else - vty_out(vty, " Remote VTEP: %s\n", - inet_ntoa(n->r_vtep_ip)); - } - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) { - if (!json) { - vty_out(vty, " Flags: Default-gateway"); - flags_present = true; - } else - json_object_boolean_true_add(json, "defaultGateway"); - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) { - if (!json) { - vty_out(vty, - flags_present ? " ,Router" : " Flags: Router"); - flags_present = true; - } - } - if (json == NULL) { - if (flags_present) - vty_out(vty, "\n"); - vty_out(vty, " Local Seq: %u Remote Seq: %u\n", - n->loc_seq, n->rem_seq); - - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) { - vty_out(vty, " Duplicate, detected at %s", - time_to_string(n->dad_dup_detect_time, - timebuf)); - } else if (n->dad_count) { - monotime_since(&n->detect_start_time, - &detect_start_time); - if (detect_start_time.tv_sec <= zvrf->dad_time) { - time_to_string(n->detect_start_time.tv_sec, - timebuf); - vty_out(vty, - " Duplicate detection started at %s, detection count %u\n", - timebuf, n->dad_count); - } - } - } else { - json_object_int_add(json, "localSequence", n->loc_seq); - json_object_int_add(json, "remoteSequence", n->rem_seq); - json_object_int_add(json, "detectionCount", - n->dad_count); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - json_object_boolean_true_add(json, "isDuplicate"); - else - json_object_boolean_false_add(json, "isDuplicate"); - - - } -} - -static void zvni_print_neigh_hdr(struct vty *vty, - struct neigh_walk_ctx *wctx) -{ - vty_out(vty, - "Flags: I=local-inactive, P=peer-active, X=peer-proxy\n"); - vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %s\n", - -wctx->addr_width, "Neighbor", "Type", "Flags", - "State", "MAC", "Remote ES/VTEP", "Seq #'s"); -} - -static char *zvni_print_neigh_flags(zebra_neigh_t *n, char *flags_buf, - uint32_t flags_buf_sz) -{ - snprintf(flags_buf, flags_buf_sz, "%s%s%s", - (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE) ? - "P" : "", - (n->flags & ZEBRA_NEIGH_ES_PEER_PROXY) ? - "X" : "", - (n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE) ? - "I" : ""); - - return flags_buf; -} - -/* - * Print neighbor hash entry - called for display of all neighbors. - */ -static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - struct vty *vty; - json_object *json_vni = NULL, *json_row = NULL; - zebra_neigh_t *n; - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - struct neigh_walk_ctx *wctx = ctxt; - const char *state_str; - char flags_buf[6]; - - vty = wctx->vty; - json_vni = wctx->json; - n = (zebra_neigh_t *)bucket->data; - - if (json_vni) - json_row = json_object_new_object(); - - prefix_mac2str(&n->emac, buf1, sizeof(buf1)); - ipaddr2str(&n->ip, buf2, sizeof(buf2)); - state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) - return; - - if (json_vni == NULL) { - vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n", - -wctx->addr_width, buf2, "local", - zvni_print_neigh_flags(n, flags_buf, - sizeof(flags_buf)), state_str, - buf1, "", n->loc_seq, n->rem_seq); - } else { - json_object_string_add(json_row, "type", "local"); - json_object_string_add(json_row, "state", state_str); - json_object_string_add(json_row, "mac", buf1); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) - json_object_boolean_true_add( - json_row, "defaultGateway"); - json_object_int_add(json_row, "localSequence", - n->loc_seq); - json_object_int_add(json_row, "remoteSequence", - n->rem_seq); - json_object_int_add(json_row, "detectionCount", - n->dad_count); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - json_object_boolean_true_add(json_row, - "isDuplicate"); - else - json_object_boolean_false_add(json_row, - "isDuplicate"); - } - wctx->count++; - } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) && - !IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) - return; - - if (json_vni == NULL) { - if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) && - (wctx->count == 0)) - zvni_print_neigh_hdr(vty, wctx); - vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n", - -wctx->addr_width, buf2, "remote", - zvni_print_neigh_flags(n, flags_buf, - sizeof(flags_buf)), - state_str, buf1, - n->mac->es ? n->mac->es->esi_str : - inet_ntoa(n->r_vtep_ip), - n->loc_seq, n->rem_seq); - } else { - json_object_string_add(json_row, "type", "remote"); - json_object_string_add(json_row, "state", state_str); - json_object_string_add(json_row, "mac", buf1); - if (n->mac->es) - json_object_string_add(json_row, "remoteEs", - n->mac->es->esi_str); - else - json_object_string_add(json_row, "remoteVtep", - inet_ntoa(n->r_vtep_ip)); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) - json_object_boolean_true_add(json_row, - "defaultGateway"); - json_object_int_add(json_row, "localSequence", - n->loc_seq); - json_object_int_add(json_row, "remoteSequence", - n->rem_seq); - json_object_int_add(json_row, "detectionCount", - n->dad_count); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - json_object_boolean_true_add(json_row, - "isDuplicate"); - else - json_object_boolean_false_add(json_row, - "isDuplicate"); - } - wctx->count++; - } - - if (json_vni) - json_object_object_add(json_vni, buf2, json_row); -} - -/* - * Print neighbor hash entry in detail - called for display of all neighbors. - */ -static void zvni_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt) -{ - struct vty *vty; - json_object *json_vni = NULL, *json_row = NULL; - zebra_neigh_t *n; - char buf[INET6_ADDRSTRLEN]; - struct neigh_walk_ctx *wctx = ctxt; - - vty = wctx->vty; - json_vni = wctx->json; - n = (zebra_neigh_t *)bucket->data; - if (!n) - return; - - ipaddr2str(&n->ip, buf, sizeof(buf)); - if (json_vni) - json_row = json_object_new_object(); - - zvni_print_neigh(n, vty, json_row); - - if (json_vni) - json_object_object_add(json_vni, buf, json_row); -} - -/* - * Print neighbors for all VNI. - */ -static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, +static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket, void **args) { struct vty *vty; - json_object *json = NULL, *json_vni = NULL; - zebra_vni_t *zvni; + json_object *json = NULL, *json_evpn = NULL; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; char vni_str[VNI_STR_LEN]; @@ -1057,26 +181,26 @@ static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, json = (json_object *)args[1]; print_dup = (uint32_t)(uintptr_t)args[2]; - zvni = (zebra_vni_t *)bucket->data; + zevpn = (zebra_evpn_t *)bucket->data; - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (print_dup) - num_neigh = num_dup_detected_neighs(zvni); + num_neigh = num_dup_detected_neighs(zevpn); if (json == NULL) { vty_out(vty, "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", - zvni->vni, num_neigh); + zevpn->vni, num_neigh); } else { - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "numArpNd", num_neigh); - snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni); + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "numArpNd", num_neigh); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); } if (!num_neigh) { if (json) - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); return; } @@ -1085,59 +209,36 @@ static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, * the maximum width. */ memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; - wctx.json = json_vni; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + wctx.json = json_evpn; + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); if (json == NULL) - zvni_print_neigh_hdr(vty, &wctx); + zebra_evpn_print_neigh_hdr(vty, &wctx); if (print_dup) - hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, - &wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_print_dad_neigh_hash, &wctx); else - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, + &wctx); if (json) - json_object_object_add(json, vni_str, json_vni); -} - -static void zvni_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_neigh_t *nbr; - - nbr = (zebra_neigh_t *)bucket->data; - if (!nbr) - return; - - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - zvni_print_neigh_hash(bucket, ctxt); -} - -static void zvni_print_dad_neigh_hash_detail(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_neigh_t *nbr; - - nbr = (zebra_neigh_t *)bucket->data; - if (!nbr) - return; - - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - zvni_print_neigh_hash_detail(bucket, ctxt); + json_object_object_add(json, vni_str, json_evpn); } /* - * Print neighbors for all VNIs in detail. + * Print neighbors for all EVPNs in detail. */ -static void zvni_print_neigh_hash_all_vni_detail(struct hash_bucket *bucket, +static void zevpn_print_neigh_hash_all_evpn_detail(struct hash_bucket *bucket, void **args) { struct vty *vty; - json_object *json = NULL, *json_vni = NULL; - zebra_vni_t *zvni; + json_object *json = NULL, *json_evpn = NULL; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; char vni_str[VNI_STR_LEN]; @@ -1147,47 +248,47 @@ static void zvni_print_neigh_hash_all_vni_detail(struct hash_bucket *bucket, json = (json_object *)args[1]; print_dup = (uint32_t)(uintptr_t)args[2]; - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) { + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) { if (json) vty_out(vty, "{}\n"); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); - if (print_dup && num_dup_detected_neighs(zvni) == 0) + if (print_dup && num_dup_detected_neighs(zevpn) == 0) return; if (json == NULL) { vty_out(vty, "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", - zvni->vni, num_neigh); + zevpn->vni, num_neigh); } else { - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "numArpNd", num_neigh); - snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni); + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "numArpNd", num_neigh); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); } if (!num_neigh) { if (json) - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); return; } memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; - wctx.json = json_vni; + wctx.json = json_evpn; if (print_dup) - hash_iterate(zvni->neigh_table, - zvni_print_dad_neigh_hash_detail, &wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_print_dad_neigh_hash_detail, &wctx); else - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash_detail, - &wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_print_neigh_hash_detail, &wctx); if (json) - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); } /* print a specific next hop for an l3vni */ @@ -1266,442 +367,15 @@ static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, } } -static void -zebra_vxlan_mac_get_access_info(zebra_mac_t *mac, - struct interface **ifpP, vlanid_t *vid) -{ - /* if the mac is associated with an ES we must get the access - * info from the ES - */ - if (mac->es) { - struct zebra_if *zif; - - /* get the access port from the es */ - *ifpP = mac->es->zif ? mac->es->zif->ifp : NULL; - /* get the vlan from the VNI */ - if (mac->zvni->vxlan_if) { - zif = mac->zvni->vxlan_if->info; - *vid = zif->l2info.vxl.access_vlan; - } else { - *vid = 0; - } - } else { - struct zebra_ns *zns; - - *vid = mac->fwd_info.local.vid; - zns = zebra_ns_lookup(NS_DEFAULT); - *ifpP = if_lookup_by_index_per_ns(zns, - mac->fwd_info.local.ifindex); - } -} - -/* - * Print a specific MAC entry. - */ -static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) -{ - struct vty *vty; - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - struct zebra_vrf *zvrf; - struct timeval detect_start_time = {0, 0}; - char timebuf[MONOTIME_STRLEN]; - char thread_buf[THREAD_TIMER_STRLEN]; - - zvrf = zebra_vrf_get_evpn(); - if (!zvrf) - return; - - vty = (struct vty *)ctxt; - prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); - - if (json) { - json_object *json_mac = json_object_new_object(); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - struct interface *ifp; - vlanid_t vid; - - zebra_vxlan_mac_get_access_info(mac, - &ifp, &vid); - json_object_string_add(json_mac, "type", "local"); - if (ifp) { - json_object_string_add(json_mac, - "intf", ifp->name); - json_object_int_add(json_mac, - "ifindex", ifp->ifindex); - } - if (vid) - json_object_int_add(json_mac, "vlan", - vid); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - json_object_string_add(json_mac, "type", "remote"); - json_object_string_add( - json_mac, "remoteVtep", - inet_ntoa(mac->fwd_info.r_vtep_ip)); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) - json_object_string_add(json_mac, "type", "auto"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) - json_object_boolean_true_add(json_mac, "stickyMac"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) - json_object_boolean_true_add(json_mac, - "defaultGateway"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) - json_object_boolean_true_add(json_mac, - "remoteGatewayMac"); - - json_object_int_add(json_mac, "localSequence", mac->loc_seq); - json_object_int_add(json_mac, "remoteSequence", mac->rem_seq); - - json_object_int_add(json_mac, "detectionCount", mac->dad_count); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - json_object_boolean_true_add(json_mac, "isDuplicate"); - else - json_object_boolean_false_add(json_mac, "isDuplicate"); - - json_object_int_add(json_mac, "syncNeighCount", mac->sync_neigh_cnt); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) - json_object_boolean_true_add(json_mac, - "localInactive"); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY)) - json_object_boolean_true_add(json_mac, - "peerProxy"); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) - json_object_boolean_true_add(json_mac, - "peerActive"); - if (mac->hold_timer) - json_object_string_add(json_mac, "peerActiveHold", - thread_timer_to_hhmmss( - thread_buf, - sizeof(thread_buf), - mac->hold_timer)); - if (mac->es) - json_object_string_add(json_mac, "esi", - mac->es->esi_str); - /* print all the associated neigh */ - if (!listcount(mac->neigh_list)) - json_object_string_add(json_mac, "neighbors", "none"); - else { - json_object *json_active_nbrs = json_object_new_array(); - json_object *json_inactive_nbrs = - json_object_new_array(); - json_object *json_nbrs = json_object_new_object(); - - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { - if (IS_ZEBRA_NEIGH_ACTIVE(n)) - json_object_array_add( - json_active_nbrs, - json_object_new_string( - ipaddr2str( - &n->ip, buf2, - sizeof(buf2)))); - else - json_object_array_add( - json_inactive_nbrs, - json_object_new_string( - ipaddr2str( - &n->ip, buf2, - sizeof(buf2)))); - } - - json_object_object_add(json_nbrs, "active", - json_active_nbrs); - json_object_object_add(json_nbrs, "inactive", - json_inactive_nbrs); - json_object_object_add(json_mac, "neighbors", - json_nbrs); - } - - json_object_object_add(json, buf1, json_mac); - } else { - vty_out(vty, "MAC: %s\n", buf1); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - struct interface *ifp; - vlanid_t vid; - - zebra_vxlan_mac_get_access_info(mac, - &ifp, &vid); - - if (mac->es) - vty_out(vty, " ESI: %s\n", mac->es->esi_str); - - if (ifp) - vty_out(vty, " Intf: %s(%u)", - ifp->name, ifp->ifindex); - else - vty_out(vty, " Intf: -"); - vty_out(vty, " VLAN: %u", vid); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - if (mac->es) - vty_out(vty, " Remote ES: %s", - mac->es->esi_str); - else - vty_out(vty, " Remote VTEP: %s", - inet_ntoa(mac->fwd_info.r_vtep_ip)); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { - vty_out(vty, " Auto Mac "); - } - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) - vty_out(vty, " Sticky Mac "); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) - vty_out(vty, " Default-gateway Mac "); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) - vty_out(vty, " Remote-gateway Mac "); - - vty_out(vty, "\n"); - vty_out(vty, " Sync-info: neigh#: %u", mac->sync_neigh_cnt); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) - vty_out(vty, " local-inactive"); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY)) - vty_out(vty, " peer-proxy"); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) - vty_out(vty, " peer-active"); - if (mac->hold_timer) - vty_out(vty, " (ht: %s)", - thread_timer_to_hhmmss( - thread_buf, - sizeof(thread_buf), - mac->hold_timer)); - vty_out(vty, "\n"); - vty_out(vty, " Local Seq: %u Remote Seq: %u", - mac->loc_seq, mac->rem_seq); - vty_out(vty, "\n"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { - vty_out(vty, " Duplicate, detected at %s", - time_to_string(mac->dad_dup_detect_time, - timebuf)); - } else if (mac->dad_count) { - monotime_since(&mac->detect_start_time, - &detect_start_time); - if (detect_start_time.tv_sec <= zvrf->dad_time) { - time_to_string(mac->detect_start_time.tv_sec, - timebuf); - vty_out(vty, - " Duplicate detection started at %s, detection count %u\n", - timebuf, mac->dad_count); - } - } - - /* print all the associated neigh */ - vty_out(vty, " Neighbors:\n"); - if (!listcount(mac->neigh_list)) - vty_out(vty, " No Neighbors\n"); - else { - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { - vty_out(vty, " %s %s\n", - ipaddr2str(&n->ip, buf2, sizeof(buf2)), - (IS_ZEBRA_NEIGH_ACTIVE(n) - ? "Active" - : "Inactive")); - } - } - - vty_out(vty, "\n"); - } -} - -static char *zvni_print_mac_flags(zebra_mac_t *mac, char *flags_buf, - uint32_t flags_buf_sz) -{ - snprintf(flags_buf, flags_buf_sz, "%s%s%s%s", - mac->sync_neigh_cnt ? - "N" : "", - (mac->flags & ZEBRA_MAC_ES_PEER_ACTIVE) ? - "P" : "", - (mac->flags & ZEBRA_MAC_ES_PEER_PROXY) ? - "X" : "", - (mac->flags & ZEBRA_MAC_LOCAL_INACTIVE) ? - "I" : ""); - - return flags_buf; -} - /* - * Print MAC hash entry - called for display of all MACs. + * Print MACs for all EVPNs. */ -static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt) +static void zevpn_print_mac_hash_all_evpn(struct hash_bucket *bucket, void *ctxt) { struct vty *vty; - json_object *json_mac_hdr = NULL, *json_mac = NULL; - zebra_mac_t *mac; - char buf1[ETHER_ADDR_STRLEN]; - struct mac_walk_ctx *wctx = ctxt; - char flags_buf[6]; - - vty = wctx->vty; - json_mac_hdr = wctx->json; - mac = (zebra_mac_t *)bucket->data; - - prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); - - if (json_mac_hdr) - json_mac = json_object_new_object(); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - struct interface *ifp; - vlanid_t vid; - - if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) - return; - - zebra_vxlan_mac_get_access_info(mac, - &ifp, &vid); - if (json_mac_hdr == NULL) { - vty_out(vty, "%-17s %-6s %-5s %-30s", buf1, "local", - zvni_print_mac_flags(mac, flags_buf, - sizeof(flags_buf)), - ifp ? ifp->name : "-"); - } else { - json_object_string_add(json_mac, "type", "local"); - if (ifp) - json_object_string_add(json_mac, - "intf", ifp->name); - } - if (vid) { - if (json_mac_hdr == NULL) - vty_out(vty, " %-5u", vid); - else - json_object_int_add(json_mac, "vlan", vid); - } else /* No vid? fill out the space */ - if (json_mac_hdr == NULL) - vty_out(vty, " %-5s", ""); - if (json_mac_hdr == NULL) { - vty_out(vty, " %u/%u", mac->loc_seq, mac->rem_seq); - vty_out(vty, "\n"); - } else { - json_object_int_add(json_mac, "localSequence", - mac->loc_seq); - json_object_int_add(json_mac, "remoteSequence", - mac->rem_seq); - json_object_int_add(json_mac, "detectionCount", - mac->dad_count); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - json_object_boolean_true_add(json_mac, - "isDuplicate"); - else - json_object_boolean_false_add(json_mac, - "isDuplicate"); - json_object_object_add(json_mac_hdr, buf1, json_mac); - } - - wctx->count++; - - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - - if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) && - !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, - &wctx->r_vtep_ip)) - return; - - if (json_mac_hdr == NULL) { - if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) && - (wctx->count == 0)) { - vty_out(vty, "\nVNI %u\n\n", wctx->zvni->vni); - vty_out(vty, "%-17s %-6s %-5s%-30s %-5s %s\n", - "MAC", "Type", "Flags", - "Intf/Remote ES/VTEP", - "VLAN", "Seq #'s"); - } - vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %u/%u\n", buf1, - "remote", - zvni_print_mac_flags(mac, flags_buf, - sizeof(flags_buf)), - mac->es ? mac->es->esi_str : - inet_ntoa(mac->fwd_info.r_vtep_ip), - "", mac->loc_seq, mac->rem_seq); - } else { - json_object_string_add(json_mac, "type", "remote"); - json_object_string_add(json_mac, "remoteVtep", - inet_ntoa(mac->fwd_info.r_vtep_ip)); - json_object_object_add(json_mac_hdr, buf1, json_mac); - json_object_int_add(json_mac, "localSequence", - mac->loc_seq); - json_object_int_add(json_mac, "remoteSequence", - mac->rem_seq); - json_object_int_add(json_mac, "detectionCount", - mac->dad_count); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - json_object_boolean_true_add(json_mac, - "isDuplicate"); - else - json_object_boolean_false_add(json_mac, - "isDuplicate"); - - } - - wctx->count++; - } -} - -/* Print Duplicate MAC */ -static void zvni_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_mac_t *mac; - - mac = (zebra_mac_t *)bucket->data; - if (!mac) - return; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - zvni_print_mac_hash(bucket, ctxt); -} - -/* - * Print MAC hash entry in detail - called for display of all MACs. - */ -static void zvni_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt) -{ - struct vty *vty; - json_object *json_mac_hdr = NULL; - zebra_mac_t *mac; - struct mac_walk_ctx *wctx = ctxt; - char buf1[ETHER_ADDR_STRLEN]; - - vty = wctx->vty; - json_mac_hdr = wctx->json; - mac = (zebra_mac_t *)bucket->data; - if (!mac) - return; - - wctx->count++; - prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); - - zvni_print_mac(mac, vty, json_mac_hdr); -} - -/* Print Duplicate MAC in detail */ -static void zvni_print_dad_mac_hash_detail(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_mac_t *mac; - - mac = (zebra_mac_t *)bucket->data; - if (!mac) - return; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - zvni_print_mac_hash_detail(bucket, ctxt); -} - -/* - * Print MACs for all VNI. - */ -static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt) -{ - struct vty *vty; - json_object *json = NULL, *json_vni = NULL; + json_object *json = NULL, *json_evpn = NULL; json_object *json_mac = NULL; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_macs; struct mac_walk_ctx *wctx = ctxt; char vni_str[VNI_STR_LEN]; @@ -1709,73 +383,74 @@ static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt) vty = wctx->vty; json = wctx->json; - zvni = (zebra_vni_t *)bucket->data; - wctx->zvni = zvni; + zevpn = (zebra_evpn_t *)bucket->data; + wctx->zevpn = zevpn; /*We are iterating over a new VNI, set the count to 0*/ wctx->count = 0; - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; if (wctx->print_dup) - num_macs = num_dup_detected_macs(zvni); + num_macs = num_dup_detected_macs(zevpn); if (json) { - json_vni = json_object_new_object(); + json_evpn = json_object_new_object(); json_mac = json_object_new_object(); - snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); } if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { if (json == NULL) { vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", - zvni->vni, num_macs); + zevpn->vni, num_macs); vty_out(vty, "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n"); vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC", "Type", "Flags", "Intf/Remote ES/VTEP", "VLAN", "Seq #'s"); } else - json_object_int_add(json_vni, "numMacs", num_macs); + json_object_int_add(json_evpn, "numMacs", num_macs); } if (!num_macs) { if (json) { - json_object_int_add(json_vni, "numMacs", num_macs); - json_object_object_add(json, vni_str, json_vni); + json_object_int_add(json_evpn, "numMacs", num_macs); + json_object_object_add(json, vni_str, json_evpn); } return; } - /* assign per-vni to wctx->json object to fill macs - * under the vni. Re-assign primary json object to fill - * next vni information. + /* assign per-evpn to wctx->json object to fill macs + * under the evpn. Re-assign primary json object to fill + * next evpn information. */ wctx->json = json_mac; if (wctx->print_dup) - hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_dad_mac_hash, + wctx); else - hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, wctx); wctx->json = json; if (json) { if (wctx->count) - json_object_object_add(json_vni, "macs", json_mac); - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json_evpn, "macs", json_mac); + json_object_object_add(json, vni_str, json_evpn); } } /* - * Print MACs in detail for all VNI. + * Print MACs in detail for all EVPNs. */ -static void zvni_print_mac_hash_all_vni_detail(struct hash_bucket *bucket, +static void zevpn_print_mac_hash_all_evpn_detail(struct hash_bucket *bucket, void *ctxt) { struct vty *vty; - json_object *json = NULL, *json_vni = NULL; + json_object *json = NULL, *json_evpn = NULL; json_object *json_mac = NULL; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_macs; struct mac_walk_ctx *wctx = ctxt; char vni_str[VNI_STR_LEN]; @@ -1783,52 +458,53 @@ static void zvni_print_mac_hash_all_vni_detail(struct hash_bucket *bucket, vty = wctx->vty; json = wctx->json; - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) { + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) { if (json) vty_out(vty, "{}\n"); return; } - wctx->zvni = zvni; + wctx->zevpn = zevpn; - /*We are iterating over a new VNI, set the count to 0*/ + /*We are iterating over a new EVPN, set the count to 0*/ wctx->count = 0; - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; - if (wctx->print_dup && (num_dup_detected_macs(zvni) == 0)) + if (wctx->print_dup && (num_dup_detected_macs(zevpn) == 0)) return; if (json) { - json_vni = json_object_new_object(); + json_evpn = json_object_new_object(); json_mac = json_object_new_object(); - snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); } if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { if (json == NULL) { vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", - zvni->vni, num_macs); + zevpn->vni, num_macs); } else - json_object_int_add(json_vni, "numMacs", num_macs); + json_object_int_add(json_evpn, "numMacs", num_macs); } - /* assign per-vni to wctx->json object to fill macs - * under the vni. Re-assign primary json object to fill - * next vni information. + /* assign per-evpn to wctx->json object to fill macs + * under the evpn. Re-assign primary json object to fill + * next evpn information. */ wctx->json = json_mac; if (wctx->print_dup) - hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash_detail, - wctx); + hash_iterate(zevpn->mac_table, + zebra_evpn_print_dad_mac_hash_detail, wctx); else - hash_iterate(zvni->mac_table, zvni_print_mac_hash_detail, wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash_detail, + wctx); wctx->json = json; if (json) { if (wctx->count) - json_object_object_add(json_vni, "macs", json_mac); - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json_evpn, "macs", json_mac); + json_object_object_add(json, vni_str, json_evpn); } } @@ -1836,7 +512,7 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx) { struct nh_walk_ctx *wctx = NULL; struct vty *vty = NULL; - struct json_object *json_vni = NULL; + struct json_object *json_evpn = NULL; struct json_object *json_nh = NULL; zebra_neigh_t *n = NULL; char buf1[ETHER_ADDR_STRLEN]; @@ -1844,12 +520,12 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx) wctx = (struct nh_walk_ctx *)ctx; vty = wctx->vty; - json_vni = wctx->json; - if (json_vni) + json_evpn = wctx->json; + if (json_evpn) json_nh = json_object_new_object(); n = (zebra_neigh_t *)bucket->data; - if (!json_vni) { + if (!json_evpn) { vty_out(vty, "%-15s %-17s\n", ipaddr2str(&(n->ip), buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf1, sizeof(buf1))); @@ -1859,7 +535,7 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx) json_object_string_add( json_nh, "routerMac", prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - json_object_object_add(json_vni, + json_object_object_add(json_evpn, ipaddr2str(&(n->ip), buf2, sizeof(buf2)), json_nh); } @@ -1870,7 +546,7 @@ static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket, { struct vty *vty = NULL; json_object *json = NULL; - json_object *json_vni = NULL; + json_object *json_evpn = NULL; zebra_l3vni_t *zl3vni = NULL; uint32_t num_nh = 0; struct nh_walk_ctx wctx; @@ -1886,22 +562,22 @@ static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket, return; if (json) { - json_vni = json_object_new_object(); - snprintf(vni_str, sizeof(vni_str), "%u", zl3vni->vni); + json_evpn = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); } if (json == NULL) { vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", zl3vni->vni, num_nh); vty_out(vty, "%-15s %-17s\n", "IP", "RMAC"); } else - json_object_int_add(json_vni, "numNextHops", num_nh); + json_object_int_add(json_evpn, "numNextHops", num_nh); memset(&wctx, 0, sizeof(struct nh_walk_ctx)); wctx.vty = vty; - wctx.json = json_vni; + wctx.json = json_evpn; hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); if (json) - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); } static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, @@ -1909,7 +585,7 @@ static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, { struct vty *vty = NULL; json_object *json = NULL; - json_object *json_vni = NULL; + json_object *json_evpn = NULL; zebra_l3vni_t *zl3vni = NULL; uint32_t num_rmacs; struct rmac_walk_ctx wctx; @@ -1925,15 +601,15 @@ static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, return; if (json) { - json_vni = json_object_new_object(); - snprintf(vni_str, sizeof(vni_str), "%u", zl3vni->vni); + json_evpn = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); } if (json == NULL) { vty_out(vty, "\nVNI %u #RMACs %u\n\n", zl3vni->vni, num_rmacs); vty_out(vty, "%-17s %-21s\n", "RMAC", "Remote VTEP"); } else - json_object_int_add(json_vni, "numRmacs", num_rmacs); + json_object_int_add(json_evpn, "numRmacs", num_rmacs); /* assign per-vni to wctx->json object to fill macs * under the vni. Re-assign primary json object to fill @@ -1941,10 +617,10 @@ static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, */ memset(&wctx, 0, sizeof(struct rmac_walk_ctx)); wctx.vty = vty; - wctx.json = json_vni; + wctx.json = json_evpn; hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx); if (json) - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); } static void zl3vni_print_rmac_hash(struct hash_bucket *bucket, void *ctx) @@ -1985,8 +661,8 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) char buf[ETHER_ADDR_STRLEN]; struct vty *vty = NULL; json_object *json = NULL; - zebra_vni_t *zvni = NULL; - json_object *json_vni_list = NULL; + zebra_evpn_t *zevpn = NULL; + json_object *json_evpn_list = NULL; struct listnode *node = NULL, *nnode = NULL; vty = ctx[0]; @@ -2011,11 +687,11 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) vty_out(vty, " Router MAC: %s\n", zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); vty_out(vty, " L2 VNIs: "); - for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) - vty_out(vty, "%u ", zvni->vni); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zevpn)) + vty_out(vty, "%u ", zevpn->vni); vty_out(vty, "\n"); } else { - json_vni_list = json_object_new_array(); + json_evpn_list = json_object_new_array(); json_object_int_add(json, "vni", zl3vni->vni); json_object_string_add(json, "type", "L3"); json_object_string_add(json, "localVtepIp", @@ -2037,105 +713,11 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? "prefix-routes-only" : "none"); - for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) { - json_object_array_add(json_vni_list, - json_object_new_int(zvni->vni)); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zevpn)) { + json_object_array_add(json_evpn_list, + json_object_new_int(zevpn->vni)); } - json_object_object_add(json, "l2Vnis", json_vni_list); - } -} - -/* - * Print a specific VNI entry. - */ -static void zvni_print(zebra_vni_t *zvni, void **ctxt) -{ - struct vty *vty; - zebra_vtep_t *zvtep; - uint32_t num_macs; - uint32_t num_neigh; - json_object *json = NULL; - json_object *json_vtep_list = NULL; - json_object *json_ip_str = NULL; - - vty = ctxt[0]; - json = ctxt[1]; - - if (json == NULL) { - vty_out(vty, "VNI: %u\n", zvni->vni); - vty_out(vty, " Type: %s\n", "L2"); - vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zvni->vrf_id)); - } else { - json_object_int_add(json, "vni", zvni->vni); - json_object_string_add(json, "type", "L2"); - json_object_string_add(json, "vrf", - vrf_id_to_name(zvni->vrf_id)); - } - - if (!zvni->vxlan_if) { // unexpected - if (json == NULL) - vty_out(vty, " VxLAN interface: unknown\n"); - return; - } - num_macs = num_valid_macs(zvni); - num_neigh = hashcount(zvni->neigh_table); - if (json == NULL) { - vty_out(vty, " VxLAN interface: %s\n", zvni->vxlan_if->name); - vty_out(vty, " VxLAN ifIndex: %u\n", zvni->vxlan_if->ifindex); - vty_out(vty, " Local VTEP IP: %s\n", - inet_ntoa(zvni->local_vtep_ip)); - vty_out(vty, " Mcast group: %s\n", - inet_ntoa(zvni->mcast_grp)); - } else { - json_object_string_add(json, "vxlanInterface", - zvni->vxlan_if->name); - json_object_int_add(json, "ifindex", zvni->vxlan_if->ifindex); - json_object_string_add(json, "vtepIp", - inet_ntoa(zvni->local_vtep_ip)); - json_object_string_add(json, "mcastGroup", - inet_ntoa(zvni->mcast_grp)); - json_object_string_add(json, "advertiseGatewayMacip", - zvni->advertise_gw_macip ? "Yes" : "No"); - json_object_int_add(json, "numMacs", num_macs); - json_object_int_add(json, "numArpNd", num_neigh); - } - if (!zvni->vteps) { - if (json == NULL) - vty_out(vty, " No remote VTEPs known for this VNI\n"); - } else { - if (json == NULL) - vty_out(vty, " Remote VTEPs for this VNI:\n"); - else - json_vtep_list = json_object_new_array(); - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - const char *flood_str = lookup_msg(zvtep_flood_str, - zvtep->flood_control, - VXLAN_FLOOD_STR_DEFAULT); - - if (json == NULL) { - vty_out(vty, " %s flood: %s\n", - inet_ntoa(zvtep->vtep_ip), - flood_str); - } else { - json_ip_str = json_object_new_string( - inet_ntoa(zvtep->vtep_ip)); - json_object_array_add(json_vtep_list, - json_ip_str); - } - } - if (json) - json_object_object_add(json, "numRemoteVteps", - json_vtep_list); - } - if (json == NULL) { - vty_out(vty, - " Number of MACs (local and remote) known for this VNI: %u\n", - num_macs); - vty_out(vty, - " Number of ARPs (IPv4 and IPv6, local and remote) known for this VNI: %u\n", - num_neigh); - vty_out(vty, " Advertise-gw-macip: %s\n", - zvni->advertise_gw_macip ? "Yes" : "No"); + json_object_object_add(json, "l2Vnis", json_evpn_list); } } @@ -2144,7 +726,7 @@ static void zl3vni_print_hash(struct hash_bucket *bucket, void *ctx[]) { struct vty *vty = NULL; json_object *json = NULL; - json_object *json_vni = NULL; + json_object *json_evpn = NULL; zebra_l3vni_t *zl3vni = NULL; vty = (struct vty *)ctx[0]; @@ -2161,31 +743,23 @@ static void zl3vni_print_hash(struct hash_bucket *bucket, void *ctx[]) } else { char vni_str[VNI_STR_LEN]; - snprintf(vni_str, sizeof(vni_str), "%u", zl3vni->vni); - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "vni", zl3vni->vni); - json_object_string_add(json_vni, "vxlanIf", + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "vni", zl3vni->vni); + json_object_string_add(json_evpn, "vxlanIf", zl3vni_vxlan_if_name(zl3vni)); - json_object_int_add(json_vni, "numMacs", + json_object_int_add(json_evpn, "numMacs", hashcount(zl3vni->rmac_table)); - json_object_int_add(json_vni, "numArpNd", + json_object_int_add(json_evpn, "numArpNd", hashcount(zl3vni->nh_table)); - json_object_string_add(json_vni, "numRemoteVteps", "n/a"); - json_object_string_add(json_vni, "type", "L3"); - json_object_string_add(json_vni, "tenantVrf", + json_object_string_add(json_evpn, "numRemoteVteps", "n/a"); + json_object_string_add(json_evpn, "type", "L3"); + json_object_string_add(json_evpn, "tenantVrf", zl3vni_vrf_name(zl3vni)); - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); } } -/* Private Structure to pass callback data for hash iterator */ -struct zvni_evpn_show { - struct vty *vty; - json_object *json; - struct zebra_vrf *zvrf; - bool use_json; -}; - /* print a L3 VNI hash entry in detail*/ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) { @@ -2193,7 +767,7 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) zebra_l3vni_t *zl3vni = NULL; json_object *json_array = NULL; bool use_json = false; - struct zvni_evpn_show *zes = data; + struct zebra_evpn_show *zes = data; vty = zes->vty; json_array = zes->json; @@ -2208,1833 +782,6 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) vty_out(vty, "\n"); } - -/* - * Print a VNI hash entry - called for display of all VNIs. - */ -static void zvni_print_hash(struct hash_bucket *bucket, void *ctxt[]) -{ - struct vty *vty; - zebra_vni_t *zvni; - zebra_vtep_t *zvtep; - uint32_t num_vteps = 0; - uint32_t num_macs = 0; - uint32_t num_neigh = 0; - json_object *json = NULL; - json_object *json_vni = NULL; - json_object *json_ip_str = NULL; - json_object *json_vtep_list = NULL; - - vty = ctxt[0]; - json = ctxt[1]; - - zvni = (zebra_vni_t *)bucket->data; - - zvtep = zvni->vteps; - while (zvtep) { - num_vteps++; - zvtep = zvtep->next; - } - - num_macs = num_valid_macs(zvni); - num_neigh = hashcount(zvni->neigh_table); - if (json == NULL) - vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n", - zvni->vni, "L2", - zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", - num_macs, num_neigh, num_vteps, - vrf_id_to_name(zvni->vrf_id)); - else { - char vni_str[VNI_STR_LEN]; - snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni); - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "vni", zvni->vni); - json_object_string_add(json_vni, "type", "L2"); - json_object_string_add(json_vni, "vxlanIf", - zvni->vxlan_if ? zvni->vxlan_if->name - : "unknown"); - json_object_int_add(json_vni, "numMacs", num_macs); - json_object_int_add(json_vni, "numArpNd", num_neigh); - json_object_int_add(json_vni, "numRemoteVteps", num_vteps); - json_object_string_add(json_vni, "tenantVrf", - vrf_id_to_name(zvni->vrf_id)); - if (num_vteps) { - json_vtep_list = json_object_new_array(); - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - json_ip_str = json_object_new_string( - inet_ntoa(zvtep->vtep_ip)); - json_object_array_add(json_vtep_list, - json_ip_str); - } - json_object_object_add(json_vni, "remoteVteps", - json_vtep_list); - } - json_object_object_add(json, vni_str, json_vni); - } -} - -/* - * Print a VNI hash entry in detail - called for display of all VNIs. - */ -static void zvni_print_hash_detail(struct hash_bucket *bucket, void *data) -{ - struct vty *vty; - zebra_vni_t *zvni; - json_object *json_array = NULL; - bool use_json = false; - struct zvni_evpn_show *zes = data; - - vty = zes->vty; - json_array = zes->json; - use_json = zes->use_json; - - zvni = (zebra_vni_t *)bucket->data; - - zebra_vxlan_print_vni(vty, zes->zvrf, zvni->vni, use_json, json_array); - - if (!use_json) - vty_out(vty, "\n"); -} - -/* - * Inform BGP about local MACIP. - */ -static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, - struct ipaddr *ip, uint8_t flags, - uint32_t seq, int state, - struct zebra_evpn_es *es, - uint16_t cmd) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - int ipa_len; - struct zserv *client = NULL; - struct stream *s = NULL; - esi_t *esi = es ? &es->esi : zero_esi; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, cmd, zebra_vrf_get_evpn_id()); - stream_putl(s, vni); - stream_put(s, macaddr->octet, ETH_ALEN); - if (ip) { - ipa_len = 0; - if (IS_IPADDR_V4(ip)) - ipa_len = IPV4_MAX_BYTELEN; - else if (IS_IPADDR_V6(ip)) - ipa_len = IPV6_MAX_BYTELEN; - - stream_putl(s, ipa_len); /* IP address length */ - if (ipa_len) - stream_put(s, &ip->ip.addr, ipa_len); /* IP address */ - } else - stream_putl(s, 0); /* Just MAC. */ - - if (cmd == ZEBRA_MACIP_ADD) { - stream_putc(s, flags); /* sticky mac/gateway mac */ - stream_putl(s, seq); /* sequence number */ - stream_put(s, esi, sizeof(esi_t)); - } else { - stream_putl(s, state); /* state - active/inactive */ - } - - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Send MACIP %s f 0x%x MAC %s IP %s seq %u L2-VNI %u ESI %s to %s", - (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni, - es ? es->esi_str : "-", - zebra_route_string(client->proto)); - - if (cmd == ZEBRA_MACIP_ADD) - client->macipadd_cnt++; - else - client->macipdel_cnt++; - - return zserv_send_message(client, s); -} - -/* - * Make hash key for neighbors. - */ -static unsigned int neigh_hash_keymake(const void *p) -{ - const zebra_neigh_t *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); -} - -/* - * Compare two neighbor hash structures. - */ -static bool neigh_cmp(const void *p1, const void *p2) -{ - const zebra_neigh_t *n1 = p1; - const zebra_neigh_t *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); -} - -static int neigh_list_cmp(void *p1, void *p2) -{ - const zebra_neigh_t *n1 = p1; - const zebra_neigh_t *n2 = p2; - - return memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)); -} - -/* - * Callback to allocate neighbor hash entry. - */ -static void *zvni_neigh_alloc(void *p) -{ - const zebra_neigh_t *tmp_n = p; - zebra_neigh_t *n; - - n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); - *n = *tmp_n; - - return ((void *)n); -} - -/* - * Add neighbor entry. - */ -static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, - struct ethaddr *mac, zebra_mac_t *zmac, - uint32_t n_flags) -{ - zebra_neigh_t tmp_n; - zebra_neigh_t *n = NULL; - - memset(&tmp_n, 0, sizeof(zebra_neigh_t)); - memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); - n = hash_get(zvni->neigh_table, &tmp_n, zvni_neigh_alloc); - assert(n); - - n->state = ZEBRA_NEIGH_INACTIVE; - n->zvni = zvni; - n->dad_ip_auto_recovery_timer = NULL; - n->flags = n_flags; - - if (!zmac) - zmac = zvni_mac_lookup(zvni, mac); - zebra_vxlan_local_neigh_ref_mac(n, mac, - zmac, false /* send_mac_update */); - - return n; -} - -/* - * Delete neighbor entry. - */ -static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - zebra_neigh_t *tmp_n; - - if (n->mac) - listnode_delete(n->mac->neigh_list, n); - - /* Cancel auto recovery */ - THREAD_OFF(n->dad_ip_auto_recovery_timer); - - /* Free the VNI hash entry and allocated memory. */ - tmp_n = hash_release(zvni->neigh_table, n); - XFREE(MTYPE_NEIGH, tmp_n); - - return 0; -} - -/* - * Free neighbor hash entry (callback) - */ -static void zvni_neigh_del_hash_entry(struct hash_bucket *bucket, void *arg) -{ - struct neigh_walk_ctx *wctx = arg; - zebra_neigh_t *n = bucket->data; - - if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL)) - || ((wctx->flags & DEL_REMOTE_NEIGH) - && (n->flags & ZEBRA_NEIGH_REMOTE)) - || ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP) - && (n->flags & ZEBRA_NEIGH_REMOTE) - && IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))) { - if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL)) - zvni_neigh_send_del_to_client(wctx->zvni->vni, &n->ip, - &n->emac, n->flags, n->state, - false /*force*/); - - if (wctx->uninstall) { - if (zebra_vxlan_neigh_is_static(n)) - zebra_vxlan_sync_neigh_dp_install(n, - false /* set_inactive */, - true /* force_clear_static */, - __func__); - if ((n->flags & ZEBRA_NEIGH_REMOTE)) - zvni_neigh_uninstall(wctx->zvni, n); - } - - zvni_neigh_del(wctx->zvni, n); - } - - return; -} - -/* - * Delete all neighbor entries for this VNI. - */ -static void zvni_neigh_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags) -{ - struct neigh_walk_ctx wctx; - - if (!zvni->neigh_table) - return; - - memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; - wctx.uninstall = uninstall; - wctx.upd_client = upd_client; - wctx.flags = flags; - - hash_iterate(zvni->neigh_table, zvni_neigh_del_hash_entry, &wctx); -} - -/* - * Look up neighbor hash entry. - */ -static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip) -{ - zebra_neigh_t tmp; - zebra_neigh_t *n; - - memset(&tmp, 0, sizeof(tmp)); - memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); - n = hash_lookup(zvni->neigh_table, &tmp); - - return n; -} - -/* - * Process all neighbors associated with a MAC upon the MAC being learnt - * locally or undergoing any other change (such as sequence number). - */ -static void zvni_process_neigh_on_local_mac_change(zebra_vni_t *zvni, - zebra_mac_t *zmac, bool seq_change, bool es_change) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - struct zebra_vrf *zvrf = NULL; - char buf[ETHER_ADDR_STRLEN]; - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Processing neighbors on local MAC %s %s, VNI %u", - prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), - seq_change ? "CHANGE" : "ADD", zvni->vni); - - /* Walk all neighbors and mark any inactive local neighbors as - * active and/or update sequence number upon a move, and inform BGP. - * The action for remote neighbors is TBD. - * NOTE: We can't simply uninstall remote neighbors as the kernel may - * accidentally end up deleting a just-learnt local neighbor. - */ - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change || - es_change) { - ZEBRA_NEIGH_SET_ACTIVE(n); - n->loc_seq = zmac->loc_seq; - if (!(zvrf->dup_addr_detect && - zvrf->dad_freeze && !!CHECK_FLAG(n->flags, - ZEBRA_NEIGH_DUPLICATE))) - zvni_neigh_send_add_to_client( - zvni->vni, &n->ip, &n->emac, - n->mac, n->flags, n->loc_seq); - } - } - } -} - -/* - * Process all neighbors associated with a local MAC upon the MAC being - * deleted. - */ -static void zvni_process_neigh_on_local_mac_del(zebra_vni_t *zvni, - zebra_mac_t *zmac) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - char buf[ETHER_ADDR_STRLEN]; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Processing neighbors on local MAC %s DEL, VNI %u", - prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), - zvni->vni); - - /* Walk all local neighbors and mark as inactive and inform - * BGP, if needed. - * TBD: There is currently no handling for remote neighbors. We - * don't expect them to exist, if they do, do we install the MAC - * as a remote MAC and the neighbor as remote? - */ - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (IS_ZEBRA_NEIGH_ACTIVE(n)) { - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, n->flags, - ZEBRA_NEIGH_ACTIVE, - false /*force*/); - } - } - } -} - -/* - * Process all neighbors associated with a MAC upon the MAC being remotely - * learnt. - */ -static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni, - zebra_mac_t *zmac) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - char buf[ETHER_ADDR_STRLEN]; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Processing neighbors on remote MAC %s ADD, VNI %u", - prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), - zvni->vni); - - /* Walk all local neighbors and mark as inactive and inform - * BGP, if needed. - */ - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (IS_ZEBRA_NEIGH_ACTIVE(n)) { - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, n->flags, - ZEBRA_NEIGH_ACTIVE, - false /* force */); - } - } - } -} - -/* - * Process all neighbors associated with a remote MAC upon the MAC being - * deleted. - */ -static void zvni_process_neigh_on_remote_mac_del(zebra_vni_t *zvni, - zebra_mac_t *zmac) -{ - /* NOTE: Currently a NO-OP. */ -} - -static void zvni_probe_neigh_on_mac_add(zebra_vni_t *zvni, zebra_mac_t *zmac) -{ - zebra_neigh_t *nbr = NULL; - struct listnode *node = NULL; - - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, nbr)) { - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL) && - IS_ZEBRA_NEIGH_INACTIVE(nbr)) - zvni_neigh_probe(zvni, nbr); - } -} - -/* - * Inform BGP about local neighbor addition. - */ -static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, - zebra_mac_t *zmac, - uint32_t neigh_flags, - uint32_t seq) -{ - uint8_t flags = 0; - - if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) { - /* host reachability has not been verified locally */ - - /* if no ES peer is claiming reachability we can't advertise - * the entry - */ - if (!CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) - return 0; - - /* ES peers are claiming reachability; we will - * advertise the entry but with a proxy flag - */ - SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT); - } - - if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - /* Set router flag (R-bit) based on local neigh entry add */ - if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ROUTER_FLAG)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); - if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_SVI_IP)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP); - - return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags, - seq, ZEBRA_NEIGH_ACTIVE, - zmac ? zmac->es : NULL, - ZEBRA_MACIP_ADD); -} - -/* - * Inform BGP about local neighbor deletion. - */ -static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, uint32_t flags, - int state, bool force) -{ - if (!force) { - if (CHECK_FLAG(flags, ZEBRA_NEIGH_LOCAL_INACTIVE) && - !CHECK_FLAG(flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) - /* the neigh was not advertised - nothing to delete */ - return 0; - } - - return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags, - 0, state, NULL, ZEBRA_MACIP_DEL); -} - -/* - * Install remote neighbor into the kernel. - */ -static int zvni_rem_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n, - bool was_static) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - struct interface *vlan_if; - int flags; - int ret = 0; - - if (!(n->flags & ZEBRA_NEIGH_REMOTE)) - return 0; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return -1; - - flags = DPLANE_NTF_EXT_LEARNED; - if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) - flags |= DPLANE_NTF_ROUTER; - ZEBRA_NEIGH_SET_ACTIVE(n); - - dplane_rem_neigh_add(vlan_if, &n->ip, &n->emac, flags, - was_static); - - return ret; -} - -/* - * Uninstall remote neighbor from the kernel. - */ -static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - struct interface *vlan_if; - - if (!(n->flags & ZEBRA_NEIGH_REMOTE)) - return 0; - - if (!zvni->vxlan_if) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", - zvni->vni, zvni); - return -1; - } - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return -1; - - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - - dplane_rem_neigh_delete(vlan_if, &n->ip); - - return 0; -} - -/* - * Probe neighbor from the kernel. - */ -static int zvni_neigh_probe(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - struct interface *vlan_if; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return -1; - - dplane_rem_neigh_update(vlan_if, &n->ip, &n->emac); - - return 0; -} - -/* - * Install neighbor hash entry - called upon access VLAN change. - */ -static void zvni_install_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_neigh_t *n; - struct neigh_walk_ctx *wctx = ctxt; - - n = (zebra_neigh_t *)bucket->data; - - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - zvni_rem_neigh_install(wctx->zvni, n, false /*was_static*/); -} - -/* Get the VRR interface for SVI if any */ -struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp) -{ - struct zebra_vrf *zvrf = NULL; - struct interface *tmp_if = NULL; - struct zebra_if *zif = NULL; - - zvrf = vrf_info_lookup(ifp->vrf_id); - assert(zvrf); - - FOR_ALL_INTERFACES (zvrf->vrf, tmp_if) { - zif = tmp_if->info; - if (!zif) - continue; - - if (!IS_ZEBRA_IF_MACVLAN(tmp_if)) - continue; - - if (zif->link == ifp) - return tmp_if; - } - - return NULL; -} - -static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) -{ - struct listnode *cnode = NULL, *cnnode = NULL; - struct connected *c = NULL; - struct ethaddr macaddr; - - memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { - struct ipaddr ip; - - memset(&ip, 0, sizeof(struct ipaddr)); - if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) - continue; - - if (c->address->family == AF_INET) { - ip.ipa_type = IPADDR_V4; - memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), - sizeof(struct in_addr)); - } else if (c->address->family == AF_INET6) { - ip.ipa_type = IPADDR_V6; - memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), - sizeof(struct in6_addr)); - } else { - continue; - } - - zvni_gw_macip_del(ifp, zvni, &ip); - } - - return 0; -} - -static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) -{ - struct listnode *cnode = NULL, *cnnode = NULL; - struct connected *c = NULL; - struct ethaddr macaddr; - - memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { - struct ipaddr ip; - - memset(&ip, 0, sizeof(struct ipaddr)); - if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) - continue; - - if (c->address->family == AF_INET) { - ip.ipa_type = IPADDR_V4; - memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), - sizeof(struct in_addr)); - } else if (c->address->family == AF_INET6) { - ip.ipa_type = IPADDR_V6; - memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), - sizeof(struct in6_addr)); - } else { - continue; - } - - zvni_gw_macip_add(ifp, zvni, &macaddr, &ip); - } - return 0; -} - - -static int zvni_advertise_subnet(zebra_vni_t *zvni, struct interface *ifp, - int advertise) -{ - struct listnode *cnode = NULL, *cnnode = NULL; - struct connected *c = NULL; - struct ethaddr macaddr; - - memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { - struct prefix p; - - memcpy(&p, c->address, sizeof(struct prefix)); - - /* skip link local address */ - if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) - continue; - - apply_mask(&p); - if (advertise) - ip_prefix_send_to_client(ifp->vrf_id, &p, - ZEBRA_IP_PREFIX_ROUTE_ADD); - else - ip_prefix_send_to_client(ifp->vrf_id, &p, - ZEBRA_IP_PREFIX_ROUTE_DEL); - } - return 0; -} - -/* - * zvni_gw_macip_add_to_client - */ -static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, - struct ethaddr *macaddr, struct ipaddr *ip) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - - vxl = &zif->l2info.vxl; - - mac = zvni_mac_lookup(zvni, macaddr); - if (!mac) { - mac = zvni_mac_add(zvni, macaddr); - if (!mac) { - flog_err(EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add MAC %s intf %s(%u) VID %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vxl->access_vlan); - return -1; - } - } - - /* Set "local" forwarding info. */ - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - mac->fwd_info.local.ifindex = ifp->ifindex; - mac->fwd_info.local.vid = vxl->access_vlan; - - n = zvni_neigh_lookup(zvni, ip); - if (!n) { - n = zvni_neigh_add(zvni, ip, macaddr, mac, 0); - if (!n) { - flog_err( - EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", - ipaddr2str(ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, zvni->vni); - return -1; - } - } - - /* Set "local" forwarding info. */ - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - ZEBRA_NEIGH_SET_ACTIVE(n); - memcpy(&n->emac, macaddr, ETH_ALEN); - n->ifindex = ifp->ifindex; - - /* Only advertise in BGP if the knob is enabled */ - if (advertise_gw_macip_enabled(zvni)) { - - SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); - SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW); - /* Set Router flag (R-bit) */ - if (ip->ipa_type == IPADDR_V6) - SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x", - ifp->name, ifp->ifindex, zvni->vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); - - zvni_neigh_send_add_to_client(zvni->vni, ip, &n->emac, n->mac, - n->flags, n->loc_seq); - } else if (advertise_svi_macip_enabled(zvni)) { - - SET_FLAG(n->flags, ZEBRA_NEIGH_SVI_IP); - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "SVI %s(%u) L2-VNI %u, sending SVI MAC %s IP %s add to BGP with flags 0x%x", - ifp->name, ifp->ifindex, zvni->vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); - - zvni_neigh_send_add_to_client(zvni->vni, ip, &n->emac, n->mac, - n->flags, n->loc_seq); - } - - return 0; -} - -/* - * zvni_gw_macip_del_from_client - */ -static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, - struct ipaddr *ip) -{ - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; - - /* If the neigh entry is not present nothing to do*/ - n = zvni_neigh_lookup(zvni, ip); - if (!n) - return 0; - - /* mac entry should be present */ - mac = zvni_mac_lookup(zvni, &n->emac); - if (!mac) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("MAC %s doesn't exist for neigh %s on VNI %u", - prefix_mac2str(&n->emac, - buf1, sizeof(buf1)), - ipaddr2str(ip, buf2, sizeof(buf2)), - zvni->vni); - return -1; - } - - /* If the entry is not local nothing to do*/ - if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) - return -1; - - /* only need to delete the entry from bgp if we sent it before */ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", - ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni, - prefix_mac2str(&(n->emac), buf1, sizeof(buf1)), - ipaddr2str(ip, buf2, sizeof(buf2))); - - /* Remove neighbor from BGP. */ - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac, - n->flags, ZEBRA_NEIGH_ACTIVE, - false /*force*/); - - /* Delete this neighbor entry. */ - zvni_neigh_del(zvni, n); - - /* see if the mac needs to be deleted as well*/ - if (mac) - zvni_deref_ip2mac(zvni, mac); - - return 0; -} - -static void zvni_gw_macip_del_for_vni_hash(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_vni_t *zvni = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; - struct interface *vlan_if = NULL; - struct interface *vrr_if = NULL; - struct interface *ifp; - - /* Add primary SVI MAC*/ - zvni = (zebra_vni_t *)bucket->data; - - /* Global (Zvrf) advertise-default-gw is disabled, - * but zvni advertise-default-gw is enabled - */ - if (zvni->advertise_gw_macip) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("VNI: %u GW-MACIP enabled, retain gw-macip", - zvni->vni); - return; - } - - ifp = zvni->vxlan_if; - if (!ifp) - return; - zif = ifp->info; - - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return; - - zl2_info = zif->l2info.vxl; - - vlan_if = - zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return; - - /* Del primary MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); - - /* Del VRR MAC-IP - if any*/ - vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); - if (vrr_if) - zvni_del_macip_for_intf(vrr_if, zvni); - - return; -} - -static void zvni_gw_macip_add_for_vni_hash(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_vni_t *zvni = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; - struct interface *vlan_if = NULL; - struct interface *vrr_if = NULL; - struct interface *ifp = NULL; - - zvni = (zebra_vni_t *)bucket->data; - - ifp = zvni->vxlan_if; - if (!ifp) - return; - zif = ifp->info; - - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return; - zl2_info = zif->l2info.vxl; - - vlan_if = - zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return; - - /* Add primary SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); - - if (advertise_gw_macip_enabled(zvni)) { - /* Add VRR MAC-IP - if any*/ - vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); - if (vrr_if) - zvni_add_macip_for_intf(vrr_if, zvni); - } - - return; -} - -static void zvni_svi_macip_del_for_vni_hash(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_vni_t *zvni = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; - struct interface *vlan_if = NULL; - struct interface *ifp; - - /* Add primary SVI MAC*/ - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) - return; - - /* Global(vrf) advertise-svi-ip disabled, but zvni advertise-svi-ip - * enabled - */ - if (zvni->advertise_svi_macip) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("VNI: %u SVI-MACIP enabled, retain svi-macip", - zvni->vni); - return; - } - - ifp = zvni->vxlan_if; - if (!ifp) - return; - zif = ifp->info; - - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return; - - zl2_info = zif->l2info.vxl; - - vlan_if = zvni_map_to_svi(zl2_info.access_vlan, - zif->brslave_info.br_if); - if (!vlan_if) - return; - - /* Del primary MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); - - return; -} - -static inline void zvni_local_neigh_update_log(const char *pfx, - zebra_neigh_t *n, bool is_router, bool local_inactive, - bool old_bgp_ready, bool new_bgp_ready, - bool inform_dataplane, bool inform_bgp, const char *sfx) -{ - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - - if (!IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - return; - - zlog_debug("%s neigh vni %u ip %s mac %s f 0x%x%s%s%s%s%s%s %s", - pfx, n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), - n->flags, is_router ? " router" : "", - local_inactive ? " local-inactive" : "", - old_bgp_ready ? " old_bgp_ready" : "", - new_bgp_ready ? " new_bgp_ready" : "", - inform_dataplane ? " inform_dp" : "", - inform_bgp ? " inform_bgp" : "", - sfx); -} - -static int zvni_local_neigh_update(zebra_vni_t *zvni, - struct interface *ifp, - struct ipaddr *ip, - struct ethaddr *macaddr, - bool is_router, - bool local_inactive, bool dp_static) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - struct zebra_vrf *zvrf; - zebra_neigh_t *n = NULL; - zebra_mac_t *zmac = NULL, *old_zmac = NULL; - uint32_t old_mac_seq = 0, mac_new_seq = 0; - bool upd_mac_seq = false; - bool neigh_mac_change = false; - bool neigh_on_hold = false; - bool neigh_was_remote = false; - bool do_dad = false; - struct in_addr vtep_ip = {.s_addr = 0}; - bool inform_dataplane = false; - bool created = false; - bool new_static = false; - bool old_bgp_ready = false; - bool new_bgp_ready; - - /* Check if the MAC exists. */ - zmac = zvni_mac_lookup(zvni, macaddr); - if (!zmac) { - /* create a dummy MAC if the MAC is not already present */ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "AUTO MAC %s created for neigh %s on VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni); - - zmac = zvni_mac_add(zvni, macaddr); - if (!zmac) { - zlog_debug("Failed to add MAC %s VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni); - return -1; - } - - memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info)); - memset(&zmac->flags, 0, sizeof(uint32_t)); - SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO); - } else { - if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { - /* - * We don't change the MAC to local upon a neighbor - * learn event, we wait for the explicit local MAC - * learn. However, we have to compute its sequence - * number in preparation for when it actually turns - * local. - */ - upd_mac_seq = true; - } - } - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - if (!zvrf) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug(" Unable to find vrf for: %d", - zvni->vxlan_if->vrf_id); - return -1; - } - - /* Check if the neighbor exists. */ - n = zvni_neigh_lookup(zvni, ip); - if (!n) { - /* New neighbor - create */ - n = zvni_neigh_add(zvni, ip, macaddr, zmac, 0); - if (!n) { - flog_err( - EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", - ipaddr2str(ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, zvni->vni); - return -1; - } - /* Set "local" forwarding info. */ - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - n->ifindex = ifp->ifindex; - created = true; - } else { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - bool mac_different; - bool cur_is_router; - bool old_local_inactive; - - old_local_inactive = !!CHECK_FLAG(n->flags, - ZEBRA_NEIGH_LOCAL_INACTIVE); - - old_bgp_ready = - zebra_vxlan_neigh_is_ready_for_bgp(n); - - /* Note any changes and see if of interest to BGP. */ - mac_different = !!memcmp(&n->emac, - macaddr, ETH_ALEN); - cur_is_router = !!CHECK_FLAG(n->flags, - ZEBRA_NEIGH_ROUTER_FLAG); - new_static = zebra_vxlan_neigh_is_static(n); - if (!mac_different && is_router == cur_is_router && - old_local_inactive == local_inactive && - dp_static != new_static) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - " Ignoring entry mac is the same and is_router == cur_is_router"); - n->ifindex = ifp->ifindex; - return 0; - } - - old_zmac = n->mac; - if (!mac_different) { - /* XXX - cleanup this code duplication */ - bool is_neigh_freezed = false; - - /* Only the router flag has changed. */ - if (is_router) - SET_FLAG(n->flags, - ZEBRA_NEIGH_ROUTER_FLAG); - else - UNSET_FLAG(n->flags, - ZEBRA_NEIGH_ROUTER_FLAG); - - if (local_inactive) - SET_FLAG(n->flags, - ZEBRA_NEIGH_LOCAL_INACTIVE); - else - UNSET_FLAG(n->flags, - ZEBRA_NEIGH_LOCAL_INACTIVE); - new_bgp_ready = - zebra_vxlan_neigh_is_ready_for_bgp(n); - - /* Neigh is in freeze state and freeze action - * is enabled, do not send update to client. - */ - is_neigh_freezed = (zvrf->dup_addr_detect && - zvrf->dad_freeze && - CHECK_FLAG(n->flags, - ZEBRA_NEIGH_DUPLICATE)); - - zvni_local_neigh_update_log("local", n, - is_router, local_inactive, - old_bgp_ready, new_bgp_ready, - false, false, "flag-update"); - - /* if the neigh can no longer be advertised - * remove it from bgp - */ - if (!is_neigh_freezed) { - zebra_vxlan_neigh_send_add_del_to_client( - n, old_bgp_ready, new_bgp_ready); - } else { - if (IS_ZEBRA_DEBUG_VXLAN && - IS_ZEBRA_NEIGH_ACTIVE(n)) - zlog_debug( - " Neighbor active and frozen"); - } - return 0; - } - - /* The MAC has changed, need to issue a delete - * first as this means a different MACIP route. - * Also, need to do some unlinking/relinking. - * We also need to update the MAC's sequence number - * in different situations. - */ - if (old_bgp_ready) { - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, n->flags, n->state, - false /*force*/); - old_bgp_ready = false; - } - if (old_zmac) { - old_mac_seq = CHECK_FLAG(old_zmac->flags, - ZEBRA_MAC_REMOTE) ? - old_zmac->rem_seq : old_zmac->loc_seq; - neigh_mac_change = upd_mac_seq = true; - zebra_vxlan_local_neigh_deref_mac(n, - true /* send_mac_update */); - } - - /* if mac changes abandon peer flags and tell - * dataplane to clear the static flag - */ - if (zebra_vxlan_neigh_clear_sync_info(n)) - inform_dataplane = true; - /* Update the forwarding info. */ - n->ifindex = ifp->ifindex; - - /* Link to new MAC */ - zebra_vxlan_local_neigh_ref_mac(n, macaddr, zmac, - true /* send_mac_update */); - } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - /* - * Neighbor has moved from remote to local. Its - * MAC could have also changed as part of the move. - */ - if (memcmp(n->emac.octet, macaddr->octet, - ETH_ALEN) != 0) { - old_zmac = n->mac; - if (old_zmac) { - old_mac_seq = CHECK_FLAG( - old_zmac->flags, - ZEBRA_MAC_REMOTE) ? - old_zmac->rem_seq : - old_zmac->loc_seq; - neigh_mac_change = upd_mac_seq = true; - zebra_vxlan_local_neigh_deref_mac(n, - true /* send_update */); - } - - /* Link to new MAC */ - zebra_vxlan_local_neigh_ref_mac(n, macaddr, - zmac, true /*send_update*/); - } - /* Based on Mobility event Scenario-B from the - * draft, neigh's previous state was remote treat this - * event for DAD. - */ - neigh_was_remote = true; - vtep_ip = n->r_vtep_ip; - /* Mark appropriately */ - UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); - n->r_vtep_ip.s_addr = INADDR_ANY; - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - n->ifindex = ifp->ifindex; - } - } - - /* If MAC was previously remote, or the neighbor had a different - * MAC earlier, recompute the sequence number. - */ - if (upd_mac_seq) { - uint32_t seq1, seq2; - - seq1 = CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE) ? - zmac->rem_seq + 1 : zmac->loc_seq; - seq2 = neigh_mac_change ? old_mac_seq + 1 : 0; - mac_new_seq = zmac->loc_seq < MAX(seq1, seq2) ? - MAX(seq1, seq2) : zmac->loc_seq; - } - - if (local_inactive) - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); - else - UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); - - /* Mark Router flag (R-bit) */ - if (is_router) - SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - else - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - - /* if the dataplane thinks that this is a sync entry but - * zebra doesn't we need to re-concile the diff - * by re-installing the dataplane entry - */ - if (dp_static) { - new_static = zebra_vxlan_neigh_is_static(n); - if (!new_static) - inform_dataplane = true; - } - - /* Check old and/or new MAC detected as duplicate mark - * the neigh as duplicate - */ - if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) { - flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC", - zvni->vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(&n->ip, buf2, sizeof(buf2))); - } - - /* For IP Duplicate Address Detection (DAD) is trigger, - * when the event is extended mobility based on scenario-B - * from the draft, IP/Neigh's MAC binding changed and - * neigh's previous state was remote. - */ - if (neigh_mac_change && neigh_was_remote) - do_dad = true; - - zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad, - &neigh_on_hold, true); - - if (inform_dataplane) - zebra_vxlan_sync_neigh_dp_install(n, false /* set_inactive */, - false /* force_clear_static */, __func__); - - /* Before we program this in BGP, we need to check if MAC is locally - * learnt. If not, force neighbor to be inactive and reset its seq. - */ - if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) { - zvni_local_neigh_update_log("local", - n, is_router, local_inactive, - false, false, inform_dataplane, false, - "auto-mac"); - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - zmac->loc_seq = mac_new_seq; - return 0; - } - - zvni_local_neigh_update_log("local", - n, is_router, local_inactive, false, false, inform_dataplane, - true, created ? "created" : "updated"); - - /* If the MAC's sequence number has changed, inform the MAC and all - * neighbors associated with the MAC to BGP, else just inform this - * neighbor. - */ - if (upd_mac_seq && zmac->loc_seq != mac_new_seq) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Seq changed for MAC %s VNI %u - old %u new %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni, zmac->loc_seq, mac_new_seq); - zmac->loc_seq = mac_new_seq; - if (zvni_mac_send_add_to_client(zvni->vni, macaddr, - zmac->flags, zmac->loc_seq, zmac->es)) - return -1; - zvni_process_neigh_on_local_mac_change(zvni, zmac, 1, - 0 /*es_change*/); - return 0; - } - - n->loc_seq = zmac->loc_seq; - - if (!neigh_on_hold) { - ZEBRA_NEIGH_SET_ACTIVE(n); - new_bgp_ready = - zebra_vxlan_neigh_is_ready_for_bgp(n); - zebra_vxlan_neigh_send_add_del_to_client(n, - old_bgp_ready, new_bgp_ready); - } else { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug(" Neighbor on hold not sending"); - } - return 0; -} - -static int zvni_remote_neigh_update(zebra_vni_t *zvni, - struct interface *ifp, - struct ipaddr *ip, - struct ethaddr *macaddr, - uint16_t state) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - zebra_neigh_t *n = NULL; - zebra_mac_t *zmac = NULL; - - /* If the neighbor is unknown, there is no further action. */ - n = zvni_neigh_lookup(zvni, ip); - if (!n) - return 0; - - /* If a remote entry, see if it needs to be refreshed */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { -#ifdef GNU_LINUX - if (state & NUD_STALE) - zvni_rem_neigh_install(zvni, n, false /*was_static*/); -#endif - } else { - /* We got a "remote" neighbor notification for an entry - * we think is local. This can happen in a multihoming - * scenario - but only if the MAC is already "remote". - * Just mark our entry as "remote". - */ - zmac = zvni_mac_lookup(zvni, macaddr); - if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { - zlog_debug( - "Ignore remote neigh %s (MAC %s) on L2-VNI %u - MAC unknown or local", - ipaddr2str(&n->ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni); - return -1; - } - - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS); - SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); - ZEBRA_NEIGH_SET_ACTIVE(n); - n->r_vtep_ip = zmac->fwd_info.r_vtep_ip; - } - - return 0; -} - -/* - * Make hash key for MAC. - */ -static unsigned int mac_hash_keymake(const void *p) -{ - const zebra_mac_t *pmac = p; - const void *pnt = (void *)pmac->macaddr.octet; - - return jhash(pnt, ETH_ALEN, 0xa5a5a55a); -} - -/* - * Compare two MAC addresses. - */ -static bool mac_cmp(const void *p1, const void *p2) -{ - const zebra_mac_t *pmac1 = p1; - const zebra_mac_t *pmac2 = p2; - - if (pmac1 == NULL && pmac2 == NULL) - return true; - - if (pmac1 == NULL || pmac2 == NULL) - return false; - - return (memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETH_ALEN) - == 0); -} - -/* - * Callback to allocate MAC hash entry. - */ -static void *zvni_mac_alloc(void *p) -{ - const zebra_mac_t *tmp_mac = p; - zebra_mac_t *mac; - - mac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); - *mac = *tmp_mac; - - return ((void *)mac); -} - -/* - * Add MAC entry. - */ -static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr) -{ - zebra_mac_t tmp_mac; - zebra_mac_t *mac = NULL; - - memset(&tmp_mac, 0, sizeof(zebra_mac_t)); - memcpy(&tmp_mac.macaddr, macaddr, ETH_ALEN); - mac = hash_get(zvni->mac_table, &tmp_mac, zvni_mac_alloc); - assert(mac); - - mac->zvni = zvni; - mac->dad_mac_auto_recovery_timer = NULL; - - mac->neigh_list = list_new(); - mac->neigh_list->cmp = neigh_list_cmp; - - if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) { - char buf[ETHER_ADDR_STRLEN]; - - zlog_debug("%s: MAC %s flags 0x%x", - __func__, - prefix_mac2str(&mac->macaddr, - buf, sizeof(buf)), - mac->flags); - } - return mac; -} - -/* - * Delete MAC entry. - */ -static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - zebra_mac_t *tmp_mac; - - if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) { - char buf[ETHER_ADDR_STRLEN]; - - zlog_debug("%s: MAC %s flags 0x%x", - __func__, - prefix_mac2str(&mac->macaddr, - buf, sizeof(buf)), - mac->flags); - } - - /* force de-ref any ES entry linked to the MAC */ - zebra_evpn_es_mac_deref_entry(mac); - - /* Cancel proxy hold timer */ - zebra_vxlan_mac_stop_hold_timer(mac); - - /* Cancel auto recovery */ - THREAD_OFF(mac->dad_mac_auto_recovery_timer); - - list_delete(&mac->neigh_list); - - /* Free the VNI hash entry and allocated memory. */ - tmp_mac = hash_release(zvni->mac_table, mac); - XFREE(MTYPE_MAC, tmp_mac); - - return 0; -} - -static bool zvni_check_mac_del_from_db(struct mac_walk_ctx *wctx, - zebra_mac_t *mac) -{ - if ((wctx->flags & DEL_LOCAL_MAC) && - (mac->flags & ZEBRA_MAC_LOCAL)) - return true; - else if ((wctx->flags & DEL_REMOTE_MAC) && - (mac->flags & ZEBRA_MAC_REMOTE)) - return true; - else if ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) && - (mac->flags & ZEBRA_MAC_REMOTE) && - IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip)) - return true; - else if ((wctx->flags & DEL_LOCAL_MAC) && - (mac->flags & ZEBRA_MAC_AUTO) && - !listcount(mac->neigh_list)) { - if (IS_ZEBRA_DEBUG_VXLAN) { - char buf[ETHER_ADDR_STRLEN]; - - zlog_debug( - "%s: Del MAC %s flags 0x%x", __func__, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - mac->flags); - } - wctx->uninstall = 0; - - return true; - } - - return false; -} - -/* - * Free MAC hash entry (callback) - */ -static void zvni_mac_del_hash_entry(struct hash_bucket *bucket, void *arg) -{ - struct mac_walk_ctx *wctx = arg; - zebra_mac_t *mac = bucket->data; - - if (zvni_check_mac_del_from_db(wctx, mac)) { - if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) { - zvni_mac_send_del_to_client(wctx->zvni->vni, - &mac->macaddr, mac->flags, false); - } - if (wctx->uninstall) { - if (zebra_vxlan_mac_is_static(mac)) - zebra_vxlan_sync_mac_dp_install(mac, - false /* set_inactive */, - true /* force_clear_static */, - __func__); - - if (mac->flags & ZEBRA_MAC_REMOTE) - zvni_rem_mac_uninstall(wctx->zvni, mac); - } - - zvni_mac_del(wctx->zvni, mac); - } - - return; -} - -/* - * Delete all MAC entries for this VNI. - */ -static void zvni_mac_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags) -{ - struct mac_walk_ctx wctx; - - if (!zvni->mac_table) - return; - - memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; - wctx.uninstall = uninstall; - wctx.upd_client = upd_client; - wctx.flags = flags; - - hash_iterate(zvni->mac_table, zvni_mac_del_hash_entry, &wctx); -} - -/* - * Look up MAC hash entry. - */ -static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *mac) -{ - zebra_mac_t tmp; - zebra_mac_t *pmac; - - memset(&tmp, 0, sizeof(tmp)); - memcpy(&tmp.macaddr, mac, ETH_ALEN); - pmac = hash_lookup(zvni->mac_table, &tmp); - - return pmac; -} - -/* - * Inform BGP about local MAC addition. - */ -static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, - uint32_t mac_flags, uint32_t seq, struct zebra_evpn_es *es) -{ - uint8_t flags = 0; - - if (CHECK_FLAG(mac_flags, ZEBRA_MAC_LOCAL_INACTIVE)) { - /* host reachability has not been verified locally */ - - /* if no ES peer is claiming reachability we can't advertise the - * entry - */ - if (!CHECK_FLAG(mac_flags, ZEBRA_MAC_ES_PEER_ACTIVE)) - return 0; - - /* ES peers are claiming reachability; we will - * advertise the entry but with a proxy flag - */ - SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT); - } - - if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); - if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - - return zvni_macip_send_msg_to_client(vni, macaddr, NULL, flags, - seq, ZEBRA_NEIGH_ACTIVE, es, - ZEBRA_MACIP_ADD); -} - -/* - * Inform BGP about local MAC deletion. - */ -static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr, - uint32_t flags, bool force) -{ - if (!force) { - if (CHECK_FLAG(flags, ZEBRA_MAC_LOCAL_INACTIVE) && - !CHECK_FLAG(flags, ZEBRA_MAC_ES_PEER_ACTIVE)) - /* the host was not advertised - nothing to delete */ - return 0; - } - - return zvni_macip_send_msg_to_client(vni, macaddr, NULL, 0 /* flags */, - 0 /* seq */, ZEBRA_NEIGH_ACTIVE, NULL, - ZEBRA_MACIP_DEL); -} - -/* - * Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC - * notifications, to see if they are of interest. - */ -static zebra_vni_t *zvni_map_vlan(struct interface *ifp, - struct interface *br_if, vlanid_t vid) -{ - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; - struct zebra_l2info_bridge *br; - struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - zebra_vni_t *zvni; - int found = 0; - - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; - assert(zif); - br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - - /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - if (!tmp_if) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - vxl = &zif->l2info.vxl; - - if (zif->brslave_info.br_if != br_if) - continue; - - if (!bridge_vlan_aware || vxl->access_vlan == vid) { - found = 1; - break; - } - } - - if (!found) - return NULL; - - zvni = zvni_lookup(vxl->vni); - return zvni; -} - -/* - * Map SVI and associated bridge to a VNI. This is invoked upon getting - * neighbor notifications, to see if they are of interest. - */ -static zebra_vni_t *zvni_from_svi(struct interface *ifp, - struct interface *br_if) -{ - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; - struct zebra_l2info_bridge *br; - struct zebra_l2info_vxlan *vxl = NULL; - uint8_t bridge_vlan_aware; - vlanid_t vid = 0; - zebra_vni_t *zvni; - int found = 0; - - if (!br_if) - return NULL; - - /* Make sure the linked interface is a bridge. */ - if (!IS_ZEBRA_IF_BRIDGE(br_if)) - return NULL; - - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; - assert(zif); - br = &zif->l2info.br; - bridge_vlan_aware = br->vlan_aware; - if (bridge_vlan_aware) { - struct zebra_l2info_vlan *vl; - - if (!IS_ZEBRA_IF_VLAN(ifp)) - return NULL; - - zif = ifp->info; - assert(zif); - vl = &zif->l2info.vl; - vid = vl->vid; - } - - /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - /* TODO: Optimize with a hash. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - if (!tmp_if) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - vxl = &zif->l2info.vxl; - - if (zif->brslave_info.br_if != br_if) - continue; - - if (!bridge_vlan_aware || vxl->access_vlan == vid) { - found = 1; - break; - } - } - - if (!found) - return NULL; - - zvni = zvni_lookup(vxl->vni); - return zvni; -} - /* Map to SVI on bridge corresponding to specified VLAN. This can be one * of two cases: * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface @@ -4042,7 +789,7 @@ static zebra_vni_t *zvni_from_svi(struct interface *ifp, * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge interface * itself */ -static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) +struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) { struct zebra_ns *zns; struct route_node *rn; @@ -4090,496 +837,30 @@ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) return found ? tmp_if : NULL; } -/* Map to MAC-VLAN interface corresponding to specified SVI interface. - */ -static struct interface *zvni_map_to_macvlan(struct interface *br_if, - struct interface *svi_if) -{ - struct zebra_ns *zns; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; - int found = 0; - - /* Defensive check, caller expected to invoke only with valid bridge. */ - if (!br_if) - return NULL; - - if (!svi_if) { - zlog_debug("svi_if is not passed."); - return NULL; - } - - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; - assert(zif); - - /* Identify corresponding VLAN interface. */ - zns = zebra_ns_lookup(NS_DEFAULT); - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - - if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) - continue; - - if (zif->link == svi_if) { - found = 1; - break; - } - } - - return found ? tmp_if : NULL; -} - - -/* - * Install remote MAC into the forwarding plane. - */ -static int zvni_rem_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac, - bool was_static) -{ - const struct zebra_if *zif, *br_zif; - const struct zebra_l2info_vxlan *vxl; - bool sticky; - enum zebra_dplane_result res; - const struct interface *br_ifp; - vlanid_t vid; - uint32_t nhg_id; - struct in_addr vtep_ip; - - if (!(mac->flags & ZEBRA_MAC_REMOTE)) - return 0; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - - br_ifp = zif->brslave_info.br_if; - if (br_ifp == NULL) - return -1; - - vxl = &zif->l2info.vxl; - - sticky = !!CHECK_FLAG(mac->flags, - (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)); - - /* If nexthop group for the FDB entry is inactive (not programmed in - * the dataplane) the MAC entry cannot be installed - */ - if (mac->es) { - if (!(mac->es->flags & ZEBRA_EVPNES_NHG_ACTIVE)) - return -1; - nhg_id = mac->es->nhg_id; - vtep_ip.s_addr = 0; - } else { - nhg_id = 0; - vtep_ip = mac->fwd_info.r_vtep_ip; - } - - br_zif = (const struct zebra_if *)(br_ifp->info); - - if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) - vid = vxl->access_vlan; - else - vid = 0; - - res = dplane_rem_mac_add(zvni->vxlan_if, br_ifp, vid, - &mac->macaddr, vtep_ip, sticky, - nhg_id, was_static); - if (res != ZEBRA_DPLANE_REQUEST_FAILURE) - return 0; - else - return -1; -} - -/* - * Uninstall remote MAC from the forwarding plane. - */ -static int zvni_rem_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - const struct zebra_if *zif, *br_zif; - const struct zebra_l2info_vxlan *vxl; - struct in_addr vtep_ip; - const struct interface *ifp, *br_ifp; - vlanid_t vid; - enum zebra_dplane_result res; - - if (!(mac->flags & ZEBRA_MAC_REMOTE)) - return 0; - - if (!zvni->vxlan_if) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", - zvni->vni, zvni); - return -1; - } - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - - br_ifp = zif->brslave_info.br_if; - if (br_ifp == NULL) - return -1; - - vxl = &zif->l2info.vxl; - - br_zif = (const struct zebra_if *)br_ifp->info; - - if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) - vid = vxl->access_vlan; - else - vid = 0; - - ifp = zvni->vxlan_if; - vtep_ip = mac->fwd_info.r_vtep_ip; - - res = dplane_rem_mac_del(ifp, br_ifp, vid, &mac->macaddr, vtep_ip); - if (res != ZEBRA_DPLANE_REQUEST_FAILURE) - return 0; - else - return -1; -} - -/* - * Install MAC hash entry - called upon access VLAN change. - */ -static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_mac_t *mac; - struct mac_walk_ctx *wctx = ctxt; - - mac = (zebra_mac_t *)bucket->data; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) - zvni_rem_mac_install(wctx->zvni, mac, false); -} - -/* - * Count of remote neighbors referencing this MAC. - */ -static int remote_neigh_count(zebra_mac_t *zmac) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - int count = 0; - - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - count++; - } - - return count; -} - -/* - * Decrement neighbor refcount of MAC; uninstall and free it if - * appropriate. - */ -static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) - return; - - /* If all remote neighbors referencing a remote MAC go away, - * we need to uninstall the MAC. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) && - remote_neigh_count(mac) == 0) { - zvni_rem_mac_uninstall(zvni, mac); - zebra_evpn_es_mac_deref_entry(mac); - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - } - - /* If no neighbors, delete the MAC. */ - if (list_isempty(mac->neigh_list)) - zvni_mac_del(zvni, mac); -} - -/* - * Read and populate local MACs and neighbors corresponding to this VNI. - */ -static void zvni_read_mac_neigh(zebra_vni_t *zvni, struct interface *ifp) -{ - struct zebra_ns *zns; - struct zebra_if *zif; - struct interface *vlan_if; - struct zebra_l2info_vxlan *vxl; - struct interface *vrr_if; - - zif = ifp->info; - vxl = &zif->l2info.vxl; - zns = zebra_ns_lookup(NS_DEFAULT); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u", - ifp->name, ifp->ifindex, zvni->vni, - zif->brslave_info.bridge_ifindex); - - macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if); - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (vlan_if) { - - /* Add SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); - - /* Add VRR MAC-IP - if any*/ - vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); - if (vrr_if) - zvni_add_macip_for_intf(vrr_if, zvni); - - neigh_read_for_vlan(zns, vlan_if); - } -} - -/* - * Hash function for VNI. - */ -static unsigned int vni_hash_keymake(const void *p) -{ - const zebra_vni_t *zvni = p; - - return (jhash_1word(zvni->vni, 0)); -} - -/* - * Compare 2 VNI hash entries. - */ -static bool vni_hash_cmp(const void *p1, const void *p2) -{ - const zebra_vni_t *zvni1 = p1; - const zebra_vni_t *zvni2 = p2; - - return (zvni1->vni == zvni2->vni); -} - -int vni_list_cmp(void *p1, void *p2) -{ - const zebra_vni_t *zvni1 = p1; - const zebra_vni_t *zvni2 = p2; - - if (zvni1->vni == zvni2->vni) - return 0; - return (zvni1->vni < zvni2->vni) ? -1 : 1; -} - -/* - * Callback to allocate VNI hash entry. - */ -static void *zvni_alloc(void *p) -{ - const zebra_vni_t *tmp_vni = p; - zebra_vni_t *zvni; - - zvni = XCALLOC(MTYPE_ZVNI, sizeof(zebra_vni_t)); - zvni->vni = tmp_vni->vni; - return ((void *)zvni); -} - -/* - * Look up VNI hash entry. - */ -zebra_vni_t *zvni_lookup(vni_t vni) -{ - struct zebra_vrf *zvrf; - zebra_vni_t tmp_vni; - zebra_vni_t *zvni = NULL; - - zvrf = zebra_vrf_get_evpn(); - assert(zvrf); - memset(&tmp_vni, 0, sizeof(zebra_vni_t)); - tmp_vni.vni = vni; - zvni = hash_lookup(zvrf->vni_table, &tmp_vni); - - return zvni; -} - -/* - * Add VNI hash entry. - */ -static zebra_vni_t *zvni_add(vni_t vni) -{ - struct zebra_vrf *zvrf; - zebra_vni_t tmp_zvni; - zebra_vni_t *zvni = NULL; - - zvrf = zebra_vrf_get_evpn(); - assert(zvrf); - memset(&tmp_zvni, 0, sizeof(zebra_vni_t)); - tmp_zvni.vni = vni; - zvni = hash_get(zvrf->vni_table, &tmp_zvni, zvni_alloc); - assert(zvni); - - zebra_evpn_vni_es_init(zvni); - - /* Create hash table for MAC */ - zvni->mac_table = - hash_create(mac_hash_keymake, mac_cmp, "Zebra VNI MAC Table"); - - /* Create hash table for neighbors */ - zvni->neigh_table = hash_create(neigh_hash_keymake, neigh_cmp, - "Zebra VNI Neighbor Table"); - - return zvni; -} - -/* vni<=>vxlan_zif association */ -static void zvni_vxlan_if_set(zebra_vni_t *zvni, struct interface *ifp, - bool set) -{ - struct zebra_if *zif; - - if (set) { - if (zvni->vxlan_if == ifp) - return; - zvni->vxlan_if = ifp; - } else { - if (!zvni->vxlan_if) - return; - zvni->vxlan_if = NULL; - } - - if (ifp) - zif = ifp->info; - else - zif = NULL; - - zebra_evpn_vxl_vni_set(zif, zvni, set); -} - -/* - * Delete VNI hash entry. - */ -static int zvni_del(zebra_vni_t *zvni) +static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn) { - struct zebra_vrf *zvrf; - zebra_vni_t *tmp_zvni; - - zvrf = zebra_vrf_get_evpn(); - assert(zvrf); - - zvni_vxlan_if_set(zvni, zvni->vxlan_if, false /* set */); + zevpn_vxlan_if_set(zevpn, zevpn->vxlan_if, false /* set */); /* Remove references to the BUM mcast grp */ - zebra_vxlan_sg_deref(zvni->local_vtep_ip, zvni->mcast_grp); - - /* Free the neighbor hash table. */ - hash_free(zvni->neigh_table); - zvni->neigh_table = NULL; - - /* Free the MAC hash table. */ - hash_free(zvni->mac_table); - zvni->mac_table = NULL; + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, zevpn->mcast_grp); - zebra_evpn_vni_es_cleanup(zvni); - - /* Free the VNI hash entry and allocated memory. */ - tmp_zvni = hash_release(zvrf->vni_table, zvni); - XFREE(MTYPE_ZVNI, tmp_zvni); - - return 0; + return zebra_evpn_del(zevpn); } - -/* - * Inform BGP about local VNI addition. - */ -static int zvni_send_add_to_client(zebra_vni_t *zvni) -{ - struct zserv *client; - struct stream *s; - int rc; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id()); - stream_putl(s, zvni->vni); - stream_put_in_addr(s, &zvni->local_vtep_ip); - stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ - stream_put_in_addr(s, &zvni->mcast_grp); - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send VNI_ADD %u %s tenant vrf %s to %s", zvni->vni, - inet_ntoa(zvni->local_vtep_ip), - vrf_id_to_name(zvni->vrf_id), - zebra_route_string(client->proto)); - - client->vniadd_cnt++; - rc = zserv_send_message(client, s); - - if (!(zvni->flags & ZVNI_READY_FOR_BGP)) { - zvni->flags |= ZVNI_READY_FOR_BGP; - /* once the VNI is sent the ES-EVIs can also be replayed - * to BGP - */ - zebra_evpn_vni_update_all_es(zvni); - } - return rc; -} - -/* - * Inform BGP about local VNI deletion. - */ -static int zvni_send_del_to_client(zebra_vni_t *zvni) -{ - struct zserv *client; - struct stream *s; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - if (zvni->flags & ZVNI_READY_FOR_BGP) { - zvni->flags &= ~ZVNI_READY_FOR_BGP; - /* the ES-EVIs must be removed from BGP before the VNI is */ - zebra_evpn_vni_update_all_es(zvni); - } - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - stream_reset(s); - - zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id()); - stream_putl(s, zvni->vni); - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send VNI_DEL %u to %s", zvni->vni, - zebra_route_string(client->proto)); - - client->vnidel_cnt++; - return zserv_send_message(client, s); -} - /* * Build the VNI hash table by going over the VxLAN interfaces. This * is called when EVPN (advertise-all-vni) is enabled. */ -static void zvni_build_hash_table(void) +static void zevpn_build_hash_table(void) { struct zebra_ns *zns; struct route_node *rn; struct interface *ifp; - /* Walk VxLAN interfaces and create VNI hash. */ + /* Walk VxLAN interfaces and create EVPN hash. */ zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { vni_t vni; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; @@ -4636,11 +917,11 @@ static void zvni_build_hash_table(void) ifp->name, ifp->ifindex, vni, inet_ntoa(vxl->vtep_ip)); - /* VNI hash entry is expected to exist, if the BGP process is killed */ - zvni = zvni_lookup(vni); - if (zvni) { + /* EVPN hash entry is expected to exist, if the BGP process is killed */ + zevpn = zebra_evpn_lookup(vni); + if (zevpn) { zlog_debug( - "VNI hash already present for IF %s(%u) L2-VNI %u", + "EVPN hash already present for IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); /* @@ -4649,48 +930,49 @@ static void zvni_build_hash_table(void) */ if (if_is_operative(ifp) && zif->brslave_info.br_if) - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); /* Send Local MAC-entries to client */ - zvni_send_mac_to_client(zvni); + zebra_evpn_send_mac_list_to_client(zevpn); /* Send Loval Neighbor entries to client */ - zvni_send_neigh_to_client(zvni); + zebra_evpn_send_neigh_to_client(zevpn); } else { - zvni = zvni_add(vni); - if (!zvni) { + zevpn = zebra_evpn_add(vni); + if (!zevpn) { zlog_debug( - "Failed to add VNI hash, IF %s(%u) L2-VNI %u", + "Failed to add EVPN hash, IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); return; } - if (zvni->local_vtep_ip.s_addr != + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zvni->mcast_grp.s_addr != + zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { zebra_vxlan_sg_deref( - zvni->local_vtep_ip, - zvni->mcast_grp); + zevpn->local_vtep_ip, + zevpn->mcast_grp); zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->mcast_grp = vxl->mcast_grp; + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vxl->mcast_grp; /* on local vtep-ip check if ES * orig-ip needs to be updated */ - zebra_evpn_es_set_base_vni(zvni); + zebra_evpn_es_set_base_evpn(zevpn); } - zvni_vxlan_if_set(zvni, ifp, true /* set */); - vlan_if = zvni_map_to_svi(vxl->access_vlan, - zif->brslave_info.br_if); + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + vlan_if = zvni_map_to_svi( + vxl->access_vlan, + zif->brslave_info.br_if); if (vlan_if) { - zvni->vrf_id = vlan_if->vrf_id; + zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf( vlan_if->vrf_id); if (zl3vni) listnode_add_sort( - zl3vni->l2vnis, zvni); + zl3vni->l2vnis, zevpn); } /* @@ -4699,182 +981,30 @@ static void zvni_build_hash_table(void) */ if (if_is_operative(ifp) && zif->brslave_info.br_if) - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); } } } } /* - * See if remote VTEP matches with prefix. + * Cleanup EVPN/VTEP and update kernel */ -static int zvni_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep) +static void zebra_evpn_vxlan_cleanup_all(struct hash_bucket *bucket, void *arg) { - return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip)); -} - -/* - * Locate remote VTEP in VNI hash table. - */ -static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip) -{ - zebra_vtep_t *zvtep; - - if (!zvni) - return NULL; - - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - if (zvni_vtep_match(vtep_ip, zvtep)) - break; - } - - return zvtep; -} - -/* - * Add remote VTEP to VNI hash table. - */ -static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip, - int flood_control) - -{ - zebra_vtep_t *zvtep; - - zvtep = XCALLOC(MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t)); - - zvtep->vtep_ip = *vtep_ip; - zvtep->flood_control = flood_control; - - if (zvni->vteps) - zvni->vteps->prev = zvtep; - zvtep->next = zvni->vteps; - zvni->vteps = zvtep; - - return zvtep; -} - -/* - * Remove remote VTEP from VNI hash table. - */ -static int zvni_vtep_del(zebra_vni_t *zvni, zebra_vtep_t *zvtep) -{ - if (zvtep->next) - zvtep->next->prev = zvtep->prev; - if (zvtep->prev) - zvtep->prev->next = zvtep->next; - else - zvni->vteps = zvtep->next; - - zvtep->prev = zvtep->next = NULL; - XFREE(MTYPE_ZVNI_VTEP, zvtep); - - return 0; -} - -/* - * Delete all remote VTEPs for this VNI (upon VNI delete). Also - * uninstall from kernel if asked to. - */ -static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall) -{ - zebra_vtep_t *zvtep, *zvtep_next; - - if (!zvni) - return -1; - - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next) { - zvtep_next = zvtep->next; - if (uninstall) - zvni_vtep_uninstall(zvni, &zvtep->vtep_ip); - zvni_vtep_del(zvni, zvtep); - } - - return 0; -} - -/* - * Install remote VTEP into the kernel if the remote VTEP has asked - * for head-end-replication. - */ -static int zvni_vtep_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep) -{ - if (is_vxlan_flooding_head_end() && - (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) { - if (ZEBRA_DPLANE_REQUEST_FAILURE == - dplane_vtep_add(zvni->vxlan_if, - &zvtep->vtep_ip, zvni->vni)) - return -1; - } - - return 0; -} - -/* - * Uninstall remote VTEP from the kernel. - */ -static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip) -{ - if (!zvni->vxlan_if) { - zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", - zvni->vni, zvni); - return -1; - } - - if (ZEBRA_DPLANE_REQUEST_FAILURE == - dplane_vtep_delete(zvni->vxlan_if, vtep_ip, zvni->vni)) - return -1; - - return 0; -} - -/* - * Install or uninstall flood entries in the kernel corresponding to - * remote VTEPs. This is invoked upon change to BUM handling. - */ -static void zvni_handle_flooding_remote_vteps(struct hash_bucket *bucket, - void *zvrf) -{ - zebra_vni_t *zvni; - zebra_vtep_t *zvtep; - - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) - return; - - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - if (is_vxlan_flooding_head_end()) - zvni_vtep_install(zvni, zvtep); - else - zvni_vtep_uninstall(zvni, &zvtep->vtep_ip); - } -} - -/* - * Cleanup VNI/VTEP and update kernel - */ -static void zvni_cleanup_all(struct hash_bucket *bucket, void *arg) -{ - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; struct zebra_vrf *zvrf = (struct zebra_vrf *)arg; - zvni = (zebra_vni_t *)bucket->data; + zevpn = (zebra_evpn_t *)bucket->data; /* remove from l3-vni list */ if (zvrf->l3vni) zl3vni = zl3vni_lookup(zvrf->l3vni); if (zl3vni) - listnode_delete(zl3vni->l2vnis, zvni); - - /* Free up all neighbors and MACs, if any. */ - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + listnode_delete(zl3vni->l2vnis, zevpn); - /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 1); - - /* Delete the hash entry. */ - zvni_del(zvni); + zebra_evpn_cleanup_all(bucket, arg); } /* cleanup L3VNI */ @@ -4947,7 +1077,7 @@ static void *zl3vni_rmac_alloc(void *p) const zebra_mac_t *tmp_rmac = p; zebra_mac_t *zrmac; - zrmac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); + zrmac = XCALLOC(MTYPE_L3VNI_MAC, sizeof(zebra_mac_t)); *zrmac = *tmp_rmac; return ((void *)zrmac); @@ -4991,7 +1121,7 @@ static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) } tmp_rmac = hash_release(zl3vni->rmac_table, zrmac); - XFREE(MTYPE_MAC, tmp_rmac); + XFREE(MTYPE_L3VNI_MAC, tmp_rmac); return 0; } @@ -5188,7 +1318,7 @@ static void *zl3vni_nh_alloc(void *p) const zebra_neigh_t *tmp_n = p; zebra_neigh_t *n; - n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); + n = XCALLOC(MTYPE_L3NEIGH, sizeof(zebra_neigh_t)); *n = *tmp_n; return ((void *)n); @@ -5234,7 +1364,7 @@ static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) } tmp_n = hash_release(zl3vni->nh_table, n); - XFREE(MTYPE_NEIGH, tmp_n); + XFREE(MTYPE_L3NEIGH, tmp_n); return 0; } @@ -5450,15 +1580,13 @@ static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id) zl3vni->svi_if = NULL; zl3vni->vxlan_if = NULL; zl3vni->l2vnis = list_new(); - zl3vni->l2vnis->cmp = vni_list_cmp; + zl3vni->l2vnis->cmp = zebra_evpn_list_cmp; /* Create hash table for remote RMAC */ - zl3vni->rmac_table = hash_create(mac_hash_keymake, mac_cmp, - "Zebra L3-VNI RMAC-Table"); + zl3vni->rmac_table = zebra_mac_db_create("Zebra L3-VNI RMAC-Table"); /* Create hash table for neighbors */ - zl3vni->nh_table = hash_create(neigh_hash_keymake, neigh_cmp, - "Zebra L3-VNI next-hop table"); + zl3vni->nh_table = zebra_neigh_db_create("Zebra L3-VNI next-hop table"); return zl3vni; } @@ -5554,7 +1682,8 @@ struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni) if (!zif) return NULL; - return zvni_map_to_macvlan(zif->brslave_info.br_if, zl3vni->svi_if); + return zebra_evpn_map_to_macvlan(zif->brslave_info.br_if, + zl3vni->svi_if); } @@ -5767,13 +1896,13 @@ static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni) zl3vni_send_del_to_client(zl3vni); } -static void zvni_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt) +static void zevpn_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt) { - zebra_vni_t *zvni = (zebra_vni_t *)bucket->data; + zebra_evpn_t *zevpn = (zebra_evpn_t *)bucket->data; zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)ctxt; - if (zvni->vrf_id == zl3vni_vrf_id(zl3vni)) - listnode_add_sort(zl3vni->l2vnis, zvni); + if (zevpn->vrf_id == zl3vni_vrf_id(zl3vni)) + listnode_add_sort(zl3vni->l2vnis, zevpn); } /* @@ -5782,7 +1911,7 @@ static void zvni_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt) static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, int add) { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; /* There is a possibility that VNI notification was already received * from kernel and we programmed it as L2-VNI @@ -5794,28 +1923,27 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, */ if (add) { /* Locate hash entry */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Del L2-VNI %u - transition to L3-VNI", vni); - /* Delete VNI from BGP. */ - zvni_send_del_to_client(zvni); + /* Delete EVPN from BGP. */ + zebra_evpn_send_del_to_client(zevpn); - /* Free up all neighbors and MAC, if any. */ - zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC); /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 0); + zebra_evpn_vtep_del_all(zevpn, 0); /* Delete the hash entry. */ - if (zvni_del(zvni)) { + if (zebra_evpn_vxlan_del(zevpn)) { flog_err(EC_ZEBRA_VNI_DEL_FAILED, - "Failed to del VNI hash %p, VNI %u", zvni, - zvni->vni); + "Failed to del EVPN hash %p, VNI %u", zevpn, + zevpn->vni); return -1; } } else { @@ -5857,40 +1985,6 @@ static void zl3vni_del_nh_hash_entry(struct hash_bucket *bucket, void *ctx) zl3vni_nh_del(zl3vni, n); } -static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, - uint16_t cmd) -{ - struct zserv *client = NULL; - struct stream *s = NULL; - char buf[PREFIX_STRLEN]; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, cmd, vrf_id); - stream_put(s, p, sizeof(struct prefix)); - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send ip prefix %s %s on vrf %s", - prefix2str(p, buf, sizeof(buf)), - (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL", - vrf_id_to_name(vrf_id)); - - if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) - client->prefixadd_cnt++; - else - client->prefixdel_cnt++; - - return zserv_send_message(client, s); -} - /* re-add remote rmac if needed */ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) @@ -5910,1720 +2004,6 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni, return 0; } -/**************************** SYNC MAC handling *****************************/ -/* if the mac has been added of a mac-route from the peer - * or if it is being referenced by a neigh added by the - * peer we cannot let it age out i.e. we set the static bit - * in the dataplane - */ -static inline bool zebra_vxlan_mac_is_static(zebra_mac_t *mac) -{ - return ((mac->flags & ZEBRA_MAC_ALL_PEER_FLAGS) || - mac->sync_neigh_cnt); -} - -/* mac needs to be locally active or active on an ES peer */ -static inline bool zebra_vxlan_mac_is_ready_for_bgp(uint32_t flags) -{ - return (flags & ZEBRA_MAC_LOCAL) && - (!(flags & ZEBRA_MAC_LOCAL_INACTIVE) || - (flags & ZEBRA_MAC_ES_PEER_ACTIVE)); -} - -/* program sync mac flags in the dataplane */ -void zebra_vxlan_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, - bool force_clear_static, const char *caller) -{ - char macbuf[ETHER_ADDR_STRLEN]; - struct interface *ifp; - bool sticky; - bool set_static; - zebra_vni_t *zvni = mac->zvni; - vlanid_t vid; - struct zebra_if *zif; - struct interface *br_ifp; - - /* get the access vlan from the vxlan_device */ - zebra_vxlan_mac_get_access_info(mac, - &ifp, &vid); - - if (!ifp) { - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no access-port", - caller, - zvni->vni, - prefix_mac2str(&mac->macaddr, macbuf, - sizeof(macbuf)), - mac->es ? - mac->es->esi_str : "-", - mac->flags, - set_inactive ? "inactive " : ""); - return; - } - - zif = ifp->info; - br_ifp = zif->brslave_info.br_if; - if (!br_ifp) { - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no br", - caller, - zvni->vni, - prefix_mac2str(&mac->macaddr, macbuf, - sizeof(macbuf)), - mac->es ? - mac->es->esi_str : "-", - mac->flags, - set_inactive ? "inactive " : ""); - return; - } - - sticky = !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY); - if (force_clear_static) - set_static = false; - else - set_static = zebra_vxlan_mac_is_static(mac); - - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("dp-install sync-mac vni %u mac %s es %s 0x%x %s%s", - zvni->vni, - prefix_mac2str(&mac->macaddr, macbuf, - sizeof(macbuf)), - mac->es ? - mac->es->esi_str : "-", mac->flags, - set_static ? "static " : "", - set_inactive ? "inactive " : ""); - - dplane_local_mac_add(ifp, br_ifp, vid, &mac->macaddr, sticky, - set_static, set_inactive); - -} - -static void zebra_vxlan_mac_send_add_del_to_client(zebra_mac_t *mac, - bool old_bgp_ready, bool new_bgp_ready) -{ - if (new_bgp_ready) - zvni_mac_send_add_to_client(mac->zvni->vni, - &mac->macaddr, mac->flags, - mac->loc_seq, mac->es); - else if (old_bgp_ready) - zvni_mac_send_del_to_client(mac->zvni->vni, - &mac->macaddr, mac->flags, - true /* force */); -} - -/* MAC hold timer is used to age out peer-active flag. - * - * During this wait time we expect the dataplane component or an - * external neighmgr daemon to probe existing hosts to independently - * establish their presence on the ES. - */ -static int zebra_vxlan_mac_hold_exp_cb(struct thread *t) -{ - zebra_mac_t *mac; - bool old_bgp_ready; - bool new_bgp_ready; - bool old_static; - bool new_static; - char macbuf[ETHER_ADDR_STRLEN]; - - mac = THREAD_ARG(t); - /* the purpose of the hold timer is to age out the peer-active - * flag - */ - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) - return 0; - - old_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(mac->flags); - old_static = zebra_vxlan_mac_is_static(mac); - UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE); - new_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(mac->flags); - new_static = zebra_vxlan_mac_is_static(mac); - - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("sync-mac vni %u mac %s es %s 0x%x hold expired", - mac->zvni->vni, - prefix_mac2str(&mac->macaddr, macbuf, - sizeof(macbuf)), - mac->es ? - mac->es->esi_str : "-", - mac->flags); - - /* re-program the local mac in the dataplane if the mac is no - * longer static - */ - if (old_static != new_static) - zebra_vxlan_sync_mac_dp_install(mac, false /* set_inactive */, - false /* force_clear_static */, __func__); - - /* inform bgp if needed */ - if (old_bgp_ready != new_bgp_ready) - zebra_vxlan_mac_send_add_del_to_client(mac, - old_bgp_ready, new_bgp_ready); - - return 0; -} - -static inline void zebra_vxlan_mac_start_hold_timer(zebra_mac_t *mac) -{ - char macbuf[ETHER_ADDR_STRLEN]; - - if (mac->hold_timer) - return; - - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("sync-mac vni %u mac %s es %s 0x%x hold started", - mac->zvni->vni, - prefix_mac2str(&mac->macaddr, macbuf, - sizeof(macbuf)), - mac->es ? - mac->es->esi_str : "-", - mac->flags); - thread_add_timer(zrouter.master, - zebra_vxlan_mac_hold_exp_cb, - mac, zmh_info->mac_hold_time, - &mac->hold_timer); -} - -static inline void zebra_vxlan_mac_stop_hold_timer(zebra_mac_t *mac) -{ - char macbuf[ETHER_ADDR_STRLEN]; - - if (!mac->hold_timer) - return; - - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("sync-mac vni %u mac %s es %s 0x%x hold stopped", - mac->zvni->vni, - prefix_mac2str(&mac->macaddr, macbuf, - sizeof(macbuf)), - mac->es ? - mac->es->esi_str : "-", - mac->flags); - THREAD_OFF(mac->hold_timer); -} - -static inline void zebra_vxlan_mac_clear_sync_info(zebra_mac_t *mac) -{ - UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_PEER_FLAGS); - zebra_vxlan_mac_stop_hold_timer(mac); -} - -static void zebra_vxlan_sync_mac_del(zebra_mac_t *mac) -{ - char macbuf[ETHER_ADDR_STRLEN]; - bool old_static; - bool new_static; - - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("sync-mac del vni %u mac %s es %s seq %d f 0x%x", - mac->zvni->vni, - prefix_mac2str(&mac->macaddr, - macbuf, sizeof(macbuf)), - mac->es ? mac->es->esi_str : "-", - mac->loc_seq, - mac->flags); - old_static = zebra_vxlan_mac_is_static(mac); - UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) - zebra_vxlan_mac_start_hold_timer(mac); - new_static = zebra_vxlan_mac_is_static(mac); - - if (old_static != new_static) - /* program the local mac in the kernel */ - zebra_vxlan_sync_mac_dp_install(mac, false /* set_inactive */, - false /* force_clear_static */, __func__); -} - -static inline bool zebra_vxlan_mac_is_bgp_seq_ok(zebra_vni_t *zvni, - zebra_mac_t *mac, uint32_t seq, uint16_t ipa_len, - struct ipaddr *ipaddr) -{ - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - uint32_t tmp_seq; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) - tmp_seq = mac->loc_seq; - else - tmp_seq = mac->rem_seq; - - if (seq < tmp_seq) { - /* if the mac was never advertised to bgp we must accept - * whatever sequence number bgp sends - * XXX - check with Vivek - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) && - !zebra_vxlan_mac_is_ready_for_bgp(mac->flags)) { - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("sync-macip accept vni %u mac %s%s%s lower seq %u f 0x%x", - zvni->vni, - prefix_mac2str(&mac->macaddr, - macbuf, sizeof(macbuf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, - ipbuf, sizeof(ipbuf)) : "", - tmp_seq, mac->flags); - return true; - } - - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("sync-macip ignore vni %u mac %s%s%s as existing has higher seq %u f 0x%x", - zvni->vni, - prefix_mac2str(&mac->macaddr, - macbuf, sizeof(macbuf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, - ipbuf, sizeof(ipbuf)) : "", - tmp_seq, mac->flags); - return false; - } - - return true; -} - -/* sync-path that is active on an ES peer */ -static zebra_mac_t *zebra_vxlan_proc_sync_mac_update(zebra_vni_t *zvni, - struct ethaddr *macaddr, uint16_t ipa_len, - struct ipaddr *ipaddr, uint8_t flags, - uint32_t seq, esi_t *esi, - struct sync_mac_ip_ctx *ctx) -{ - zebra_mac_t *mac; - bool inform_bgp = false; - bool inform_dataplane = false; - bool seq_change = false; - bool es_change = false; - uint32_t tmp_seq; - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - bool old_local = false; - bool old_bgp_ready; - bool new_bgp_ready; - - mac = zvni_mac_lookup(zvni, macaddr); - if (!mac) { - /* if it is a new local path we need to inform both - * the control protocol and the data-plane - */ - inform_bgp = true; - inform_dataplane = true; - ctx->mac_created = true; - ctx->mac_inactive = true; - - /* create the MAC and associate it with the dest ES */ - mac = zvni_mac_add(zvni, macaddr); - zebra_evpn_es_mac_ref(mac, esi); - - /* local mac activated by an ES peer */ - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - /* if mac-only route setup peer flags */ - if (!ipa_len) { - if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) - SET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY); - else - SET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE); - } - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); - old_bgp_ready = false; - new_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(mac->flags); - } else { - uint32_t old_flags; - uint32_t new_flags; - bool old_static; - bool new_static; - bool sticky; - bool remote_gw; - - old_flags = mac->flags; - sticky = !!CHECK_FLAG(old_flags, ZEBRA_MAC_STICKY); - remote_gw = !!CHECK_FLAG(old_flags, ZEBRA_MAC_REMOTE_DEF_GW); - if (sticky || remote_gw) { - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("Ignore sync-macip vni %u mac %s%s%s%s%s", - zvni->vni, - prefix_mac2str(macaddr, - macbuf, sizeof(macbuf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, ipbuf, - sizeof(ipbuf)) : "", - sticky ? " sticky" : "", - remote_gw ? " remote_gw" : ""); - ctx->ignore_macip = true; - return NULL; - } - if (!zebra_vxlan_mac_is_bgp_seq_ok(zvni, mac, seq, - ipa_len, ipaddr)) { - ctx->ignore_macip = true; - return NULL; - } - - old_local = !!CHECK_FLAG(old_flags, ZEBRA_MAC_LOCAL); - old_static = zebra_vxlan_mac_is_static(mac); - - /* re-build the mac flags */ - new_flags = 0; - SET_FLAG(new_flags, ZEBRA_MAC_LOCAL); - /* retain old local activity flag */ - if (old_flags & ZEBRA_MAC_LOCAL) { - new_flags |= (old_flags & ZEBRA_MAC_LOCAL_INACTIVE); - } else { - new_flags |= ZEBRA_MAC_LOCAL_INACTIVE; - ctx->mac_inactive = true; - } - if (ipa_len) { - /* if mac-ip route do NOT update the peer flags - * i.e. retain only flags as is - */ - new_flags |= (old_flags & ZEBRA_MAC_ALL_PEER_FLAGS); - } else { - /* if mac-only route update peer flags */ - if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) { - SET_FLAG(new_flags, ZEBRA_MAC_ES_PEER_PROXY); - /* if the mac was peer-active previously we - * need to keep the flag and start the - * holdtimer on it. the peer-active flag is - * cleared on holdtimer expiry. - */ - if (CHECK_FLAG(old_flags, - ZEBRA_MAC_ES_PEER_ACTIVE)) { - SET_FLAG(new_flags, - ZEBRA_MAC_ES_PEER_ACTIVE); - zebra_vxlan_mac_start_hold_timer(mac); - } - } else { - SET_FLAG(new_flags, ZEBRA_MAC_ES_PEER_ACTIVE); - /* stop hold timer if a peer has verified - * reachability - */ - zebra_vxlan_mac_stop_hold_timer(mac); - } - } - mac->rem_seq = 0; - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - mac->flags = new_flags; - - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC && - (old_flags != new_flags)) - zlog_debug("sync-mac vni %u mac %s old_f 0x%x new_f 0x%x", - zvni->vni, - prefix_mac2str(macaddr, - macbuf, sizeof(macbuf)), - old_flags, mac->flags); - - /* update es */ - es_change = zebra_evpn_es_mac_ref(mac, esi); - /* if mac dest change - inform both sides */ - if (es_change) { - inform_bgp = true; - inform_dataplane = true; - ctx->mac_inactive = true; - } - /* if peer-flag is being set notify dataplane that the - * entry must not be expired because of local inactivity - */ - new_static = zebra_vxlan_mac_is_static(mac); - if (old_static != new_static) - inform_dataplane = true; - - old_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(old_flags); - new_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(mac->flags); - if (old_bgp_ready != new_bgp_ready) - inform_bgp = true; - } - - - /* update sequence number; if that results in a new local sequence - * inform bgp - */ - tmp_seq = MAX(mac->loc_seq, seq); - if (tmp_seq != mac->loc_seq) { - mac->loc_seq = tmp_seq; - seq_change = true; - inform_bgp = true; - } - - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("sync-mac %s vni %u mac %s es %s seq %d f 0x%x%s%s", - ctx->mac_created ? - "created" : "updated", - zvni->vni, - prefix_mac2str(macaddr, - macbuf, sizeof(macbuf)), - mac->es ? mac->es->esi_str : "-", - mac->loc_seq, mac->flags, - inform_bgp ? " inform_bgp" : "", - inform_dataplane ? " inform_dp" : ""); - - if (inform_bgp) - zebra_vxlan_mac_send_add_del_to_client(mac, - old_bgp_ready, new_bgp_ready); - - /* neighs using the mac may need to be re-sent to - * bgp with updated info - */ - if (seq_change || es_change || !old_local) - zvni_process_neigh_on_local_mac_change(zvni, mac, - seq_change, es_change); - - if (inform_dataplane) { - if (ipa_len) - /* if the mac is being created as a part of MAC-IP - * route wait for the neigh to be updated or - * created before programming the mac - */ - ctx->mac_dp_update_deferred = true; - else - /* program the local mac in the kernel. when the ES - * change we need to force the dataplane to reset - * the activity as we are yet to establish activity - * locally - */ - zebra_vxlan_sync_mac_dp_install(mac, - ctx->mac_inactive, - false /* force_clear_static */, - __func__); - } - - return mac; -} - -/**************************** SYNC neigh handling **************************/ -static inline bool zebra_vxlan_neigh_is_static(zebra_neigh_t *neigh) -{ - return !!(neigh->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS); -} - -static inline bool zebra_vxlan_neigh_is_ready_for_bgp(zebra_neigh_t *n) -{ - bool mac_ready; - bool neigh_ready; - - mac_ready = !!(n->mac->flags & ZEBRA_MAC_LOCAL); - neigh_ready = ((n->flags & ZEBRA_NEIGH_LOCAL) && - IS_ZEBRA_NEIGH_ACTIVE(n) && - (!(n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE) || - (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE))) ? - true : false; - - return mac_ready && neigh_ready; -} - -static void zebra_vxlan_sync_neigh_dp_install(zebra_neigh_t *n, - bool set_inactive, bool force_clear_static, const char *caller) -{ - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - struct zebra_ns *zns; - struct interface *ifp; - bool set_static; - bool set_router; - - zns = zebra_ns_lookup(NS_DEFAULT); - ifp = if_lookup_by_index_per_ns(zns, n->ifindex); - if (!ifp) { - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("%s: dp-install sync-neigh vni %u ip %s mac %s if %d f 0x%x skipped", - caller, n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - n->ifindex, n->flags); - return; - } - - if (force_clear_static) - set_static = false; - else - set_static = zebra_vxlan_neigh_is_static(n); - - set_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - - /* XXX - this will change post integration with the new kernel */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) - set_inactive = true; - - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("%s: dp-install sync-neigh vni %u ip %s mac %s if %s(%d) f 0x%x%s%s%s", - caller, n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - ifp->name, n->ifindex, n->flags, - set_router ? " router":"", - set_static ? " static":"", - set_inactive ? " inactive":""); - dplane_local_neigh_add(ifp, &n->ip, - &n->emac, set_router, set_static, set_inactive); -} - -static void zebra_vxlan_neigh_send_add_del_to_client(zebra_neigh_t *n, - bool old_bgp_ready, bool new_bgp_ready) -{ - if (new_bgp_ready) - zvni_neigh_send_add_to_client(n->zvni->vni, &n->ip, - &n->emac, n->mac, n->flags, n->loc_seq); - else if (old_bgp_ready) - zvni_neigh_send_del_to_client(n->zvni->vni, &n->ip, - &n->emac, n->flags, n->state, true /*force*/); -} - -/* if the static flag associated with the neigh changes we need - * to update the sync-neigh references against the MAC - * and inform the dataplane about the static flag changes. - */ -static void zebra_vxlan_sync_neigh_static_chg(zebra_neigh_t *n, - bool old_n_static, bool new_n_static, - bool defer_n_dp, bool defer_mac_dp, - const char *caller) -{ - zebra_mac_t *mac = n->mac; - bool old_mac_static; - bool new_mac_static; - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - - if (old_n_static == new_n_static) - return; - - /* update the neigh sync references in the dataplane. if - * the neigh is in the middle of updates the caller can - * request for a defer - */ - if (!defer_n_dp) - zebra_vxlan_sync_neigh_dp_install(n, false /* set_inactive */, - false /* force_clear_static */, __func__); - - if (!mac) - return; - - /* update the mac sync ref cnt */ - old_mac_static = zebra_vxlan_mac_is_static(mac); - if (new_n_static) { - ++mac->sync_neigh_cnt; - } else if (old_n_static) { - if (mac->sync_neigh_cnt) - --mac->sync_neigh_cnt; - } - new_mac_static = zebra_vxlan_mac_is_static(mac); - - /* update the mac sync references in the dataplane */ - if ((old_mac_static != new_mac_static) && !defer_mac_dp) - zebra_vxlan_sync_mac_dp_install(mac, - false /* set_inactive */, - false /* force_clear_static */, - __func__); - - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-neigh ref-chg vni %u ip %s mac %s f 0x%x %d%s%s%s%s by %s", - n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - n->flags, mac->sync_neigh_cnt, - old_n_static ? " old_n_static" : "", - new_n_static ? " new_n_static" : "", - old_mac_static ? " old_mac_static" : "", - new_mac_static ? " new_mac_static" : "", - caller); -} - -/* Neigh hold timer is used to age out peer-active flag. - * - * During this wait time we expect the dataplane component or an - * external neighmgr daemon to probe existing hosts to independently - * establish their presence on the ES. - */ -static int zebra_vxlan_neigh_hold_exp_cb(struct thread *t) -{ - zebra_neigh_t *n; - bool old_bgp_ready; - bool new_bgp_ready; - bool old_n_static; - bool new_n_static; - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - - n = THREAD_ARG(t); - /* the purpose of the hold timer is to age out the peer-active - * flag - */ - if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) - return 0; - - old_bgp_ready = zebra_vxlan_neigh_is_ready_for_bgp(n); - old_n_static = zebra_vxlan_neigh_is_static(n); - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); - new_bgp_ready = zebra_vxlan_neigh_is_ready_for_bgp(n); - new_n_static = zebra_vxlan_neigh_is_static(n); - - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold expired", - n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - n->flags); - - /* re-program the local neigh in the dataplane if the neigh is no - * longer static - */ - if (old_n_static != new_n_static) - zebra_vxlan_sync_neigh_static_chg(n, old_n_static, - new_n_static, false /*defer_n_dp*/, - false /*defer_mac_dp*/, __func__); - - /* inform bgp if needed */ - if (old_bgp_ready != new_bgp_ready) - zebra_vxlan_neigh_send_add_del_to_client(n, - old_bgp_ready, new_bgp_ready); - - return 0; -} - -static inline void zebra_vxlan_neigh_start_hold_timer(zebra_neigh_t *n) -{ - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - - if (n->hold_timer) - return; - - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold start", - n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - n->flags); - thread_add_timer(zrouter.master, - zebra_vxlan_neigh_hold_exp_cb, - n, zmh_info->neigh_hold_time, - &n->hold_timer); -} - -static inline void zebra_vxlan_neigh_stop_hold_timer(zebra_neigh_t *n) -{ - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - - if (!n->hold_timer) - return; - - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold stop", - n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - n->flags); - THREAD_OFF(n->hold_timer); -} - -static inline bool zebra_vxlan_neigh_clear_sync_info(zebra_neigh_t *n) -{ - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - bool old_n_static = false; - bool new_n_static = false; - - if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) { - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x clear", - n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - n->flags); - - old_n_static = zebra_vxlan_neigh_is_static(n); - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_PEER_FLAGS); - new_n_static = zebra_vxlan_neigh_is_static(n); - if (old_n_static != new_n_static) - zebra_vxlan_sync_neigh_static_chg(n, old_n_static, - new_n_static, true /*defer_dp)*/, - false/*defer_mac_dp*/, __func__); - } - zebra_vxlan_neigh_stop_hold_timer(n); - - /* if the neigh static flag changed inform that a dp - * re-install maybe needed - */ - return old_n_static != new_n_static; -} - -static void zebra_vxlan_local_neigh_deref_mac(zebra_neigh_t *n, - bool send_mac_update) -{ - zebra_mac_t *mac = n->mac; - zebra_vni_t *zvni = n->zvni; - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - bool old_static; - bool new_static; - - n->mac = NULL; - if (!mac) - return; - - if ((n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) && - mac->sync_neigh_cnt){ - old_static = zebra_vxlan_mac_is_static(mac); - --mac->sync_neigh_cnt; - new_static = zebra_vxlan_mac_is_static(mac); - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-neigh deref mac vni %u ip %s mac %s ref %d", - n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, - sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - mac->sync_neigh_cnt); - if ((old_static != new_static) && send_mac_update) - /* program the local mac in the kernel */ - zebra_vxlan_sync_mac_dp_install(mac, - false /* set_inactive */, - false /* force_clear_static */, - __func__); - } - - listnode_delete(mac->neigh_list, n); - zvni_deref_ip2mac(zvni, mac); -} - -static void zebra_vxlan_local_neigh_ref_mac(zebra_neigh_t *n, - struct ethaddr *macaddr, zebra_mac_t *mac, - bool send_mac_update) -{ - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - bool old_static; - bool new_static; - - memcpy(&n->emac, macaddr, ETH_ALEN); - n->mac = mac; - - /* Link to new MAC */ - if (!mac) - return; - - listnode_add_sort(mac->neigh_list, n); - if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) { - old_static = zebra_vxlan_mac_is_static(mac); - ++mac->sync_neigh_cnt; - new_static = zebra_vxlan_mac_is_static(mac); - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-neigh ref mac vni %u ip %s mac %s ref %d", - n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, - sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - mac->sync_neigh_cnt); - if ((old_static != new_static) && send_mac_update) - /* program the local mac in the kernel */ - zebra_vxlan_sync_mac_dp_install(mac, - false /*set_inactive*/, - false /*force_clear_static*/, - __func__); - } -} - -static inline bool zebra_vxlan_neigh_is_bgp_seq_ok(zebra_vni_t *zvni, - zebra_neigh_t *n, struct ethaddr *macaddr, uint32_t seq) -{ - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - uint32_t tmp_seq; - - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) - tmp_seq = n->loc_seq; - else - tmp_seq = n->rem_seq; - - if (seq < tmp_seq) { - /* if the neigh was never advertised to bgp we must accept - * whatever sequence number bgp sends - * XXX - check with Vivek - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) && - !zebra_vxlan_neigh_is_ready_for_bgp(n)) { - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-macip accept vni %u mac %s IP %s lower seq %u f 0x%x", - zvni->vni, - prefix_mac2str(macaddr, - macbuf, sizeof(macbuf)), - ipaddr2str(&n->ip, - ipbuf, sizeof(ipbuf)), - tmp_seq, n->flags); - return true; - } - - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-macip ignore vni %u mac %s IP %s as existing has higher seq %u f 0x%x", - zvni->vni, - prefix_mac2str(macaddr, - macbuf, sizeof(macbuf)), - ipaddr2str(&n->ip, - ipbuf, sizeof(ipbuf)), - tmp_seq, n->flags); - return false; - } - - return true; -} - -static void zebra_vxlan_sync_neigh_del(zebra_neigh_t *n) -{ - bool old_n_static; - bool new_n_static; - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-neigh del vni %u ip %s mac %s f 0x%x", - n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - n->flags); - - old_n_static = zebra_vxlan_neigh_is_static(n); - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) - zebra_vxlan_neigh_start_hold_timer(n); - new_n_static = zebra_vxlan_neigh_is_static(n); - - if (old_n_static != new_n_static) - zebra_vxlan_sync_neigh_static_chg(n, old_n_static, - new_n_static, false /*defer-dp*/, - false /*defer_mac_dp*/, __func__); -} - -static zebra_neigh_t *zebra_vxlan_proc_sync_neigh_update(zebra_vni_t *zvni, - zebra_neigh_t *n, uint16_t ipa_len, - struct ipaddr *ipaddr, uint8_t flags, uint32_t seq, - esi_t *esi, struct sync_mac_ip_ctx *ctx) -{ - struct interface *ifp = NULL; - bool is_router; - zebra_mac_t *mac = ctx->mac; - uint32_t tmp_seq; - bool old_router = false; - bool old_bgp_ready = false; - bool new_bgp_ready; - bool inform_dataplane = false; - bool inform_bgp = false; - bool old_mac_static; - bool new_mac_static; - bool set_dp_inactive = false; - struct zebra_if *zif; - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - bool created; - ifindex_t ifindex = 0; - - /* locate l3-svi */ - zif = zvni->vxlan_if->info; - if (zif) { - struct zebra_l2info_vxlan *vxl; - - vxl = &zif->l2info.vxl; - ifp = zvni_map_to_svi(vxl->access_vlan, - zif->brslave_info.br_if); - if (ifp) - ifindex = ifp->ifindex; - } - - is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); - old_mac_static = zebra_vxlan_mac_is_static(mac); - - if (!n) { - uint32_t n_flags = 0; - - /* New neighbor - create */ - SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL); - if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) - SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_PROXY); - else - SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); - SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL_INACTIVE); - - n = zvni_neigh_add(zvni, ipaddr, &mac->macaddr, mac, - n_flags); - n->ifindex = ifindex; - ZEBRA_NEIGH_SET_ACTIVE(n); - - created = true; - inform_dataplane = true; - inform_bgp = true; - set_dp_inactive = true; - } else { - bool mac_change; - uint32_t old_flags = n->flags; - bool old_n_static; - bool new_n_static; - - created = false; - old_n_static = zebra_vxlan_neigh_is_static(n); - old_bgp_ready = zebra_vxlan_neigh_is_ready_for_bgp(n); - old_router = !!CHECK_FLAG(n->flags, - ZEBRA_NEIGH_ROUTER_FLAG); - - mac_change = !!memcmp(&n->emac, &mac->macaddr, ETH_ALEN); - - /* deref and clear old info */ - if (mac_change) { - if (old_bgp_ready) { - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, n->flags, n->state, - false /*force*/); - old_bgp_ready = false; - } - if (n->mac) - zebra_vxlan_local_neigh_deref_mac(n, - false /*send_mac_update*/); - } - /* clear old fwd info */ - n->rem_seq = 0; - n->r_vtep_ip.s_addr = 0; - - /* setup new flags */ - n->flags = 0; - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - /* retain activity flag if the neigh was - * previously local - */ - if (old_flags & ZEBRA_NEIGH_LOCAL) { - n->flags |= (old_flags & ZEBRA_NEIGH_LOCAL_INACTIVE); - } else { - inform_dataplane = true; - set_dp_inactive = true; - n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE; - } - - if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) - SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); - else - SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); - - if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) { - SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); - /* if the neigh was peer-active previously we - * need to keep the flag and start the - * holdtimer on it. the peer-active flag is - * cleared on holdtimer expiry. - */ - if (CHECK_FLAG(old_flags, - ZEBRA_NEIGH_ES_PEER_ACTIVE)) { - SET_FLAG(n->flags, - ZEBRA_NEIGH_ES_PEER_ACTIVE); - zebra_vxlan_neigh_start_hold_timer(n); - } - } else { - SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); - /* stop hold timer if a peer has verified - * reachability - */ - zebra_vxlan_neigh_stop_hold_timer(n); - } - ZEBRA_NEIGH_SET_ACTIVE(n); - - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH && - (old_flags != n->flags)) - zlog_debug("sync-neigh vni %u ip %s mac %s old_f 0x%x new_f 0x%x", - n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - old_flags, n->flags); - - new_n_static = zebra_vxlan_neigh_is_static(n); - if (mac_change) { - set_dp_inactive = true; - n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE; - inform_dataplane = true; - zebra_vxlan_local_neigh_ref_mac(n, &mac->macaddr, - mac, false /*send_mac_update*/); - } else if (old_n_static != new_n_static) { - inform_dataplane = true; - /* if static flags have changed without a mac change - * we need to create the correct sync-refs against - * the existing mac - */ - zebra_vxlan_sync_neigh_static_chg(n, - old_n_static, new_n_static, - true /*defer_dp*/, true /*defer_mac_dp*/, - __func__); - } - - /* Update the forwarding info. */ - if (n->ifindex != ifindex) { - n->ifindex = ifindex; - inform_dataplane = true; - } - } - - /* update the neigh seq. we don't bother with the mac seq as - * sync_mac_update already took care of that - */ - tmp_seq = MAX(n->loc_seq, seq); - if (tmp_seq != n->loc_seq) { - n->loc_seq = tmp_seq; - inform_bgp = true; - } - - /* Mark Router flag (R-bit) */ - if (is_router) - SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - else - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - - if (old_router != is_router) - inform_dataplane = true; - - new_bgp_ready = zebra_vxlan_neigh_is_ready_for_bgp(n); - if (old_bgp_ready != new_bgp_ready) - inform_bgp = true; - - new_mac_static = zebra_vxlan_mac_is_static(mac); - if ((old_mac_static != new_mac_static) || - ctx->mac_dp_update_deferred) - zebra_vxlan_sync_mac_dp_install(mac, - ctx->mac_inactive, - false /* force_clear_static */, - __func__); - - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync-neigh %s vni %u ip %s mac %s if %s(%d) seq %d f 0x%x%s%s", - created ? - "created" : "updated", - n->zvni->vni, - ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), - prefix_mac2str(&n->emac, macbuf, - sizeof(macbuf)), - ifp ? ifp->name : "", ifindex, - n->loc_seq, n->flags, - inform_bgp ? " inform_bgp" : "", - inform_dataplane ? " inform_dp" : ""); - - if (inform_dataplane) - zebra_vxlan_sync_neigh_dp_install(n, set_dp_inactive, - false /* force_clear_static */, __func__); - - if (inform_bgp) - zebra_vxlan_neigh_send_add_del_to_client(n, - old_bgp_ready, new_bgp_ready); - - return n; -} - -static void zebra_vxlan_process_sync_macip_add(zebra_vni_t *zvni, - struct ethaddr *macaddr, - uint16_t ipa_len, - struct ipaddr *ipaddr, - uint8_t flags, - uint32_t seq, - esi_t *esi) -{ - struct sync_mac_ip_ctx ctx; - char macbuf[ETHER_ADDR_STRLEN]; - char ipbuf[INET6_ADDRSTRLEN]; - bool sticky; - bool remote_gw; - zebra_neigh_t *n = NULL; - - sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); - remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - /* if sticky or remote-gw ignore updates from the peer */ - if (sticky || remote_gw) { - if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH || - IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("Ignore sync-macip vni %u mac %s%s%s%s%s", - zvni->vni, - prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, ipbuf, sizeof(ipbuf)) : "", - sticky ? " sticky" : "", - remote_gw ? " remote_gw" : ""); - return; - } - - if (ipa_len) { - n = zvni_neigh_lookup(zvni, ipaddr); - if (n && - !zebra_vxlan_neigh_is_bgp_seq_ok(zvni, - n, macaddr, seq)) - return; - } - - memset(&ctx, 0, sizeof(ctx)); - ctx.mac = zebra_vxlan_proc_sync_mac_update(zvni, macaddr, ipa_len, - ipaddr, flags, seq, esi, &ctx); - if (ctx.ignore_macip || !ctx.mac || !ipa_len) - return; - - zebra_vxlan_proc_sync_neigh_update(zvni, n, ipa_len, - ipaddr, flags, seq, esi, &ctx); -} - -/************************** remote mac-ip handling **************************/ -/* Process a remote MACIP add from BGP. */ -static void process_remote_macip_add(vni_t vni, - struct ethaddr *macaddr, - uint16_t ipa_len, - struct ipaddr *ipaddr, - uint8_t flags, - uint32_t seq, - struct in_addr vtep_ip, - esi_t *esi) -{ - zebra_vni_t *zvni; - zebra_vtep_t *zvtep; - zebra_mac_t *mac = NULL, *old_mac = NULL; - zebra_neigh_t *n = NULL; - int update_mac = 0, update_neigh = 0; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - struct interface *ifp = NULL; - struct zebra_if *zif = NULL; - struct zebra_vrf *zvrf; - uint32_t tmp_seq; - bool sticky; - bool remote_gw; - bool is_router; - bool do_dad = false; - bool is_dup_detect = false; - esi_t *old_esi; - bool old_static = false; - - /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_warn("Unknown VNI %u upon remote MACIP ADD", vni); - return; - } - - ifp = zvni->vxlan_if; - if (ifp) - zif = ifp->info; - if (!ifp || - !if_is_operative(ifp) || - !zif || - !zif->brslave_info.br_if) { - zlog_warn("Ignoring remote MACIP ADD VNI %u, invalid interface state or info", - vni); - return; - } - - /* Type-2 routes from another PE can be interpreted as remote or - * SYNC based on the destination ES - - * SYNC - if ES is local - * REMOTE - if ES is not local - */ - if (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) { - zebra_vxlan_process_sync_macip_add(zvni, macaddr, ipa_len, - ipaddr, flags, seq, esi); - return; - } - - /* The remote VTEP specified should normally exist, but it is - * possible that when peering comes up, peer may advertise MACIP - * routes before advertising type-3 routes. - */ - if (vtep_ip.s_addr) { - zvtep = zvni_vtep_find(zvni, &vtep_ip); - if (!zvtep) { - zvtep = zvni_vtep_add(zvni, &vtep_ip, - VXLAN_FLOOD_DISABLED); - if (!zvtep) { - flog_err( - EC_ZEBRA_VTEP_ADD_FAILED, - "Failed to add remote VTEP, VNI %u zvni %p upon remote MACIP ADD", - vni, zvni); - return; - } - - zvni_vtep_install(zvni, zvtep); - } - } - - sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); - remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); - - mac = zvni_mac_lookup(zvni, macaddr); - - /* Ignore if the mac is already present as a gateway mac */ - if (mac && - CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW) && - CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", - vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, buf1, sizeof(buf1)) : ""); - return; - } - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - if (!zvrf) - return; - - old_esi = (mac && mac->es) ? &mac->es->esi : zero_esi; - - /* check if the remote MAC is unknown or has a change. - * If so, that needs to be updated first. Note that client could - * install MAC and MACIP separately or just install the latter. - */ - if (!mac - || !CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) - || sticky != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) - || remote_gw != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW) - || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip) - || memcmp(old_esi, esi, sizeof(esi_t)) - || seq != mac->rem_seq) - update_mac = 1; - - if (update_mac) { - if (!mac) { - mac = zvni_mac_add(zvni, macaddr); - if (!mac) { - zlog_warn( - "Failed to add MAC %s VNI %u Remote VTEP %s", - prefix_mac2str(macaddr, buf, - sizeof(buf)), - vni, inet_ntoa(vtep_ip)); - return; - } - - zebra_evpn_es_mac_ref(mac, esi); - - /* Is this MAC created for a MACIP? */ - if (ipa_len) - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - } else { - zebra_evpn_es_mac_ref(mac, esi); - - /* When host moves but changes its (MAC,IP) - * binding, BGP may install a MACIP entry that - * corresponds to "older" location of the host - * in transient situations (because {IP1,M1} - * is a different route from {IP1,M2}). Check - * the sequence number and ignore this update - * if appropriate. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) - tmp_seq = mac->loc_seq; - else - tmp_seq = mac->rem_seq; - - if (seq < tmp_seq) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing MAC has higher seq %u flags 0x%x", - vni, - prefix_mac2str(macaddr, - buf, sizeof(buf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, - buf1, sizeof(buf1)) : "", - tmp_seq, mac->flags); - return; - } - } - - /* Check MAC's curent state is local (this is the case - * where MAC has moved from L->R) and check previous - * detection started via local learning. - * RFC-7432: A PE/VTEP that detects a MAC mobility - * event via local learning starts an M-second timer. - * - * VTEP-IP or seq. change alone is not considered - * for dup. detection. - * - * MAC is already marked duplicate set dad, then - * is_dup_detect will be set to not install the entry. - */ - if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) && - mac->dad_count) || - CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - do_dad = true; - - /* Remove local MAC from BGP. */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - /* force drop the sync flags */ - old_static = zebra_vxlan_mac_is_static(mac); - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("sync-mac->remote vni %u mac %s es %s seq %d f 0x%x", - zvni->vni, - prefix_mac2str(macaddr, - buf, sizeof(buf)), - mac->es ? - mac->es->esi_str : "-", - mac->loc_seq, - mac->flags); - zebra_vxlan_mac_clear_sync_info(mac); - zvni_mac_send_del_to_client(zvni->vni, macaddr, - mac->flags, false /* force */); - } - - /* Set "auto" and "remote" forwarding info. */ - UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - mac->fwd_info.r_vtep_ip = vtep_ip; - - if (sticky) - SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - else - UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - - if (remote_gw) - SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); - else - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); - - zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, - mac->fwd_info.r_vtep_ip, - do_dad, &is_dup_detect, - false); - - if (!is_dup_detect) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); - /* Install the entry. */ - zvni_rem_mac_install(zvni, mac, old_static); - } - } - - /* Update seq number. */ - mac->rem_seq = seq; - - /* If there is no IP, return after clearing AUTO flag of MAC. */ - if (!ipa_len) { - UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - return; - } - - /* Reset flag */ - do_dad = false; - old_static = false; - - /* Check if the remote neighbor itself is unknown or has a - * change. If so, create or update and then install the entry. - */ - n = zvni_neigh_lookup(zvni, ipaddr); - if (!n - || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) - || is_router != !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG) - || (memcmp(&n->emac, macaddr, sizeof(*macaddr)) != 0) - || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip) - || seq != n->rem_seq) - update_neigh = 1; - - if (update_neigh) { - if (!n) { - n = zvni_neigh_add(zvni, ipaddr, macaddr, mac, 0); - if (!n) { - zlog_warn( - "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s", - ipaddr2str(ipaddr, buf1, - sizeof(buf1)), - prefix_mac2str(macaddr, buf, - sizeof(buf)), - vni, inet_ntoa(vtep_ip)); - return; - } - - } else { - const char *n_type; - - /* When host moves but changes its (MAC,IP) - * binding, BGP may install a MACIP entry that - * corresponds to "older" location of the host - * in transient situations (because {IP1,M1} - * is a different route from {IP1,M2}). Check - * the sequence number and ignore this update - * if appropriate. - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - tmp_seq = n->loc_seq; - n_type = "local"; - } else { - tmp_seq = n->rem_seq; - n_type = "remote"; - } - if (seq < tmp_seq) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s Neigh has higher seq %u", - vni, - prefix_mac2str(macaddr, - buf, sizeof(buf)), - " IP ", - ipaddr2str(ipaddr, buf1, sizeof(buf1)), - n_type, - tmp_seq); - return; - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - old_static = zebra_vxlan_neigh_is_static(n); - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("sync->remote neigh vni %u ip %s mac %s seq %d f0x%x", - n->zvni->vni, - ipaddr2str(&n->ip, buf1, - sizeof(buf1)), - prefix_mac2str(&n->emac, buf, - sizeof(buf)), - seq, n->flags); - zebra_vxlan_neigh_clear_sync_info(n); - if (IS_ZEBRA_NEIGH_ACTIVE(n)) - zvni_mac_send_del_to_client(zvni->vni, - macaddr, mac->flags, - false /*force*/); - } - if (memcmp(&n->emac, macaddr, sizeof(*macaddr)) != 0) { - /* update neigh list for macs */ - old_mac = zvni_mac_lookup(zvni, &n->emac); - if (old_mac) { - listnode_delete(old_mac->neigh_list, n); - n->mac = NULL; - zvni_deref_ip2mac(zvni, old_mac); - } - n->mac = mac; - listnode_add_sort(mac->neigh_list, n); - memcpy(&n->emac, macaddr, ETH_ALEN); - - /* Check Neigh's curent state is local - * (this is the case where neigh/host has moved - * from L->R) and check previous detction - * started via local learning. - * - * RFC-7432: A PE/VTEP that detects a MAC - * mobilit event via local learning starts - * an M-second timer. - * VTEP-IP or seq. change along is not - * considered for dup. detection. - * - * Mobilty event scenario-B IP-MAC binding - * changed. - */ - if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - && n->dad_count) - do_dad = true; - - } - } - - /* Set "remote" forwarding info. */ - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS); - n->r_vtep_ip = vtep_ip; - SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); - - /* Set router flag (R-bit) to this Neighbor entry */ - if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG)) - SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - else - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - - /* Check old or new MAC detected as duplicate, - * inherit duplicate flag to this neigh. - */ - if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_mac, - mac, n)) { - flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC", - zvni->vni, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - ipaddr2str(&n->ip, buf1, sizeof(buf1))); - } - - /* Check duplicate address detection for IP */ - zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, - n->r_vtep_ip, - do_dad, - &is_dup_detect, - false); - /* Install the entry. */ - if (!is_dup_detect) - zvni_rem_neigh_install(zvni, n, old_static); - } - - zvni_probe_neigh_on_mac_add(zvni, mac); - - /* Update seq number. */ - n->rem_seq = seq; -} - -static void zebra_vxlan_rem_mac_del(zebra_vni_t *zvni, - zebra_mac_t *mac) -{ - zvni_process_neigh_on_remote_mac_del(zvni, mac); - /* the remote sequence number in the auto mac entry - * needs to be reset to 0 as the mac entry may have - * been removed on all VTEPs (including - * the originating one) - */ - mac->rem_seq = 0; - - /* If all remote neighbors referencing a remote MAC - * go away, we need to uninstall the MAC. - */ - if (remote_neigh_count(mac) == 0) { - zvni_rem_mac_uninstall(zvni, mac); - zebra_evpn_es_mac_deref_entry(mac); - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - } - - if (list_isempty(mac->neigh_list)) - zvni_mac_del(zvni, mac); - else - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); -} - -/* Process a remote MACIP delete from BGP. */ -static void process_remote_macip_del(vni_t vni, - struct ethaddr *macaddr, - uint16_t ipa_len, - struct ipaddr *ipaddr, - struct in_addr vtep_ip) -{ - zebra_vni_t *zvni; - zebra_mac_t *mac = NULL; - zebra_neigh_t *n = NULL; - struct interface *ifp = NULL; - struct zebra_if *zif = NULL; - struct zebra_ns *zns; - struct zebra_l2info_vxlan *vxl; - struct zebra_vrf *zvrf; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - - /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni); - return; - } - - ifp = zvni->vxlan_if; - if (ifp) - zif = ifp->info; - if (!ifp || - !if_is_operative(ifp) || - !zif || - !zif->brslave_info.br_if) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignoring remote MACIP DEL VNI %u, invalid interface state or info", - vni); - return; - } - zns = zebra_ns_lookup(NS_DEFAULT); - vxl = &zif->l2info.vxl; - - mac = zvni_mac_lookup(zvni, macaddr); - if (ipa_len) - n = zvni_neigh_lookup(zvni, ipaddr); - - if (n && !mac) { - zlog_warn("Failed to locate MAC %s for neigh %s VNI %u upon remote MACIP DEL", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ipaddr, buf1, sizeof(buf1)), vni); - return; - } - - /* If the remote mac or neighbor doesn't exist there is nothing - * more to do. Otherwise, uninstall the entry and then remove it. - */ - if (!mac && !n) - return; - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - - /* Ignore the delete if this mac is a gateway mac-ip */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) - && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) { - zlog_warn( - "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", - vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, buf1, sizeof(buf1)) : ""); - return; - } - - /* Uninstall remote neighbor or MAC. */ - if (n) { - if (zvrf->dad_freeze && - CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE) && - CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) && - (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0)) { - struct interface *vlan_if; - - vlan_if = zvni_map_to_svi(vxl->access_vlan, - zif->brslave_info.br_if); - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry", - __func__, - ipaddr2str(ipaddr, buf1, sizeof(buf1)), - n->flags, - vlan_if ? vlan_if->name : "Unknown"); - if (vlan_if) - neigh_read_specific_ip(ipaddr, vlan_if); - } - - /* When the MAC changes for an IP, it is possible the - * client may update the new MAC before trying to delete the - * "old" neighbor (as these are two different MACIP routes). - * Do the delete only if the MAC matches. - */ - if (!memcmp(n->emac.octet, macaddr->octet, ETH_ALEN)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - zebra_vxlan_sync_neigh_del(n); - } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_neigh_uninstall(zvni, n); - zvni_neigh_del(zvni, n); - zvni_deref_ip2mac(zvni, mac); - } - } - } else { - /* DAD: when MAC is freeze state as remote learn event, - * remote mac-ip delete event is received will result in freeze - * entry removal, first fetch kernel for the same entry present - * as LOCAL and reachable, avoid deleting this entry instead - * use kerenel local entry to update during unfreeze time. - */ - if (zvrf->dad_freeze && - CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE) && - CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: MAC %s (flags 0x%x) is remote and duplicate, read kernel for local entry", - __func__, - prefix_mac2str(macaddr, buf, - sizeof(buf)), - mac->flags); - macfdb_read_specific_mac(zns, zif->brslave_info.br_if, - macaddr, vxl->access_vlan); - } - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - if (!ipa_len) - zebra_vxlan_sync_mac_del(mac); - } else if (CHECK_FLAG(mac->flags, ZEBRA_NEIGH_REMOTE)) { - zebra_vxlan_rem_mac_del(zvni, mac); - } - } -} - - /* Public functions */ int is_l3vni_for_prefix_routes_only(vni_t vni) @@ -8017,22 +2397,22 @@ void zebra_vxlan_print_vrf_vni(struct vty *vty, struct zebra_vrf *zvrf, void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (!num_neigh) return; @@ -8044,21 +2424,22 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, * the maximum width. */ memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; wctx.json = json; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); if (!use_json) { vty_out(vty, "Number of ARPs (local and remote) known for this VNI: %u\n", num_neigh); - zvni_print_neigh_hdr(vty, &wctx); + zebra_evpn_print_neigh_hdr(vty, &wctx); } else json_object_int_add(json, "numArpNd", num_neigh); - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); @@ -8085,9 +2466,9 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; - hash_iterate(zvrf->vni_table, + hash_iterate(zvrf->evpn_table, (void (*)(struct hash_bucket *, - void *))zvni_print_neigh_hash_all_vni, + void *))zevpn_print_neigh_hash_all_evpn, args); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -8116,9 +2497,9 @@ void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty, args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; - hash_iterate(zvrf->vni_table, + hash_iterate(zvrf->evpn_table, (void (*)(struct hash_bucket *, - void *))zvni_print_neigh_hash_all_vni_detail, + void *))zevpn_print_neigh_hash_all_evpn_detail, args); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -8134,21 +2515,21 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_neigh_t *n; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - n = zvni_neigh_lookup(zvni, ip); + n = zebra_evpn_neigh_lookup(zevpn, ip); if (!n) { if (!use_json) vty_out(vty, @@ -8159,7 +2540,7 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, if (use_json) json = json_object_new_object(); - zvni_print_neigh(n, vty, json); + zebra_evpn_print_neigh(n, vty, json); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -8176,22 +2557,22 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct in_addr vtep_ip, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (!num_neigh) return; @@ -8199,14 +2580,15 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, json = json_object_new_object(); memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; wctx.json = json; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -8224,7 +2606,7 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; json_object *json = NULL; @@ -8232,17 +2614,17 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (!num_neigh) return; - num_neigh = num_dup_detected_neighs(zvni); + num_neigh = num_dup_detected_neighs(zevpn); if (!num_neigh) return; @@ -8254,11 +2636,12 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, * the maximum width. */ memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; wctx.json = json; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); if (!use_json) { vty_out(vty, @@ -8270,7 +2653,8 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, } else json_object_int_add(json, "numArpNd", num_neigh); - hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_dad_neigh_hash, + &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -8285,7 +2669,7 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_macs; struct mac_walk_ctx wctx; json_object *json = NULL; @@ -8293,15 +2677,15 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; @@ -8311,7 +2695,7 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, } memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.json = json_mac; @@ -8327,7 +2711,7 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, } else json_object_int_add(json, "numMacs", num_macs); - hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, &wctx); if (use_json) { json_object_object_add(json, "macs", json_mac); @@ -8358,7 +2742,7 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, wctx.vty = vty; wctx.json = json; wctx.print_dup = print_dup; - hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); + hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -8389,7 +2773,7 @@ void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty, wctx.vty = vty; wctx.json = json; wctx.print_dup = print_dup; - hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni_detail, + hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn_detail, &wctx); if (use_json) { @@ -8420,7 +2804,7 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; wctx.json = json; - hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); + hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -8436,22 +2820,22 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *macaddr, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_mac_t *mac; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { if (use_json) vty_out(vty, "{}\n"); @@ -8465,7 +2849,7 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, if (use_json) json = json_object_new_object(); - zvni_print_mac(mac, vty, json); + zebra_evpn_print_mac(mac, vty, json); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); @@ -8478,7 +2862,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct mac_walk_ctx wctx; uint32_t num_macs; json_object *json = NULL; @@ -8487,17 +2871,17 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; - num_macs = num_dup_detected_macs(zvni); + num_macs = num_dup_detected_macs(zevpn); if (!num_macs) return; @@ -8507,7 +2891,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, } memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.json = json_mac; @@ -8520,7 +2904,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, } else json_object_int_add(json, "numMacs", num_macs); - hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, &wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_dad_mac_hash, &wctx); if (use_json) { json_object_object_add(json, "macs", json_mac); @@ -8534,7 +2918,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *macaddr) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_mac_t *mac; struct listnode *node = NULL; zebra_neigh_t *nbr = NULL; @@ -8542,13 +2926,13 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, if (!is_evpn_enabled()) return 0; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_warn("VNI %u does not exist\n", vni); return -1; } - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { zlog_warn("Requested MAC does not exist in VNI %u\n", vni); return -1; @@ -8573,8 +2957,8 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) ZEBRA_NEIGH_SET_INACTIVE(nbr); else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) - zvni_rem_neigh_install(zvni, nbr, - false /*was_static*/); + zebra_evpn_rem_neigh_install( + zevpn, nbr, false /*was_static*/); } UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); @@ -8597,21 +2981,20 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, /* Local: Notify Peer VTEPs, Remote: Install the entry */ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { /* Inform to BGP */ - if (zvni_mac_send_add_to_client(zvni->vni, - &mac->macaddr, - mac->flags, - mac->loc_seq, mac->es)) + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr, + mac->flags, mac->loc_seq, + mac->es)) return 0; /* Process all neighbors associated with this MAC. */ - zvni_process_neigh_on_local_mac_change(zvni, mac, 0, - 0 /*es_change*/); + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + 0 /*es_change*/); } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); /* Install the entry. */ - zvni_rem_mac_install(zvni, mac, false /* was_static */); + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); } return 0; @@ -8620,7 +3003,7 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_neigh_t *nbr; zebra_mac_t *mac; char buf[INET6_ADDRSTRLEN]; @@ -8629,13 +3012,13 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, if (!is_evpn_enabled()) return 0; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug("VNI %u does not exist\n", vni); return -1; } - nbr = zvni_neigh_lookup(zvni, ip); + nbr = zebra_evpn_neigh_lookup(zevpn, ip); if (!nbr) { zlog_warn("Requested host IP does not exist in VNI %u\n", vni); return -1; @@ -8649,7 +3032,7 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, return -1; } - mac = zvni_mac_lookup(zvni, &nbr->emac); + mac = zebra_evpn_mac_lookup(zevpn, &nbr->emac); if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { zlog_warn( @@ -8670,21 +3053,21 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, THREAD_OFF(nbr->dad_ip_auto_recovery_timer); if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { - zvni_neigh_send_add_to_client(zvni->vni, ip, - &nbr->emac, nbr->mac, - nbr->flags, nbr->loc_seq); + zebra_evpn_neigh_send_add_to_client(zevpn->vni, ip, &nbr->emac, + nbr->mac, nbr->flags, + nbr->loc_seq); } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_rem_neigh_install(zvni, nbr, false /*was_static*/); + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); } return 0; } -static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) +static void zevpn_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) { struct mac_walk_ctx *wctx = ctxt; zebra_mac_t *mac; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct listnode *node = NULL; zebra_neigh_t *nbr = NULL; @@ -8692,7 +3075,7 @@ static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) if (!mac) return; - zvni = wctx->zvni; + zevpn = wctx->zevpn; if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) return; @@ -8719,88 +3102,50 @@ static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) /* Local: Notify Peer VTEPs, Remote: Install the entry */ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { /* Inform to BGP */ - if (zvni_mac_send_add_to_client(zvni->vni, - &mac->macaddr, - mac->flags, mac->loc_seq, mac->es)) + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr, + mac->flags, mac->loc_seq, + mac->es)) return; /* Process all neighbors associated with this MAC. */ - zvni_process_neigh_on_local_mac_change(zvni, mac, 0, - 0 /*es_change*/); + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + 0 /*es_change*/); } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); /* Install the entry. */ - zvni_rem_mac_install(zvni, mac, false /* was_static */); - } -} - -static void zvni_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - struct neigh_walk_ctx *wctx = ctxt; - zebra_neigh_t *nbr; - zebra_vni_t *zvni; - char buf[INET6_ADDRSTRLEN]; - - nbr = (zebra_neigh_t *)bucket->data; - if (!nbr) - return; - - zvni = wctx->zvni; - - if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - return; - - if (IS_ZEBRA_DEBUG_VXLAN) { - ipaddr2str(&nbr->ip, buf, sizeof(buf)); - zlog_debug("%s: clear neigh %s dup state, flags 0x%x seq %u", - __func__, buf, nbr->flags, nbr->loc_seq); - } - - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->detect_start_time.tv_usec = 0; - nbr->dad_dup_detect_time = 0; - THREAD_OFF(nbr->dad_ip_auto_recovery_timer); - - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { - zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, - &nbr->emac, nbr->mac, - nbr->flags, nbr->loc_seq); - } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_rem_neigh_install(zvni, nbr, false /*was_static*/); + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); } } -static void zvni_clear_dup_detect_hash_vni_all(struct hash_bucket *bucket, +static void zevpn_clear_dup_detect_hash_vni_all(struct hash_bucket *bucket, void **args) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct zebra_vrf *zvrf; struct mac_walk_ctx m_wctx; struct neigh_walk_ctx n_wctx; - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) return; zvrf = (struct zebra_vrf *)args[0]; - if (hashcount(zvni->neigh_table)) { + if (hashcount(zevpn->neigh_table)) { memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; + n_wctx.zevpn = zevpn; n_wctx.zvrf = zvrf; - hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash, - &n_wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_clear_dup_neigh_hash, &n_wctx); } - if (num_valid_macs(zvni)) { + if (num_valid_macs(zevpn)) { memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; + m_wctx.zevpn = zevpn; m_wctx.zvrf = zvrf; - hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx); + hash_iterate(zevpn->mac_table, zevpn_clear_dup_mac_hash, &m_wctx); } } @@ -8814,41 +3159,41 @@ int zebra_vxlan_clear_dup_detect_vni_all(struct zebra_vrf *zvrf) args[0] = zvrf; - hash_iterate(zvrf->vni_table, + hash_iterate(zvrf->evpn_table, (void (*)(struct hash_bucket *, void *)) - zvni_clear_dup_detect_hash_vni_all, args); + zevpn_clear_dup_detect_hash_vni_all, args); return 0; } int zebra_vxlan_clear_dup_detect_vni(struct zebra_vrf *zvrf, vni_t vni) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct mac_walk_ctx m_wctx; struct neigh_walk_ctx n_wctx; if (!is_evpn_enabled()) return 0; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_warn("VNI %u does not exist\n", vni); - return -1; + return CMD_WARNING; } - if (hashcount(zvni->neigh_table)) { + if (hashcount(zevpn->neigh_table)) { memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; + n_wctx.zevpn = zevpn; n_wctx.zvrf = zvrf; - hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash, - &n_wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_clear_dup_neigh_hash, &n_wctx); } - if (num_valid_macs(zvni)) { + if (num_valid_macs(zevpn)) { memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; + m_wctx.zevpn = zevpn; m_wctx.zvrf = zvrf; - hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx); + hash_iterate(zevpn->mac_table, zevpn_clear_dup_mac_hash, &m_wctx); } return 0; @@ -8861,7 +3206,7 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct in_addr vtep_ip, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_macs; struct mac_walk_ctx wctx; json_object *json = NULL; @@ -8869,15 +3214,15 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; @@ -8887,12 +3232,12 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, } memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; wctx.json = json_mac; - hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, &wctx); if (use_json) { json_object_int_add(json, "numMacs", wctx.count); @@ -8919,7 +3264,7 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, json_object *json = NULL; void *args[2]; zebra_l3vni_t *zl3vni = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; if (!is_evpn_enabled()) return; @@ -8934,9 +3279,9 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, if (zl3vni) { zl3vni_print(zl3vni, (void *)args); } else { - zvni = zvni_lookup(vni); - if (zvni) - zvni_print(zvni, (void *)args); + zevpn = zebra_evpn_lookup(vni); + if (zevpn) + zebra_evpn_print(zevpn, (void *)args); else if (!json) vty_out(vty, "%% VNI %u does not exist\n", vni); } @@ -8974,7 +3319,7 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) return; num_l3vnis = hashcount(zrouter.l3vni_table); - num_l2vnis = hashcount(zvrf->vni_table); + num_l2vnis = hashcount(zvrf->evpn_table); num_vnis = num_l2vnis + num_l3vnis; if (uj) { @@ -9046,9 +3391,10 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, args[1] = json; /* Display all L2-VNIs */ - hash_iterate(zvrf->vni_table, - (void (*)(struct hash_bucket *, void *))zvni_print_hash, - args); + hash_iterate( + zvrf->evpn_table, + (void (*)(struct hash_bucket *, void *))zebra_evpn_print_hash, + args); /* Display all L3-VNIs */ hash_iterate(zrouter.l3vni_table, @@ -9112,7 +3458,7 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, { json_object *json_array = NULL; struct zebra_ns *zns = NULL; - struct zvni_evpn_show zes; + struct zebra_evpn_show zes; if (!is_evpn_enabled()) return; @@ -9130,10 +3476,10 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, zes.use_json = use_json; /* Display all L2-VNIs */ - hash_iterate( - zvrf->vni_table, - (void (*)(struct hash_bucket *, void *))zvni_print_hash_detail, - &zes); + hash_iterate(zvrf->evpn_table, + (void (*)(struct hash_bucket *, + void *))zebra_evpn_print_hash_detail, + &zes); /* Display all L3-VNIs */ hash_iterate(zrouter.l3vni_table, @@ -9160,14 +3506,8 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, struct ipaddr *ip) { char buf[INET6_ADDRSTRLEN]; - char buf2[ETHER_ADDR_STRLEN]; - zebra_neigh_t *n = NULL; - zebra_vni_t *zvni = NULL; - zebra_mac_t *zmac = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; - struct zebra_vrf *zvrf; - bool old_bgp_ready; - bool new_bgp_ready; /* check if this is a remote neigh entry corresponding to remote * next-hop @@ -9179,111 +3519,29 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_from_svi(ifp, link_if); - if (!zvni) { + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (!zevpn) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "%s: Del neighbor %s VNI is not present for interface %s", + "%s: Del neighbor %s EVPN is not present for interface %s", __func__, ipaddr2str(ip, buf, sizeof(buf)), ifp->name); return 0; } - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug( "VNI %u hash %p doesn't have intf upon local neighbor DEL", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u", ipaddr2str(ip, buf, sizeof(buf)), ifp->name, - ifp->ifindex, zvni->vni); - - /* If entry doesn't exist, nothing to do. */ - n = zvni_neigh_lookup(zvni, ip); - if (!n) - return 0; + ifp->ifindex, zevpn->vni); - zmac = zvni_mac_lookup(zvni, &n->emac); - if (!zmac) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Trying to del a neigh %s without a mac %s on VNI %u", - ipaddr2str(ip, buf, sizeof(buf)), - prefix_mac2str(&n->emac, buf2, sizeof(buf2)), - zvni->vni); - - return 0; - } - - /* If it is a remote entry, the kernel has aged this out or someone has - * deleted it, it needs to be re-installed as Quagga is the owner. - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_rem_neigh_install(zvni, n, false /*was_static*/); - return 0; - } - - /* if this is a sync entry it cannot be dropped re-install it in - * the dataplane - */ - old_bgp_ready = - zebra_vxlan_neigh_is_ready_for_bgp(n); - if (zebra_vxlan_neigh_is_static(n)) { - if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) - zlog_debug("re-add sync neigh vni %u ip %s mac %s 0x%x", - n->zvni->vni, - ipaddr2str(&n->ip, buf, sizeof(buf)), - prefix_mac2str(&n->emac, buf2, - sizeof(buf2)), - n->flags); - - if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); - /* inform-bgp about change in local-activity if any */ - new_bgp_ready = - zebra_vxlan_neigh_is_ready_for_bgp(n); - zebra_vxlan_neigh_send_add_del_to_client(n, - old_bgp_ready, new_bgp_ready); - - /* re-install the entry in the kernel */ - zebra_vxlan_sync_neigh_dp_install(n, false /* set_inactive */, - false /* force_clear_static */, __func__); - - return 0; - } - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - if (!zvrf) { - zlog_debug("%s: VNI %u vrf lookup failed.", __func__, - zvni->vni); - return -1; - } - - /* In case of feeze action, if local neigh is in duplicate state, - * Mark the Neigh as inactive before sending delete request to BGPd, - * If BGPd has remote entry, it will re-install - */ - if (zvrf->dad_freeze && - CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - ZEBRA_NEIGH_SET_INACTIVE(n); - - /* Remove neighbor from BGP. */ - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, n->flags, n->state, - false /* force */); - - /* Delete this neighbor entry. */ - zvni_neigh_del(zvni, n); - - /* see if the AUTO mac needs to be deleted */ - if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) - && !listcount(zmac->neigh_list)) - zvni_mac_del(zvni, zmac); - - return 0; + return zebra_evpn_neigh_del_ip(zevpn, ip); } /* @@ -9303,7 +3561,7 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp, { char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* check if this is a remote neigh entry corresponding to remote @@ -9316,8 +3574,8 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp, /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_from_svi(ifp, link_if); - if (!zvni) + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (!zevpn) return 0; if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) @@ -9328,14 +3586,15 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp, ifp->ifindex, state, is_ext ? "ext-learned " : "", is_router ? "router " : "", local_inactive ? "local_inactive " : "", - zvni->vni); + zevpn->vni); /* Is this about a local neighbor or a remote one? */ if (!is_ext) - return zvni_local_neigh_update(zvni, ifp, ip, macaddr, - is_router, local_inactive, dp_static); + return zebra_evpn_local_neigh_update(zevpn, ifp, ip, macaddr, + is_router, local_inactive, + dp_static); - return zvni_remote_neigh_update(zvni, ifp, ip, macaddr, state); + return zebra_evpn_remote_neigh_update(zevpn, ifp, ip, macaddr, state); } static int32_t @@ -9513,7 +3772,7 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp, struct zebra_vrf *zvrf = NULL; struct zebra_l2info_vxlan *vxl; vni_t vni; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_vtep_t *zvtep = NULL; zif = ifp->info; @@ -9531,12 +3790,12 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp, return -1; /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; /* If the remote vtep entry doesn't exists nothing to do */ - zvtep = zvni_vtep_find(zvni, &vtep_ip); + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); if (!zvtep) return 0; @@ -9545,7 +3804,7 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp, "Del MAC for remote VTEP %s intf %s(%u) VNI %u - readd", inet_ntoa(vtep_ip), ifp->name, ifp->ifindex, vni); - zvni_vtep_install(zvni, zvtep); + zebra_evpn_vtep_install(zevpn, zvtep); return 0; } @@ -9561,7 +3820,7 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; vni_t vni; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_mac_t *mac; char buf[ETHER_ADDR_STRLEN]; @@ -9575,12 +3834,12 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, return 0; /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; /* If entry doesn't exist, nothing to do. */ - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) return 0; @@ -9595,15 +3854,15 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, ifp->ifindex, vni, mac->flags); /* Remove MAC from BGP. */ - zvni_mac_send_del_to_client(zvni->vni, macaddr, - mac->flags, false /* force */); + zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, mac->flags, + false /* force */); /* * If there are no neigh associated with the mac delete the mac * else mark it as AUTO for forward reference */ if (!listcount(mac->neigh_list)) { - zvni_mac_del(zvni, mac); + zebra_evpn_mac_del(zevpn, mac); } else { UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); @@ -9625,7 +3884,7 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; vni_t vni; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; zebra_mac_t *mac = NULL; char buf[ETHER_ADDR_STRLEN]; @@ -9645,12 +3904,12 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, return zebra_vxlan_readd_remote_rmac(zl3vni, macaddr); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; /* If entry doesn't exist, nothing to do. */ - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) return 0; @@ -9663,7 +3922,7 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, ifp->ifindex, vni); - zvni_rem_mac_install(zvni, mac, false /* was_static */); + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); return 0; } @@ -9673,116 +3932,22 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, struct ethaddr *macaddr, vlanid_t vid) { - zebra_vni_t *zvni; - zebra_mac_t *mac; - char buf[ETHER_ADDR_STRLEN]; - bool old_bgp_ready; - bool new_bgp_ready; + zebra_evpn_t *zevpn; /* We are interested in MACs only on ports or (port, VLAN) that * map to a VNI. */ - zvni = zvni_map_vlan(ifp, br_if, vid); - if (!zvni) + zevpn = zebra_evpn_map_vlan(ifp, br_if, vid); + if (!zevpn) return 0; - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug( "VNI %u hash %p doesn't have intf upon local MAC DEL", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } - /* If entry doesn't exist, nothing to do. */ - mac = zvni_mac_lookup(zvni, macaddr); - if (!mac) - return 0; - - /* Is it a local entry? */ - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) - return 0; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("DEL MAC %s intf %s(%u) VID %u -> VNI %u seq %u flags 0x%x nbr count %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, - ifp->ifindex, vid, zvni->vni, mac->loc_seq, - mac->flags, listcount(mac->neigh_list)); - - old_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(mac->flags); - if (zebra_vxlan_mac_is_static(mac)) { - /* this is a synced entry and can only be removed when the - * es-peers stop advertising it. - */ - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("re-add sync-mac vni %u mac %s es %s seq %d f 0x%x", - zvni->vni, - prefix_mac2str(macaddr, - buf, sizeof(buf)), - mac->es ? mac->es->esi_str : "-", - mac->loc_seq, - mac->flags); - - /* inform-bgp about change in local-activity if any */ - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) { - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); - new_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(mac->flags); - zebra_vxlan_mac_send_add_del_to_client(mac, - old_bgp_ready, new_bgp_ready); - } - - /* re-install the entry in the kernel */ - zebra_vxlan_sync_mac_dp_install(mac, false /* set_inactive */, - false /* force_clear_static */, - __func__); - - return 0; - } - - /* Update all the neigh entries associated with this mac */ - zvni_process_neigh_on_local_mac_del(zvni, mac); - - /* Remove MAC from BGP. */ - zvni_mac_send_del_to_client(zvni->vni, macaddr, - mac->flags, false /* force */); - - zebra_evpn_es_mac_deref_entry(mac); - - /* - * If there are no neigh associated with the mac delete the mac - * else mark it as AUTO for forward reference - */ - if (!listcount(mac->neigh_list)) { - zvni_mac_del(zvni, mac); - } else { - UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); - UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - } - - return 0; -} - -/* update local fowarding info. return true if a dest-ES change - * is detected - */ -static bool zebra_vxlan_local_mac_update_fwd_info(zebra_mac_t *mac, - struct interface *ifp, vlanid_t vid) -{ - struct zebra_if *zif = ifp->info; - bool es_change; - - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - - es_change = zebra_evpn_es_mac_ref_entry(mac, zif->es_info.es); - - if (!mac->es) { - /* if es is set fwd_info is not-relevant/taped-out */ - mac->fwd_info.local.ifindex = ifp->ifindex; - mac->fwd_info.local.vid = vid; - } - - return es_change; + return zebra_evpn_del_local_mac(zevpn, macaddr, ifp); } /* @@ -9794,274 +3959,49 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, bool sticky, bool local_inactive, bool dp_static) { - zebra_vni_t *zvni; - zebra_mac_t *mac; + zebra_evpn_t *zevpn; struct zebra_vrf *zvrf; char buf[ETHER_ADDR_STRLEN]; - bool mac_sticky = false; - bool inform_client = false; - bool upd_neigh = false; - bool is_dup_detect = false; - struct in_addr vtep_ip = {.s_addr = 0}; - bool es_change = false; - bool new_bgp_ready; - /* assume inactive if not present or if not local */ - bool old_local_inactive = true; - bool old_bgp_ready = false; - bool inform_dataplane = false; - bool new_static = false; + + assert(ifp); /* We are interested in MACs only on ports or (port, VLAN) that - * map to a VNI. + * map to an EVPN. */ - zvni = zvni_map_vlan(ifp, br_if, vid); - if (!zvni) { + zevpn = zebra_evpn_map_vlan(ifp, br_if, vid); + if (!zevpn) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - " Add/Update %sMAC %s intf %s(%u) VID %u, could not find VNI", + " Add/Update %sMAC %s intf %s(%u) VID %u, could not find EVPN", sticky ? "sticky " : "", prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, ifp->ifindex, vid); return 0; } - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( " VNI %u hash %p doesn't have intf upon local MAC ADD", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); if (!zvrf) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug(" No Vrf found for vrf_id: %d", - zvni->vxlan_if->vrf_id); + zevpn->vxlan_if->vrf_id); return -1; } - /* Check if we need to create or update or it is a NO-OP. */ - mac = zvni_mac_lookup(zvni, macaddr); - if (!mac) { - if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug( - "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u%s", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vid, zvni->vni, - local_inactive ? " local-inactive" : ""); - - mac = zvni_mac_add(zvni, macaddr); - if (!mac) { - flog_err( - EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add MAC %s intf %s(%u) VID %u VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vid, zvni->vni); - return -1; - } - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - es_change = zebra_vxlan_local_mac_update_fwd_info(mac, - ifp, vid); - if (sticky) - SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - inform_client = true; - } else { - if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug( - "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u %scurFlags 0x%x", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vid, zvni->vni, - local_inactive ? "local-inactive " : "", - mac->flags); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - struct interface *old_ifp; - vlanid_t old_vid; - bool old_static; - - zebra_vxlan_mac_get_access_info(mac, - &old_ifp, &old_vid); - old_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp( - mac->flags); - old_local_inactive = !!(mac->flags & - ZEBRA_MAC_LOCAL_INACTIVE); - old_static = zebra_vxlan_mac_is_static(mac); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) - mac_sticky = true; - - /* - * Update any changes and if changes are relevant to - * BGP, note it. - */ - if (mac_sticky == sticky - && old_ifp == ifp - && old_vid == vid - && old_local_inactive == local_inactive - && dp_static == old_static) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - " Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u%s, entry exists and has not changed ", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, - sizeof(buf)), - ifp->name, ifp->ifindex, vid, - zvni->vni, - local_inactive ? - " local_inactive" : ""); - return 0; - } - if (mac_sticky != sticky) { - if (sticky) - SET_FLAG(mac->flags, - ZEBRA_MAC_STICKY); - else - UNSET_FLAG(mac->flags, - ZEBRA_MAC_STICKY); - inform_client = true; - } - - es_change = zebra_vxlan_local_mac_update_fwd_info(mac, - ifp, vid); - /* If an es_change is detected we need to advertise - * the route with a sequence that is one - * greater. This is need to indicate a mac-move - * to the ES peers - */ - if (es_change) { - mac->loc_seq = mac->loc_seq + 1; - /* force drop the peer/sync info as it is - * simply no longer relevant - */ - if (CHECK_FLAG(mac->flags, - ZEBRA_MAC_ALL_PEER_FLAGS)) { - zebra_vxlan_mac_clear_sync_info(mac); - new_static = - zebra_vxlan_mac_is_static(mac); - /* if we clear peer-flags we - * also need to notify the dataplane - * to drop the static flag - */ - if (old_static != new_static) - inform_dataplane = true; - } - } - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) || - CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { - bool do_dad = false; - - /* - * MAC has either moved or was "internally" created due - * to a neighbor learn and is now actually learnt. If - * it was learnt as a remote sticky MAC, this is an - * operator error. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) { - flog_warn( - EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT, - "MAC %s already learnt as remote sticky MAC behind VTEP %s VNI %u", - prefix_mac2str(macaddr, buf, - sizeof(buf)), - inet_ntoa(mac->fwd_info.r_vtep_ip), - zvni->vni); - return 0; - } - - /* If an actual move, compute MAC's seq number */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - mac->loc_seq = MAX(mac->rem_seq + 1, - mac->loc_seq); - vtep_ip = mac->fwd_info.r_vtep_ip; - /* Trigger DAD for remote MAC */ - do_dad = true; - } - - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - es_change = zebra_vxlan_local_mac_update_fwd_info(mac, - ifp, vid); - if (sticky) - SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - else - UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - /* - * We have to inform BGP of this MAC as well as process - * all neighbors. - */ - inform_client = true; - upd_neigh = true; - - zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, vtep_ip, - do_dad, - &is_dup_detect, - true); - if (is_dup_detect) { - inform_client = false; - upd_neigh = false; - } - } - } - - /* if the dataplane thinks the entry is sync but it is - * not sync in zebra we need to re-install to fixup - */ - if (dp_static) { - new_static = zebra_vxlan_mac_is_static(mac); - if (!new_static) - inform_dataplane = true; - } - - if (local_inactive) - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); - else - UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); - - new_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(mac->flags); - /* if local-activity has changed we need update bgp - * even if bgp already knows about the mac - */ - if ((old_local_inactive != local_inactive) || - (new_bgp_ready != old_bgp_ready)) { - if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) - zlog_debug("local mac vni %u mac %s es %s seq %d f 0x%x%s", - zvni->vni, - prefix_mac2str(macaddr, - buf, sizeof(buf)), - mac->es ? mac->es->esi_str : "", - mac->loc_seq, - mac->flags, - local_inactive ? - " local-inactive" : ""); - inform_client = true; - } - - if (es_change) { - inform_client = true; - upd_neigh = true; - } - - /* Inform dataplane if required. */ - if (inform_dataplane) - zebra_vxlan_sync_mac_dp_install(mac, false /* set_inactive */, - false /* force_clear_static */, __func__); - - /* Inform BGP if required. */ - if (inform_client) - zebra_vxlan_mac_send_add_del_to_client(mac, - old_bgp_ready, new_bgp_ready); - - /* Process all neighbors associated with this MAC, if required. */ - if (upd_neigh) - zvni_process_neigh_on_local_mac_change(zvni, mac, 0, es_change); - - return 0; + return zebra_evpn_add_update_local_mac(zvrf, zevpn, ifp, macaddr, vid, + sticky, local_inactive, + dp_static); } /* - * Handle message from client to delete a remote VTEP for a VNI. + * Handle message from client to delete a remote VTEP for an EVPN. */ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) { @@ -10069,7 +4009,7 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) unsigned short l = 0; vni_t vni; struct in_addr vtep_ip; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_vtep_t *zvtep; struct interface *ifp; struct zebra_if *zif; @@ -10108,8 +4048,8 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) zebra_route_string(client->proto)); /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "Failed to locate VNI hash upon remote VTEP DEL, VNI %u", @@ -10117,11 +4057,11 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) continue; } - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) { zlog_debug( "VNI %u hash %p doesn't have intf upon remote VTEP DEL", - zvni->vni, zvni); + zevpn->vni, zevpn); continue; } zif = ifp->info; @@ -10136,12 +4076,12 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) * and * then, the VTEP entry itself and remove it. */ - zvtep = zvni_vtep_find(zvni, &vtep_ip); + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); if (!zvtep) continue; - zvni_vtep_uninstall(zvni, &vtep_ip); - zvni_vtep_del(zvni, zvtep); + zebra_evpn_vtep_uninstall(zevpn, &vtep_ip); + zebra_evpn_vtep_del(zevpn, zvtep); } stream_failure: @@ -10149,7 +4089,7 @@ stream_failure: } /* - * Handle message from client to add a remote VTEP for a VNI. + * Handle message from client to add a remote VTEP for an EVPN. */ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) { @@ -10157,7 +4097,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) unsigned short l = 0; vni_t vni; struct in_addr vtep_ip; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct interface *ifp; struct zebra_if *zif; int flood_control; @@ -10192,21 +4132,21 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) zebra_route_string(client->proto)); /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { flog_err( EC_ZEBRA_VTEP_ADD_FAILED, - "Failed to locate VNI hash upon remote VTEP ADD, VNI %u", + "Failed to locate EVPN hash upon remote VTEP ADD, VNI %u", vni); continue; } - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) { flog_err( EC_ZEBRA_VTEP_ADD_FAILED, "VNI %u hash %p doesn't have intf upon remote VTEP ADD", - zvni->vni, zvni); + zevpn->vni, zevpn); continue; } @@ -10216,7 +4156,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) if (!if_is_operative(ifp) || !zif->brslave_info.br_if) continue; - zvtep = zvni_vtep_find(zvni, &vtep_ip); + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); if (zvtep) { /* If the remote VTEP already exists check if * the flood mode has changed @@ -10228,18 +4168,20 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) * is no longer; get rid of the HER fdb * entry installed before */ - zvni_vtep_uninstall(zvni, &vtep_ip); + zebra_evpn_vtep_uninstall(zevpn, + &vtep_ip); zvtep->flood_control = flood_control; - zvni_vtep_install(zvni, zvtep); + zebra_evpn_vtep_install(zevpn, zvtep); } } else { - zvtep = zvni_vtep_add(zvni, &vtep_ip, flood_control); + zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip, + flood_control); if (zvtep) - zvni_vtep_install(zvni, zvtep); + zebra_evpn_vtep_install(zevpn, zvtep); else flog_err(EC_ZEBRA_VTEP_ADD_FAILED, - "Failed to add remote VTEP, VNI %u zvni %p", - vni, zvni); + "Failed to add remote VTEP, VNI %u zevpn %p", + vni, zevpn); } } @@ -10260,7 +4202,7 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, { struct ipaddr ip; struct ethaddr macaddr; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; memset(&ip, 0, sizeof(struct ipaddr)); memset(&macaddr, 0, sizeof(struct ethaddr)); @@ -10304,14 +4246,15 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, svi_if_link = if_lookup_by_index_per_ns( zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex); - zvni = zvni_from_svi(svi_if, svi_if_link); + zevpn = zebra_evpn_from_svi(svi_if, + svi_if_link); } } else if (IS_ZEBRA_IF_BRIDGE(svi_if)) { /* * If it is a vlan unaware bridge then svi is the bridge * itself */ - zvni = zvni_from_svi(svi_if, svi_if); + zevpn = zebra_evpn_from_svi(svi_if, svi_if); } } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_if *svi_if_zif = @@ -10325,18 +4268,18 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex); if (svi_if_link) - zvni = zvni_from_svi(ifp, svi_if_link); + zevpn = zebra_evpn_from_svi(ifp, svi_if_link); } } else if (IS_ZEBRA_IF_BRIDGE(ifp)) { - zvni = zvni_from_svi(ifp, ifp); + zevpn = zebra_evpn_from_svi(ifp, ifp); } - if (!zvni) + if (!zevpn) return 0; - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug("VNI %u hash %p doesn't have intf upon MACVLAN up", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } @@ -10355,9 +4298,9 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, if (add) - zvni_gw_macip_add(ifp, zvni, &macaddr, &ip); + zebra_evpn_gw_macip_add(ifp, zevpn, &macaddr, &ip); else - zvni_gw_macip_del(ifp, zvni, &ip); + zebra_evpn_gw_macip_del(ifp, zevpn, &ip); return 0; } @@ -10367,7 +4310,7 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, * SVI can be associated to either L3-VNI or L2-VNI. * For L2-VNI: At this point, this is a NOP since * the kernel deletes the neighbor entries on this SVI (if any). - * We only need to update the vrf corresponding to zvni. + * We only need to update the vrf corresponding to zevpn. * For L3-VNI: L3-VNI is operationally down, update mac-ip routes and delete * from bgp */ @@ -10384,17 +4327,17 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) /* remove association with svi-if */ zl3vni->svi_if = NULL; } else { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; - /* since we dont have svi corresponding to zvni, we associate it + /* since we dont have svi corresponding to zevpn, we associate it * to default vrf. Note: the corresponding neigh entries on the * SVI would have already been deleted */ - zvni = zvni_from_svi(ifp, link_if); - if (zvni) { - zvni->vrf_id = VRF_DEFAULT; + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (zevpn) { + zevpn->vrf_id = VRF_DEFAULT; /* update the tenant vrf in BGP */ - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); } } return 0; @@ -10410,7 +4353,7 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) */ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; zl3vni = zl3vni_from_svi(ifp, link_if); @@ -10427,31 +4370,31 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) /* process SVI up for l2-vni */ struct neigh_walk_ctx n_wctx; - zvni = zvni_from_svi(ifp, link_if); - if (!zvni) + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (!zevpn) return 0; - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug( "VNI %u hash %p doesn't have intf upon SVI up", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "SVI %s(%u) VNI %u VRF %s is UP, installing neighbors", - ifp->name, ifp->ifindex, zvni->vni, + ifp->name, ifp->ifindex, zevpn->vni, vrf_id_to_name(ifp->vrf_id)); /* update the vrf information for l2-vni and inform bgp */ - zvni->vrf_id = ifp->vrf_id; - zvni_send_add_to_client(zvni); + zevpn->vrf_id = ifp->vrf_id; + zebra_evpn_send_add_to_client(zevpn); /* Install any remote neighbors for this VNI. */ memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, + n_wctx.zevpn = zevpn; + hash_iterate(zevpn->neigh_table, zebra_evpn_install_neigh_hash, &n_wctx); } @@ -10538,7 +4481,7 @@ int zebra_vxlan_if_down(struct interface *ifp) struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; zebra_l3vni_t *zl3vni = NULL; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -10564,25 +4507,25 @@ int zebra_vxlan_if_down(struct interface *ifp) ifp->ifindex, vni); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } - assert(zvni->vxlan_if == ifp); + assert(zevpn->vxlan_if == ifp); /* Delete this VNI from BGP. */ - zvni_send_del_to_client(zvni); + zebra_evpn_send_del_to_client(zevpn); /* Free up all neighbors and MACs, if any. */ - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 1); + zebra_evpn_vtep_del_all(zevpn, 1); } return 0; } @@ -10595,7 +4538,7 @@ int zebra_vxlan_if_up(struct interface *ifp) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -10633,29 +4576,29 @@ int zebra_vxlan_if_up(struct interface *ifp) ifp->ifindex, vni); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( - "Failed to locate VNI hash at UP, IF %s(%u) VNI %u", + "Failed to locate EVPN hash at UP, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } - assert(zvni->vxlan_if == ifp); + assert(zevpn->vxlan_if == ifp); vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { - zvni->vrf_id = vlan_if->vrf_id; + zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) - listnode_add_sort(zl3vni->l2vnis, zvni); + listnode_add_sort(zl3vni->l2vnis, zevpn); } /* If part of a bridge, inform BGP about this VNI. */ /* Also, read and populate local MACs and neighbors. */ if (zif->brslave_info.br_if) { - zvni_send_add_to_client(zvni); - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_send_add_to_client(zevpn); + zebra_evpn_read_mac_neigh(zevpn, ifp); } } @@ -10671,7 +4614,7 @@ int zebra_vxlan_if_del(struct interface *ifp) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -10704,8 +4647,8 @@ int zebra_vxlan_if_del(struct interface *ifp) ifp->ifindex); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( "Failed to locate VNI hash at del, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); @@ -10713,24 +4656,24 @@ int zebra_vxlan_if_del(struct interface *ifp) } /* remove from l3-vni list */ - zl3vni = zl3vni_from_vrf(zvni->vrf_id); + zl3vni = zl3vni_from_vrf(zevpn->vrf_id); if (zl3vni) - listnode_delete(zl3vni->l2vnis, zvni); + listnode_delete(zl3vni->l2vnis, zevpn); /* Delete VNI from BGP. */ - zvni_send_del_to_client(zvni); + zebra_evpn_send_del_to_client(zevpn); /* Free up all neighbors and MAC, if any. */ - zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC); /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 0); + zebra_evpn_vtep_del_all(zevpn, 0); /* Delete the hash entry. */ - if (zvni_del(zvni)) { + if (zebra_evpn_vxlan_del(zevpn)) { flog_err(EC_ZEBRA_VNI_DEL_FAILED, - "Failed to del VNI hash %p, IF %s(%u) VNI %u", - zvni, ifp->name, ifp->ifindex, zvni->vni); + "Failed to del EVPN hash %p, IF %s(%u) VNI %u", + zevpn, ifp->name, ifp->ifindex, zevpn->vni); return -1; } } @@ -10745,7 +4688,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -10816,10 +4759,10 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) } else { /* Update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( - "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u", + "Failed to find EVPN hash on update, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } @@ -10836,10 +4779,10 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { /* Delete from client, remove all remote VTEPs */ /* Also, free up all MACs and neighbors. */ - zvni_send_del_to_client(zvni); - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); - zvni_vtep_del_all(zvni, 1); + zebra_evpn_send_del_to_client(zevpn); + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + zebra_evpn_vtep_del_all(zevpn, 1); return 0; } @@ -10848,23 +4791,23 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) /* Remove all existing local neigh and MACs for this VNI * (including from BGP) */ - zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC); - zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC); + zebra_evpn_neigh_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); + zebra_evpn_mac_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); } - if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { - zebra_vxlan_sg_deref(zvni->local_vtep_ip, - zvni->mcast_grp); + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->mcast_grp = vxl->mcast_grp; + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vxl->mcast_grp; /* on local vtep-ip check if ES orig-ip * needs to be updated */ - zebra_evpn_es_set_base_vni(zvni); + zebra_evpn_es_set_base_evpn(zevpn); } - zvni_vxlan_if_set(zvni, ifp, true /* set */); + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); /* Take further actions needed. * Note that if we are here, there is a change of interest. */ @@ -10877,7 +4820,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE | ZEBRA_VXLIF_MCAST_GRP_CHANGE)) - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); /* If there is a valid new master or a VLAN mapping change, * read and populate local MACs and neighbors. @@ -10885,22 +4828,22 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) * for this VNI (based on new VLAN). */ if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_read_mac_neigh(zevpn, ifp); else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { struct mac_walk_ctx m_wctx; struct neigh_walk_ctx n_wctx; - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_read_mac_neigh(zevpn, ifp); memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; - hash_iterate(zvni->mac_table, zvni_install_mac_hash, - &m_wctx); + m_wctx.zevpn = zevpn; + hash_iterate(zevpn->mac_table, + zebra_evpn_install_mac_hash, &m_wctx); memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, - &n_wctx); + n_wctx.zevpn = zevpn; + hash_iterate(zevpn->neigh_table, + zebra_evpn_install_neigh_hash, &n_wctx); } } @@ -10915,7 +4858,7 @@ int zebra_vxlan_if_add(struct interface *ifp) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -10955,39 +4898,39 @@ int zebra_vxlan_if_add(struct interface *ifp) /* process if-add for l2-vni */ struct interface *vlan_if = NULL; - /* Create or update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zvni = zvni_add(vni); - if (!zvni) { + /* Create or update EVPN hash. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zevpn = zebra_evpn_add(vni); + if (!zevpn) { flog_err( EC_ZEBRA_VNI_ADD_FAILED, - "Failed to add VNI hash, IF %s(%u) VNI %u", + "Failed to add EVPN hash, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } } - if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { - zebra_vxlan_sg_deref(zvni->local_vtep_ip, - zvni->mcast_grp); + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->mcast_grp = vxl->mcast_grp; + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vxl->mcast_grp; /* on local vtep-ip check if ES orig-ip * needs to be updated */ - zebra_evpn_es_set_base_vni(zvni); + zebra_evpn_es_set_base_evpn(zevpn); } - zvni_vxlan_if_set(zvni, ifp, true /* set */); + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { - zvni->vrf_id = vlan_if->vrf_id; + zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) - listnode_add_sort(zl3vni->l2vnis, zvni); + listnode_add_sort(zl3vni->l2vnis, zevpn); } if (IS_ZEBRA_DEBUG_VXLAN) { @@ -11014,10 +4957,10 @@ int zebra_vxlan_if_add(struct interface *ifp) return 0; /* Inform BGP */ - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); /* Read and populate local MACs and neighbors */ - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_read_mac_neigh(zevpn, ifp); } return 0; @@ -11095,7 +5038,7 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, : "NIL"); /* formulate l2vni list */ - hash_iterate(zvrf_evpn->vni_table, zvni_add_to_l3vni_list, + hash_iterate(zvrf_evpn->evpn_table, zevpn_add_to_l3vni_list, zl3vni); if (is_l3vni_oper_up(zl3vni)) @@ -11225,7 +5168,7 @@ void zebra_vxlan_flood_control(ZAPI_HANDLER_ARGS) /* Install or uninstall flood entries corresponding to * remote VTEPs. */ - hash_iterate(zvrf->vni_table, zvni_handle_flooding_remote_vteps, + hash_iterate(zvrf->evpn_table, zebra_evpn_handle_flooding_remote_vteps, zvrf); stream_failure: @@ -11241,7 +5184,7 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) struct stream *s; int advertise; vni_t vni = 0; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; struct interface *ifp = NULL; if (!EVPN_ENABLED(zvrf)) { @@ -11268,11 +5211,13 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) if (advertise) { zvrf->advertise_svi_macip = advertise; - hash_iterate(zvrf->vni_table, - zvni_gw_macip_add_for_vni_hash, NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_add_for_evpn_hash, + NULL); } else { - hash_iterate(zvrf->vni_table, - zvni_svi_macip_del_for_vni_hash, NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_svi_macip_del_for_evpn_hash, + NULL); zvrf->advertise_svi_macip = advertise; } @@ -11281,27 +5226,27 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "EVPN SVI macip Adv %s on VNI %d , currently %s", advertise ? "enabled" : "disabled", vni, - advertise_svi_macip_enabled(zvni) + advertise_svi_macip_enabled(zevpn) ? "enabled" : "disabled"); - if (zvni->advertise_svi_macip == advertise) + if (zevpn->advertise_svi_macip == advertise) return; /* Store flag even though SVI is not present. * Once SVI comes up triggers self MAC-IP route add. */ - zvni->advertise_svi_macip = advertise; + zevpn->advertise_svi_macip = advertise; - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) return; @@ -11319,10 +5264,10 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) if (advertise) { /* Add primary SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); } else { /* Del primary SVI MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); } } @@ -11339,7 +5284,7 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) struct stream *s; int advertise; vni_t vni = 0; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; struct interface *ifp = NULL; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan zl2_info; @@ -11355,22 +5300,22 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) STREAM_GETC(s, advertise); STREAM_GET(&vni, s, 3); - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return; - if (zvni->advertise_subnet == advertise) + if (zevpn->advertise_subnet == advertise) return; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("EVPN subnet Adv %s on VNI %d , currently %s", advertise ? "enabled" : "disabled", vni, - zvni->advertise_subnet ? "enabled" : "disabled"); + zevpn->advertise_subnet ? "enabled" : "disabled"); - zvni->advertise_subnet = advertise; + zevpn->advertise_subnet = advertise; - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) return; @@ -11387,10 +5332,10 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) if (!vlan_if) return; - if (zvni->advertise_subnet) - zvni_advertise_subnet(zvni, vlan_if, 1); + if (zevpn->advertise_subnet) + zebra_evpn_advertise_subnet(zevpn, vlan_if, 1); else - zvni_advertise_subnet(zvni, vlan_if, 0); + zebra_evpn_advertise_subnet(zevpn, vlan_if, 0); stream_failure: return; @@ -11405,7 +5350,7 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) struct stream *s; int advertise; vni_t vni = 0; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; struct interface *ifp = NULL; if (!EVPN_ENABLED(zvrf)) { @@ -11431,12 +5376,14 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) zvrf->advertise_gw_macip = advertise; - if (advertise_gw_macip_enabled(zvni)) - hash_iterate(zvrf->vni_table, - zvni_gw_macip_add_for_vni_hash, NULL); + if (advertise_gw_macip_enabled(zevpn)) + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_add_for_evpn_hash, + NULL); else - hash_iterate(zvrf->vni_table, - zvni_gw_macip_del_for_vni_hash, NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_del_for_evpn_hash, + NULL); } else { struct zebra_if *zif = NULL; @@ -11444,23 +5391,23 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) struct interface *vlan_if = NULL; struct interface *vrr_if = NULL; - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "EVPN gateway macip Adv %s on VNI %d , currently %s", advertise ? "enabled" : "disabled", vni, - advertise_gw_macip_enabled(zvni) ? "enabled" + advertise_gw_macip_enabled(zevpn) ? "enabled" : "disabled"); - if (zvni->advertise_gw_macip == advertise) + if (zevpn->advertise_gw_macip == advertise) return; - zvni->advertise_gw_macip = advertise; + zevpn->advertise_gw_macip = advertise; - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) return; @@ -11477,22 +5424,22 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) if (!vlan_if) return; - if (advertise_gw_macip_enabled(zvni)) { + if (advertise_gw_macip_enabled(zevpn)) { /* Add primary SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); /* Add VRR MAC-IP - if any*/ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); if (vrr_if) - zvni_add_macip_for_intf(vrr_if, zvni); + zebra_evpn_add_macip_for_intf(vrr_if, zevpn); } else { /* Del primary MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); /* Del VRR MAC-IP - if any*/ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); if (vrr_if) - zvni_del_macip_for_intf(vrr_if, zvni); + zebra_evpn_del_macip_for_intf(vrr_if, zevpn); } } @@ -11544,12 +5491,12 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) /* Replay all ESs */ zebra_evpn_es_send_all_to_client(true /* add */); - /* Build VNI hash table and inform BGP. */ - zvni_build_hash_table(); + /* Build EVPN hash table and inform BGP. */ + zevpn_build_hash_table(); /* Add all SVI (L3 GW) MACs to BGP*/ - hash_iterate(zvrf->vni_table, zvni_gw_macip_add_for_vni_hash, - NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_add_for_evpn_hash, NULL); /* Read the MAC FDB */ macfdb_read(zvrf->zns); @@ -11557,10 +5504,11 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) /* Read neighbors */ neigh_read(zvrf->zns); } else { - /* Cleanup VTEPs for all VNIs - uninstall from + /* Cleanup VTEPs for all EVPNs - uninstall from * kernel and free entries. */ - hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, + zvrf); /* Delete all ESs in BGP */ zebra_evpn_es_send_all_to_client(false /* add */); @@ -11577,40 +5525,41 @@ stream_failure: } /* - * Allocate VNI hash table for this VRF and do other initialization. + * Allocate EVPN hash table for this VRF and do other initialization. * NOTE: Currently supported only for default VRF. */ void zebra_vxlan_init_tables(struct zebra_vrf *zvrf) { if (!zvrf) return; - zvrf->vni_table = hash_create(vni_hash_keymake, vni_hash_cmp, - "Zebra VRF VNI Table"); + zvrf->evpn_table = + hash_create(zebra_evpn_hash_keymake, zebra_evpn_hash_cmp, + "Zebra VRF EVPN Table"); zvrf->vxlan_sg_table = hash_create(zebra_vxlan_sg_hash_key_make, zebra_vxlan_sg_hash_eq, "Zebra VxLAN SG Table"); } -/* Cleanup VNI info, but don't free the table. */ +/* Cleanup EVPN info, but don't free the table. */ void zebra_vxlan_cleanup_tables(struct zebra_vrf *zvrf) { struct zebra_vrf *evpn_zvrf = zebra_vrf_get_evpn(); if (!zvrf) return; - hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, zvrf); hash_iterate(zvrf->vxlan_sg_table, zebra_vxlan_sg_cleanup, NULL); if (zvrf == evpn_zvrf) zebra_evpn_es_cleanup(); } -/* Close all VNI handling */ +/* Close all EVPN handling */ void zebra_vxlan_close_tables(struct zebra_vrf *zvrf) { if (!zvrf) return; - hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); - hash_free(zvrf->vni_table); + hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, zvrf); + hash_free(zvrf->evpn_table); } /* init the l3vni table */ @@ -11641,130 +5590,6 @@ ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id) return zl3vni->svi_if->ifindex; } -static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t) -{ - struct zebra_vrf *zvrf = NULL; - zebra_neigh_t *nbr = NULL; - zebra_vni_t *zvni = NULL; - char buf1[INET6_ADDRSTRLEN]; - char buf2[ETHER_ADDR_STRLEN]; - - nbr = THREAD_ARG(t); - - /* since this is asynchronous we need sanity checks*/ - zvrf = vrf_info_lookup(nbr->zvni->vrf_id); - if (!zvrf) - return 0; - - zvni = zvni_lookup(nbr->zvni->vni); - if (!zvni) - return 0; - - nbr = zvni_neigh_lookup(zvni, &nbr->ip); - if (!nbr) - return 0; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired", - __func__, - prefix_mac2str(&nbr->emac, buf2, sizeof(buf2)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), nbr->flags, - nbr->dad_count, zvni->vni); - - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->detect_start_time.tv_usec = 0; - nbr->dad_dup_detect_time = 0; - nbr->dad_ip_auto_recovery_timer = NULL; - ZEBRA_NEIGH_SET_ACTIVE(nbr); - - /* Send to BGP */ - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { - zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, &nbr->emac, - nbr->mac, nbr->flags, nbr->loc_seq); - } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_rem_neigh_install(zvni, nbr, false /*was_static*/); - } - - return 0; -} - -static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t) -{ - struct zebra_vrf *zvrf = NULL; - zebra_mac_t *mac = NULL; - zebra_vni_t *zvni = NULL; - struct listnode *node = NULL; - zebra_neigh_t *nbr = NULL; - char buf[ETHER_ADDR_STRLEN]; - - mac = THREAD_ARG(t); - - /* since this is asynchronous we need sanity checks*/ - zvrf = vrf_info_lookup(mac->zvni->vrf_id); - if (!zvrf) - return 0; - - zvni = zvni_lookup(mac->zvni->vni); - if (!zvni) - return 0; - - mac = zvni_mac_lookup(zvni, &mac->macaddr); - if (!mac) - return 0; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired", - __func__, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - mac->flags, mac->dad_count, listcount(mac->neigh_list)); - - /* Remove all IPs as duplicate associcated with this MAC */ - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) - ZEBRA_NEIGH_SET_INACTIVE(nbr); - else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) - zvni_rem_neigh_install(zvni, nbr, - false /*was_static*/); - } - - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->dad_dup_detect_time = 0; - } - - UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); - mac->dad_count = 0; - mac->detect_start_time.tv_sec = 0; - mac->detect_start_time.tv_usec = 0; - mac->dad_dup_detect_time = 0; - mac->dad_mac_auto_recovery_timer = NULL; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - /* Inform to BGP */ - if (zvni_mac_send_add_to_client(zvni->vni, &mac->macaddr, - mac->flags, mac->loc_seq, mac->es)) - return -1; - - /* Process all neighbors associated with this MAC. */ - zvni_process_neigh_on_local_mac_change(zvni, mac, 0, - 0 /*es_change*/); - - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); - - /* Install the entry. */ - zvni_rem_mac_install(zvni, mac, false /* was_static */); - } - - return 0; -} - /************************** vxlan SG cache management ************************/ /* Inform PIM about the mcast group */ static int zebra_vxlan_sg_send(struct zebra_vrf *zvrf, @@ -12016,87 +5841,6 @@ void zebra_vxlan_sg_replay(ZAPI_HANDLER_ARGS) hash_iterate(zvrf->vxlan_sg_table, zebra_vxlan_sg_replay_send, NULL); } -/************************** EVPN BGP config management ************************/ -/* Notify Local MACs to the clienti, skips GW MAC */ -static void zvni_send_mac_hash_entry_to_client(struct hash_bucket *bucket, - void *arg) -{ - struct mac_walk_ctx *wctx = arg; - zebra_mac_t *zmac = bucket->data; - - if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_DEF_GW)) - return; - - if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) - zvni_mac_send_add_to_client(wctx->zvni->vni, &zmac->macaddr, - zmac->flags, zmac->loc_seq, zmac->es); -} - -/* Iterator to Notify Local MACs of a L2VNI */ -static void zvni_send_mac_to_client(zebra_vni_t *zvni) -{ - struct mac_walk_ctx wctx; - - if (!zvni->mac_table) - return; - - memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; - - hash_iterate(zvni->mac_table, zvni_send_mac_hash_entry_to_client, - &wctx); -} - -/* Notify Neighbor entries to the Client, skips the GW entry */ -static void zvni_send_neigh_hash_entry_to_client(struct hash_bucket *bucket, - void *arg) -{ - struct mac_walk_ctx *wctx = arg; - zebra_neigh_t *zn = bucket->data; - zebra_mac_t *zmac = NULL; - - if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_DEF_GW)) - return; - - if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_LOCAL) && - IS_ZEBRA_NEIGH_ACTIVE(zn)) { - zmac = zvni_mac_lookup(wctx->zvni, &zn->emac); - if (!zmac) - return; - - zvni_neigh_send_add_to_client(wctx->zvni->vni, &zn->ip, - &zn->emac, zn->mac, zn->flags, - zn->loc_seq); - } -} - -/* Iterator of a specific L2VNI */ -static void zvni_send_neigh_to_client(zebra_vni_t *zvni) -{ - struct neigh_walk_ctx wctx; - - memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; - - hash_iterate(zvni->neigh_table, zvni_send_neigh_hash_entry_to_client, - &wctx); -} - -static void zvni_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt) -{ - zebra_vni_t *zvni = NULL; - - zvni = (zebra_vni_t *)bucket->data; - zvni->advertise_gw_macip = 0; - zvni->advertise_svi_macip = 0; - zvni->advertise_subnet = 0; - - zvni_neigh_del_all(zvni, 1, 0, - DEL_REMOTE_NEIGH | DEL_REMOTE_NEIGH_FROM_VTEP); - zvni_mac_del_all(zvni, 1, 0, - DEL_REMOTE_MAC | DEL_REMOTE_MAC_FROM_VTEP); - zvni_vtep_del_all(zvni, 1); -} /* Cleanup EVPN configuration of a specific VRF */ static void zebra_evpn_vrf_cfg_cleanup(struct zebra_vrf *zvrf) @@ -12108,7 +5852,7 @@ static void zebra_evpn_vrf_cfg_cleanup(struct zebra_vrf *zvrf) zvrf->advertise_svi_macip = 0; zvrf->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL; - hash_iterate(zvrf->vni_table, zvni_evpn_cfg_cleanup, NULL); + hash_iterate(zvrf->evpn_table, zebra_evpn_cfg_cleanup, NULL); if (zvrf->l3vni) zl3vni = zl3vni_lookup(zvrf->l3vni); diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index e2eae56873..9a88d98b81 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -29,6 +29,8 @@ #include "if.h" #include "linklist.h" #include "zebra_vxlan.h" +#include "zebra_evpn.h" +#include "zebra_evpn_mac.h" #ifdef __cplusplus extern "C" { @@ -37,85 +39,8 @@ extern "C" { #define ERR_STR_SZ 256 /* definitions */ -typedef struct zebra_vni_t_ zebra_vni_t; -typedef struct zebra_vtep_t_ zebra_vtep_t; -typedef struct zebra_mac_t_ zebra_mac_t; -typedef struct zebra_neigh_t_ zebra_neigh_t; typedef struct zebra_l3vni_t_ zebra_l3vni_t; -/* - * VTEP info - * - * Right now, this just has each remote VTEP's IP address. - */ -struct zebra_vtep_t_ { - /* Remote IP. */ - /* NOTE: Can only be IPv4 right now. */ - struct in_addr vtep_ip; - /* Flood mode (one of enum vxlan_flood_control) based on the PMSI - * tunnel type advertised by the remote VTEP - */ - int flood_control; - - /* Links. */ - struct zebra_vtep_t_ *next; - struct zebra_vtep_t_ *prev; -}; - -RB_HEAD(zebra_es_evi_rb_head, zebra_evpn_es_evi); -RB_PROTOTYPE(zebra_es_evi_rb_head, zebra_evpn_es_evi, rb_node, - zebra_es_evi_rb_cmp); - -/* - * VNI hash table - * - * Contains information pertaining to a VNI: - * - the list of remote VTEPs (with this VNI) - */ -struct zebra_vni_t_ { - /* VNI - key */ - vni_t vni; - - /* ES flags */ - uint32_t flags; -#define ZVNI_READY_FOR_BGP (1 << 0) /* ready to be sent to BGP */ - - /* Flag for advertising gw macip */ - uint8_t advertise_gw_macip; - - /* Flag for advertising svi macip */ - uint8_t advertise_svi_macip; - - /* Flag for advertising gw macip */ - uint8_t advertise_subnet; - - /* Corresponding VxLAN interface. */ - struct interface *vxlan_if; - - /* List of remote VTEPs */ - zebra_vtep_t *vteps; - - /* Local IP */ - struct in_addr local_vtep_ip; - - /* PIM-SM MDT group for BUM flooding */ - struct in_addr mcast_grp; - - /* tenant VRF, if any */ - vrf_id_t vrf_id; - - /* List of local or remote MAC */ - struct hash *mac_table; - - /* List of local or remote neighbors (MAC+IP) */ - struct hash *neigh_table; - - /* RB tree of ES-EVIs */ - struct zebra_es_evi_rb_head es_evi_rb_tree; - - /* List of local ESs */ - struct list *local_es_evi_list; -}; /* L3 VNI hash table */ struct zebra_l3vni_t_ { @@ -279,260 +204,11 @@ static inline void zl3vni_get_svi_rmac(zebra_l3vni_t *zl3vni, memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN); } -struct host_rb_entry { - RB_ENTRY(host_rb_entry) hl_entry; - - struct prefix p; -}; - -RB_HEAD(host_rb_tree_entry, host_rb_entry); -RB_PROTOTYPE(host_rb_tree_entry, host_rb_entry, hl_entry, - host_rb_entry_compare); -/* - * MAC hash table. - * - * This table contains the MAC addresses pertaining to this VNI. - * This includes local MACs learnt on an attached VLAN that maps - * to this VNI as well as remote MACs learnt and installed by BGP. - * Local MACs will be known either on a VLAN sub-interface or - * on (port, VLAN); however, it is sufficient for zebra to maintain - * against the VNI i.e., it does not need to retain the local "port" - * information. The correct VNI will be obtained as zebra maintains - * the mapping (of VLAN to VNI). - */ -struct zebra_mac_t_ { - /* MAC address. */ - struct ethaddr macaddr; - - uint32_t flags; -#define ZEBRA_MAC_LOCAL 0x01 -#define ZEBRA_MAC_REMOTE 0x02 -#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */ -#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */ -#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */ -#define ZEBRA_MAC_DEF_GW 0x20 -/* remote VTEP advertised MAC as default GW */ -#define ZEBRA_MAC_REMOTE_DEF_GW 0x40 -#define ZEBRA_MAC_DUPLICATE 0x80 -#define ZEBRA_MAC_FPM_SENT 0x100 /* whether or not this entry was sent. */ -/* MAC is locally active on an ethernet segment peer */ -#define ZEBRA_MAC_ES_PEER_ACTIVE 0x200 -/* MAC has been proxy-advertised by peers. This means we need to - * keep the entry for forwarding but cannot advertise it - */ -#define ZEBRA_MAC_ES_PEER_PROXY 0x400 -/* We have not been able to independently establish that the host is - * local connected but one or more ES peers claims it is. - * We will maintain the entry for forwarding purposes and continue - * to advertise it as locally attached but with a "proxy" flag - */ -#define ZEBRA_MAC_LOCAL_INACTIVE 0x800 - -#define ZEBRA_MAC_ALL_LOCAL_FLAGS (ZEBRA_MAC_LOCAL |\ - ZEBRA_MAC_LOCAL_INACTIVE) -#define ZEBRA_MAC_ALL_PEER_FLAGS (ZEBRA_MAC_ES_PEER_PROXY |\ - ZEBRA_MAC_ES_PEER_ACTIVE) - - /* back pointer to zvni */ - zebra_vni_t *zvni; - - /* Local or remote info. */ - union { - struct { - ifindex_t ifindex; - vlanid_t vid; - } local; - - struct in_addr r_vtep_ip; - } fwd_info; - - /* Local or remote ES */ - struct zebra_evpn_es *es; - /* memory used to link the mac to the es */ - struct listnode es_listnode; - - /* Mobility sequence numbers associated with this entry. */ - uint32_t rem_seq; - uint32_t loc_seq; - - /* List of neigh associated with this mac */ - struct list *neigh_list; - - /* list of hosts pointing to this remote RMAC */ - struct host_rb_tree_entry host_rb; - - /* Duplicate mac detection */ - uint32_t dad_count; - - struct thread *dad_mac_auto_recovery_timer; - - struct timeval detect_start_time; - - time_t dad_dup_detect_time; - - /* used for ageing out the PEER_ACTIVE flag */ - struct thread *hold_timer; - - /* number of neigh entries (using this mac) that have - * ZEBRA_MAC_ES_PEER_ACTIVE or ZEBRA_NEIGH_ES_PEER_PROXY - */ - uint32_t sync_neigh_cnt; -}; - -/* - * Context for MAC hash walk - used by callbacks. - */ -struct mac_walk_ctx { - zebra_vni_t *zvni; /* VNI hash */ - struct zebra_vrf *zvrf; /* VRF - for client notification. */ - int uninstall; /* uninstall from kernel? */ - int upd_client; /* uninstall from client? */ - - uint32_t flags; -#define DEL_LOCAL_MAC 0x1 -#define DEL_REMOTE_MAC 0x2 -#define DEL_ALL_MAC (DEL_LOCAL_MAC | DEL_REMOTE_MAC) -#define DEL_REMOTE_MAC_FROM_VTEP 0x4 -#define SHOW_REMOTE_MAC_FROM_VTEP 0x8 - - struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */ - - struct vty *vty; /* Used by VTY handlers */ - uint32_t count; /* Used by VTY handlers */ - struct json_object *json; /* Used for JSON Output */ - bool print_dup; /* Used to print dup addr list */ -}; - -struct rmac_walk_ctx { - struct vty *vty; - struct json_object *json; -}; - -/* temporary datastruct to pass info between the mac-update and - * neigh-update while handling mac-ip routes - */ -struct sync_mac_ip_ctx { - bool ignore_macip; - bool mac_created; - bool mac_inactive; - bool mac_dp_update_deferred; - zebra_mac_t *mac; -}; - -#define IS_ZEBRA_NEIGH_ACTIVE(n) (n->state == ZEBRA_NEIGH_ACTIVE) - -#define IS_ZEBRA_NEIGH_INACTIVE(n) (n->state == ZEBRA_NEIGH_INACTIVE) - -#define ZEBRA_NEIGH_SET_ACTIVE(n) n->state = ZEBRA_NEIGH_ACTIVE - -#define ZEBRA_NEIGH_SET_INACTIVE(n) n->state = ZEBRA_NEIGH_INACTIVE - -/* - * Neighbor hash table. - * - * This table contains the neighbors (IP to MAC bindings) pertaining to - * this VNI. This includes local neighbors learnt on the attached VLAN - * device that maps to this VNI as well as remote neighbors learnt and - * installed by BGP. - * Local neighbors will be known against the VLAN device (SVI); however, - * it is sufficient for zebra to maintain against the VNI. The correct - * VNI will be obtained as zebra maintains the mapping (of VLAN to VNI). - */ -struct zebra_neigh_t_ { - /* IP address. */ - struct ipaddr ip; - - /* MAC address. */ - struct ethaddr emac; - - /* Back pointer to MAC. Only applicable to hosts in a L2-VNI. */ - zebra_mac_t *mac; - - /* Underlying interface. */ - ifindex_t ifindex; - - zebra_vni_t *zvni; - - uint32_t flags; -#define ZEBRA_NEIGH_LOCAL 0x01 -#define ZEBRA_NEIGH_REMOTE 0x02 -#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */ -#define ZEBRA_NEIGH_DEF_GW 0x08 -#define ZEBRA_NEIGH_ROUTER_FLAG 0x10 -#define ZEBRA_NEIGH_DUPLICATE 0x20 -#define ZEBRA_NEIGH_SVI_IP 0x40 -/* rxed from an ES peer */ -#define ZEBRA_NEIGH_ES_PEER_ACTIVE 0x80 -/* rxed from an ES peer as a proxy advertisement */ -#define ZEBRA_NEIGH_ES_PEER_PROXY 0x100 -/* We have not been able to independently establish that the host - * is local connected - */ -#define ZEBRA_NEIGH_LOCAL_INACTIVE 0x200 -#define ZEBRA_NEIGH_ALL_LOCAL_FLAGS (ZEBRA_NEIGH_LOCAL |\ - ZEBRA_NEIGH_LOCAL_INACTIVE) -#define ZEBRA_NEIGH_ALL_PEER_FLAGS (ZEBRA_NEIGH_ES_PEER_PROXY |\ - ZEBRA_NEIGH_ES_PEER_ACTIVE) - - enum zebra_neigh_state state; - - /* Remote VTEP IP - applicable only for remote neighbors. */ - struct in_addr r_vtep_ip; - - /* - * Mobility sequence numbers associated with this entry. The rem_seq - * represents the sequence number from the client (BGP) for the most - * recent add or update of this entry while the loc_seq represents - * the sequence number informed (or to be informed) by zebra to BGP - * for this entry. - */ - uint32_t rem_seq; - uint32_t loc_seq; - - /* list of hosts pointing to this remote NH entry */ - struct host_rb_tree_entry host_rb; - - /* Duplicate ip detection */ - uint32_t dad_count; - - struct thread *dad_ip_auto_recovery_timer; - - struct timeval detect_start_time; - - time_t dad_dup_detect_time; - - /* used for ageing out the PEER_ACTIVE flag */ - struct thread *hold_timer; -}; - -/* - * Context for neighbor hash walk - used by callbacks. - */ -struct neigh_walk_ctx { - zebra_vni_t *zvni; /* VNI hash */ - struct zebra_vrf *zvrf; /* VRF - for client notification. */ - int uninstall; /* uninstall from kernel? */ - int upd_client; /* uninstall from client? */ - - uint32_t flags; -#define DEL_LOCAL_NEIGH 0x1 -#define DEL_REMOTE_NEIGH 0x2 -#define DEL_ALL_NEIGH (DEL_LOCAL_NEIGH | DEL_REMOTE_NEIGH) -#define DEL_REMOTE_NEIGH_FROM_VTEP 0x4 -#define SHOW_REMOTE_NEIGH_FROM_VTEP 0x8 - - struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */ - - struct vty *vty; /* Used by VTY handlers */ - uint32_t count; /* Used by VTY handlers */ - uint8_t addr_width; /* Used by VTY handlers */ - struct json_object *json; /* Used for JSON Output */ -}; /* context for neigh hash walk - update l3vni and rmac */ struct neigh_l3info_walk_ctx { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_l3vni_t *zl3vni; int add; }; @@ -579,7 +255,7 @@ typedef struct zebra_vxlan_sg_ { uint32_t ref_cnt; } zebra_vxlan_sg_t; -extern zebra_vni_t *zvni_lookup(vni_t vni); +extern zebra_evpn_t *zevpn_lookup(vni_t vni); extern void zebra_vxlan_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, bool force_clear_static, const char *caller); diff --git a/zebra/zserv.c b/zebra/zserv.c index f1b7dcc848..4c8656af0d 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -628,8 +628,8 @@ 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->ridinfo); /* * If any instance are graceful restart enabled, @@ -750,8 +750,8 @@ static struct zserv *zserv_client_create(int sock) for (i = 0; i < ZEBRA_ROUTE_MAX; i++) client->redist[afi][i] = vrf_bitmap_init(); client->redist_default[afi] = vrf_bitmap_init(); + client->ridinfo[afi] = vrf_bitmap_init(); } - client->ridinfo = vrf_bitmap_init(); /* Add this client to linked list. */ frr_with_mutex(&client_mutex) { @@ -1294,17 +1294,6 @@ DEFUN (show_zebra_client_summary, return CMD_SUCCESS; } -#if defined(HANDLE_ZAPI_FUZZING) -void zserv_read_file(char *input) -{ - int fd; - - fd = open(input, O_RDONLY | O_NONBLOCK); - - zserv_client_create(fd); -} -#endif - void zserv_init(void) { /* Client list init. */ diff --git a/zebra/zserv.h b/zebra/zserv.h index e904460782..c60799b8ba 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -135,7 +135,7 @@ struct zserv { vrf_bitmap_t redist_default[AFI_MAX]; /* Router-id information. */ - vrf_bitmap_t ridinfo; + vrf_bitmap_t ridinfo[AFI_MAX]; bool notify_owner; @@ -375,10 +375,6 @@ extern void zserv_close_client(struct zserv *client); void zserv_log_message(const char *errmsg, struct stream *msg, struct zmsghdr *hdr); -#if defined(HANDLE_ZAPI_FUZZING) -extern void zserv_read_file(char *input); -#endif - /* TODO */ __attribute__((__noreturn__)) int zebra_finalize(struct thread *event); |
