diff options
77 files changed, 1771 insertions, 452 deletions
diff --git a/bfdd/bfd.c b/bfdd/bfd.c index 3cbb3691ec..9e5bc20a49 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -90,7 +90,8 @@ static void bfd_profile_set_default(struct bfd_profile *bp) 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_echo_rx = BFD_DEF_REQ_MIN_ECHO_RX; + bp->min_echo_tx = BFD_DEF_DES_MIN_ECHO_TX; bp->min_rx = BFD_DEFREQUIREDMINRX; bp->min_tx = BFD_DEFDESIREDMINTX; } @@ -179,13 +180,19 @@ void bfd_session_apply(struct bfd_session *bs) /* We can only apply echo options on single hop sessions. */ if (!CHECK_FLAG(bs->flags, BFD_SESS_FLAG_MH)) { - /* Configure remote echo if it was default. */ - if (bs->peer_profile.min_echo_rx == BFD_DEF_REQ_MIN_ECHO) - bs->timers.required_min_echo = bp->min_echo_rx; + /* Configure echo timers if they were default. */ + if (bs->peer_profile.min_echo_rx == BFD_DEF_REQ_MIN_ECHO_RX) + bs->timers.required_min_echo_rx = bp->min_echo_rx; else - bs->timers.required_min_echo = + bs->timers.required_min_echo_rx = bs->peer_profile.min_echo_rx; + if (bs->peer_profile.min_echo_tx == BFD_DEF_DES_MIN_ECHO_TX) + bs->timers.desired_min_echo_tx = bp->min_echo_tx; + else + bs->timers.desired_min_echo_tx = + bs->peer_profile.min_echo_tx; + /* Toggle echo if default value. */ if (bs->peer_profile.echo_mode == false) bfd_set_echo(bs, bp->echo_mode); @@ -494,8 +501,10 @@ void ptm_bfd_echo_stop(struct bfd_session *bfd) void ptm_bfd_echo_start(struct bfd_session *bfd) { bfd->echo_detect_TO = (bfd->remote_detect_mult * bfd->echo_xmt_TO); - if (bfd->echo_detect_TO > 0) + if (bfd->echo_detect_TO > 0) { + bfd_echo_recvtimer_update(bfd); ptm_bfd_echo_xmt_TO(bfd); + } } void ptm_bfd_sess_up(struct bfd_session *bfd) @@ -700,7 +709,8 @@ struct bfd_session *bfd_session_new(void) bs->timers.desired_min_tx = BFD_DEFDESIREDMINTX; bs->timers.required_min_rx = BFD_DEFREQUIREDMINRX; - bs->timers.required_min_echo = BFD_DEF_REQ_MIN_ECHO; + bs->timers.required_min_echo_rx = BFD_DEF_REQ_MIN_ECHO_RX; + bs->timers.desired_min_echo_tx = BFD_DEF_DES_MIN_ECHO_TX; bs->detect_mult = BFD_DEFDETECTMULT; bs->mh_ttl = BFD_DEF_MHOP_TTL; bs->ses_state = PTM_BFD_DOWN; @@ -769,9 +779,14 @@ static void _bfd_session_update(struct bfd_session *bs, bs->peer_profile.detection_multiplier = bs->detect_mult; } - if (bpc->bpc_has_echointerval) { - bs->timers.required_min_echo = bpc->bpc_echointerval * 1000; - bs->peer_profile.min_echo_rx = bs->timers.required_min_echo; + if (bpc->bpc_has_echorecvinterval) { + bs->timers.required_min_echo_rx = bpc->bpc_echorecvinterval * 1000; + bs->peer_profile.min_echo_rx = bs->timers.required_min_echo_rx; + } + + if (bpc->bpc_has_echotxinterval) { + bs->timers.desired_min_echo_tx = bpc->bpc_echotxinterval * 1000; + bs->peer_profile.min_echo_tx = bs->timers.desired_min_echo_tx; } if (bpc->bpc_has_label) @@ -1189,10 +1204,10 @@ void bs_echo_timer_handler(struct bfd_session *bs) * RFC 5880, Section 6.8.9. */ old_timer = bs->echo_xmt_TO; - if (bs->remote_timers.required_min_echo > bs->timers.required_min_echo) + if (bs->remote_timers.required_min_echo > bs->timers.desired_min_echo_tx) bs->echo_xmt_TO = bs->remote_timers.required_min_echo; else - bs->echo_xmt_TO = bs->timers.required_min_echo; + bs->echo_xmt_TO = bs->timers.desired_min_echo_tx; if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO_ACTIVE) == 0 || old_timer != bs->echo_xmt_TO) diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 7c537b40d0..503b5ca7d6 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -215,6 +215,8 @@ struct bfd_profile { /** Echo mode (only applies to single hop). */ bool echo_mode; + /** Desired echo transmission interval (in microseconds). */ + uint32_t min_echo_tx; /** Minimum required echo receive interval (in microseconds). */ uint32_t min_echo_rx; @@ -228,6 +230,13 @@ TAILQ_HEAD(bfdproflist, bfd_profile); /* bfd_session shortcut label forwarding. */ struct peer_label; +struct bfd_config_timers { + uint32_t desired_min_tx; + uint32_t required_min_rx; + uint32_t desired_min_echo_tx; + uint32_t required_min_echo_rx; +}; + /* * Session state information */ @@ -251,7 +260,7 @@ struct bfd_session { struct bfd_profile peer_profile; /* Timers */ - struct bfd_timers timers; + struct bfd_config_timers timers; struct bfd_timers cur_timers; uint64_t detect_TO; struct thread *echo_recvtimer_ev; @@ -332,7 +341,8 @@ TAILQ_HEAD(obslist, bfd_session_observer); #define BFD_DEFDETECTMULT 3 #define BFD_DEFDESIREDMINTX (300 * 1000) /* microseconds. */ #define BFD_DEFREQUIREDMINRX (300 * 1000) /* microseconds. */ -#define BFD_DEF_REQ_MIN_ECHO (50 * 1000) /* microseconds. */ +#define BFD_DEF_DES_MIN_ECHO_TX (50 * 1000) /* microseconds. */ +#define BFD_DEF_REQ_MIN_ECHO_RX (50 * 1000) /* microseconds. */ #define BFD_DEF_SLOWTX (1000 * 1000) /* microseconds. */ /** Minimum multi hop TTL. */ #define BFD_DEF_MHOP_TTL 254 diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 12bb52cf67..c871e2abe1 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -267,7 +267,7 @@ void ptm_bfd_snd(struct bfd_session *bfd, int fbit) cp.timers.required_min_rx = htonl(bfd->cur_timers.required_min_rx); } - cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo); + cp.timers.required_min_echo = htonl(bfd->timers.required_min_echo_rx); if (_ptm_bfd_send(bfd, NULL, &cp, BFD_PKT_LEN) != 0) return; diff --git a/bfdd/bfdctl.h b/bfdd/bfdctl.h index e1cff9a31c..c4b2e80306 100644 --- a/bfdd/bfdctl.h +++ b/bfdd/bfdctl.h @@ -47,7 +47,8 @@ struct sockaddr_any { #define BPC_DEF_DETECTMULTIPLIER 3 #define BPC_DEF_RECEIVEINTERVAL 300 /* milliseconds */ #define BPC_DEF_TRANSMITINTERVAL 300 /* milliseconds */ -#define BPC_DEF_ECHOINTERVAL 50 /* milliseconds */ +#define BPC_DEF_ECHORECEIVEINTERVAL 50 /* milliseconds */ +#define BPC_DEF_ECHOTRANSMITINTERVAL 50 /* milliseconds */ /* Peer status */ enum bfd_peer_status { @@ -81,8 +82,11 @@ struct bfd_peer_cfg { bool bpc_has_txinterval; uint64_t bpc_txinterval; - bool bpc_has_echointerval; - uint64_t bpc_echointerval; + bool bpc_has_echorecvinterval; + uint64_t bpc_echorecvinterval; + + bool bpc_has_echotxinterval; + uint64_t bpc_echotxinterval; bool bpc_has_minimum_ttl; uint8_t bpc_minimum_ttl; diff --git a/bfdd/bfdd_cli.c b/bfdd/bfdd_cli.c index 206f6c7d0c..ba80b2363f 100644 --- a/bfdd/bfdd_cli.c +++ b/bfdd/bfdd_cli.c @@ -433,6 +433,10 @@ DEFPY_YANG( return CMD_WARNING_CONFIG_FAILED; } + if (!no && !bglobal.bg_use_dplane) { + vty_out(vty, "%% Current implementation of echo mode works only when the peer is also FRR.\n"); + } + nb_cli_enqueue_change(vty, "./echo-mode", NB_OP_MODIFY, no ? "false" : "true"); return nb_cli_apply_changes(vty, NULL); @@ -451,8 +455,31 @@ void bfd_cli_show_echo(struct vty *vty, struct lyd_node *dnode, DEFPY_YANG( bfd_peer_echo_interval, bfd_peer_echo_interval_cmd, "echo-interval (10-60000)$interval", - "Configure peer echo interval\n" - "Configure peer echo interval value in milliseconds\n") + "Configure peer echo intervals\n" + "Configure peer echo rx/tx intervals value in milliseconds\n") +{ + char value[32]; + + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { + vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + snprintf(value, sizeof(value), "%ld", interval * 1000); + nb_cli_enqueue_change(vty, "./desired-echo-transmission-interval", + NB_OP_MODIFY, value); + nb_cli_enqueue_change(vty, "./required-echo-receive-interval", + NB_OP_MODIFY, value); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG( + bfd_peer_echo_transmit_interval, bfd_peer_echo_transmit_interval_cmd, + "echo transmit-interval (10-60000)$interval", + "Configure peer echo intervals\n" + "Configure desired transmit interval\n" + "Configure interval value in milliseconds\n") { char value[32]; @@ -468,17 +495,61 @@ DEFPY_YANG( return nb_cli_apply_changes(vty, NULL); } -void bfd_cli_show_echo_interval(struct vty *vty, struct lyd_node *dnode, - bool show_defaults) +void bfd_cli_show_desired_echo_transmission_interval(struct vty *vty, + struct lyd_node *dnode, bool show_defaults) +{ + uint32_t value; + + if (show_defaults) + vty_out(vty, " echo transmit-interval %d\n", + BFD_DEF_DES_MIN_ECHO_TX); + else { + value = yang_dnode_get_uint32(dnode, NULL); + vty_out(vty, " echo transmit-interval %u\n", value / 1000); + } +} + +DEFPY_YANG( + bfd_peer_echo_receive_interval, bfd_peer_echo_receive_interval_cmd, + "echo receive-interval <disabled$disabled|(10-60000)$interval>", + "Configure peer echo intervals\n" + "Configure required receive interval\n" + "Disable echo packets receive\n" + "Configure interval value in milliseconds\n") +{ + char value[32]; + + if (!bfd_cli_is_profile(vty) && !bfd_cli_is_single_hop(vty)) { + vty_out(vty, "%% Echo mode is only available for single hop sessions.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (disabled) + snprintf(value, sizeof(value), "0"); + else + snprintf(value, sizeof(value), "%ld", interval * 1000); + + nb_cli_enqueue_change(vty, "./required-echo-receive-interval", + NB_OP_MODIFY, value); + + return nb_cli_apply_changes(vty, NULL); +} + +void bfd_cli_show_required_echo_receive_interval(struct vty *vty, + struct lyd_node *dnode, bool show_defaults) { uint32_t value; if (show_defaults) - vty_out(vty, " echo-interval %d\n", - BFD_DEF_REQ_MIN_ECHO); + vty_out(vty, " echo receive-interval %d\n", + BFD_DEF_REQ_MIN_ECHO_RX); else { value = yang_dnode_get_uint32(dnode, NULL); - vty_out(vty, " echo-interval %u\n", value / 1000); + if (value) + vty_out(vty, " echo receive-interval %u\n", + value / 1000); + else + vty_out(vty, " echo receive-interval disabled\n"); } } @@ -575,6 +646,21 @@ ALIAS_YANG(bfd_peer_echo_interval, bfd_profile_echo_interval_cmd, "Configure peer echo interval\n" "Configure peer echo interval value in milliseconds\n") +ALIAS_YANG( + bfd_peer_echo_transmit_interval, bfd_profile_echo_transmit_interval_cmd, + "echo transmit-interval (10-60000)$interval", + "Configure peer echo intervals\n" + "Configure desired transmit interval\n" + "Configure interval value in milliseconds\n") + +ALIAS_YANG( + bfd_peer_echo_receive_interval, bfd_profile_echo_receive_interval_cmd, + "echo receive-interval <disabled$disabled|(10-60000)$interval>", + "Configure peer echo intervals\n" + "Configure required receive interval\n" + "Disable echo packets receive\n" + "Configure interval value in milliseconds\n") + DEFPY_YANG(bfd_peer_profile, bfd_peer_profile_cmd, "[no] profile BFDPROF$pname", NO_STR @@ -632,6 +718,8 @@ bfdd_cli_init(void) install_element(BFD_PEER_NODE, &bfd_peer_tx_cmd); 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_echo_transmit_interval_cmd); + install_element(BFD_PEER_NODE, &bfd_peer_echo_receive_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); @@ -652,6 +740,8 @@ 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_echo_transmit_interval_cmd); + install_element(BFD_PROFILE_NODE, &bfd_profile_echo_receive_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 64ba3cf811..29a9b5f2d5 100644 --- a/bfdd/bfdd_nb.c +++ b/bfdd/bfdd_nb.c @@ -103,8 +103,15 @@ const struct frr_yang_module_info frr_bfdd_info = { .xpath = "/frr-bfdd:bfdd/bfd/profile/desired-echo-transmission-interval", .cbs = { .modify = bfdd_bfd_profile_desired_echo_transmission_interval_modify, - .cli_show = bfd_cli_show_echo_interval, - } + .cli_show = bfd_cli_show_desired_echo_transmission_interval, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/profile/required-echo-receive-interval", + .cbs = { + .modify = bfdd_bfd_profile_required_echo_receive_interval_modify, + .cli_show = bfd_cli_show_required_echo_receive_interval, + } }, { .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop", @@ -179,7 +186,14 @@ const struct frr_yang_module_info frr_bfdd_info = { .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/desired-echo-transmission-interval", .cbs = { .modify = bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify, - .cli_show = bfd_cli_show_echo_interval, + .cli_show = bfd_cli_show_desired_echo_transmission_interval, + } + }, + { + .xpath = "/frr-bfdd:bfdd/bfd/sessions/single-hop/required-echo-receive-interval", + .cbs = { + .modify = bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify, + .cli_show = bfd_cli_show_required_echo_receive_interval, } }, { diff --git a/bfdd/bfdd_nb.h b/bfdd/bfdd_nb.h index fbd557b6b1..874e98691f 100644 --- a/bfdd/bfdd_nb.h +++ b/bfdd/bfdd_nb.h @@ -43,6 +43,8 @@ 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); +int bfdd_bfd_profile_required_echo_receive_interval_modify( + struct nb_cb_modify_args *args); int bfdd_bfd_sessions_single_hop_create(struct nb_cb_create_args *args); int bfdd_bfd_sessions_single_hop_destroy(struct nb_cb_destroy_args *args); const void * @@ -71,6 +73,8 @@ 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( struct nb_cb_modify_args *args); +int bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify( + struct nb_cb_modify_args *args); struct yang_data * bfdd_bfd_sessions_single_hop_stats_local_discriminator_get_elem( struct nb_cb_get_elem_args *args); @@ -209,8 +213,10 @@ void bfd_cli_show_shutdown(struct vty *vty, struct lyd_node *dnode, bool show_defaults); void bfd_cli_show_echo(struct vty *vty, struct lyd_node *dnode, bool show_defaults); -void bfd_cli_show_echo_interval(struct vty *vty, struct lyd_node *dnode, - bool show_defaults); +void bfd_cli_show_desired_echo_transmission_interval( + struct vty *vty, struct lyd_node *dnode, bool show_defaults); +void bfd_cli_show_required_echo_receive_interval( + struct vty *vty, struct lyd_node *dnode, bool show_defaults); 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, diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c index c8dd5cc3f6..26bce4f357 100644 --- a/bfdd/bfdd_nb_config.c +++ b/bfdd/bfdd_nb_config.c @@ -483,17 +483,57 @@ int bfdd_bfd_profile_echo_mode_modify(struct nb_cb_modify_args *args) } /* - * XPath: /frr-bfdd:bfdd/bfd/profile/desired-echo-echo-transmission-interval + * XPath: /frr-bfdd:bfdd/bfd/profile/desired-echo-transmission-interval */ int bfdd_bfd_profile_desired_echo_transmission_interval_modify( struct nb_cb_modify_args *args) { struct bfd_profile *bp; + uint32_t min_tx; + + switch (args->event) { + case NB_EV_VALIDATE: + min_tx = yang_dnode_get_uint32(args->dnode, NULL); + if (min_tx < 10000 || min_tx > 60000000) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + /* NOTHING */ + break; + + case NB_EV_APPLY: + min_tx = yang_dnode_get_uint32(args->dnode, NULL); + bp = nb_running_get_entry(args->dnode, NULL, true); + if (bp->min_echo_tx == min_tx) + return NB_OK; + + bp->min_echo_tx = min_tx; + bfd_profile_update(bp); + break; + + case NB_EV_ABORT: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-bfdd:bfdd/bfd/profile/required-echo-receive-interval + */ +int bfdd_bfd_profile_required_echo_receive_interval_modify( + struct nb_cb_modify_args *args) +{ + struct bfd_profile *bp; uint32_t min_rx; switch (args->event) { case NB_EV_VALIDATE: min_rx = yang_dnode_get_uint32(args->dnode, NULL); + if (min_rx == 0) + return NB_OK; if (min_rx < 10000 || min_rx > 60000000) return NB_ERR_VALIDATION; break; @@ -789,7 +829,46 @@ int bfdd_bfd_sessions_single_hop_desired_echo_transmission_interval_modify( case NB_EV_APPLY: bs = nb_running_get_entry(args->dnode, NULL, true); - if (echo_interval == bs->timers.required_min_echo) + if (echo_interval == bs->timers.desired_min_echo_tx) + return NB_OK; + + bs->peer_profile.min_echo_tx = echo_interval; + bfd_session_apply(bs); + break; + + case NB_EV_ABORT: + /* NOTHING */ + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-bfdd:bfdd/bfd/sessions/single-hop/required-echo-receive-interval + */ +int bfdd_bfd_sessions_single_hop_required_echo_receive_interval_modify( + struct nb_cb_modify_args *args) +{ + uint32_t echo_interval = yang_dnode_get_uint32(args->dnode, NULL); + struct bfd_session *bs; + + switch (args->event) { + case NB_EV_VALIDATE: + if (echo_interval == 0) + return NB_OK; + if (echo_interval < 10000 || echo_interval > 60000000) + return NB_ERR_VALIDATION; + break; + + case NB_EV_PREPARE: + /* NOTHING */ + break; + + case NB_EV_APPLY: + bs = nb_running_get_entry(args->dnode, NULL, true); + if (echo_interval == bs->timers.required_min_echo_rx) return NB_OK; bs->peer_profile.min_echo_rx = echo_interval; diff --git a/bfdd/bfdd_vty.c b/bfdd/bfdd_vty.c index cb140f7b18..a03fb9f216 100644 --- a/bfdd/bfdd_vty.c +++ b/bfdd/bfdd_vty.c @@ -158,8 +158,16 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) bs->timers.required_min_rx / 1000); vty_out(vty, "\t\t\tTransmission interval: %ums\n", bs->timers.desired_min_tx / 1000); - vty_out(vty, "\t\t\tEcho transmission interval: %ums\n", - bs->timers.required_min_echo / 1000); + if (bs->timers.required_min_echo_rx != 0) + vty_out(vty, "\t\t\tEcho receive interval: %ums\n", + bs->timers.required_min_echo_rx / 1000); + else + vty_out(vty, "\t\t\tEcho receive interval: disabled\n"); + if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) + vty_out(vty, "\t\t\tEcho transmission interval: %ums\n", + bs->timers.desired_min_echo_tx / 1000); + else + vty_out(vty, "\t\t\tEcho transmission interval: disabled\n"); vty_out(vty, "\t\tRemote timers:\n"); vty_out(vty, "\t\t\tDetect-multiplier: %u\n", @@ -168,8 +176,11 @@ static void _display_peer(struct vty *vty, struct bfd_session *bs) bs->remote_timers.required_min_rx / 1000); vty_out(vty, "\t\t\tTransmission interval: %ums\n", bs->remote_timers.desired_min_tx / 1000); - vty_out(vty, "\t\t\tEcho transmission interval: %ums\n", - bs->remote_timers.required_min_echo / 1000); + if (bs->remote_timers.required_min_echo != 0) + vty_out(vty, "\t\t\tEcho receive interval: %ums\n", + bs->remote_timers.required_min_echo / 1000); + else + vty_out(vty, "\t\t\tEcho receive interval: disabled\n"); vty_out(vty, "\n"); } @@ -245,11 +256,13 @@ static struct json_object *__display_peer_json(struct bfd_session *bs) bs->timers.required_min_rx / 1000); json_object_int_add(jo, "transmit-interval", bs->timers.desired_min_tx / 1000); + json_object_int_add(jo, "echo-receive-interval", + bs->timers.required_min_echo_rx / 1000); if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) - json_object_int_add(jo, "echo-interval", - bs->timers.required_min_echo / 1000); + json_object_int_add(jo, "echo-transmit-interval", + bs->timers.desired_min_echo_tx / 1000); else - json_object_int_add(jo, "echo-interval", 0); + json_object_int_add(jo, "echo-transmit-interval", 0); json_object_int_add(jo, "detect-multiplier", bs->detect_mult); @@ -257,7 +270,7 @@ static struct json_object *__display_peer_json(struct bfd_session *bs) bs->remote_timers.required_min_rx / 1000); json_object_int_add(jo, "remote-transmit-interval", bs->remote_timers.desired_min_tx / 1000); - json_object_int_add(jo, "remote-echo-interval", + json_object_int_add(jo, "remote-echo-receive-interval", bs->remote_timers.required_min_echo / 1000); json_object_int_add(jo, "remote-detect-multiplier", bs->remote_detect_mult); @@ -844,7 +857,8 @@ static int bfd_configure_peer(struct bfd_peer_cfg *bpc, bool mhop, bpc->bpc_detectmultiplier = BPC_DEF_DETECTMULTIPLIER; bpc->bpc_recvinterval = BPC_DEF_RECEIVEINTERVAL; bpc->bpc_txinterval = BPC_DEF_TRANSMITINTERVAL; - bpc->bpc_echointerval = BPC_DEF_ECHOINTERVAL; + bpc->bpc_echorecvinterval = BPC_DEF_ECHORECEIVEINTERVAL; + bpc->bpc_echotxinterval = BPC_DEF_ECHOTRANSMITINTERVAL; bpc->bpc_lastevent = monotime(NULL); /* Safety check: when no error buf is provided len must be zero. */ diff --git a/bfdd/bfddp_packet.h b/bfdd/bfddp_packet.h index 8865baef6a..4ece94f577 100644 --- a/bfdd/bfddp_packet.h +++ b/bfdd/bfddp_packet.h @@ -163,6 +163,11 @@ struct bfddp_session { */ uint32_t min_rx; /** + * Minimum desired echo transmission interval (in microseconds) + * without jitter. + */ + uint32_t min_echo_tx; + /** * Required minimum echo receive interval rate (in microseconds) * without jitter. */ diff --git a/bfdd/config.c b/bfdd/config.c index b71670f012..a97caf137e 100644 --- a/bfdd/config.c +++ b/bfdd/config.c @@ -135,7 +135,8 @@ static int parse_list(struct json_object *jo, enum peer_list_type plt, bpc.bpc_detectmultiplier = BFD_DEFDETECTMULT; bpc.bpc_recvinterval = BFD_DEFREQUIREDMINRX; bpc.bpc_txinterval = BFD_DEFDESIREDMINTX; - bpc.bpc_echointerval = BFD_DEF_REQ_MIN_ECHO; + bpc.bpc_echorecvinterval = BFD_DEF_REQ_MIN_ECHO_RX; + bpc.bpc_echotxinterval = BFD_DEF_DES_MIN_ECHO_TX; switch (plt) { case PLT_IPV4: @@ -250,11 +251,16 @@ static int parse_peer_config(struct json_object *jo, struct bfd_peer_cfg *bpc) bpc->bpc_has_txinterval = true; zlog_debug(" transmit-interval: %" PRIu64, bpc->bpc_txinterval); - } else if (strcmp(key, "echo-interval") == 0) { - bpc->bpc_echointerval = json_object_get_int64(jo_val); - bpc->bpc_has_echointerval = true; - zlog_debug(" echo-interval: %" PRIu64, - bpc->bpc_echointerval); + } else if (strcmp(key, "echo-receive-interval") == 0) { + bpc->bpc_echorecvinterval = json_object_get_int64(jo_val); + bpc->bpc_has_echorecvinterval = true; + zlog_debug(" echo-receive-interval: %" PRIu64, + bpc->bpc_echorecvinterval); + } else if (strcmp(key, "echo-transmit-interval") == 0) { + bpc->bpc_echotxinterval = json_object_get_int64(jo_val); + bpc->bpc_has_echotxinterval = true; + zlog_debug(" echo-transmit-interval: %" PRIu64, + bpc->bpc_echotxinterval); } else if (strcmp(key, "create-only") == 0) { bpc->bpc_createonly = json_object_get_boolean(jo_val); zlog_debug(" create-only: %s", @@ -463,8 +469,10 @@ char *config_notify_config(const char *op, struct bfd_session *bs) bs->timers.required_min_rx / 1000); json_object_int_add(resp, "transmit-interval", bs->timers.desired_min_tx / 1000); - json_object_int_add(resp, "echo-interval", - bs->timers.required_min_echo / 1000); + json_object_int_add(resp, "echo-receive-interval", + bs->timers.required_min_echo_rx / 1000); + json_object_int_add(resp, "echo-transmit-interval", + bs->timers.desired_min_echo_tx / 1000); json_object_int_add(resp, "remote-detect-multiplier", bs->remote_detect_mult); @@ -472,7 +480,7 @@ char *config_notify_config(const char *op, struct bfd_session *bs) bs->remote_timers.required_min_rx / 1000); json_object_int_add(resp, "remote-transmit-interval", bs->remote_timers.desired_min_tx / 1000); - json_object_int_add(resp, "remote-echo-interval", + json_object_int_add(resp, "remote-echo-receive-interval", bs->remote_timers.required_min_echo / 1000); if (CHECK_FLAG(bs->flags, BFD_SESS_FLAG_ECHO)) diff --git a/bfdd/dplane.c b/bfdd/dplane.c index 66b79f3b13..9cb0b0ea85 100644 --- a/bfdd/dplane.c +++ b/bfdd/dplane.c @@ -765,7 +765,8 @@ static void _bfd_dplane_session_fill(const struct bfd_session *bs, msg->data.session.lid = htonl(bs->discrs.my_discr); msg->data.session.min_tx = htonl(bs->timers.desired_min_tx); msg->data.session.min_rx = htonl(bs->timers.required_min_rx); - msg->data.session.min_echo_rx = htonl(bs->timers.required_min_echo); + msg->data.session.min_echo_tx = htonl(bs->timers.desired_min_echo_tx); + msg->data.session.min_echo_rx = htonl(bs->timers.required_min_echo_rx); } static int _bfd_dplane_add_session(struct bfd_dplane_ctx *bdc, diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index 3162579688..5d1a7a98d7 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -62,6 +62,9 @@ struct as_filter { regex_t *reg; char *reg_str; + + /* Sequence number. */ + int64_t seq; }; /* AS path filter list. */ @@ -77,6 +80,38 @@ struct as_list { struct as_filter *tail; }; + +/* Calculate new sequential number. */ +static int64_t bgp_alist_new_seq_get(struct as_list *list) +{ + int64_t maxseq; + int64_t newseq; + struct as_filter *entry; + + maxseq = 0; + + for (entry = list->head; entry; entry = entry->next) { + if (maxseq < entry->seq) + maxseq = entry->seq; + } + + newseq = ((maxseq / 5) * 5) + 5; + + return (newseq > UINT_MAX) ? UINT_MAX : newseq; +} + +/* Return as-list entry which has same seq number. */ +static struct as_filter *bgp_aslist_seq_check(struct as_list *list, int64_t seq) +{ + struct as_filter *entry; + + for (entry = list->head; entry; entry = entry->next) + if (entry->seq == seq) + return entry; + + return NULL; +} + /* as-path access-list 10 permit AS1. */ static struct as_list_master as_list_master = {{NULL, NULL}, @@ -125,17 +160,69 @@ static struct as_filter *as_filter_lookup(struct as_list *aslist, return NULL; } +static void as_filter_entry_replace(struct as_list *list, + struct as_filter *replace, + struct as_filter *entry) +{ + if (replace->next) { + entry->next = replace->next; + replace->next->prev = entry; + } else { + entry->next = NULL; + list->tail = entry; + } + + if (replace->prev) { + entry->prev = replace->prev; + replace->prev->next = entry; + } else { + entry->prev = NULL; + list->head = entry; + } + + as_filter_free(replace); +} + static void as_list_filter_add(struct as_list *aslist, struct as_filter *asfilter) { - asfilter->next = NULL; - asfilter->prev = aslist->tail; + struct as_filter *point; + struct as_filter *replace; - if (aslist->tail) - aslist->tail->next = asfilter; - else - aslist->head = asfilter; - aslist->tail = asfilter; + if (aslist->tail && asfilter->seq > aslist->tail->seq) + point = NULL; + else { + replace = bgp_aslist_seq_check(aslist, asfilter->seq); + if (replace) { + as_filter_entry_replace(aslist, replace, asfilter); + return; + } + + /* Check insert point. */ + for (point = aslist->head; point; point = point->next) + if (point->seq >= asfilter->seq) + break; + } + + asfilter->next = point; + + if (point) { + if (point->prev) + point->prev->next = asfilter; + else + aslist->head = asfilter; + + asfilter->prev = point->prev; + point->prev = asfilter; + } else { + if (aslist->tail) + aslist->tail->next = asfilter; + else + aslist->head = asfilter; + + asfilter->prev = aslist->tail; + aslist->tail = asfilter; + } /* Run hook function. */ if (as_list_master.add_hook) @@ -391,11 +478,13 @@ bool config_bgp_aspath_validate(const char *regstr) } DEFUN(as_path, bgp_as_path_cmd, - "bgp as-path access-list WORD <deny|permit> LINE...", + "bgp as-path access-list WORD [seq (0-4294967295)] <deny|permit> LINE...", BGP_STR "BGP autonomous system path filter\n" "Specify an access list name\n" "Regular expression access list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") @@ -406,11 +495,15 @@ DEFUN(as_path, bgp_as_path_cmd, struct as_list *aslist; regex_t *regex; char *regstr; + int64_t seqnum = ASPATH_SEQ_NUMBER_AUTO; /* Retrieve access list name */ argv_find(argv, argc, "WORD", &idx); char *alname = argv[idx]->arg; + if (argv_find(argv, argc, "(0-4294967295)", &idx)) + seqnum = (int64_t)atol(argv[idx]->arg); + /* Check the filter type. */ type = argv_find(argv, argc, "deny", &idx) ? AS_FILTER_DENY : AS_FILTER_PERMIT; @@ -439,6 +532,11 @@ DEFUN(as_path, bgp_as_path_cmd, /* Install new filter to the access_list. */ aslist = as_list_get(alname); + if (seqnum == ASPATH_SEQ_NUMBER_AUTO) + seqnum = bgp_alist_new_seq_get(aslist); + + asfilter->seq = seqnum; + /* Duplicate insertion check. */; if (as_list_dup_check(aslist, asfilter)) as_filter_free(asfilter); @@ -449,12 +547,14 @@ DEFUN(as_path, bgp_as_path_cmd, } DEFUN(no_as_path, no_bgp_as_path_cmd, - "no bgp as-path access-list WORD <deny|permit> LINE...", + "no bgp as-path access-list WORD [seq (0-4294967295)] <deny|permit> LINE...", NO_STR BGP_STR "BGP autonomous system path filter\n" "Specify an access list name\n" "Regular expression access list name\n" + "Sequence number of an entry\n" + "Sequence number\n" "Specify packets to reject\n" "Specify packets to forward\n" "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n") @@ -643,8 +743,11 @@ static int config_write_as_list(struct vty *vty) for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) { - vty_out(vty, "bgp as-path access-list %s %s %s\n", - aslist->name, filter_type_str(asfilter->type), + vty_out(vty, + "bgp as-path access-list %s seq %" PRId64 + " %s %s\n", + aslist->name, asfilter->seq, + filter_type_str(asfilter->type), asfilter->reg_str); write++; } @@ -652,8 +755,11 @@ static int config_write_as_list(struct vty *vty) for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) { - vty_out(vty, "bgp as-path access-list %s %s %s\n", - aslist->name, filter_type_str(asfilter->type), + vty_out(vty, + "bgp as-path access-list %s seq %" PRId64 + " %s %s\n", + aslist->name, asfilter->seq, + filter_type_str(asfilter->type), asfilter->reg_str); write++; } diff --git a/bgpd/bgp_filter.h b/bgpd/bgp_filter.h index 9357a2d382..66c83d97e9 100644 --- a/bgpd/bgp_filter.h +++ b/bgpd/bgp_filter.h @@ -21,6 +21,8 @@ #ifndef _QUAGGA_BGP_FILTER_H #define _QUAGGA_BGP_FILTER_H +#define ASPATH_SEQ_NUMBER_AUTO -1 + enum as_filter_type { AS_FILTER_DENY, AS_FILTER_PERMIT }; extern void bgp_filter_init(void); diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 757d76f69e..6abba18418 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -343,8 +343,8 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) bgp_reads_on(peer); bgp_writes_on(peer); - thread_add_timer_msec(bm->master, bgp_process_packet, peer, 0, - &peer->t_process_packet); + thread_add_event(bm->master, bgp_process_packet, peer, 0, + &peer->t_process_packet); return (peer); } diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index 7aa489e932..a696d95697 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -268,8 +268,8 @@ static int bgp_process_reads(struct thread *thread) thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd, &peer->t_read); if (added_pkt) - thread_add_timer_msec(bm->master, bgp_process_packet, - peer, 0, &peer->t_process_packet); + thread_add_event(bm->master, bgp_process_packet, + peer, 0, &peer->t_process_packet); } return 0; diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index f04b89594e..e08f19fb45 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -2680,7 +2680,7 @@ int bgp_process_packet(struct thread *thread) frr_with_mutex(&peer->io_mtx) { // more work to do, come back later if (peer->ibuf->count > 0) - thread_add_timer_msec( + thread_add_event( bm->master, bgp_process_packet, peer, 0, &peer->t_process_packet); } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index a4e56c95c8..4b012430d9 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -24,6 +24,7 @@ #include "lib/json.h" #include "lib_errors.h" #include "lib/zclient.h" +#include "lib/printfrr.h" #include "prefix.h" #include "plist.h" #include "buffer.h" @@ -739,7 +740,7 @@ int bgp_nb_errmsg_return(char *errmsg, size_t errmsg_len, int ret) str = "Operation not allowed on a directly connected neighbor"; break; case BGP_ERR_PEER_SAFI_CONFLICT: - str = GR_INVALID; + str = "Cannot activate peer for both 'ipv4 unicast' and 'ipv4 labeled-unicast'"; break; case BGP_ERR_GR_INVALID_CMD: str = "The Graceful Restart command used is not valid at this moment."; @@ -831,7 +832,7 @@ int bgp_vty_return(struct vty *vty, int ret) str = "Operation not allowed on a directly connected neighbor"; break; case BGP_ERR_PEER_SAFI_CONFLICT: - str = GR_INVALID; + str = "Cannot activate peer for both 'ipv4 unicast' and 'ipv4 labeled-unicast'"; break; case BGP_ERR_GR_INVALID_CMD: str = "The Graceful Restart command used is not valid at this moment."; @@ -15464,7 +15465,8 @@ DEFPY(show_ip_bgp_instance_updgrps_adj_s, return CMD_SUCCESS; } -static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group) +static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group, + json_object *json) { struct listnode *node, *nnode; struct prefix *range; @@ -15473,64 +15475,143 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group) afi_t afi; safi_t safi; const char *peer_status; - const char *af_str; int lr_count; int dynamic; - int af_cfgd; + bool af_cfgd; + json_object *json_peer_group = NULL; + json_object *json_peer_group_afc = NULL; + json_object *json_peer_group_members = NULL; + json_object *json_peer_group_dynamic = NULL; + json_object *json_peer_group_dynamic_af = NULL; + json_object *json_peer_group_ranges = NULL; conf = group->conf; + if (json) { + json_peer_group = json_object_new_object(); + json_peer_group_afc = json_object_new_array(); + } + if (conf->as_type == AS_SPECIFIED || conf->as_type == AS_EXTERNAL) { - vty_out(vty, "\nBGP peer-group %s, remote AS %u\n", - group->name, conf->as); + if (json) + json_object_int_add(json_peer_group, "remoteAs", + conf->as); + else + vty_out(vty, "\nBGP peer-group %s, remote AS %u\n", + group->name, conf->as); } else if (conf->as_type == AS_INTERNAL) { - vty_out(vty, "\nBGP peer-group %s, remote AS %u\n", - group->name, group->bgp->as); + if (json) + json_object_int_add(json_peer_group, "remoteAs", + group->bgp->as); + else + vty_out(vty, "\nBGP peer-group %s, remote AS %u\n", + group->name, group->bgp->as); } else { - vty_out(vty, "\nBGP peer-group %s\n", group->name); + if (!json) + vty_out(vty, "\nBGP peer-group %s\n", group->name); } - if ((group->bgp->as == conf->as) || (conf->as_type == AS_INTERNAL)) - vty_out(vty, " Peer-group type is internal\n"); - else - vty_out(vty, " Peer-group type is external\n"); + if ((group->bgp->as == conf->as) || (conf->as_type == AS_INTERNAL)) { + if (json) + json_object_string_add(json_peer_group, "type", + "internal"); + else + vty_out(vty, " Peer-group type is internal\n"); + } else { + if (json) + json_object_string_add(json_peer_group, "type", + "external"); + else + vty_out(vty, " Peer-group type is external\n"); + } /* Display AFs configured. */ - vty_out(vty, " Configured address-families:"); + if (!json) + vty_out(vty, " Configured address-families:"); + FOREACH_AFI_SAFI (afi, safi) { if (conf->afc[afi][safi]) { - af_cfgd = 1; - vty_out(vty, " %s;", get_afi_safi_str(afi, safi, false)); + af_cfgd = true; + if (json) + json_object_array_add( + json_peer_group_afc, + json_object_new_string(get_afi_safi_str( + afi, safi, false))); + else + vty_out(vty, " %s;", + get_afi_safi_str(afi, safi, false)); } } - if (!af_cfgd) - vty_out(vty, " none\n"); - else - vty_out(vty, "\n"); + + if (json) { + json_object_object_add(json_peer_group, + "addressFamiliesConfigured", + json_peer_group_afc); + } else { + if (!af_cfgd) + vty_out(vty, " none\n"); + else + vty_out(vty, "\n"); + } /* Display listen ranges (for dynamic neighbors), if any */ for (afi = AFI_IP; afi < AFI_MAX; afi++) { - if (afi == AFI_IP) - af_str = "IPv4"; - else if (afi == AFI_IP6) - af_str = "IPv6"; - else - af_str = "???"; lr_count = listcount(group->listen_range[afi]); if (lr_count) { - vty_out(vty, " %d %s listen range(s)\n", lr_count, - af_str); - + if (json) { + if (!json_peer_group_dynamic) + json_peer_group_dynamic = + json_object_new_object(); + + json_peer_group_dynamic_af = + json_object_new_object(); + json_peer_group_ranges = + json_object_new_array(); + json_object_int_add(json_peer_group_dynamic_af, + "count", lr_count); + } else { + vty_out(vty, " %d %s listen range(s)\n", + lr_count, afi2str(afi)); + } for (ALL_LIST_ELEMENTS(group->listen_range[afi], node, - nnode, range)) - vty_out(vty, " %pFX\n", range); + nnode, range)) { + if (json) { + char buf[BUFSIZ]; + + snprintfrr(buf, sizeof(buf), "%pFX", + range); + + json_object_array_add( + json_peer_group_ranges, + json_object_new_string(buf)); + } else { + vty_out(vty, " %pFX\n", range); + } + } + + if (json) { + json_object_object_add( + json_peer_group_dynamic_af, "ranges", + json_peer_group_ranges); + + json_object_object_add( + json_peer_group_dynamic, afi2str(afi), + json_peer_group_dynamic_af); + } } } + if (json_peer_group_dynamic) + json_object_object_add(json_peer_group, "dynamicRanges", + json_peer_group_dynamic); + /* Display group members and their status */ if (listcount(group->peer)) { - vty_out(vty, " Peer-group members:\n"); + if (json) + json_peer_group_members = json_object_new_object(); + else + vty_out(vty, " Peer-group members:\n"); for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) { if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN) || CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN)) @@ -15543,65 +15624,106 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group) peer->status, NULL); dynamic = peer_dynamic_neighbor(peer); - vty_out(vty, " %s %s %s \n", peer->host, - dynamic ? "(dynamic)" : "", peer_status); + + if (json) { + json_object *json_peer_group_member = + json_object_new_object(); + + json_object_string_add(json_peer_group_member, + "status", peer_status); + + if (dynamic) + json_object_boolean_true_add( + json_peer_group_member, + "dynamic"); + + json_object_object_add(json_peer_group_members, + peer->host, + json_peer_group_member); + } else { + vty_out(vty, " %s %s %s \n", peer->host, + dynamic ? "(dynamic)" : "", + peer_status); + } } + if (json) + json_object_object_add(json_peer_group, "members", + json_peer_group_members); } + if (json) + json_object_object_add(json, group->name, json_peer_group); + return CMD_SUCCESS; } static int bgp_show_peer_group_vty(struct vty *vty, const char *name, - const char *group_name) + const char *group_name, bool uj) { struct bgp *bgp; struct listnode *node, *nnode; struct peer_group *group; bool found = false; + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); bgp = name ? bgp_lookup_by_name(name) : bgp_get_default(); if (!bgp) { - vty_out(vty, "%% BGP instance not found\n"); + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else { + vty_out(vty, "%% BGP instance not found\n"); + } + return CMD_WARNING; } for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) { if (group_name) { if (strmatch(group->name, group_name)) { - bgp_show_one_peer_group(vty, group); + bgp_show_one_peer_group(vty, group, json); found = true; break; } } else { - bgp_show_one_peer_group(vty, group); + bgp_show_one_peer_group(vty, group, json); } } - if (group_name && !found) + if (group_name && !found && !uj) vty_out(vty, "%% No such peer-group\n"); + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + return CMD_SUCCESS; } -DEFUN (show_ip_bgp_peer_groups, - show_ip_bgp_peer_groups_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] peer-group [PGNAME]", - SHOW_STR - IP_STR - BGP_STR - BGP_INSTANCE_HELP_STR - "Detailed information on BGP peer groups\n" - "Peer group name\n") +DEFUN(show_ip_bgp_peer_groups, show_ip_bgp_peer_groups_cmd, + "show [ip] bgp [<view|vrf> VIEWVRFNAME] peer-group [PGNAME] [json]", + SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR + "Detailed information on BGP peer groups\n" + "Peer group name\n" JSON_STR) { char *vrf, *pg; int idx = 0; + bool uj = use_json(argc, argv); vrf = argv_find(argv, argc, "VIEWVRFNAME", &idx) ? argv[idx]->arg : NULL; pg = argv_find(argv, argc, "PGNAME", &idx) ? argv[idx]->arg : NULL; - return bgp_show_peer_group_vty(vty, vrf, pg); + return bgp_show_peer_group_vty(vty, vrf, pg, uj); } diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 33c8f3c1f0..d51ce817d1 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2203,7 +2203,7 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) /* If this is the first peer to be activated for this * afi/labeled-unicast recalc bestpaths to trigger label allocation */ - if (safi == SAFI_LABELED_UNICAST + if (ret != BGP_ERR_PEER_SAFI_CONFLICT && safi == SAFI_LABELED_UNICAST && !bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) { if (BGP_DEBUG(zebra, ZEBRA)) diff --git a/doc/developer/cross-compiling.rst b/doc/developer/cross-compiling.rst index 339e00c921..3bf78f7633 100644 --- a/doc/developer/cross-compiling.rst +++ b/doc/developer/cross-compiling.rst @@ -189,7 +189,7 @@ later on to FRR. One may get burned when compiling gRPC if the ``protoc`` version on the build machine differs from the version of ``protoc`` being linked to during a gRPC build. The error messages from this defect look like: -.. code-block:: terminal +.. code-block:: shell gens/src/proto/grpc/channelz/channelz.pb.h: In member function ‘void grpc::channelz::v1::ServerRef::set_name(const char*, size_t)’: gens/src/proto/grpc/channelz/channelz.pb.h:9127:64: error: ‘EmptyDefault’ is not a member of ‘google::protobuf::internal::ArenaStringPtr’ diff --git a/doc/developer/scripting.rst b/doc/developer/scripting.rst index b0413619ab..708f65ff7d 100644 --- a/doc/developer/scripting.rst +++ b/doc/developer/scripting.rst @@ -271,7 +271,7 @@ has). For example, here is the encoder function for ``struct prefix``: This function pushes a single value onto the Lua stack. It is a table whose equivalent in Lua is: -.. code-block:: +.. code-block:: c { ["network"] = "1.2.3.4/24", ["prefixlen"] = 24, ["family"] = 2 } diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index f7e4486ef0..d16420c7e7 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -57,6 +57,7 @@ dev_RSTFILES = \ doc/developer/tracing.rst \ doc/developer/testing.rst \ doc/developer/topotests-snippets.rst \ + doc/developer/topotests-markers.rst \ doc/developer/topotests.rst \ doc/developer/workflow.rst \ doc/developer/xrefs.rst \ diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index 93d81548b2..7976a206f7 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -388,11 +388,19 @@ This is the recommended test writing routine: - Format the new code using `black <https://github.com/psf/black>`_ - Create a Pull Request -.. Note:: +Some things to keep in mind: + +- BGP tests MUST use generous convergence timeouts - you must ensure + that any test involving BGP uses a convergence timeout of at least + 130 seconds. +- Topotests are run on a range of Linux versions: if your test + requires some OS-specific capability (like mpls support, or vrf + support), there are test functions available in the libraries that + will help you determine whether your test should run or be skipped. +- Avoid including unstable data in your test: don't rely on link-local + addresses or ifindex values, for example, because these can change + from run to run. - BGP tests MUST use generous convergence timeouts - you must ensure - that any test involving BGP uses a convergence timeout of at least - 130 seconds. Topotest File Hierarchy """"""""""""""""""""""" @@ -795,7 +803,7 @@ Requirements: - Use `black <https://github.com/psf/black>`_ code formatter before creating a pull request. This ensures we have a unified code style. - Mark test modules with pytest markers depending on the daemons used during the - tests (s. Markers) + tests (see :ref:`topotests-markers`) Tips: diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index b8f749b740..7d136b183e 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -166,15 +166,22 @@ BFD peers and profiles share the same BFD session configuration commands. The minimum transmission interval (less jitter) that this system wants to use to send BFD control packets. Defaults to 300ms. -.. clicmd:: echo-interval (10-60000) +.. clicmd:: echo receive-interval <disabled|(10-60000)> - Configures the minimal echo receive transmission interval that this - system is capable of handling. + Configures the minimum interval that this system is capable of + receiving echo packets. Disabled means that this system doesn't want + to receive echo packets. The default value is 50 milliseconds. + +.. clicmd:: echo transmit-interval (10-60000) + + The minimum transmission interval (less jitter) that this system + wants to use to send BFD echo packets. Defaults to 50ms. .. clicmd:: echo-mode Enables or disables the echo transmission mode. This mode is disabled - by default. + by default. If you are not using distributed BFD then echo mode works + only when the peer is also FRR. It is recommended that the transmission interval of control packets to be increased after enabling echo-mode to reduce bandwidth usage. @@ -445,12 +452,13 @@ You can inspect the current BFD peer status with the following commands: Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms + Echo receive interval: 50ms Echo transmission interval: disabled Remote timers: Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms - Echo transmission interval: 50ms + Echo receive interval: 50ms peer 192.168.1.1 label: router3-peer @@ -465,12 +473,13 @@ You can inspect the current BFD peer status with the following commands: Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms + Echo receive interval: 50ms Echo transmission interval: disabled Remote timers: Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms - Echo transmission interval: 50ms + Echo receive interval: 50ms frr# show bfd peer 192.168.1.1 BFD Peer: @@ -487,15 +496,16 @@ You can inspect the current BFD peer status with the following commands: Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms + Echo receive interval: 50ms Echo transmission interval: disabled Remote timers: Detect-multiplier: 3 Receive interval: 300ms Transmission interval: 300ms - Echo transmission interval: 50ms + Echo receive interval: 50ms frr# show bfd peer 192.168.0.1 json - {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-interval":50,"detect-multiplier":3,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-interval":50,"remote-detect-multiplier":3,"peer-type":"dynamic"} + {"multihop":false,"peer":"192.168.0.1","id":1,"remote-id":1,"status":"up","uptime":161,"diagnostic":"ok","remote-diagnostic":"ok","receive-interval":300,"transmit-interval":300,"echo-receive-interval":50,"echo-transmit-interval":0,"detect-multiplier":3,"remote-receive-interval":300,"remote-transmit-interval":300,"remote-echo-receive-interval":50,"remote-detect-multiplier":3,"peer-type":"dynamic"} You can inspect the current BFD peer status in brief with the following commands: diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index da6c0d9824..3f8dafa09d 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1667,6 +1667,74 @@ and will share updates. should not be reflected back to the peer. This command only is only meaningful when there is a single peer defined in the peer-group. +.. clicmd:: show [ip] bgp peer-group [json] + + This command displays configured BGP peer-groups. + + .. code-block:: frr + + exit1-debian-9# show bgp peer-group + + BGP peer-group test1, remote AS 65001 + Peer-group type is external + Configured address-families: IPv4 Unicast; IPv6 Unicast; + 1 IPv4 listen range(s) + 192.168.100.0/24 + 2 IPv6 listen range(s) + 2001:db8:1::/64 + 2001:db8:2::/64 + Peer-group members: + 192.168.200.1 Active + 2001:db8::1 Active + + BGP peer-group test2 + Peer-group type is external + Configured address-families: IPv4 Unicast; + + Optional ``json`` parameter is used to display JSON output. + + .. code-block:: frr + + { + "test1":{ + "remoteAs":65001, + "type":"external", + "addressFamiliesConfigured":[ + "IPv4 Unicast", + "IPv6 Unicast" + ], + "dynamicRanges":{ + "IPv4":{ + "count":1, + "ranges":[ + "192.168.100.0\/24" + ] + }, + "IPv6":{ + "count":2, + "ranges":[ + "2001:db8:1::\/64", + "2001:db8:2::\/64" + ] + } + }, + "members":{ + "192.168.200.1":{ + "status":"Active" + }, + "2001:db8::1":{ + "status":"Active" + } + } + }, + "test2":{ + "type":"external", + "addressFamiliesConfigured":[ + "IPv4 Unicast" + ] + } + } + Capability Negotiation ^^^^^^^^^^^^^^^^^^^^^^ @@ -1715,7 +1783,7 @@ AS Path Access Lists AS path access list is user defined AS path. -.. clicmd:: bgp as-path access-list WORD permit|deny LINE +.. clicmd:: bgp as-path access-list WORD [seq (0-4294967295)] permit|deny LINE This command defines a new AS path access list. @@ -1731,6 +1799,7 @@ Bogon ASN filter policy configuration example bgp as-path access-list 99 permit _0_ bgp as-path access-list 99 permit _23456_ bgp as-path access-list 99 permit _1310[0-6][0-9]_|_13107[0-1]_ + bgp as-path access-list 99 seq 20 permit ^65 .. _bgp-using-as-path-in-route-map: @@ -3529,8 +3598,8 @@ certainly contains silly mistakes, if not serious flaws. ip prefix-list pl-peer2-network permit 192.168.2.0/24 ip prefix-list pl-peer2-network permit 172.16.1/24 ! - bgp as-path access-list asp-own-as permit ^$ - bgp as-path access-list asp-own-as permit _64512_ + bgp as-path access-list seq 5 asp-own-as permit ^$ + bgp as-path access-list seq 10 asp-own-as permit _64512_ ! ! ################################################################# ! Match communities we provide actions for, on routes receives from diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 3c3a68764e..744f5761c9 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -260,22 +260,26 @@ void isis_adj_process_threeway(struct isis_adjacency *adj, adj->threeway_state = next_tw_state; } -void isis_log_adj_change(struct isis_adjacency *adj, - enum isis_adj_state old_state, - enum isis_adj_state new_state, const char *reason) +const char *isis_adj_name(const struct isis_adjacency *adj) { - const char *adj_name; + if (!adj) + return "NONE"; + struct isis_dynhn *dyn; dyn = dynhn_find_by_id(adj->sysid); if (dyn) - adj_name = dyn->hostname; + return dyn->hostname; else - adj_name = sysid_print(adj->sysid); - + return sysid_print(adj->sysid); +} +void isis_log_adj_change(struct isis_adjacency *adj, + enum isis_adj_state old_state, + enum isis_adj_state new_state, const char *reason) +{ zlog_info( "%%ADJCHANGE: Adjacency to %s (%s) for %s changed from %s to %s, %s", - adj_name, adj->circuit->interface->name, + isis_adj_name(adj), adj->circuit->interface->name, adj_level2string(adj->level), adj_state2string(old_state), adj_state2string(new_state), reason ? reason : "unspecified"); } @@ -462,11 +466,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, struct isis_dynhn *dyn; int level; - dyn = dynhn_find_by_id(adj->sysid); - if (dyn) - vty_out(vty, " %-20s", dyn->hostname); - else - vty_out(vty, " %-20s", sysid_print(adj->sysid)); + vty_out(vty, " %-20s", isis_adj_name(adj)); if (detail == ISIS_UI_LEVEL_BRIEF) { if (adj->circuit) diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 3afb7209f3..b517a8e03f 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -142,5 +142,6 @@ void isis_adj_build_neigh_list(struct list *adjdb, struct list *list); void isis_adj_build_up_list(struct list *adjdb, struct list *list); int isis_adj_usage2levels(enum isis_adj_usage usage); int isis_bfd_startup_timer(struct thread *thread); +const char *isis_adj_name(const struct isis_adjacency *adj); #endif /* ISIS_ADJACENCY_H */ diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c index 4fac73511b..8386ada761 100644 --- a/isisd/isis_bfd.c +++ b/isisd/isis_bfd.c @@ -92,22 +92,42 @@ static bool bfd_session_same(const struct bfd_session *session, int family, static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst, int new_status) { - if (!adj->bfd_session) + if (!adj->bfd_session) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: Ignoring update for adjacency with %s, could not find bfd session on the adjacency", + isis_adj_name(adj)); return; + } - if (adj->bfd_session->family != dst->family) + if (adj->bfd_session->family != dst->family) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: Ignoring update for adjacency with %s, address family does not match the family on the adjacency", + isis_adj_name(adj)); return; + } switch (adj->bfd_session->family) { case AF_INET: if (!IPV4_ADDR_SAME(&adj->bfd_session->dst_ip.ipv4, - &dst->u.prefix4)) + &dst->u.prefix4)) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: Ignoring update for adjacency with %s, IPv4 address does not match", + isis_adj_name(adj)); return; + } break; case AF_INET6: if (!IPV6_ADDR_SAME(&adj->bfd_session->dst_ip.ipv6, - &dst->u.prefix6)) + &dst->u.prefix6)) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: Ignoring update for adjacency with %s, IPv6 address does not match", + isis_adj_name(adj)); return; + } break; default: flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u", @@ -119,8 +139,13 @@ static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst, BFD_SET_CLIENT_STATUS(adj->bfd_session->status, new_status); - if (old_status == new_status) + if (old_status == new_status) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: Ignoring update for adjacency with %s, new status matches current known status", + isis_adj_name(adj)); return; + } if (IS_DEBUG_BFD) { char dst_str[INET6_ADDRSTRLEN]; @@ -166,8 +191,12 @@ static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) struct isis_circuit *circuit = circuit_scan_by_ifp(ifp); - if (!circuit) + if (!circuit) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: Ignoring update, could not find circuit"); return 0; + } if (circuit->circ_type == CIRCUIT_T_BROADCAST) { for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { @@ -326,15 +355,26 @@ static void bfd_handle_adj_up(struct isis_adjacency *adj, int command) struct list *local_ips; struct prefix *local_ip; - if (!circuit->bfd_info) + if (!circuit->bfd_info) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: skipping BFD initialization on adjacency with %s because there is no bfd_info in the circuit", + isis_adj_name(adj)); goto out; + } /* If IS-IS IPv6 is configured wait for IPv6 address to be programmed * before starting up BFD */ - if ((circuit->ipv6_router && listcount(circuit->ipv6_link) == 0) - || adj->ipv6_address_count == 0) + if (circuit->ipv6_router + && (listcount(circuit->ipv6_link) == 0 + || adj->ipv6_address_count == 0)) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: skipping BFD initialization on adjacency with %s because IPv6 is enabled but not ready", + isis_adj_name(adj)); return; + } /* * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer @@ -344,16 +384,24 @@ static void bfd_handle_adj_up(struct isis_adjacency *adj, int command) family = AF_INET6; dst_ip.ipv6 = adj->ipv6_addresses[0]; local_ips = circuit->ipv6_link; - if (!local_ips || list_isempty(local_ips)) + if (!local_ips || list_isempty(local_ips)) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: skipping BFD initialization: IPv6 enabled and no local IPv6 addresses"); goto out; + } local_ip = listgetdata(listhead(local_ips)); src_ip.ipv6 = local_ip->u.prefix6; } else if (circuit->ip_router && adj->ipv4_address_count) { family = AF_INET; dst_ip.ipv4 = adj->ipv4_addresses[0]; local_ips = fabricd_ip_addrs(adj->circuit); - if (!local_ips || list_isempty(local_ips)) + if (!local_ips || list_isempty(local_ips)) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: skipping BFD initialization: IPv4 enabled and no local IPv4 addresses"); goto out; + } local_ip = listgetdata(listhead(local_ips)); src_ip.ipv4 = local_ip->u.prefix4; } else @@ -365,8 +413,13 @@ static void bfd_handle_adj_up(struct isis_adjacency *adj, int command) bfd_handle_adj_down(adj); } - if (!adj->bfd_session) + if (!adj->bfd_session) { + if (IS_DEBUG_BFD) + zlog_debug( + "ISIS-BFD: creating BFD session for adjacency with %s", + isis_adj_name(adj)); adj->bfd_session = bfd_session_new(family, &dst_ip, &src_ip); + } bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, circuit->interface->name, command); diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 06a5a69e3f..d610495de6 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -1561,18 +1561,28 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, /* * Schedule LSP refresh ASAP */ - timeout = 0; - if (area->bfd_signalled_down) { sched_debug( "ISIS (%s): Scheduling immediately due to BDF 'down' message.", area->area_tag); area->bfd_signalled_down = false; area->bfd_force_spf_refresh = true; + timeout = 0; } else { - sched_debug( - "ISIS (%s): Last generation was more than lsp_gen_interval ago. Scheduling for execution now.", - area->area_tag); + int64_t time_since_last = monotime_since( + &area->last_lsp_refresh_event[lvl - 1], + NULL); + timeout = time_since_last < 100000L + ? (100000L - time_since_last)/1000 + : 0; + if (timeout > 0) + sched_debug( + "ISIS (%s): Last generation was more than lsp_gen_interval ago. Scheduling for execution in %ld ms due to the instability timer.", + area->area_tag, timeout); + else + sched_debug( + "ISIS (%s): Last generation was more than lsp_gen_interval ago. Scheduling for execution now.", + area->area_tag); } } diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c index cab9199731..50dc0f2a1c 100644 --- a/isisd/isis_snmp.c +++ b/isisd/isis_snmp.c @@ -255,7 +255,7 @@ #define ISIS_TRAP_LSP_ERROR 18 /* Change this definition if number of traps changes */ -#define ISIS_TRAP_LAST_TRAP ISIS_TRAP_LSP_ERROR +#define ISIS_TRAP_LAST_TRAP ISIS_TRAP_LSP_ERROR + 1 #define ISIS_SNMP_TRAP_VAR 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 @@ -944,7 +944,7 @@ static int isis_snmp_circuit_lookup_next(oid *oid_idx, size_t oid_idx_len, start = 0; - if (oid_idx != NULL || oid_idx_len != 0) { + if (oid_idx != NULL && oid_idx_len != 0) { if (oid_idx[0] > SNMP_CIRCUITS_MAX) return 0; @@ -1018,7 +1018,7 @@ static int isis_snmp_circuit_level_lookup_next( start = 0; - if (oid_idx != NULL || oid_idx_len != 0) { + if (oid_idx != NULL && oid_idx_len != 0) { if (oid_idx[0] > SNMP_CIRCUITS_MAX) return 0; @@ -2012,8 +2012,8 @@ static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name, oid *oid_idx; size_t oid_idx_len; struct isis_circuit *circuit; - uint64_t up_ticks; - uint64_t delta_ticks; + uint32_t up_ticks; + uint32_t delta_ticks; uint32_t now_time; int res; @@ -2121,7 +2121,7 @@ static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name, if (circuit->last_uptime == 0) return SNMP_INTEGER(0); - up_ticks = netsnmp_get_agent_uptime(); + up_ticks = (uint32_t)netsnmp_get_agent_uptime(); now_time = isis_snmp_time(); if (circuit->last_uptime >= now_time) @@ -2132,7 +2132,7 @@ static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name, if (up_ticks < delta_ticks) return SNMP_INTEGER(up_ticks); - return SNMP_INTEGER((uint32_t)(up_ticks - delta_ticks)); + return SNMP_INTEGER(up_ticks - delta_ticks); case ISIS_CIRC_3WAYENABLED: /* Not supported */ @@ -2434,8 +2434,8 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, int res; uint32_t val; struct isis_adjacency *adj; - uint64_t up_ticks; - uint64_t delta_ticks; + uint32_t up_ticks; + uint32_t delta_ticks; uint32_t now_time; *write_method = NULL; @@ -2561,7 +2561,7 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, if (adj->flaps == 0) return SNMP_INTEGER(0); - up_ticks = netsnmp_get_agent_uptime(); + up_ticks = (uint32_t)netsnmp_get_agent_uptime(); now_time = isis_snmp_time(); @@ -2573,7 +2573,7 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, if (up_ticks < delta_ticks) return SNMP_INTEGER(up_ticks); - return SNMP_INTEGER((uint32_t)(up_ticks - delta_ticks)); + return SNMP_INTEGER(up_ticks - delta_ticks); default: break; diff --git a/ldpd/ldp_snmp.c b/ldpd/ldp_snmp.c index c9b37c0d27..97dde616a7 100644 --- a/ldpd/ldp_snmp.c +++ b/ldpd/ldp_snmp.c @@ -374,6 +374,76 @@ static uint8_t *ldpEntityTable(struct variable *v, oid name[], size_t *length, return NULL; } +static uint8_t *ldpEntityStatsTable(struct variable *v, oid name[], + size_t *length, int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct in_addr entityLdpId = {.s_addr = 0}; + int len; + + *write_method = NULL; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + if (exact) { + if (*length != LDP_ENTITY_TOTAL_LEN) + return NULL; + } else { + len = *length - v->namelen - LDP_ENTITY_MAX_IDX_LEN; + if (len > 0) + return NULL; + + entityLdpId.s_addr = ldp_rtr_id_get(leconf); + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + *length = LDP_ENTITY_TOTAL_LEN; + oid_copy_addr(name + v->namelen, &entityLdpId, + IN_ADDR_SIZE); + name[v->namelen + 4] = 0; + name[v->namelen + 5] = 0; + name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX; + } + + /* Return the current value of the variable */ + switch (v->magic) { + case MPLSLDPENTITYSTATSSESSIONATTEMPTS: + return SNMP_INTEGER(leconf->stats.session_attempts); + case MPLSLDPENTITYSTATSSESSIONREJHELLO: + return SNMP_INTEGER(leconf->stats.session_rejects_hello); + case MPLSLDPENTITYSTATSSESSIONREJAD: + return SNMP_INTEGER(leconf->stats.session_rejects_ad); + case MPLSLDPENTITYSTATSSESSIONREJMAXPDU: + return SNMP_INTEGER(leconf->stats.session_rejects_max_pdu); + case MPLSLDPENTITYSTATSSESSIONREJLR: + return SNMP_INTEGER(leconf->stats.session_rejects_lr); + case MPLSLDPENTITYSTATSBADLDPID: + return SNMP_INTEGER(leconf->stats.bad_ldp_id); + case MPLSLDPENTITYSTATSBADPDULENGTH: + return SNMP_INTEGER(leconf->stats.bad_pdu_len); + case MPLSLDPENTITYSTATSBADMSGLENGTH: + return SNMP_INTEGER(leconf->stats.bad_msg_len); + case MPLSLDPENTITYSTATSBADTLVLENGTH: + return SNMP_INTEGER(leconf->stats.bad_tlv_len); + case MPLSLDPENTITYSTATSMALFORMEDTLV: + return SNMP_INTEGER(leconf->stats.malformed_tlv); + case MPLSLDPENTITYSTATSKEEPALIVEEXP: + return SNMP_INTEGER(leconf->stats.keepalive_timer_exp); + case MPLSLDPENTITYSTATSSHUTDOWNRCVNOTIFY: + return SNMP_INTEGER(leconf->stats.shutdown_rcv_notify); + case MPLSLDPENTITYSTATSSHUTDOWNSENTNOTIFY: + return SNMP_INTEGER(leconf->stats.shutdown_send_notify); + default: + return NULL; + } + + return NULL; +} + #define LDP_ADJACENCY_ENTRY_MAX_IDX_LEN 14 static void ldpHelloAdjacencyTable_oid_to_index( @@ -863,6 +933,59 @@ static uint8_t *ldpSessionTable(struct variable *v, oid name[], size_t *length, return NULL; } +static uint8_t *ldpSessionStatsTable(struct variable *v, oid name[], + size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct in_addr entityLdpId = {.s_addr = 0}; + uint32_t entityIndex = 0; + struct in_addr peerLdpId = {.s_addr = 0}; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + struct ctl_nbr *ctl_nbr = ldpPeerTable_lookup(v, name, length, exact, + &entityLdpId, &entityIndex, &peerLdpId); + + if (!ctl_nbr) + return NULL; + + if (!exact) { + entityLdpId.s_addr = ldp_rtr_id_get(leconf); + entityIndex = LDP_DEFAULT_ENTITY_INDEX; + peerLdpId = ctl_nbr->id; + + /* Copy the name out */ + memcpy(name, v->name, v->namelen * sizeof(oid)); + + /* Append index */ + oid_copy_addr(name + v->namelen, &entityLdpId, + sizeof(struct in_addr)); + name[v->namelen + 4] = 0; + name[v->namelen + 5] = 0; + name[v->namelen + 6] = entityIndex; + oid_copy_addr(name + v->namelen + 7, &peerLdpId, + sizeof(struct in_addr)); + name[v->namelen + 11] = 0; + name[v->namelen + 12] = 0; + + *length = v->namelen + LDP_PEER_ENTRY_MAX_IDX_LEN; + } + + switch (v->magic) { + case MPLSLDPSESSIONSTATSUNKNOWNMESTYPEERRORS: + return SNMP_INTEGER(ctl_nbr->stats.unknown_msg); + case MPLSLDPSESSIONSTATSUNKNOWNTLVERRORS: + return SNMP_INTEGER(ctl_nbr->stats.unknown_tlv); + default: + return NULL; + } + + return NULL; +} + static struct variable ldpe_variables[] = { {MPLS_LDP_LSR_ID, STRING, RONLY, ldpLsrId, 3, {1, 1, 1}}, {MPLS_LDP_LSR_LOOP_DETECTION_CAPABLE, INTEGER, RONLY, @@ -920,6 +1043,34 @@ static struct variable ldpe_variables[] = { {MPLSLDPENTITYROWSTATUS, INTEGER, RONLY, ldpEntityTable, 5, {1, 2, 3, 1, 23}}, + /* MPLS LDP mplsLdpEntityStatsTable. */ + { MPLSLDPENTITYSTATSSESSIONATTEMPTS, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 1}}, + { MPLSLDPENTITYSTATSSESSIONREJHELLO, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 2}}, + { MPLSLDPENTITYSTATSSESSIONREJAD, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 3}}, + { MPLSLDPENTITYSTATSSESSIONREJMAXPDU, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 4}}, + { MPLSLDPENTITYSTATSSESSIONREJLR, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 5}}, + { MPLSLDPENTITYSTATSBADLDPID, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 6}}, + { MPLSLDPENTITYSTATSBADPDULENGTH, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 7}}, + { MPLSLDPENTITYSTATSBADMSGLENGTH, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 8}}, + { MPLSLDPENTITYSTATSBADTLVLENGTH, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 9}}, + { MPLSLDPENTITYSTATSMALFORMEDTLV, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 10}}, + { MPLSLDPENTITYSTATSKEEPALIVEEXP, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 11}}, + { MPLSLDPENTITYSTATSSHUTDOWNRCVNOTIFY, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 12}}, + { MPLSLDPENTITYSTATSSHUTDOWNSENTNOTIFY, COUNTER32, RONLY, + ldpEntityStatsTable, 5, {1, 2, 4, 1, 13}}, + /* MPLS LDP mplsLdpPeerTable */ {MPLSLDPPEERLDPID, STRING, RONLY, ldpPeerTable, 5, {1, 3, 2, 1, 1}}, {MPLSLDPPEERLABELDISTMETHOD, INTEGER, RONLY, ldpPeerTable, @@ -949,6 +1100,12 @@ static struct variable ldpe_variables[] = { {MPLSLDPSESSIONDISCONTINUITYTIME, TIMESTAMP, RONLY, ldpSessionTable, 5, {1, 3, 3, 1, 8}}, + /* MPLS LDP mplsLdpSessionStatsTable */ + {MPLSLDPSESSIONSTATSUNKNOWNMESTYPEERRORS, COUNTER32, RONLY, + ldpSessionStatsTable, 5, {1, 3, 4, 1, 1}}, + {MPLSLDPSESSIONSTATSUNKNOWNTLVERRORS, COUNTER32, RONLY, + ldpSessionStatsTable, 5, {1, 3, 4, 1, 2}}, + /* MPLS LDP mplsLdpHelloAdjacencyTable. */ {MPLSLDPHELLOADJACENCYINDEX, UNSIGNED32, RONLY, ldpHelloAdjacencyTable, 6, {1, 3, 5, 1, 1, 1}}, diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index 103f4f228d..73c81349ce 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -435,9 +435,27 @@ struct ldp_stats { uint32_t labelrel_rcvd; uint32_t labelabreq_sent; uint32_t labelabreq_rcvd; + uint32_t unknown_tlv; + uint32_t unknown_msg; }; +struct ldp_entity_stats { + uint32_t session_attempts; + uint32_t session_rejects_hello; + uint32_t session_rejects_ad; + uint32_t session_rejects_max_pdu; + uint32_t session_rejects_lr; + uint32_t bad_ldp_id; + uint32_t bad_pdu_len; + uint32_t bad_msg_len; + uint32_t bad_tlv_len; + uint32_t malformed_tlv; + uint32_t keepalive_timer_exp; + uint32_t shutdown_rcv_notify; + uint32_t shutdown_send_notify; +}; + struct l2vpn_if { RB_ENTRY(l2vpn_if) entry; struct l2vpn *l2vpn; @@ -565,6 +583,7 @@ struct ldpd_conf { uint16_t wait_for_sync_interval; int flags; time_t config_change_time; + struct ldp_entity_stats stats; QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(ldpd_conf) diff --git a/ldpd/notification.c b/ldpd/notification.c index f84e0f893b..3ecf5d4ba5 100644 --- a/ldpd/notification.c +++ b/ldpd/notification.c @@ -69,6 +69,36 @@ send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm) tcp->nbr->stats.notif_sent++; } + /* update SNMP session counters */ + switch (nm->status_code) { + case S_NO_HELLO: + leconf->stats.session_rejects_hello++; + break; + case S_BAD_LDP_ID: + leconf->stats.bad_ldp_id++; + break; + case S_BAD_PDU_LEN: + leconf->stats.bad_pdu_len++; + break; + case S_BAD_MSG_LEN: + leconf->stats.bad_msg_len++; + break; + case S_BAD_TLV_LEN: + leconf->stats.bad_tlv_len++; + break; + case S_BAD_TLV_VAL: + leconf->stats.malformed_tlv++; + break; + case S_KEEPALIVE_TMR: + leconf->stats.keepalive_timer_exp++; + break; + case S_SHUTDOWN: + leconf->stats.shutdown_send_notify++; + break; + default: + break; + } + evbuf_enqueue(&tcp->wbuf, buf); } @@ -122,6 +152,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) if (len < STATUS_SIZE) { session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type); + leconf->stats.bad_msg_len++; return (-1); } memcpy(&st, buf, sizeof(st)); @@ -129,6 +160,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) if (ntohs(st.length) > STATUS_SIZE - TLV_HDR_SIZE || ntohs(st.length) > len - TLV_HDR_SIZE) { session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + leconf->stats.bad_tlv_len++; return (-1); } buf += STATUS_SIZE; @@ -145,6 +177,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) if (len < sizeof(tlv)) { session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + leconf->stats.bad_tlv_len++; return (-1); } @@ -153,6 +186,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) tlv_len = ntohs(tlv.length); if (tlv_len + TLV_HDR_SIZE > len) { session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); + leconf->stats.bad_tlv_len++; return (-1); } buf += TLV_HDR_SIZE; @@ -182,14 +216,17 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) if (tlen != tlv_len) { session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); + leconf->stats.bad_tlv_len++; return (-1); } nm.flags |= F_NOTIF_FEC; break; default: - if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) { + nbr->stats.unknown_tlv++; send_notification_rtlvs(nbr, S_UNKNOWN_TLV, msg.id, msg.type, tlv_type, tlv_len, buf); + } /* ignore unknown tlv */ break; } @@ -243,21 +280,57 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) * initialization, it SHOULD transmit a Shutdown message and * then close the transport connection". */ - if (nbr->state != NBR_STA_OPER && nm.status_code == S_SHUTDOWN) + if (nbr->state != NBR_STA_OPER && + nm.status_code == S_SHUTDOWN) { + leconf->stats.session_attempts++; send_notification(nbr->tcp, S_SHUTDOWN, msg.id, msg.type); + } + leconf->stats.shutdown_rcv_notify++; nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); return (-1); } - /* lde needs to know about a few notification messages */ + /* lde needs to know about a few notification messages + * and update SNMP session counters + */ switch (nm.status_code) { case S_PW_STATUS: case S_ENDOFLIB: ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0, &nm, sizeof(nm)); break; + case S_NO_HELLO: + leconf->stats.session_rejects_hello++; + break; + case S_PARM_ADV_MODE: + leconf->stats.session_rejects_ad++; + break; + case S_MAX_PDU_LEN: + leconf->stats.session_rejects_max_pdu++; + break; + case S_PARM_L_RANGE: + leconf->stats.session_rejects_lr++; + break; + case S_BAD_LDP_ID: + leconf->stats.bad_ldp_id++; + break; + case S_BAD_PDU_LEN: + leconf->stats.bad_pdu_len++; + break; + case S_BAD_MSG_LEN: + leconf->stats.bad_msg_len++; + break; + case S_BAD_TLV_LEN: + leconf->stats.bad_tlv_len++; + break; + case S_BAD_TLV_VAL: + leconf->stats.malformed_tlv++; + break; + case S_SHUTDOWN: + leconf->stats.shutdown_rcv_notify++; + break; default: break; } diff --git a/ldpd/packet.c b/ldpd/packet.c index fdcaa79d23..8735faf3dd 100644 --- a/ldpd/packet.c +++ b/ldpd/packet.c @@ -560,9 +560,11 @@ session_read(struct thread *thread) default: log_debug("%s: unknown LDP message from nbr %pI4", __func__, &nbr->id); - if (!(ntohs(msg->type) & UNKNOWN_FLAG)) + if (!(ntohs(msg->type) & UNKNOWN_FLAG)) { + nbr->stats.unknown_msg++; send_notification(nbr->tcp, S_UNKNOWN_MSG, msg->id, msg->type); + } /* ignore the message */ ret = 0; break; @@ -667,6 +669,12 @@ session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id, case NBR_STA_INITIAL: case NBR_STA_OPENREC: case NBR_STA_OPENSENT: + /* update SNMP session counters during initialization */ + leconf->stats.session_attempts++; + send_notification(nbr->tcp, status, msg_id, msg_type); + + nbr_fsm(nbr, NBR_EVT_CLOSE_SESSION); + break; case NBR_STA_OPER: send_notification(nbr->tcp, status, msg_id, msg_type); diff --git a/lib/filter_nb.c b/lib/filter_nb.c index 2007b37cdf..c83738e729 100644 --- a/lib/filter_nb.c +++ b/lib/filter_nb.c @@ -120,6 +120,101 @@ static void prefix_list_entry_set_empty(struct prefix_list_entry *ple) ple->le = 0; } +static int +prefix_list_nb_validate_v4_af_type(const struct lyd_node *plist_dnode, + char *errmsg, size_t errmsg_len) +{ + int af_type; + + af_type = yang_dnode_get_enum(plist_dnode, "./type"); + if (af_type != YPLT_IPV4) { + snprintf(errmsg, errmsg_len, + "prefix-list type %u is mismatched.", af_type); + return NB_ERR_VALIDATION; + } + + return NB_OK; +} + +static int +prefix_list_nb_validate_v6_af_type(const struct lyd_node *plist_dnode, + char *errmsg, size_t errmsg_len) +{ + int af_type; + + af_type = yang_dnode_get_enum(plist_dnode, "./type"); + if (af_type != YPLT_IPV6) { + snprintf(errmsg, errmsg_len, + "prefix-list type %u is mismatched.", af_type); + return NB_ERR_VALIDATION; + } + + return NB_OK; +} + +static int lib_prefix_list_entry_prefix_length_lesser_or_equal_modify( + struct nb_cb_modify_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->le = yang_dnode_get_uint8(args->dnode, NULL); + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_prefix_length_greater_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->ge = 0; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + +static int lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + struct prefix_list_entry *ple; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + ple = nb_running_get_entry(args->dnode, NULL, true); + + /* Start prefix entry update procedure. */ + prefix_list_entry_update_start(ple); + + ple->le = 0; + + /* Finish prefix entry update procedure. */ + prefix_list_entry_update_finish(ple); + + return NB_OK; +} + /** * Unsets the cisco style rule for addresses so it becomes disabled (the * equivalent of setting: `0.0.0.0/32`). @@ -308,45 +403,6 @@ bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda) return pda->pda_found; } -static bool plist_is_dup_nb(const struct lyd_node *dnode) -{ - const struct lyd_node *entry_dnode = - yang_dnode_get_parent(dnode, "entry"); - struct plist_dup_args pda = {}; - int idx = 0, arg_idx = 0; - static const char *entries[] = { - "./ipv4-prefix", - "./ipv4-prefix-length-greater-or-equal", - "./ipv4-prefix-length-lesser-or-equal", - "./ipv6-prefix", - "./ipv6-prefix-length-greater-or-equal", - "./ipv6-prefix-length-lesser-or-equal", - "./any", - NULL - }; - - /* Initialize. */ - pda.pda_type = yang_dnode_get_string(entry_dnode, "../type"); - pda.pda_name = yang_dnode_get_string(entry_dnode, "../name"); - pda.pda_entry_dnode = entry_dnode; - - /* Load all values/XPaths. */ - while (entries[idx] != NULL) { - if (!yang_dnode_exists(entry_dnode, entries[idx])) { - idx++; - continue; - } - - pda.pda_xpath[arg_idx] = entries[idx]; - pda.pda_value[arg_idx] = - yang_dnode_get_string(entry_dnode, entries[idx]); - arg_idx++; - idx++; - } - - return plist_is_dup(entry_dnode, &pda); -} - /* * XPath: /frr-filter:lib/access-list */ @@ -1148,25 +1204,11 @@ static int lib_prefix_list_entry_action_modify(struct nb_cb_modify_args *args) return NB_OK; } -/* - * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix - */ -static int -lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) +static int lib_prefix_list_entry_prefix_modify(struct nb_cb_modify_args *args) { struct prefix_list_entry *ple; struct prefix p; - if (args->event == NB_EV_VALIDATE) { - if (plist_is_dup_nb(args->dnode)) { - snprintf(args->errmsg, args->errmsg_len, - "duplicated prefix list value: %s", - yang_dnode_get_string(args->dnode, NULL)); - return NB_ERR_VALIDATION; - } - return NB_OK; - } - if (args->event != NB_EV_APPLY) return NB_OK; @@ -1193,8 +1235,7 @@ lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) return NB_OK; } -static int -lib_prefix_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) +static int lib_prefix_list_entry_prefix_destroy(struct nb_cb_destroy_args *args) { struct prefix_list_entry *ple; @@ -1215,6 +1256,61 @@ lib_prefix_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) } /* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix + */ +static int +lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) +{ + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + return prefix_list_nb_validate_v4_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_modify(args); +} + +static int +lib_prefix_list_entry_ipv4_prefix_destroy(struct nb_cb_destroy_args *args) +{ + + if (args->event != NB_EV_APPLY) + return NB_OK; + + return lib_prefix_list_entry_prefix_destroy(args); +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv6-prefix + */ +static int +lib_prefix_list_entry_ipv6_prefix_modify(struct nb_cb_modify_args *args) +{ + + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + return prefix_list_nb_validate_v6_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_modify(args); +} + +static int +lib_prefix_list_entry_ipv6_prefix_destroy(struct nb_cb_destroy_args *args) +{ + + if (args->event != NB_EV_APPLY) + return NB_OK; + + return lib_prefix_list_entry_prefix_destroy(args); +} + +/* * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-greater-or-equal */ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify( @@ -1226,16 +1322,6 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify( prefix_list_length_validate(args) != NB_OK) return NB_ERR_VALIDATION; - if (args->event == NB_EV_VALIDATE) { - if (plist_is_dup_nb(args->dnode)) { - snprintf(args->errmsg, args->errmsg_len, - "duplicated prefix list value: %s", - yang_dnode_get_string(args->dnode, NULL)); - return NB_ERR_VALIDATION; - } - return NB_OK; - } - if (args->event != NB_EV_APPLY) return NB_OK; @@ -1255,44 +1341,72 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify( static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy( struct nb_cb_destroy_args *args) { - struct prefix_list_entry *ple; + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); - if (args->event != NB_EV_APPLY) - return NB_OK; + return prefix_list_nb_validate_v4_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } - ple = nb_running_get_entry(args->dnode, NULL, true); + return lib_prefix_list_entry_prefix_length_greater_or_equal_destroy( + args); +} - /* Start prefix entry update procedure. */ - prefix_list_entry_update_start(ple); +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal + */ +static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( + struct nb_cb_modify_args *args) +{ + if (args->event == NB_EV_VALIDATE + && prefix_list_length_validate(args) != NB_OK) + return NB_ERR_VALIDATION; - ple->ge = 0; + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); + return prefix_list_nb_validate_v4_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } - return NB_OK; + return lib_prefix_list_entry_prefix_length_lesser_or_equal_modify(args); +} + +static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + return prefix_list_nb_validate_v4_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } + + return lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy( + args); } /* - * XPath: /frr-filter:lib/prefix-list/entry/ipv4-prefix-length-lesser-or-equal + * XPath: /frr-filter:lib/prefix-list/entry/ipv6-prefix-length-greater-or-equal */ -static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( +static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_modify( struct nb_cb_modify_args *args) { struct prefix_list_entry *ple; - if (args->event == NB_EV_VALIDATE && - prefix_list_length_validate(args) != NB_OK) + if (args->event == NB_EV_VALIDATE + && prefix_list_length_validate(args) != NB_OK) return NB_ERR_VALIDATION; if (args->event == NB_EV_VALIDATE) { - if (plist_is_dup_nb(args->dnode)) { - snprintf(args->errmsg, args->errmsg_len, - "duplicated prefix list value: %s", - yang_dnode_get_string(args->dnode, NULL)); - return NB_ERR_VALIDATION; - } - return NB_OK; + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + + return prefix_list_nb_validate_v6_af_type( + plist_dnode, args->errmsg, args->errmsg_len); } if (args->event != NB_EV_APPLY) @@ -1303,7 +1417,7 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( /* Start prefix entry update procedure. */ prefix_list_entry_update_start(ple); - ple->le = yang_dnode_get_uint8(args->dnode, NULL); + ple->ge = yang_dnode_get_uint8(args->dnode, NULL); /* Finish prefix entry update procedure. */ prefix_list_entry_update_finish(ple); @@ -1311,45 +1425,71 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( return NB_OK; } -static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy( +static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_destroy( struct nb_cb_destroy_args *args) { - struct prefix_list_entry *ple; + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); - if (args->event != NB_EV_APPLY) - return NB_OK; + return prefix_list_nb_validate_v6_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } - ple = nb_running_get_entry(args->dnode, NULL, true); + return lib_prefix_list_entry_prefix_length_greater_or_equal_destroy( + args); +} - /* Start prefix entry update procedure. */ - prefix_list_entry_update_start(ple); +/* + * XPath: /frr-filter:lib/prefix-list/entry/ipv6-prefix-length-lesser-or-equal + */ +static int lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_modify( + struct nb_cb_modify_args *args) +{ + if (args->event == NB_EV_VALIDATE + && prefix_list_length_validate(args) != NB_OK) + return NB_ERR_VALIDATION; - ple->le = 0; + if (args->event == NB_EV_VALIDATE) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); - /* Finish prefix entry update procedure. */ - prefix_list_entry_update_finish(ple); + return prefix_list_nb_validate_v6_af_type( + plist_dnode, args->errmsg, args->errmsg_len); + } - return NB_OK; + return lib_prefix_list_entry_prefix_length_lesser_or_equal_modify(args); } -/* - * XPath: /frr-filter:lib/prefix-list/entry/any - */ -static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args) +static int lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_destroy( + struct nb_cb_destroy_args *args) { - struct prefix_list_entry *ple; - int type; + int af_type; if (args->event == NB_EV_VALIDATE) { - if (plist_is_dup_nb(args->dnode)) { + const struct lyd_node *plist_dnode = + yang_dnode_get_parent(args->dnode, "prefix-list"); + af_type = yang_dnode_get_enum(plist_dnode, "./type"); + if (af_type != YPLT_IPV6) { snprintf(args->errmsg, args->errmsg_len, - "duplicated prefix list value: %s", - yang_dnode_get_string(args->dnode, NULL)); + "prefix-list type %u is mismatched.", af_type); return NB_ERR_VALIDATION; } return NB_OK; } + return lib_prefix_list_entry_prefix_length_lesser_or_equal_destroy( + args); +} + +/* + * XPath: /frr-filter:lib/prefix-list/entry/any + */ +static int lib_prefix_list_entry_any_create(struct nb_cb_create_args *args) +{ + struct prefix_list_entry *ple; + int type; + if (args->event != NB_EV_APPLY) return NB_OK; @@ -1583,22 +1723,22 @@ const struct frr_yang_module_info frr_filter_info = { { .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix", .cbs = { - .modify = lib_prefix_list_entry_ipv4_prefix_modify, - .destroy = lib_prefix_list_entry_ipv4_prefix_destroy, + .modify = lib_prefix_list_entry_ipv6_prefix_modify, + .destroy = lib_prefix_list_entry_ipv6_prefix_destroy, } }, { .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-greater-or-equal", .cbs = { - .modify = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify, - .destroy = lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_destroy, + .modify = lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_destroy, } }, { .xpath = "/frr-filter:lib/prefix-list/entry/ipv6-prefix-length-lesser-or-equal", .cbs = { - .modify = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify, - .destroy = lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_destroy, + .modify = lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_modify, + .destroy = lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_destroy, } }, { diff --git a/lib/plist.c b/lib/plist.c index 4588dfe1d3..fe4689becd 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -684,6 +684,7 @@ void prefix_list_entry_update_start(struct prefix_list_entry *ple) if (pl->head || pl->tail || pl->desc) pl->master->recent = pl; + ple->next_best = NULL; ple->installed = false; } diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c index f86dbe3d29..b348cc0def 100644 --- a/nhrpd/nhrp_interface.c +++ b/nhrpd/nhrp_interface.c @@ -464,16 +464,22 @@ void nhrp_interface_set_protection(struct interface *ifp, const char *profile, { struct nhrp_interface *nifp = ifp->info; - if (nifp->ipsec_profile) + if (nifp->ipsec_profile) { + vici_terminate_vc_by_profile_name(nifp->ipsec_profile); + nhrp_vc_reset(); free(nifp->ipsec_profile); + } nifp->ipsec_profile = profile ? strdup(profile) : NULL; - if (nifp->ipsec_fallback_profile) + if (nifp->ipsec_fallback_profile) { + vici_terminate_vc_by_profile_name(nifp->ipsec_fallback_profile); + nhrp_vc_reset(); free(nifp->ipsec_fallback_profile); + } nifp->ipsec_fallback_profile = fallback_profile ? strdup(fallback_profile) : NULL; - notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED); + notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_IPSEC_CHANGED); } void nhrp_interface_set_source(struct interface *ifp, const char *ifname) diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c index 9ed03098ac..ae5737d736 100644 --- a/nhrpd/nhrp_nhs.c +++ b/nhrpd/nhrp_nhs.c @@ -116,8 +116,20 @@ static int nhrp_reg_timeout(struct thread *t) } r->timeout <<= 1; - if (r->timeout > 64) + if (r->timeout > 64) { + /* If registration fails repeatedly, this may be because the + * IPSec connection is not working. Close the connection so it + * can be re-established correctly + */ + if (r->peer && r->peer->vc && r->peer->vc->ike_uniqueid) { + debugf(NHRP_DEBUG_COMMON, + "Terminating IPSec Connection for %d", + r->peer->vc->ike_uniqueid); + vici_terminate_vc_by_ike_id(r->peer->vc->ike_uniqueid); + r->peer->vc->ike_uniqueid = 0; + } r->timeout = 2; + } thread_add_timer_msec(master, nhrp_reg_send_req, r, 10, &r->t_register); return 0; diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index 5e9929adeb..0d589e3056 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -147,6 +147,10 @@ static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd) case NOTIFY_INTERFACE_ADDRESS_CHANGED: notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED); break; + case NOTIFY_INTERFACE_IPSEC_CHANGED: + __nhrp_peer_check(p); + notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED); + break; case NOTIFY_INTERFACE_MTU_CHANGED: notifier_call(&p->notifier_list, NOTIFY_PEER_MTU_CHANGED); break; diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h index a36d0c445d..3655463152 100644 --- a/nhrpd/nhrpd.h +++ b/nhrpd/nhrpd.h @@ -105,6 +105,7 @@ enum nhrp_notify_type { NOTIFY_INTERFACE_ADDRESS_CHANGED, NOTIFY_INTERFACE_NBMA_CHANGED, NOTIFY_INTERFACE_MTU_CHANGED, + NOTIFY_INTERFACE_IPSEC_CHANGED, NOTIFY_VC_IPSEC_CHANGED, NOTIFY_VC_IPSEC_UPDATE_NBMA, @@ -125,6 +126,7 @@ enum nhrp_notify_type { struct nhrp_vc { struct notifier_list notifier_list; uint32_t ipsec; + uint32_t ike_uniqueid; uint8_t updating; uint8_t abort_migration; @@ -399,6 +401,8 @@ void nhrp_vc_reset(void); void vici_init(void); void vici_terminate(void); +void vici_terminate_vc_by_profile_name(char *profile_name); +void vici_terminate_vc_by_ike_id(unsigned int ike_id); void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio); diff --git a/nhrpd/vici.c b/nhrpd/vici.c index 86554f53dc..9b117ddf0d 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -200,6 +200,7 @@ static void parse_sa_message(struct vici_message_ctx *ctx, nhrp_vc_ipsec_updown( sactx->child_uniqueid, vc); + vc->ike_uniqueid = sactx->ike_uniqueid; } } else { nhrp_vc_ipsec_updown(sactx->child_uniqueid, 0); @@ -521,6 +522,26 @@ void vici_terminate(void) { } +void vici_terminate_vc_by_profile_name(char *profile_name) +{ + struct vici_conn *vici = &vici_connection; + + debugf(NHRP_DEBUG_VICI, "Terminate profile = %s", profile_name); + vici_submit_request(vici, "terminate", VICI_KEY_VALUE, "ike", + strlen(profile_name), profile_name, VICI_END); +} + +void vici_terminate_vc_by_ike_id(unsigned int ike_id) +{ + struct vici_conn *vici = &vici_connection; + char ike_id_str[10]; + + snprintf(ike_id_str, sizeof(ike_id_str), "%d", ike_id); + debugf(NHRP_DEBUG_VICI, "Terminate ike_id_str = %s", ike_id_str); + vici_submit_request(vici, "terminate", VICI_KEY_VALUE, "ike-id", + strlen(ike_id_str), ike_id_str, VICI_END); +} + void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio) { diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index beca6b9690..977d810be5 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -1106,7 +1106,7 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa) struct listnode *node, *nnode; struct ospf6_area *area; struct ospf6 *ospf6 = oa->ospf6; - + const struct route_node *iterend; /* skip if router is in other non-stub areas */ for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) @@ -1114,9 +1114,12 @@ void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa) return; /* if router is only in a stub area then purge AS-External LSAs */ - for (ALL_LSDB(oa->ospf6->lsdb, lsa, lsanext)) { + iterend = ospf6_lsdb_head(ospf6->lsdb, 0, 0, 0, &lsa); + while (lsa != NULL) { + lsanext = ospf6_lsdb_next(iterend, lsa); if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) ospf6_lsdb_remove(lsa, ospf6->lsdb); + lsa = lsanext; } } diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index 160f012d78..aebe43b9ec 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -1829,7 +1829,9 @@ int ospf6_dbdesc_send(struct thread *thread) /* MTU check */ if (p - sendbuf + sizeof(struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsdb_lsa_unlock(lsa); + ospf6_lsa_unlock(lsa); + if (lsanext) + ospf6_lsa_unlock(lsanext); break; } memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header)); @@ -1876,7 +1878,9 @@ int ospf6_dbdesc_send_newone(struct thread *thread) if (size + sizeof(struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsdb_lsa_unlock(lsa); + ospf6_lsa_unlock(lsa); + if (lsanext) + ospf6_lsa_unlock(lsanext); break; } @@ -1935,7 +1939,9 @@ int ospf6_lsreq_send(struct thread *thread) /* MTU check */ if (p - sendbuf + sizeof(struct ospf6_lsreq_entry) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsdb_lsa_unlock(lsa); + ospf6_lsa_unlock(lsa); + if (lsanext) + ospf6_lsa_unlock(lsanext); break; } @@ -2396,7 +2402,9 @@ int ospf6_lsack_send_interface(struct thread *thread) thread_add_event(master, ospf6_lsack_send_interface, oi, 0, &oi->thread_send_lsack); - ospf6_lsdb_lsa_unlock(lsa); + ospf6_lsa_unlock(lsa); + if (lsanext) + ospf6_lsa_unlock(lsanext); break; } diff --git a/pathd/path_pcep_debug.c b/pathd/path_pcep_debug.c index 2dfe2130bf..d222371bbb 100644 --- a/pathd/path_pcep_debug.c +++ b/pathd/path_pcep_debug.c @@ -1288,7 +1288,7 @@ void _format_path_hop(int ps, struct path_hop *hop) &hop->nai.remote_addr.ipaddr_v6); break; case PCEP_SR_SUBOBJ_NAI_UNNUMBERED_IPV4_ADJACENCY: - PATHD_FORMAT("%*sNAI: %pI4(%u)/%pI4(%u)\n", ps, "", + PATHD_FORMAT("%*sNAI: %pI6(%u)/%pI6(%u)\n", ps, "", &hop->nai.local_addr.ipaddr_v6, hop->nai.local_iface, &hop->nai.remote_addr.ipaddr_v6, diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c index 9de3ba7811..899ce805fe 100644 --- a/pathd/path_pcep_pcc.c +++ b/pathd/path_pcep_pcc.c @@ -348,9 +348,6 @@ void pcep_pcc_reconnect(struct ctrl_state *ctrl_state, int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) { - char pcc_buff[40]; - char pce_buff[40]; - assert(pcc_state->status == PCEP_PCC_DISCONNECTED); assert(pcc_state->sess == NULL); @@ -366,17 +363,14 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV4)) { if (pcc_state->retry_count < OTHER_FAMILY_MAX_RETRIES) { flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, - "skipping connection to PCE %s:%d due to " - "missing PCC IPv4 address", - ipaddr2str(&pcc_state->pce_opts->addr, - pce_buff, sizeof(pce_buff)), + "skipping connection to PCE %pIA:%d due to missing PCC IPv4 address", + &pcc_state->pce_opts->addr, pcc_state->pce_opts->port); schedule_reconnect(ctrl_state, pcc_state); return 0; } else { flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, - "missing IPv4 PCC address, IPv4 candidate " - "paths will be ignored"); + "missing IPv4 PCC address, IPv4 candidate paths will be ignored"); } } @@ -385,17 +379,14 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) if (!CHECK_FLAG(pcc_state->flags, F_PCC_STATE_HAS_IPV6)) { if (pcc_state->retry_count < OTHER_FAMILY_MAX_RETRIES) { flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, - "skipping connection to PCE %s:%d due to " - "missing PCC IPv6 address", - ipaddr2str(&pcc_state->pce_opts->addr, - pce_buff, sizeof(pce_buff)), + "skipping connection to PCE %pIA:%d due to missing PCC IPv6 address", + &pcc_state->pce_opts->addr, pcc_state->pce_opts->port); schedule_reconnect(ctrl_state, pcc_state); return 0; } else { flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, - "missing IPv6 PCC address, IPv6 candidate " - "paths will be ignored"); + "missing IPv6 PCC address, IPv6 candidate paths will be ignored"); } } @@ -403,10 +394,8 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) * have been spent, we still need the one for the transport familly */ if (pcc_state->pcc_addr_tr.ipa_type == IPADDR_NONE) { flog_warn(EC_PATH_PCEP_MISSING_SOURCE_ADDRESS, - "skipping connection to PCE %s:%d due to missing " - "PCC address", - ipaddr2str(&pcc_state->pce_opts->addr, pce_buff, - sizeof(pce_buff)), + "skipping connection to PCE %pIA:%d due to missing PCC address", + &pcc_state->pce_opts->addr, pcc_state->pce_opts->port); schedule_reconnect(ctrl_state, pcc_state); return 0; @@ -420,12 +409,10 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) if (pcc_state->sess == NULL) { flog_warn(EC_PATH_PCEP_LIB_CONNECT, - "failed to connect to PCE %s:%d from %s:%d", - ipaddr2str(&pcc_state->pce_opts->addr, pce_buff, - sizeof(pce_buff)), + "failed to connect to PCE %pIA:%d from %pIA:%d", + &pcc_state->pce_opts->addr, pcc_state->pce_opts->port, - ipaddr2str(&pcc_state->pcc_addr_tr, pcc_buff, - sizeof(pcc_buff)), + &pcc_state->pcc_addr_tr, pcc_state->pcc_opts->port); schedule_reconnect(ctrl_state, pcc_state); return 0; @@ -498,8 +485,7 @@ void pcep_pcc_sync_path(struct ctrl_state *ctrl_state, send_report(pcc_state, path); } else { PCEP_DEBUG( - "%s Skipping %s candidate path %s " - "synchronization", + "%s Skipping %s candidate path %s synchronization", pcc_state->tag, ipaddr_type_name(&path->nbkey.endpoint), path->name); @@ -1243,8 +1229,7 @@ void handle_pcep_comp_reply(struct ctrl_state *ctrl_state, * the connection if more that a given rate. */ PCEP_DEBUG( - "%s Received computation reply for unknown request " - "%d", + "%s Received computation reply for unknown request %d", pcc_state->tag, path->req_id); PCEP_DEBUG_PATH("%s", format_path(path)); send_pcep_error(pcc_state, PCEP_ERRT_UNKNOWN_REQ_REF, @@ -1574,7 +1559,6 @@ void send_comp_request(struct ctrl_state *ctrl_state, assert(lookup_reqid(pcc_state, req->path) == req->path->req_id); int timeout; - char buff[40]; struct pcep_message *msg; if (!pcc_state->is_best) { @@ -1585,10 +1569,9 @@ void send_comp_request(struct ctrl_state *ctrl_state, specialize_outgoing_path(pcc_state, req->path); PCEP_DEBUG( - "%s Sending computation request %d for path %s to %s (retry %d)", + "%s Sending computation request %d for path %s to %pIA (retry %d)", pcc_state->tag, req->path->req_id, req->path->name, - ipaddr2str(&req->path->nbkey.endpoint, buff, sizeof(buff)), - req->retry_count); + &req->path->nbkey.endpoint, req->retry_count); PCEP_DEBUG_PATH("%s Computation request path %s: %s", pcc_state->tag, req->path->name, format_path(req->path)); @@ -1621,7 +1604,6 @@ void cancel_comp_requests(struct ctrl_state *ctrl_state, void cancel_comp_request(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state, struct req_entry *req) { - char buff[40]; struct pcep_message *msg; if (req->was_sent) { @@ -1631,10 +1613,9 @@ void cancel_comp_request(struct ctrl_state *ctrl_state, } PCEP_DEBUG( - "%s Canceling computation request %d for path %s to %s (retry %d)", + "%s Canceling computation request %d for path %s to %pIA (retry %d)", pcc_state->tag, req->path->req_id, req->path->name, - ipaddr2str(&req->path->nbkey.endpoint, buff, sizeof(buff)), - req->retry_count); + &req->path->nbkey.endpoint, req->retry_count); PCEP_DEBUG_PATH("%s Canceled computation request path %s: %s", pcc_state->tag, req->path->name, format_path(req->path)); diff --git a/pathd/pathd.c b/pathd/pathd.c index e2c7c95728..14f5167bff 100644 --- a/pathd/pathd.c +++ b/pathd/pathd.c @@ -624,8 +624,7 @@ void srte_candidate_set_metric(struct srte_candidate *candidate, char endpoint[46]; ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); zlog_debug( - "SR-TE(%s, %u): candidate %s %sconfig metric %s (%u) set to %f " - "(is-bound: %s; is_computed: %s)", + "SR-TE(%s, %u): candidate %s %sconfig metric %s (%u) set to %f (is-bound: %s; is_computed: %s)", endpoint, policy->color, candidate->name, required ? "required " : "", srte_candidate_metric_name(type), type, value, is_bound ? "true" : "false", @@ -659,8 +658,7 @@ void srte_lsp_set_metric(struct srte_lsp *lsp, char endpoint[46]; ipaddr2str(&policy->endpoint, endpoint, sizeof(endpoint)); zlog_debug( - "SR-TE(%s, %u): candidate %s %slsp metric %s (%u) set to %f " - "(is-bound: %s; is_computed: %s)", + "SR-TE(%s, %u): candidate %s %slsp metric %s (%u) set to %f (is-bound: %s; is_computed: %s)", endpoint, policy->color, candidate->name, required ? "required " : "", srte_candidate_metric_name(type), type, value, is_bound ? "true" : "false", @@ -981,8 +979,7 @@ void srte_candidate_unset_segment_list(const char *originator, bool force) if (segment_list->protocol_origin == SRTE_ORIGIN_LOCAL) { zlog_warn( - "Cannot unset segment list %s because it " - "was created locally", + "Cannot unset segment list %s because it was created locally", segment_list->name); continue; } diff --git a/pceplib/pcep_session_logic_states.c b/pceplib/pcep_session_logic_states.c index 5fac667655..e2c3c29591 100644 --- a/pceplib/pcep_session_logic_states.c +++ b/pceplib/pcep_session_logic_states.c @@ -813,8 +813,7 @@ void handle_timer_event(pcep_session_event *event) pcep_log( LOG_INFO, - "%s: [%ld-%ld] pcep_session_logic handle_timer_event: session [%d] event timer_id [%d] " - "session timers [OKW, OKA, DT, KA] [%d, %d, %d, %d]", + "%s: [%ld-%ld] pcep_session_logic handle_timer_event: session [%d] event timer_id [%d] session timers [OKW, OKA, DT, KA] [%d, %d, %d, %d]", __func__, time(NULL), pthread_self(), session->session_id, event->expired_timer_id, session->timer_id_open_keep_wait, session->timer_id_open_keep_alive, session->timer_id_dead_timer, diff --git a/tests/topotests/Dockerfile b/tests/topotests/Dockerfile index b7042d84c6..c9110d2db9 100644 --- a/tests/topotests/Dockerfile +++ b/tests/topotests/Dockerfile @@ -23,6 +23,7 @@ RUN export DEBIAN_FRONTEND=noninteractive \ libreadline-dev \ libc-ares-dev \ libcap-dev \ + libelf-dev \ man \ mininet \ pkg-config \ diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json index d1927ae49a..b436d5562e 100644 --- a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers.json @@ -8,9 +8,10 @@ "remote-diagnostic":"ok", "receive-interval":300, "transmit-interval":300, - "echo-interval":0, + "echo-receive-interval":50, + "echo-transmit-interval":0, "remote-receive-interval":300, "remote-transmit-interval":300, - "remote-echo-interval":50 + "remote-echo-receive-interval":50 } ] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json index 25b47f18ec..c7c7b96ee7 100644 --- a/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json +++ b/tests/topotests/bfd-bgp-cbit-topo3/r1/peers_down.json @@ -6,9 +6,10 @@ "status":"up", "receive-interval":300, "transmit-interval":300, - "echo-interval":0, + "echo-receive-interval":50, + "echo-transmit-interval":0, "remote-receive-interval":300, "remote-transmit-interval":300, - "remote-echo-interval":50 + "remote-echo-receive-interval":50 } ] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json index 5193f2a6e2..fc9e145340 100644 --- a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers.json @@ -8,9 +8,10 @@ "remote-diagnostic":"ok", "receive-interval":300, "transmit-interval":300, - "echo-interval":0, + "echo-receive-interval":50, + "echo-transmit-interval":0, "remote-receive-interval":300, "remote-transmit-interval":300, - "remote-echo-interval":50 + "remote-echo-receive-interval":50 } ] diff --git a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json index 9e4bd2633f..620c6ddcd4 100644 --- a/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json +++ b/tests/topotests/bfd-bgp-cbit-topo3/r3/peers_down.json @@ -6,9 +6,10 @@ "status":"down", "receive-interval":300, "transmit-interval":300, - "echo-interval":0, + "echo-receive-interval":50, + "echo-transmit-interval":0, "remote-receive-interval":300, "remote-transmit-interval":300, - "remote-echo-interval":50 + "remote-echo-receive-interval":50 } ] diff --git a/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf index 4d636ab052..688f2e839c 100644 --- a/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf +++ b/tests/topotests/bfd-profiles-topo1/r1/bfdd.conf @@ -6,6 +6,7 @@ bfd profile slowtx receive-interval 800 transmit-interval 800 + echo receive-interval 400 ! peer 172.16.0.1 interface r1-eth0 profile slowtx diff --git a/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json index 3df9ec9c9d..503f776aec 100644 --- a/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json +++ b/tests/topotests/bfd-profiles-topo1/r2/bfd-peers-initial.json @@ -12,6 +12,7 @@ "remote-id": "*", "remote-receive-interval": 800, "remote-transmit-interval": 800, + "remote-echo-receive-interval": 400, "status": "up", "transmit-interval": 800, "uptime": "*", @@ -27,7 +28,7 @@ "receive-interval": 250, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 300, "remote-transmit-interval": 300, diff --git a/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf b/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf index 23a39a6ee0..700c46ba1e 100644 --- a/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf +++ b/tests/topotests/bfd-profiles-topo1/r2/bfdd.conf @@ -10,6 +10,7 @@ bfd profile fasttx receive-interval 250 transmit-interval 250 + echo receive-interval disabled ! peer 172.16.0.2 interface r2-eth0 profile slowtx diff --git a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json index d2d0c601c3..d987a0ae7d 100644 --- a/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json +++ b/tests/topotests/bfd-profiles-topo1/r3/bfd-peers-initial.json @@ -12,6 +12,7 @@ "remote-id": "*", "remote-receive-interval": 250, "remote-transmit-interval": 250, + "remote-echo-receive-interval": 0, "status": "up", "transmit-interval": 300, "uptime": "*", diff --git a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json index 2c2e136abf..c73296ac97 100644 --- a/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json +++ b/tests/topotests/bfd-profiles-topo1/r4/bfd-peers-initial.json @@ -29,7 +29,7 @@ "receive-interval": 300, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 300, "remote-transmit-interval": 300, diff --git a/tests/topotests/bfd-topo1/r2/peers.json b/tests/topotests/bfd-topo1/r2/peers.json index 5035d643c5..267459c7a8 100644 --- a/tests/topotests/bfd-topo1/r2/peers.json +++ b/tests/topotests/bfd-topo1/r2/peers.json @@ -4,7 +4,7 @@ "status": "up" }, { - "remote-echo-interval": 100, + "remote-echo-receive-interval": 100, "peer": "192.168.1.1", "status": "up" }, diff --git a/tests/topotests/bfd-topo2/r1/peers.json b/tests/topotests/bfd-topo2/r1/peers.json index b14351cd81..9bce991d0d 100644 --- a/tests/topotests/bfd-topo2/r1/peers.json +++ b/tests/topotests/bfd-topo2/r1/peers.json @@ -8,10 +8,11 @@ "remote-diagnostic":"ok", "receive-interval":300, "transmit-interval":300, - "echo-interval":0, + "echo-receive-interval":50, + "echo-transmit-interval":0, "remote-receive-interval":300, "remote-transmit-interval":300, - "remote-echo-interval":50 + "remote-echo-receive-interval":50 }, { "multihop":false, @@ -21,9 +22,10 @@ "remote-diagnostic":"ok", "receive-interval":300, "transmit-interval":300, - "echo-interval":0, + "echo-receive-interval":50, + "echo-transmit-interval":0, "remote-receive-interval":300, "remote-transmit-interval":300, - "remote-echo-interval":50 + "remote-echo-receive-interval":50 } ] diff --git a/tests/topotests/bfd-topo2/r2/peers.json b/tests/topotests/bfd-topo2/r2/peers.json index 29075fcc80..ec2135ce37 100644 --- a/tests/topotests/bfd-topo2/r2/peers.json +++ b/tests/topotests/bfd-topo2/r2/peers.json @@ -3,39 +3,42 @@ "status": "up", "transmit-interval": 300, "remote-receive-interval": 300, - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "diagnostic": "ok", "multihop": false, "interface": "r2-eth0", "remote-transmit-interval": 300, "receive-interval": 300, - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-diagnostic": "ok" }, { "status": "up", "transmit-interval": 300, "remote-receive-interval": 300, - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "diagnostic": "ok", "multihop": false, "interface": "r2-eth2", "remote-transmit-interval": 300, "receive-interval": 300, - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-diagnostic": "ok" }, { "status": "up", "transmit-interval": 300, "remote-receive-interval": 300, - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "diagnostic": "ok", "multihop": false, "interface": "r2-eth1", "remote-transmit-interval": 300, "receive-interval": 300, - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-diagnostic": "ok", "peer": "10.0.3.1" } diff --git a/tests/topotests/bfd-topo2/r3/peers.json b/tests/topotests/bfd-topo2/r3/peers.json index 6698bff201..c19c980338 100644 --- a/tests/topotests/bfd-topo2/r3/peers.json +++ b/tests/topotests/bfd-topo2/r3/peers.json @@ -3,13 +3,14 @@ "status": "up", "transmit-interval": 300, "remote-receive-interval": 300, - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "diagnostic": "ok", "multihop": false, "interface": "r3-eth0", "remote-transmit-interval": 300, "receive-interval": 300, - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-diagnostic": "ok", "peer": "10.0.3.2" } diff --git a/tests/topotests/bfd-topo2/r4/peers.json b/tests/topotests/bfd-topo2/r4/peers.json index 83101eb47f..dd26b9b580 100644 --- a/tests/topotests/bfd-topo2/r4/peers.json +++ b/tests/topotests/bfd-topo2/r4/peers.json @@ -8,10 +8,11 @@ "remote-diagnostic":"ok", "receive-interval":300, "transmit-interval":300, - "echo-interval":0, + "echo-receive-interval": 50, + "echo-transmit-interval":0, "remote-receive-interval":300, "remote-transmit-interval":300, - "remote-echo-interval":50 + "remote-echo-receive-interval":50 }, { "multihop":false, @@ -21,9 +22,10 @@ "remote-diagnostic":"ok", "receive-interval":300, "transmit-interval":300, - "echo-interval":0, + "echo-receive-interval": 50, + "echo-transmit-interval":0, "remote-receive-interval":300, "remote-transmit-interval":300, - "remote-echo-interval":50 + "remote-echo-receive-interval":50 } ] diff --git a/tests/topotests/bfd-topo3/r1/bfd-peers.json b/tests/topotests/bfd-topo3/r1/bfd-peers.json index 56205d538b..f8a354fc20 100644 --- a/tests/topotests/bfd-topo3/r1/bfd-peers.json +++ b/tests/topotests/bfd-topo3/r1/bfd-peers.json @@ -2,7 +2,8 @@ { "detect-multiplier": 3, "diagnostic": "ok", - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "id": "*", "local": "2001:db8:1::1", "minimum-ttl": 253, @@ -12,7 +13,7 @@ "receive-interval": 2000, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 2000, "remote-transmit-interval": 2000, @@ -24,7 +25,8 @@ { "detect-multiplier": 3, "diagnostic": "ok", - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "id": "*", "interface": "r1-eth0", "local": "2001:db8:1::1", @@ -34,7 +36,7 @@ "receive-interval": 600, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 600, "remote-transmit-interval": 600, @@ -46,7 +48,8 @@ { "detect-multiplier": 3, "diagnostic": "ok", - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "id": "*", "local": "192.168.1.1", "minimum-ttl": 254, @@ -56,7 +59,7 @@ "receive-interval": 2000, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 2000, "remote-transmit-interval": 2000, diff --git a/tests/topotests/bfd-topo3/r2/bfd-peers.json b/tests/topotests/bfd-topo3/r2/bfd-peers.json index cb8985b13e..786d66dbe3 100644 --- a/tests/topotests/bfd-topo3/r2/bfd-peers.json +++ b/tests/topotests/bfd-topo3/r2/bfd-peers.json @@ -2,7 +2,8 @@ { "detect-multiplier": 3, "diagnostic": "ok", - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "id": "*", "interface": "r2-eth0", "local": "2001:db8:1::2", @@ -12,7 +13,7 @@ "receive-interval": 600, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 600, "remote-transmit-interval": 600, @@ -24,7 +25,8 @@ { "detect-multiplier": 3, "diagnostic": "ok", - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "id": "*", "interface": "r2-eth1", "local": "2001:db8:2::2", @@ -34,7 +36,7 @@ "receive-interval": 2000, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 2000, "remote-transmit-interval": 2000, diff --git a/tests/topotests/bfd-topo3/r3/bfd-peers.json b/tests/topotests/bfd-topo3/r3/bfd-peers.json index 8be35fd084..1f58663a4e 100644 --- a/tests/topotests/bfd-topo3/r3/bfd-peers.json +++ b/tests/topotests/bfd-topo3/r3/bfd-peers.json @@ -2,7 +2,8 @@ { "detect-multiplier": 3, "diagnostic": "ok", - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "id": "*", "interface": "r3-eth1", "local": "2001:db8:3::2", @@ -12,7 +13,7 @@ "receive-interval": 2000, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 2000, "remote-transmit-interval": 2000, @@ -24,7 +25,8 @@ { "detect-multiplier": 3, "diagnostic": "ok", - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "id": "*", "interface": "r3-eth0", "local": "2001:db8:2::1", @@ -34,7 +36,7 @@ "receive-interval": 2000, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 2000, "remote-transmit-interval": 2000, @@ -46,7 +48,8 @@ { "detect-multiplier": 3, "diagnostic": "ok", - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "id": "*", "local": "192.168.2.1", "minimum-ttl": 254, @@ -56,7 +59,7 @@ "receive-interval": 2000, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 2000, "remote-transmit-interval": 2000, diff --git a/tests/topotests/bfd-topo3/r4/bfd-peers.json b/tests/topotests/bfd-topo3/r4/bfd-peers.json index e2e6722ef4..5477f39120 100644 --- a/tests/topotests/bfd-topo3/r4/bfd-peers.json +++ b/tests/topotests/bfd-topo3/r4/bfd-peers.json @@ -2,7 +2,8 @@ { "detect-multiplier": 3, "diagnostic": "ok", - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "id": "*", "local": "2001:db8:3::1", "minimum-ttl": 253, @@ -12,7 +13,7 @@ "receive-interval": 2000, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 2000, "remote-transmit-interval": 2000, @@ -24,7 +25,8 @@ { "detect-multiplier": 3, "diagnostic": "ok", - "echo-interval": 0, + "echo-receive-interval": 50, + "echo-transmit-interval": 0, "id": "*", "interface": "r4-eth0", "local": "2001:db8:3::1", @@ -34,7 +36,7 @@ "receive-interval": 2000, "remote-detect-multiplier": 3, "remote-diagnostic": "ok", - "remote-echo-interval": 50, + "remote-echo-receive-interval": 50, "remote-id": "*", "remote-receive-interval": 2000, "remote-transmit-interval": 2000, diff --git a/tests/topotests/bfd-vrf-topo1/r2/peers.json b/tests/topotests/bfd-vrf-topo1/r2/peers.json index 5035d643c5..267459c7a8 100644 --- a/tests/topotests/bfd-vrf-topo1/r2/peers.json +++ b/tests/topotests/bfd-vrf-topo1/r2/peers.json @@ -4,7 +4,7 @@ "status": "up" }, { - "remote-echo-interval": 100, + "remote-echo-receive-interval": 100, "peer": "192.168.1.1", "status": "up" }, diff --git a/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py index 89f5554d41..db4eab9d3d 100755 --- a/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py +++ b/tests/topotests/bgp-snmp-mplsl3vpn/test_bgp_snmp_mplsvpn.py @@ -505,10 +505,8 @@ def test_r1_mplsvpn_VrfTable(): associated_int = r1_snmp.get( "mplsL3VpnVrfAssociatedInterfaces.{}".format(snmp_str_to_oid("VRF-a")) ) - assertmsg = ( - "mplsL3VpnVrfAssociatedInterfaces incorrect should be 3 value {}".format( - associated_int - ) + assertmsg = "mplsL3VpnVrfAssociatedInterfaces incorrect should be 3 value {}".format( + associated_int ) assert associated_int == "3", assertmsg @@ -634,7 +632,6 @@ rte_table_test = { "C0 A8 C8 0A", '""', ], - "mplsL3VpnVrfRteInetCidrIfIndex": ["5", "6", "4", "5", "0", "6", "0"], "mplsL3VpnVrfRteInetCidrType": [ "local(3)", "local(3)", @@ -732,8 +729,24 @@ def test_r1_mplsvpn_rte_table(): ) if passed: break + # generate ifindex row grabbing ifindices from vtysh + if passed: + ifindex_row = [ + router_interface_get_ifindex(r1r, "eth3"), + router_interface_get_ifindex(r1r, "eth4"), + router_interface_get_ifindex(r1r, "eth2"), + router_interface_get_ifindex(r1r, "eth3"), + "0", + router_interface_get_ifindex(r1r, "eth4"), + "0", + ] + if not r1_snmp.test_oid_walk( + "mplsL3VpnVrfRteInetCidrIfIndex", ifindex_row, oid_list + ): + passed = False + print("passed {}".format(passed)) - # assert passed, assertmsg + assert passed, assertmsg def test_memory_leak(): diff --git a/tests/topotests/docker/frr-topotests.sh b/tests/topotests/docker/frr-topotests.sh index d25856ea62..9ef59b3bbc 100755 --- a/tests/topotests/docker/frr-topotests.sh +++ b/tests/topotests/docker/frr-topotests.sh @@ -132,6 +132,7 @@ if [ -z "$TOPOTEST_FRR" ]; then echo "frr-topotests only works if you have your tree in git." >&2 exit 1 fi + git -C "$TOPOTEST_FRR" ls-files -z > "${TOPOTEST_LOGS}/git-ls-files" fi if [ -z "$TOPOTEST_BUILDCACHE" ]; then diff --git a/tests/topotests/docker/inner/compile_frr.sh b/tests/topotests/docker/inner/compile_frr.sh index dee0ec8118..495beaf3cc 100755 --- a/tests/topotests/docker/inner/compile_frr.sh +++ b/tests/topotests/docker/inner/compile_frr.sh @@ -34,19 +34,15 @@ CDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [ "${TOPOTEST_CLEAN}" != "0" ]; then log_info "Cleaning FRR builddir..." - rm -rf $FRR_SYNC_DIR $FRR_BUILD_DIR &> /dev/null + rm -rf $FRR_BUILD_DIR &> /dev/null fi log_info "Syncing FRR source with host..." -mkdir -p $FRR_SYNC_DIR +mkdir -p $FRR_BUILD_DIR rsync -a --info=progress2 \ - --exclude '*.o' \ - --exclude '*.lo'\ + --from0 --files-from=/tmp/git-ls-files \ --chown root:root \ - $FRR_HOST_DIR/. $FRR_SYNC_DIR/ -(cd $FRR_SYNC_DIR && git clean -xdf > /dev/null) -mkdir -p $FRR_BUILD_DIR -rsync -a --info=progress2 --chown root:root $FRR_SYNC_DIR/. $FRR_BUILD_DIR/ + $FRR_HOST_DIR/. $FRR_BUILD_DIR/ cd "$FRR_BUILD_DIR" || \ log_fatal "failed to find frr directory" diff --git a/tests/topotests/docker/inner/funcs.sh b/tests/topotests/docker/inner/funcs.sh index acb8b55e97..d78d5006bc 100755 --- a/tests/topotests/docker/inner/funcs.sh +++ b/tests/topotests/docker/inner/funcs.sh @@ -23,7 +23,6 @@ # SOFTWARE. FRR_HOST_DIR=/root/host-frr -FRR_SYNC_DIR=/root/persist/frr-sync FRR_BUILD_DIR=/root/persist/frr-build if [ ! -L "/root/frr" ]; then diff --git a/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py b/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py index c8760f457a..4144f9b261 100644 --- a/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py +++ b/tests/topotests/ldp-snmp/test_ldp_snmp_topo1.py @@ -301,6 +301,41 @@ def test_r1_ldp_entity_table(): 'mplsLdpEntityRowStatus', ['createAndGo(4)']) +def test_r1_ldp_entity_stats_table(): + "Test mplsLdpEntityStatsTable" + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") + + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsSessionAttempts', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsSessionRejectedNoHelloErrors', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsSessionRejectedAdErrors', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsSessionRejectedMaxPduErrors', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsSessionRejectedLRErrors', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsBadLdpIdentifierErrors', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsBadPduLengthErrors', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsBadMessageLengthErrors', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsBadTlvLengthErrors', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsMalformedTlvValueErrors', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsKeepAliveTimerExpErrors', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsShutdownReceivedNotifications', ['0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpEntityStatsShutdownSentNotifications', ['0']) + + def test_r1_ldp_peer_table(): "Test mplsLdpPeerTable" tgen = get_topogen() @@ -342,6 +377,19 @@ def test_r1_ldp_session_table(): ['(0) 0:00:00.00', '(0) 0:00:00.00']) +def test_r1_ldp_session_stats_table(): + "Test mplsLdpSessionStatsTable" + tgen = get_topogen() + + r1 = tgen.net.get("r1") + r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c") + + assert r1_snmp.test_oid_walk( + 'mplsLdpSessionStatsUnknownMesTypeErrors', ['0', '0']) + assert r1_snmp.test_oid_walk( + 'mplsLdpSessionStatsUnknownTlvErrors', ['0', '0']) + + def test_r1_ldp_hello_adjacency_table(): "Test mplsLdpHelloAdjacencyTable" tgen = get_topogen() diff --git a/yang/frr-bfdd.yang b/yang/frr-bfdd.yang index 0f090a1b72..d21ff5068a 100644 --- a/yang/frr-bfdd.yang +++ b/yang/frr-bfdd.yang @@ -210,7 +210,14 @@ module frr-bfdd { type uint32; units microseconds; default 50000; - description "Minimum desired control packet transmission interval"; + description "Minimum desired echo packet transmission interval"; + } + + leaf required-echo-receive-interval { + type uint32; + units microseconds; + default 50000; + description "Minimum required echo packet receive interval"; } } diff --git a/yang/frr-filter.yang b/yang/frr-filter.yang index eb84dd7460..9a864213ee 100644 --- a/yang/frr-filter.yang +++ b/yang/frr-filter.yang @@ -292,8 +292,6 @@ module frr-filter { mandatory true; case ipv4-prefix { - when "../type = 'ipv4'"; - leaf ipv4-prefix { description "Configure IPv4 prefix to match"; type inet:ipv4-prefix; @@ -318,8 +316,6 @@ module frr-filter { } } case ipv6-prefix { - when "../type = 'ipv6'"; - leaf ipv6-prefix { description "Configure IPv6 prefix to match"; type inet:ipv6-prefix; diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index f3ccf83fbc..9246283fdf 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -49,12 +49,23 @@ DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry"); DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected"); DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context"); +/* Map backup nexthop indices between two nhes */ +struct backup_nh_map_s { + int map_count; + + struct { + uint8_t orig_idx; + uint8_t new_idx; + } map[MULTIPATH_NUM]; +}; + /* id counter to keep in sync with kernel */ uint32_t id_counter; -/* */ +/* Controlled through ui */ static bool g_nexthops_enabled = true; static bool proto_nexthops_only; +static bool use_recursive_backups = true; static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi, int type, bool from_dplane); @@ -1626,9 +1637,10 @@ void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe) nhg_connected_tree_increment_ref(&nhe->nhg_depends); } -static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, - struct nexthop *nexthop, - struct zebra_sr_policy *policy) +static struct nexthop *nexthop_set_resolved(afi_t afi, + const struct nexthop *newhop, + struct nexthop *nexthop, + struct zebra_sr_policy *policy) { struct nexthop *resolved_hop; uint8_t num_labels = 0; @@ -1746,6 +1758,8 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, resolved_hop->rparent = nexthop; _nexthop_add(&nexthop->resolved, resolved_hop); + + return resolved_hop; } /* Checks if nexthop we are trying to resolve to is valid */ @@ -1784,12 +1798,107 @@ static bool nexthop_valid_resolve(const struct nexthop *nexthop, } /* - * Given a nexthop we need to properly recursively resolve - * the route. As such, do a table lookup to find and match - * if at all possible. Set the nexthop->ifindex and resolved_id - * as appropriate + * When resolving a recursive nexthop, capture backup nexthop(s) also + * so they can be conveyed through the dataplane to the FIB. We'll look + * at the backups in the resolving nh 'nexthop' and its nhe, and copy them + * into the route's resolved nh 'resolved' and its nhe 'nhe'. + */ +static int resolve_backup_nexthops(const struct nexthop *nexthop, + const struct nhg_hash_entry *nhe, + struct nexthop *resolved, + struct nhg_hash_entry *resolve_nhe, + struct backup_nh_map_s *map) +{ + int i, j, idx; + const struct nexthop *bnh; + struct nexthop *nh, *newnh; + + assert(nexthop->backup_num <= NEXTHOP_MAX_BACKUPS); + + if (resolve_nhe->backup_info->nhe == NULL) + resolve_nhe->backup_info->nhe = zebra_nhg_alloc(); + + /* Locate backups from the original nexthop's backup index and nhe */ + for (i = 0; i < nexthop->backup_num; i++) { + idx = nexthop->backup_idx[i]; + + /* Do we already know about this particular backup? */ + for (j = 0; j < map->map_count; j++) { + if (map->map[j].orig_idx == idx) + break; + } + + if (j < map->map_count) { + resolved->backup_idx[resolved->backup_num] = + map->map[j].new_idx; + resolved->backup_num++; + + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("%s: found map idx orig %d, new %d", + __func__, map->map[j].orig_idx, + map->map[j].new_idx); + + continue; + } + + /* We can't handle any new map entries at this point. */ + if (map->map_count == MULTIPATH_NUM) + break; + + /* Need to create/copy a new backup */ + bnh = nhe->backup_info->nhe->nhg.nexthop; + for (j = 0; j < idx; j++) { + if (bnh == NULL) + break; + bnh = bnh->next; + } + + /* Whoops - bad index in the nexthop? */ + if (bnh == NULL) + continue; + + /* Update backup info in the resolving nexthop and its nhe */ + newnh = nexthop_dup_no_recurse(bnh, NULL); + + /* Need to compute the new backup index in the new + * backup list, and add to map struct. + */ + j = 0; + nh = resolve_nhe->backup_info->nhe->nhg.nexthop; + if (nh) { + while (nh->next) { + nh = nh->next; + j++; + } + + nh->next = newnh; + + } else /* First one */ + resolve_nhe->backup_info->nhe->nhg.nexthop = newnh; + + /* Capture index */ + resolved->backup_idx[resolved->backup_num] = j; + resolved->backup_num++; + + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("%s: added idx orig %d, new %d", + __func__, idx, j); + + /* Update map/cache */ + map->map[map->map_count].orig_idx = idx; + map->map[map->map_count].new_idx = j; + map->map_count++; + } + + return 0; +} + +/* + * Given a nexthop we need to properly recursively resolve, + * do a table lookup to find and match if at all possible. + * Set the nexthop->ifindex and resolution info as appropriate. */ -static int nexthop_active(afi_t afi, struct nexthop *nexthop, +static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, const struct prefix *top, int type, uint32_t flags, uint32_t *pmtu) { @@ -1805,6 +1914,7 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop, struct zebra_vrf *zvrf; struct in_addr local_ipv4; struct in_addr *ipv4; + afi_t afi = AFI_IP; /* Reset some nexthop attributes that we'll recompute if necessary */ if ((nexthop->type == NEXTHOP_TYPE_IPV4) @@ -1816,11 +1926,13 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop, nexthop->resolved = NULL; /* + * Set afi based on nexthop type. * Some nexthop types get special handling, possibly skipping * the normal processing. */ switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: + ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); /* * If the interface exists and its operative or its a kernel @@ -1838,6 +1950,8 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop, break; case NEXTHOP_TYPE_IPV6_IFINDEX: + afi = AFI_IP6; + if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) { ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); @@ -1848,14 +1962,16 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop, } break; - case NEXTHOP_TYPE_BLACKHOLE: - return 1; - case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: + afi = AFI_IP; + break; case NEXTHOP_TYPE_IPV6: - default: + afi = AFI_IP6; break; + + case NEXTHOP_TYPE_BLACKHOLE: + return 1; } /* @@ -2052,6 +2168,8 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop, return 1; } else if (CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION)) { struct nexthop_group *nhg; + struct nexthop *resolver; + struct backup_nh_map_s map = {}; resolved = 0; @@ -2081,17 +2199,29 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop, SET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, nexthop, - NULL); + resolver = nexthop_set_resolved(afi, newhop, + nexthop, NULL); resolved = 1; + + /* If there are backup nexthops, capture + * that info with the resolving nexthop. + */ + if (resolver && newhop->backup_num > 0) { + resolve_backup_nexthops(newhop, + match->nhe, + resolver, nhe, + &map); + } } /* Examine installed backup nexthops, if any. There * are only installed backups *if* there is a - * dedicated fib list. + * dedicated fib list. The UI can also control use + * of backups for resolution. */ nhg = rib_get_fib_backup_nhg(match); - if (nhg == NULL || nhg->nexthop == NULL) + if (!use_recursive_backups || + nhg == NULL || nhg->nexthop == NULL) goto done_with_match; for (ALL_NEXTHOPS_PTR(nhg, newhop)) { @@ -2144,14 +2274,17 @@ done_with_match: /* This function verifies reachability of one given nexthop, which can be * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored * in nexthop->flags field. The nexthop->ifindex will be updated - * appropriately as well. An existing route map can turn an - * otherwise active nexthop into inactive, but not vice versa. + * appropriately as well. + * + * An existing route map can turn an otherwise active nexthop into inactive, + * but not vice versa. * * The return value is the final value of 'ACTIVE' flag. */ static unsigned nexthop_active_check(struct route_node *rn, struct route_entry *re, - struct nexthop *nexthop) + struct nexthop *nexthop, + struct nhg_hash_entry *nhe) { route_map_result_t ret = RMAP_PERMITMATCH; afi_t family; @@ -2188,7 +2321,7 @@ static unsigned nexthop_active_check(struct route_node *rn, switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: - if (nexthop_active(AFI_IP, nexthop, &rn->p, re->type, + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, &mtu)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else @@ -2197,7 +2330,7 @@ static unsigned nexthop_active_check(struct route_node *rn, case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: family = AFI_IP; - if (nexthop_active(AFI_IP, nexthop, &rn->p, re->type, + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, &mtu)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else @@ -2205,7 +2338,7 @@ static unsigned nexthop_active_check(struct route_node *rn, break; case NEXTHOP_TYPE_IPV6: family = AFI_IP6; - if (nexthop_active(AFI_IP6, nexthop, &rn->p, re->type, + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, &mtu)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else @@ -2216,7 +2349,7 @@ static unsigned nexthop_active_check(struct route_node *rn, if (rn->p.family != AF_INET) family = AFI_IP6; - if (nexthop_active(AFI_IP6, nexthop, &rn->p, re->type, + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, &mtu)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else @@ -2326,18 +2459,20 @@ done: } /* - * Process a list of nexthops, given an nhg, determining + * Process a list of nexthops, given an nhe, determining * whether each one is ACTIVE/installable at this time. */ static uint32_t nexthop_list_active_update(struct route_node *rn, struct route_entry *re, - struct nexthop_group *nhg) + struct nhg_hash_entry *nhe, + bool is_backup) { union g_addr prev_src; unsigned int prev_active, new_active; ifindex_t prev_index; uint32_t counter = 0; struct nexthop *nexthop; + struct nexthop_group *nhg = &nhe->nhg; nexthop = nhg->nexthop; @@ -2353,15 +2488,20 @@ static uint32_t nexthop_list_active_update(struct route_node *rn, prev_src = nexthop->rmap_src; prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); prev_index = nexthop->ifindex; + + /* Include the containing nhe for primary nexthops: if there's + * recursive resolution, we capture the backup info also. + */ + new_active = + nexthop_active_check(rn, re, nexthop, + (is_backup ? NULL : nhe)); + /* * We need to respect the multipath_num here * as that what we should be able to install from * a multipath perspective should not be a data plane * decision point. */ - new_active = - nexthop_active_check(rn, re, nexthop); - if (new_active && counter >= zrouter.multipath_num) { struct nexthop *nh; @@ -2375,7 +2515,7 @@ static uint32_t nexthop_list_active_update(struct route_node *rn, if (new_active) counter++; - /* Don't allow src setting on IPv6 addr for now */ + /* Check for changes to the nexthop - set ROUTE_ENTRY_CHANGED */ if (prev_active != new_active || prev_index != nexthop->ifindex || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX && nexthop->type < NEXTHOP_TYPE_IPV6) @@ -2444,7 +2584,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) curr_nhe->id = 0; /* Process nexthops */ - curr_active = nexthop_list_active_update(rn, re, &curr_nhe->nhg); + curr_active = nexthop_list_active_update(rn, re, curr_nhe, false); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: re %p curr_active %u", __func__, re, @@ -2455,7 +2595,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re) goto backups_done; backup_active = nexthop_list_active_update( - rn, re, zebra_nhg_get_backup_nhg(curr_nhe)); + rn, re, curr_nhe->backup_info->nhe, true /*is_backup*/); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: re %p backup_active %u", __func__, re, @@ -2834,6 +2974,17 @@ bool zebra_nhg_kernel_nexthops_enabled(void) return g_nexthops_enabled; } +/* Global control for use of activated backups for recursive resolution. */ +void zebra_nhg_set_recursive_use_backups(bool set) +{ + use_recursive_backups = set; +} + +bool zebra_nhg_recursive_use_backups(void) +{ + return use_recursive_backups; +} + /* * Global control to only use kernel nexthops for protocol created NHGs. * There are some use cases where you may not want zebra to implicitly diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 2de34fec68..38015bf557 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -212,6 +212,10 @@ bool zebra_nhg_kernel_nexthops_enabled(void); void zebra_nhg_set_proto_nexthops_only(bool set); bool zebra_nhg_proto_nexthops_only(void); +/* Global control for use of activated backups for recursive resolution. */ +void zebra_nhg_set_recursive_use_backups(bool set); +bool zebra_nhg_recursive_use_backups(void); + /** * NHE abstracted tree functions. * Use these where possible instead of direct access. diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index f18d8fbb6d..3349c18204 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1636,6 +1636,18 @@ DEFPY_HIDDEN(proto_nexthop_group_only, proto_nexthop_group_only_cmd, return CMD_SUCCESS; } +DEFPY_HIDDEN(backup_nexthop_recursive_use_enable, + backup_nexthop_recursive_use_enable_cmd, + "[no] zebra nexthop resolve-via-backup", + NO_STR + ZEBRA_STR + "Nexthop configuration \n" + "Configure use of backup nexthops in recursive resolution\n") +{ + zebra_nhg_set_recursive_use_backups(!no); + return CMD_SUCCESS; +} + DEFUN (no_ip_nht_default_route, no_ip_nht_default_route_cmd, "no ip nht resolve-via-default", @@ -3619,6 +3631,9 @@ static int config_write_protocol(struct vty *vty) if (zebra_nhg_proto_nexthops_only()) vty_out(vty, "zebra nexthop proto only\n"); + if (!zebra_nhg_recursive_use_backups()) + vty_out(vty, "no zebra nexthop resolve-via-backup\n"); + #ifdef HAVE_NETLINK /* Include netlink info */ netlink_config_write_helper(vty); @@ -4054,6 +4069,7 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &no_zebra_packet_process_cmd); install_element(CONFIG_NODE, &nexthop_group_use_enable_cmd); install_element(CONFIG_NODE, &proto_nexthop_group_only_cmd); + install_element(CONFIG_NODE, &backup_nexthop_recursive_use_enable_cmd); install_element(VIEW_NODE, &show_nexthop_group_cmd); install_element(VIEW_NODE, &show_interface_nexthop_group_cmd); |
