diff options
57 files changed, 1920 insertions, 1001 deletions
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 0e3ed9f0d1..1a30cb37f4 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -2163,6 +2163,9 @@ bgp_establish(struct peer_connection *connection) peer->established++; bgp_fsm_change_status(connection, Established); + if (peer->last_reset == PEER_DOWN_WAITING_OPEN) + peer->last_reset = 0; + /* bgp log-neighbor-changes of neighbor Up */ if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) { struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 164e2300c0..2ef7ec97e3 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -1278,6 +1278,25 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) } LIST_FOREACH (path, &(bnc->paths), nh_thread) { + /* + * Currently when a peer goes down, bgp immediately + * sees this via the interface events( if it is directly + * connected). And in this case it takes and puts on + * a special peer queue all path info's associated with + * but these items are not yet processed typically when + * the nexthop is being handled here. Thus we end + * up in a situation where the process Queue for BGP + * is being asked to look at the same path info multiple + * times. Let's just cut to the chase here and if + * the bnc has a peer associated with it and the path info + * being looked at uses that peer and the peer is no + * longer established we know the path_info is being + * handled elsewhere and we do not need to process + * it here at all since the pathinfo is going away + */ + if (peer && path->peer == peer && !peer_established(peer->connection)) + continue; + if (path->type == ZEBRA_ROUTE_BGP && (path->sub_type == BGP_ROUTE_NORMAL || path->sub_type == BGP_ROUTE_STATIC || diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 0780b4f72d..f8726ffff9 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -1225,6 +1225,9 @@ void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, const char *hostname = cmd_hostname_get(); const char *domainname = cmd_domainname_get(); + if (!peer) + return; + if (!peer_established(peer->connection)) return; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index f2e61e1e7f..e932738cd4 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3885,6 +3885,12 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, BGP_PATH_ATTR_CHANGED); UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG); UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG); + } else { + /* + * Ensure that on uninstall that the INSTALL_PENDING + * is no longer set + */ + UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING); } /* call bmp hook for loc-rib route update / withdraw after flags were diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 04b9ce5ea6..e18f6443b5 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -122,6 +122,9 @@ FRR_CFG_DEFAULT_BOOL(BGP_ENFORCE_FIRST_AS, { .val_bool = false, .match_version = "< 9.1", }, { .val_bool = true }, ); +FRR_CFG_DEFAULT_BOOL(BGP_RR_ALLOW_OUTBOUND_POLICY, + { .val_bool = false }, +); DEFINE_HOOK(bgp_inst_config_write, (struct bgp *bgp, struct vty *vty), @@ -622,6 +625,8 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, BGP_FLAG_DYNAMIC_CAPABILITY); if (DFLT_BGP_ENFORCE_FIRST_AS) SET_FLAG((*bgp)->flags, BGP_FLAG_ENFORCE_FIRST_AS); + if (DFLT_BGP_RR_ALLOW_OUTBOUND_POLICY) + SET_FLAG((*bgp)->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY); ret = BGP_SUCCESS; } @@ -17575,12 +17580,6 @@ DEFUN (bgp_redistribute_ipv4_ospf, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17634,12 +17633,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17697,12 +17690,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17767,12 +17754,6 @@ DEFUN (bgp_redistribute_ipv4_ospf_rmap_metric, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -17842,13 +17823,7 @@ DEFUN (bgp_redistribute_ipv4_ospf_metric_rmap, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } else if (strncmp(argv[idx_ospf_table]->arg, "table-direct", - strlen("table-direct")) == 0) { + if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; if (instance == RT_TABLE_MAIN || instance == RT_TABLE_LOCAL) { @@ -17911,12 +17886,6 @@ DEFUN (no_bgp_redistribute_ipv4_ospf, if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) protocol = ZEBRA_ROUTE_OSPF; else { - if (bgp->vrf_id != VRF_DEFAULT) { - vty_out(vty, - "%% Only default BGP instance can use '%s'\n", - argv[idx_ospf_table]->arg); - return CMD_WARNING_CONFIG_FAILED; - } if (strncmp(argv[idx_ospf_table]->arg, "table-direct", strlen("table-direct")) == 0) { protocol = ZEBRA_ROUTE_TABLE_DIRECT; @@ -19816,10 +19785,12 @@ int bgp_config_write(struct vty *vty) } } - if (CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { - vty_out(vty, - " bgp route-reflector allow-outbound-policy\n"); - } + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY) != + SAVE_BGP_RR_ALLOW_OUTBOUND_POLICY) + vty_out(vty, " %sbgp route-reflector allow-outbound-policy\n", + CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY) ? "" + : "no "); + if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID)) vty_out(vty, " bgp bestpath compare-routerid\n"); if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP)) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 8e8616c155..1669aabc60 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -1674,11 +1674,23 @@ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi) for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) && - (pi->type == ZEBRA_ROUTE_BGP - && (pi->sub_type == BGP_ROUTE_NORMAL - || pi->sub_type == BGP_ROUTE_IMPORTED))) - bgp_zebra_route_install(dest, pi, bgp, true, - NULL, false); + (pi->type == ZEBRA_ROUTE_BGP && (pi->sub_type == BGP_ROUTE_NORMAL || + pi->sub_type == BGP_ROUTE_IMPORTED))) { + bool is_add = true; + + if (bgp->table_map[afi][safi].name) { + struct attr local_attr = *pi->attr; + struct bgp_path_info local_info = *pi; + + local_info.attr = &local_attr; + + is_add = bgp_table_map_apply(bgp->table_map[afi][safi].map, + bgp_dest_get_prefix(dest), + &local_info); + } + + bgp_zebra_route_install(dest, pi, bgp, is_add, NULL, false); + } } /* Announce routes of any bgp subtype of a table to zebra */ @@ -2042,11 +2054,34 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type, /* Return if already redistribute flag is set. */ if (instance) { - if (redist_check_instance(&zclient->mi_redist[afi][type], - instance)) - return CMD_WARNING; + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + /* + * When redistribution type is `table-direct` the + * instance means `table identification`. + * + * `table_id` support 32bit integers, however since + * `instance` is being overloaded to `table_id` it + * will only be possible to use the first 65535 + * entries. + * + * Also the ZAPI must also support `int` + * (see `zebra_redistribute_add`). + */ + struct redist_table_direct table = { + .table_id = instance, + .vrf_id = bgp->vrf_id, + }; + if (redist_lookup_table_direct(&zclient->mi_redist[afi][type], &table) != + NULL) + return CMD_WARNING; + + redist_add_table_direct(&zclient->mi_redist[afi][type], &table); + } else { + if (redist_check_instance(&zclient->mi_redist[afi][type], instance)) + return CMD_WARNING; - redist_add_instance(&zclient->mi_redist[afi][type], instance); + redist_add_instance(&zclient->mi_redist[afi][type], instance); + } } else { if (vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id)) return CMD_WARNING; @@ -2174,10 +2209,22 @@ int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type, /* Return if zebra connection is disabled. */ if (instance) { - if (!redist_check_instance(&zclient->mi_redist[afi][type], - instance)) - return CMD_WARNING; - redist_del_instance(&zclient->mi_redist[afi][type], instance); + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + struct redist_table_direct table = { + .table_id = instance, + .vrf_id = bgp->vrf_id, + }; + if (redist_lookup_table_direct(&zclient->mi_redist[afi][type], &table) == + NULL) + return CMD_WARNING; + + redist_del_table_direct(&zclient->mi_redist[afi][type], &table); + } else { + if (!redist_check_instance(&zclient->mi_redist[afi][type], instance)) + return CMD_WARNING; + + redist_del_instance(&zclient->mi_redist[afi][type], instance); + } } else { if (!vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id)) return CMD_WARNING; diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 6de403b30c..edf90d3dd8 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2026,8 +2026,11 @@ struct peer *peer_create(union sockunion *su, const char *conf_if, if (bgp->autoshutdown) peer_flag_set(peer, PEER_FLAG_SHUTDOWN); /* Set up peer's events and timers. */ - else if (!active && peer_active(peer->connection)) + else if (!active && peer_active(peer->connection)) { + if (peer->last_reset == PEER_DOWN_NOAFI_ACTIVATED) + peer->last_reset = 0; bgp_timer_set(peer->connection); + } bgp_peer_gr_flags_update(peer); BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer); diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 18b13547a5..8c97dcda2f 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -1053,9 +1053,8 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, adj->algorithm)); json_object_int_add(srv6_endx_sid_json, "weight", adj->weight); - json_object_string_add(srv6_endx_sid_json, - "behavior", - seg6local_action2str( + json_object_string_add(srv6_endx_sid_json, "behavior", + srv6_endpoint_behavior_codepoint2str( adj->behavior)); json_object_boolean_add( srv6_endx_sid_json, "flagB", @@ -1081,22 +1080,17 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, for (adj = (struct isis_srv6_endx_sid_subtlv *) exts->srv6_endx_sid.head; adj; adj = adj->next) { - sbuf_push( - buf, indent, - "SRv6 End.X SID: %pI6, Algorithm: %s, Weight: %hhu, Endpoint Behavior: %s, Flags: B:%c, S:%c, P:%c\n", - &adj->sid, - sr_algorithm_string(adj->algorithm), - adj->weight, - seg6local_action2str(adj->behavior), - adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG - ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_SFLG - ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_PFLG - ? '1' - : '0'); + sbuf_push(buf, indent, + "SRv6 End.X SID: %pI6, Algorithm: %s, Weight: %hhu, Endpoint Behavior: %s, Flags: B:%c, S:%c, P:%c\n", + &adj->sid, sr_algorithm_string(adj->algorithm), + adj->weight, + srv6_endpoint_behavior_codepoint2str(adj->behavior), + adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_SFLG ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_PFLG ? '1' + : '0'); if (adj->subsubtlvs) isis_format_subsubtlvs(adj->subsubtlvs, buf, NULL, @@ -1131,9 +1125,8 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, lan->algorithm)); json_object_int_add(srv6_lan_endx_sid_json, "weight", lan->weight); - json_object_string_add(srv6_lan_endx_sid_json, - "behavior", - seg6local_action2str( + json_object_string_add(srv6_lan_endx_sid_json, "behavior", + srv6_endpoint_behavior_codepoint2str( lan->behavior)); json_object_boolean_add( srv6_lan_endx_sid_json, "flagB", @@ -1162,24 +1155,19 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, for (lan = (struct isis_srv6_lan_endx_sid_subtlv *) exts->srv6_lan_endx_sid.head; lan; lan = lan->next) { - sbuf_push( - buf, indent, - "SRv6 Lan End.X SID: %pI6, Algorithm: %s, Weight: %hhu, Endpoint Behavior: %s, Flags: B:%c, S:%c, P:%c " - "Neighbor-ID: %pSY\n", - &lan->sid, - sr_algorithm_string(lan->algorithm), - lan->weight, - seg6local_action2str(lan->behavior), - lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_SFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_PFLG - ? '1' - : '0', - lan->neighbor_id); + sbuf_push(buf, indent, + "SRv6 Lan End.X SID: %pI6, Algorithm: %s, Weight: %hhu, Endpoint Behavior: %s, Flags: B:%c, S:%c, P:%c " + "Neighbor-ID: %pSY\n", + &lan->sid, sr_algorithm_string(lan->algorithm), + lan->weight, + srv6_endpoint_behavior_codepoint2str(lan->behavior), + lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_BFLG ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_SFLG ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_SRV6_ENDX_SID_PFLG ? '1' + : '0', + lan->neighbor_id); if (lan->subsubtlvs) isis_format_subsubtlvs(lan->subsubtlvs, buf, NULL, @@ -2823,7 +2811,7 @@ static void format_item_srv6_end_sid(uint16_t mtid, struct isis_item *i, sid_json = json_object_new_object(); json_object_object_add(json, "srv6EndSid", sid_json); json_object_string_add(sid_json, "endpointBehavior", - seg6local_action2str(sid->behavior)); + srv6_endpoint_behavior_codepoint2str(sid->behavior)); json_object_string_addf(sid_json, "sidValue", "%pI6", &sid->sid); if (sid->subsubtlvs) { struct json_object *subtlvs_json; @@ -2836,7 +2824,7 @@ static void format_item_srv6_end_sid(uint16_t mtid, struct isis_item *i, } else { sbuf_push(buf, indent, "SRv6 End SID "); sbuf_push(buf, 0, "Endpoint Behavior: %s, ", - seg6local_action2str(sid->behavior)); + srv6_endpoint_behavior_codepoint2str(sid->behavior)); sbuf_push(buf, 0, "SID value: %pI6\n", &sid->sid); if (sid->subsubtlvs) { diff --git a/lib/srcdest_table.c b/lib/srcdest_table.c index 3247a0372c..7203c8ac8e 100644 --- a/lib/srcdest_table.c +++ b/lib/srcdest_table.c @@ -309,13 +309,3 @@ static ssize_t printfrr_rn(struct fbuf *buf, struct printfrr_eargs *ea, cbuf, sizeof(cbuf)); return bputs(buf, cbuf); } - -struct route_table *srcdest_srcnode_table(struct route_node *rn) -{ - if (rnode_is_dstnode(rn)) { - struct srcdest_rnode *srn = srcdest_rnode_from_rnode(rn); - - return srn->src_table; - } - return NULL; -} diff --git a/lib/srcdest_table.h b/lib/srcdest_table.h index ff97f9b735..a699d4a11b 100644 --- a/lib/srcdest_table.h +++ b/lib/srcdest_table.h @@ -87,8 +87,6 @@ static inline void *srcdest_rnode_table_info(struct route_node *rn) return route_table_get_info(srcdest_rnode_table(rn)); } -extern struct route_table *srcdest_srcnode_table(struct route_node *rn); - #ifdef __cplusplus } #endif diff --git a/lib/zclient.c b/lib/zclient.c index 063944fd3b..d8c75c9029 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -31,6 +31,7 @@ DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient"); DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs"); +DEFINE_MTYPE_STATIC(LIB, REDIST_TABLE_DIRECT, "Redistribution table direct"); /* Zebra client events. */ enum zclient_event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT }; @@ -104,6 +105,11 @@ void zclient_free(struct zclient *zclient) XFREE(MTYPE_ZCLIENT, zclient); } +static void redist_free_instance(void *data) +{ + XFREE(MTYPE_REDIST_INST, data); +} + unsigned short *redist_check_instance(struct redist_proto *red, unsigned short instance) { @@ -126,8 +132,10 @@ void redist_add_instance(struct redist_proto *red, unsigned short instance) red->enabled = 1; - if (!red->instances) + if (!red->instances) { red->instances = list_new(); + red->instances->del = redist_free_instance; + } in = XMALLOC(MTYPE_REDIST_INST, sizeof(unsigned short)); *in = instance; @@ -143,23 +151,100 @@ void redist_del_instance(struct redist_proto *red, unsigned short instance) return; listnode_delete(red->instances, id); - XFREE(MTYPE_REDIST_INST, id); + red->instances->del(id); if (!red->instances->count) { red->enabled = 0; list_delete(&red->instances); } } -void redist_del_all_instances(struct redist_proto *red) +static void redist_free_table_direct(void *data) { - struct listnode *ln, *nn; - unsigned short *id; + XFREE(MTYPE_REDIST_TABLE_DIRECT, data); +} + +struct redist_table_direct *redist_lookup_table_direct(const struct redist_proto *red, + const struct redist_table_direct *table) +{ + struct redist_table_direct *ntable; + struct listnode *node; + + if (red->instances == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(red->instances, node, ntable)) { + if (table->vrf_id != ntable->vrf_id) + continue; + if (table->table_id != ntable->table_id) + continue; + + return ntable; + } + + return NULL; +} + +bool redist_table_direct_has_id(const struct redist_proto *red, int table_id) +{ + struct redist_table_direct *table; + struct listnode *node; + + if (red->instances == NULL) + return false; + + for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) { + if (table->table_id != table_id) + continue; + + return true; + } + + return false; +} + +void redist_add_table_direct(struct redist_proto *red, const struct redist_table_direct *table) +{ + struct redist_table_direct *ntable; + + ntable = redist_lookup_table_direct(red, table); + if (ntable != NULL) + return; + + if (red->instances == NULL) { + red->instances = list_new(); + red->instances->del = redist_free_table_direct; + } + + red->enabled = 1; + + ntable = XCALLOC(MTYPE_REDIST_TABLE_DIRECT, sizeof(*ntable)); + ntable->vrf_id = table->vrf_id; + ntable->table_id = table->table_id; + listnode_add(red->instances, ntable); +} + +void redist_del_table_direct(struct redist_proto *red, const struct redist_table_direct *table) +{ + struct redist_table_direct *ntable; + ntable = redist_lookup_table_direct(red, table); + if (ntable == NULL) + return; + + listnode_delete(red->instances, ntable); + red->instances->del(ntable); + if (red->instances->count == 0) { + red->enabled = 0; + list_delete(&red->instances); + } +} + +void redist_del_all_instances(struct redist_proto *red) +{ if (!red->instances) return; - for (ALL_LIST_ELEMENTS(red->instances, ln, nn, id)) - redist_del_instance(red, *id); + list_delete(&red->instances); } /* Stop zebra client services. */ @@ -480,6 +565,17 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient, return zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); } +static void zclient_send_table_direct(struct zclient *zclient, afi_t afi, int type) +{ + struct redist_table_direct *table; + struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) + zebra_redistribute_send(type, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT, + table->table_id, table->vrf_id); +} + /* Send register requests to zebra daemon for the information in a VRF. */ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) { @@ -513,6 +609,12 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) if (!zclient->mi_redist[afi][i].enabled) continue; + if (i == ZEBRA_ROUTE_TABLE_DIRECT) { + zclient_send_table_direct(zclient, afi, + ZEBRA_REDISTRIBUTE_ADD); + continue; + } + struct listnode *node; unsigned short *id; @@ -580,6 +682,12 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) if (!zclient->mi_redist[afi][i].enabled) continue; + if (i == ZEBRA_ROUTE_TABLE_DIRECT) { + zclient_send_table_direct(zclient, afi, + ZEBRA_REDISTRIBUTE_DELETE); + continue; + } + struct listnode *node; unsigned short *id; @@ -2016,6 +2124,15 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, enum zapi_route_notify_owner *note, afi_t *afi, safi_t *safi) { + struct prefix dummy; + + return zapi_route_notify_decode_srcdest(s, p, &dummy, tableid, note, afi, safi); +} + +bool zapi_route_notify_decode_srcdest(struct stream *s, struct prefix *p, struct prefix *src_p, + uint32_t *tableid, enum zapi_route_notify_owner *note, + afi_t *afi, safi_t *safi) +{ uint32_t t; afi_t afi_val; safi_t safi_val; @@ -2025,6 +2142,9 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, STREAM_GETC(s, p->family); STREAM_GETC(s, p->prefixlen); STREAM_GET(&p->u.prefix, s, prefix_blen(p)); + src_p->family = p->family; + STREAM_GETC(s, src_p->prefixlen); + STREAM_GET(&src_p->u.prefix, s, prefix_blen(src_p)); STREAM_GETL(s, t); STREAM_GETC(s, afi_val); STREAM_GETC(s, safi_val); @@ -4634,9 +4754,52 @@ static void zclient_read(struct event *thread) zclient_event(ZCLIENT_READ, zclient); } +static void zclient_redistribute_table_direct(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi, + int instance, int command) +{ + struct redist_proto *red = &zclient->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; + bool has_table; + struct redist_table_direct table = { + .vrf_id = vrf_id, + .table_id = instance, + }; + + has_table = redist_lookup_table_direct(red, &table); + + if (command == ZEBRA_REDISTRIBUTE_ADD) { + if (has_table) + return; + + redist_add_table_direct(red, &table); + } else { + if (!has_table) + return; + + redist_del_table_direct(red, &table); + } + + if (zclient->sock > 0) + zebra_redistribute_send(command, zclient, afi, ZEBRA_ROUTE_TABLE_DIRECT, instance, + vrf_id); +} + void zclient_redistribute(int command, struct zclient *zclient, afi_t afi, int type, unsigned short instance, vrf_id_t vrf_id) { + /* + * When asking for table-direct redistribution the parameter + * `instance` has a different meaning: it means table + * identification. + * + * The table identification information is stored in + * `zclient->mi_redist` along with the VRF identification + * information in a pair (different from the usual single protocol + * instance value). + */ + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + zclient_redistribute_table_direct(zclient, vrf_id, afi, instance, command); + return; + } if (instance) { if (command == ZEBRA_REDISTRIBUTE_ADD) { diff --git a/lib/zclient.h b/lib/zclient.h index 2385a8a219..afd84acce2 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -268,6 +268,21 @@ struct redist_proto { struct list *instances; }; +/** + * Redistribute table direct instance data structure: keeps the VRF + * that subscribed to the table ID. + * + * **NOTE** + * `table_id` is an integer because that is what the netlink interface + * uses for route attribute RTA_TABLE (32bit int), however the whole + * zclient API uses `unsigned short` (and CLI commands) so it will be + * limited to the range 1 to 65535. + */ +struct redist_table_direct { + vrf_id_t vrf_id; + int table_id; +}; + struct zclient_capabilities { uint32_t ecmp; bool mpls_enabled; @@ -924,6 +939,15 @@ extern void redist_add_instance(struct redist_proto *, unsigned short); extern void redist_del_instance(struct redist_proto *, unsigned short); extern void redist_del_all_instances(struct redist_proto *red); +extern struct redist_table_direct * +redist_lookup_table_direct(const struct redist_proto *red, const struct redist_table_direct *table); +extern bool redist_table_direct_has_id(const struct redist_proto *red, int table_id); +extern void redist_add_table_direct(struct redist_proto *red, + const struct redist_table_direct *table); +extern void redist_del_table_direct(struct redist_proto *red, + const struct redist_table_direct *table); + + /* * Send to zebra that the specified vrf is using label to resolve * itself for L3VPN's. Repeated calls of this function with @@ -1144,6 +1168,9 @@ bool zapi_route_notify_decode(struct stream *s, struct prefix *p, uint32_t *tableid, enum zapi_route_notify_owner *note, afi_t *afi, safi_t *safi); +bool zapi_route_notify_decode_srcdest(struct stream *s, struct prefix *p, struct prefix *src_p, + uint32_t *tableid, enum zapi_route_notify_owner *note, + afi_t *afi, safi_t *safi); bool zapi_rule_notify_decode(struct stream *s, uint32_t *seqno, uint32_t *priority, uint32_t *unique, char *ifname, enum zapi_rule_notify_owner *note); diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index b718d498ae..f45135f44f 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -304,6 +304,27 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p, if (api.nexthop_num >= ospf->max_multipath) break; + /* + * Prune duplicate next-hops from the route that is + * installed in the zebra IP route table. OSPF Intra-Area + * routes never have duplicates. + */ + if (or->path_type != OSPF_PATH_INTRA_AREA) { + struct zapi_nexthop *api_nh = &api.nexthops[0]; + unsigned int nh_index; + bool duplicate_next_hop = false; + + for (nh_index = 0; nh_index < api.nexthop_num; api_nh++, nh_index++) { + if (IPV4_ADDR_SAME(&api_nh->gate.ipv4, &path->nexthop) && + (api_nh->ifindex == path->ifindex)) { + duplicate_next_hop = true; + break; + } + } + if (duplicate_next_hop) + continue; + } + ospf_zebra_add_nexthop(ospf, path, &api); if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { diff --git a/pimd/pim_autorp.c b/pimd/pim_autorp.c index caed914a87..dc077dbbd6 100644 --- a/pimd/pim_autorp.c +++ b/pimd/pim_autorp.c @@ -113,6 +113,12 @@ static void pim_autorp_free(struct pim_autorp *autorp) XFREE(MTYPE_PIM_AUTORP_ANNOUNCE, autorp->announce_pkt); } +static bool pim_autorp_should_close(struct pim_autorp *autorp) +{ + /* If discovery or mapping agent is active, then we need the socket open */ + return !autorp->do_discovery && !autorp->send_rp_discovery; +} + static bool pim_autorp_join_groups(struct interface *ifp) { struct pim_interface *pim_ifp; @@ -670,10 +676,19 @@ static void autorp_send_discovery(struct event *evt) &(autorp->send_discovery_timer)); } +static bool pim_autorp_socket_enable(struct pim_autorp *autorp); +static bool pim_autorp_socket_disable(struct pim_autorp *autorp); + static void autorp_send_discovery_on(struct pim_autorp *autorp) { int interval = 5; + /* Make sure the socket is open and ready */ + if (!pim_autorp_socket_enable(autorp)) { + zlog_err("%s: AutoRP failed to open socket", __func__); + return; + } + /* Send the first discovery shortly after being enabled. * If the configured interval is less than 5 seconds, then just use that. */ @@ -695,6 +710,10 @@ static void autorp_send_discovery_off(struct pim_autorp *autorp) if (PIM_DEBUG_AUTORP) zlog_debug("%s: AutoRP discovery sending disabled", __func__); event_cancel(&(autorp->send_discovery_timer)); + + /* Close the socket if we need to */ + if (pim_autorp_should_close(autorp) && !pim_autorp_socket_disable(autorp)) + zlog_warn("%s: AutoRP failed to close socket", __func__); } static bool autorp_recv_discovery(struct pim_autorp *autorp, uint8_t rpcnt, uint16_t holdtime, @@ -949,6 +968,10 @@ static bool pim_autorp_socket_enable(struct pim_autorp *autorp) { int fd; + /* Return early if socket is already enabled */ + if (autorp->sock != -1) + return true; + frr_with_privs (&pimd_privs) { fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (fd < 0) { @@ -975,6 +998,10 @@ static bool pim_autorp_socket_enable(struct pim_autorp *autorp) static bool pim_autorp_socket_disable(struct pim_autorp *autorp) { + /* Return early if socket is already disabled */ + if (autorp->sock == -1) + return true; + if (close(autorp->sock)) { zlog_warn("Failure closing autorp socket: fd=%d errno=%d: %s", autorp->sock, errno, safe_strerror(errno)); @@ -1453,6 +1480,12 @@ void pim_autorp_start_discovery(struct pim_instance *pim) struct interface *ifp; struct pim_autorp *autorp = pim->autorp; + /* Make sure the socket is open and ready */ + if (!pim_autorp_socket_enable(autorp)) { + zlog_err("%s: AutoRP failed to open socket", __func__); + return; + } + if (!autorp->do_discovery) { autorp->do_discovery = true; autorp_read_on(autorp); @@ -1482,6 +1515,10 @@ void pim_autorp_stop_discovery(struct pim_instance *pim) if (PIM_DEBUG_AUTORP) zlog_debug("%s: AutoRP Discovery stopped", __func__); } + + /* Close the socket if we need to */ + if (pim_autorp_should_close(autorp) && !pim_autorp_socket_disable(autorp)) + zlog_warn("%s: AutoRP failed to close socket", __func__); } void pim_autorp_init(struct pim_instance *pim) @@ -1510,12 +1547,6 @@ void pim_autorp_init(struct pim_instance *pim) pim->autorp = autorp; - if (!pim_autorp_socket_enable(autorp)) { - zlog_warn("%s: AutoRP failed to initialize, feature will not work correctly", - __func__); - return; - } - if (PIM_DEBUG_AUTORP) zlog_debug("%s: AutoRP Initialized", __func__); diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c index 8aebce7d27..9f09852a94 100644 --- a/pimd/pim_cmd_common.c +++ b/pimd/pim_cmd_common.c @@ -608,26 +608,14 @@ int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str, int pim_process_autorp_cmd(struct vty *vty) { - char xpath[XPATH_MAXLEN]; - - snprintf(xpath, sizeof(xpath), "%s/%s", FRR_PIM_AUTORP_XPATH, - "discovery-enabled"); - - nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "true"); - - return nb_cli_apply_changes(vty, NULL); + nb_cli_enqueue_change(vty, "./discovery-enabled", NB_OP_MODIFY, "true"); + return nb_cli_apply_changes(vty, "%s", FRR_PIM_AUTORP_XPATH); } int pim_process_no_autorp_cmd(struct vty *vty) { - char xpath[XPATH_MAXLEN]; - - snprintf(xpath, sizeof(xpath), "%s/%s", FRR_PIM_AUTORP_XPATH, - "discovery-enabled"); - - nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); + nb_cli_enqueue_change(vty, "./discovery-enabled", NB_OP_MODIFY, "false"); + return nb_cli_apply_changes(vty, "%s", FRR_PIM_AUTORP_XPATH); } int pim_process_autorp_candidate_rp_cmd(struct vty *vty, bool no, const char *rpaddr_str, diff --git a/staticd/static_nb.c b/staticd/static_nb.c index 356324126a..ef363bfe7e 100644 --- a/staticd/static_nb.c +++ b/staticd/static_nb.c @@ -135,96 +135,6 @@ const struct frr_yang_module_info frr_staticd_info = { } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list", - .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list", - .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop", - .cbs = { - .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish, - .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy, - .pre_validate = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry", - .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", - .cbs = { - .create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class", - .cbs = { - .modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify, - .destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy, - } - }, - { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing/srv6/static-sids/sid", .cbs = { .apply_finish = routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_apply_finish, diff --git a/staticd/static_nb.h b/staticd/static_nb.h index d11bf5363b..aa11f34021 100644 --- a/staticd/static_nb.h +++ b/staticd/static_nb.h @@ -72,52 +72,6 @@ int route_next_hop_bfd_source_destroy(struct nb_cb_destroy_args *args); int route_next_hop_bfd_profile_modify(struct nb_cb_modify_args *args); int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args); int route_next_hop_bfd_multi_hop_modify(struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create( - struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create( - struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create( - struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( - struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( - struct nb_cb_create_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy( - struct nb_cb_destroy_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify( - struct nb_cb_modify_args *args); -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( - struct nb_cb_destroy_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_create( struct nb_cb_create_args *args); int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_destroy( @@ -151,8 +105,6 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routi void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_apply_finish( struct nb_cb_apply_finish_args *args); -void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish( - struct nb_cb_apply_finish_args *args); void routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_srv6_local_sids_sid_apply_finish( struct nb_cb_apply_finish_args *args); @@ -169,16 +121,16 @@ int routing_control_plane_protocols_name_validate( /* xpath macros */ /* route-list */ -#define FRR_STATIC_ROUTE_INFO_KEY_XPATH \ - "/frr-routing:routing/control-plane-protocols/" \ - "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ - "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ +#define FRR_STATIC_ROUTE_INFO_KEY_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ + "frr-staticd:staticd/route-list[prefix='%s'][src-prefix='%s'][afi-safi='%s']/" \ "path-list[table-id='%u'][distance='%u']" -#define FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH \ - "/frr-routing:routing/control-plane-protocols/" \ - "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ - "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ +#define FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH \ + "/frr-routing:routing/control-plane-protocols/" \ + "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ + "frr-staticd:staticd/route-list[prefix='%s'][src-prefix='%s'][afi-safi='%s']/" \ "path-list[table-id='%u']" @@ -203,19 +155,6 @@ int routing_control_plane_protocols_name_validate( #define FRR_STATIC_ROUTE_NH_SRV6_KEY_SEG_XPATH "/entry[id='%u']/seg" -/* route-list/srclist */ -#define FRR_S_ROUTE_SRC_INFO_KEY_XPATH \ - "/frr-routing:routing/control-plane-protocols/" \ - "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ - "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ - "src-list[src-prefix='%s']/path-list[table-id='%u'][distance='%u']" - -#define FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \ - "/frr-routing:routing/control-plane-protocols/" \ - "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ - "frr-staticd:staticd/route-list[prefix='%s'][afi-safi='%s']/" \ - "src-list[src-prefix='%s']/path-list[table-id='%u']" - /* route-list/frr-nexthops */ #define FRR_DEL_S_ROUTE_NH_KEY_XPATH \ FRR_STATIC_ROUTE_INFO_KEY_XPATH \ @@ -226,16 +165,6 @@ int routing_control_plane_protocols_name_validate( FRR_STATIC_ROUTE_INFO_KEY_NO_DISTANCE_XPATH \ FRR_STATIC_ROUTE_NH_KEY_XPATH -/* route-list/src/src-list/frr-nexthops*/ -#define FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH \ - FRR_S_ROUTE_SRC_INFO_KEY_XPATH \ - FRR_STATIC_ROUTE_NH_KEY_XPATH - -/* route-list/src/src-list/frr-nexthops*/ -#define FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH \ - FRR_S_ROUTE_SRC_INFO_KEY_NO_DISTANCE_XPATH \ - FRR_STATIC_ROUTE_NH_KEY_XPATH - /* srv6 */ #define FRR_STATIC_SRV6_INFO_KEY_XPATH \ "/frr-routing:routing/control-plane-protocols/" \ diff --git a/staticd/static_nb_config.c b/staticd/static_nb_config.c index 51de05c2ea..e2ab1f2ffe 100644 --- a/staticd/static_nb_config.c +++ b/staticd/static_nb_config.c @@ -502,16 +502,6 @@ void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_p static_install_nexthop(nh); } -void routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_apply_finish( - struct nb_cb_apply_finish_args *args) -{ - struct static_nexthop *nh; - - nh = nb_running_get_entry(args->dnode, NULL, true); - - static_install_nexthop(nh); -} - int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_pre_validate( struct nb_cb_pre_validate_args *args) { @@ -576,7 +566,7 @@ int routing_control_plane_protocols_staticd_destroy( if (!stable) continue; - for (rn = route_top(stable); rn; rn = route_next(rn)) + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) static_del_route(rn); } @@ -595,7 +585,7 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr struct static_vrf *svrf; struct route_node *rn; const struct lyd_node *vrf_dnode; - struct prefix prefix; + struct prefix prefix, src_prefix, *src_p; const char *afi_safi; afi_t prefix_afi; afi_t afi; @@ -604,6 +594,8 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr switch (args->event) { case NB_EV_VALIDATE: yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); + yang_dnode_get_prefix(&src_prefix, args->dnode, "src-prefix"); + src_p = src_prefix.prefixlen ? &src_prefix : NULL; afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); yang_afi_safi_identity2value(afi_safi, &afi, &safi); prefix_afi = family2afi(prefix.family); @@ -614,6 +606,14 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr yang_dnode_get_string(args->dnode, "prefix")); return NB_ERR_VALIDATION; } + + if (src_p && afi != AFI_IP6) { + flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, + "invalid use of IPv6 dst-src prefix %s on %s", + yang_dnode_get_string(args->dnode, "src-prefix"), + yang_dnode_get_string(args->dnode, "prefix")); + return NB_ERR_VALIDATION; + } break; case NB_EV_PREPARE: case NB_EV_ABORT: @@ -624,10 +624,12 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_cr svrf = nb_running_get_entry(vrf_dnode, NULL, true); yang_dnode_get_prefix(&prefix, args->dnode, "prefix"); + yang_dnode_get_prefix(&src_prefix, args->dnode, "src-prefix"); + src_p = src_prefix.prefixlen ? &src_prefix : NULL; afi_safi = yang_dnode_get_string(args->dnode, "afi-safi"); yang_afi_safi_identity2value(afi_safi, &afi, &safi); - rn = static_add_route(afi, safi, &prefix, NULL, svrf); + rn = static_add_route(afi, safi, &prefix, (struct prefix_ipv6 *)src_p, svrf); if (!svrf->vrf || svrf->vrf->vrf_id == VRF_UNKNOWN) snprintf( args->errmsg, args->errmsg_len, @@ -1048,331 +1050,6 @@ int route_next_hop_bfd_profile_destroy(struct nb_cb_destroy_args *args) /* * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_create( - struct nb_cb_create_args *args) -{ - struct static_vrf *s_vrf; - struct route_node *rn; - struct route_node *src_rn; - struct prefix_ipv6 src_prefix = {}; - struct stable_info *info; - afi_t afi; - safi_t safi = SAFI_UNICAST; - - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - rn = nb_running_get_entry(args->dnode, NULL, true); - info = route_table_get_info(rn->table); - s_vrf = info->svrf; - yang_dnode_get_ipv6p(&src_prefix, args->dnode, "src-prefix"); - afi = family2afi(src_prefix.family); - src_rn = - static_add_route(afi, safi, &rn->p, &src_prefix, s_vrf); - nb_running_set_entry(args->dnode, src_rn); - break; - } - return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_destroy( - struct nb_cb_destroy_args *args) -{ - struct route_node *src_rn; - - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - src_rn = nb_running_unset_entry(args->dnode); - static_del_route(src_rn); - break; - } - - return NB_OK; -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_create( - struct nb_cb_create_args *args) -{ - return static_path_list_create(args); -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_destroy( - struct nb_cb_destroy_args *args) -{ - return static_path_list_destroy(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/tag - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_tag_modify( - struct nb_cb_modify_args *args) -{ - return static_path_list_tag_modify(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_create( - struct nb_cb_create_args *args) -{ - return static_nexthop_create(args); -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_destroy( - struct nb_cb_destroy_args *args) -{ - return static_nexthop_destroy(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/bh-type - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_bh_type_modify( - struct nb_cb_modify_args *args) -{ - return static_nexthop_bh_type_modify(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/onlink - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_onlink_modify( - struct nb_cb_modify_args *args) -{ - return static_nexthop_onlink_modify(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srte-color - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_modify( - struct nb_cb_modify_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - if (static_nexthop_color_modify(args) != NB_OK) - return NB_ERR; - - break; - } - return NB_OK; -} - - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( - struct nb_cb_destroy_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - if (static_nexthop_color_destroy(args) != NB_OK) - return NB_ERR; - break; - } - return NB_OK; -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( - struct nb_cb_create_args *args) -{ - return nexthop_srv6_segs_stack_entry_create(args); -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( - struct nb_cb_destroy_args *args) -{ - return nexthop_srv6_segs_stack_entry_destroy(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( - struct nb_cb_modify_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - if (static_nexthop_srv6_segs_modify(args) != NB_OK) - return NB_ERR; - break; - } - return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( - struct nb_cb_destroy_args *args) -{ - /* - * No operation is required in this call back. - * nexthop_mpls_seg_stack_entry_destroy() will take care - * to reset the seg vaue. - */ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } - return NB_OK; -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( - struct nb_cb_create_args *args) -{ - return nexthop_mpls_label_stack_entry_create(args); -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( - struct nb_cb_destroy_args *args) -{ - return nexthop_mpls_label_stack_entry_destroy(args); -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/label - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_modify( - struct nb_cb_modify_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - break; - case NB_EV_APPLY: - if (static_nexthop_mpls_label_modify(args) != NB_OK) - return NB_ERR; - break; - } - return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_label_destroy( - struct nb_cb_destroy_args *args) -{ - /* - * No operation is required in this call back. - * nexthop_mpls_label_stack_entry_destroy() will take care - * to reset the label vaue. - */ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } - return NB_OK; -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/ttl - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_modify( - struct nb_cb_modify_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } - - return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_ttl_destroy( - struct nb_cb_destroy_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } - - return NB_OK; -} - -/* - * XPath: - * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry/traffic-class - */ -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_modify( - struct nb_cb_modify_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } - - return NB_OK; -} - -int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_traffic_class_destroy( - struct nb_cb_destroy_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - break; - } - - return NB_OK; -} - -/* - * XPath: * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing */ int routing_control_plane_protocols_control_plane_protocol_staticd_segment_routing_create( diff --git a/staticd/static_nht.c b/staticd/static_nht.c index 06d27c6f59..367ee85040 100644 --- a/staticd/static_nht.c +++ b/staticd/static_nht.c @@ -49,8 +49,8 @@ static void static_nht_update_path(struct static_path *pn, struct prefix *nhp, static_zebra_route_add(pn, true); } -static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, - uint32_t nh_num, afi_t afi, safi_t safi, +static void static_nht_update_safi(const struct prefix *sp, const struct prefix *ssrc_p, + struct prefix *nhp, uint32_t nh_num, afi_t afi, safi_t safi, struct static_vrf *svrf, vrf_id_t nh_vrf_id) { struct route_table *stable; @@ -63,7 +63,7 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, return; if (sp) { - rn = srcdest_rnode_lookup(stable, sp, NULL); + rn = srcdest_rnode_lookup(stable, sp, (const struct prefix_ipv6 *)ssrc_p); if (rn && rn->info) { si = static_route_info_from_rnode(rn); frr_each(static_path_list, &si->path_list, pn) { @@ -75,7 +75,7 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, return; } - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; @@ -85,14 +85,13 @@ static void static_nht_update_safi(struct prefix *sp, struct prefix *nhp, } } -void static_nht_update(struct prefix *sp, struct prefix *nhp, uint32_t nh_num, - afi_t afi, safi_t safi, vrf_id_t nh_vrf_id) +void static_nht_update(const struct prefix *sp, const struct prefix *ssrc_p, struct prefix *nhp, + uint32_t nh_num, afi_t afi, safi_t safi, vrf_id_t nh_vrf_id) { struct static_vrf *svrf; RB_FOREACH (svrf, svrf_name_head, &svrfs) - static_nht_update_safi(sp, nhp, nh_num, afi, safi, svrf, - nh_vrf_id); + static_nht_update_safi(sp, ssrc_p, nhp, nh_num, afi, safi, svrf, nh_vrf_id); } static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi, @@ -109,7 +108,7 @@ static void static_nht_reset_start_safi(struct prefix *nhp, afi_t afi, if (!stable) return; - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; @@ -150,8 +149,8 @@ void static_nht_reset_start(struct prefix *nhp, afi_t afi, safi_t safi, static_nht_reset_start_safi(nhp, afi, safi, svrf, nh_vrf_id); } -static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi, - safi_t safi, struct vrf *vrf, +static void static_nht_mark_state_safi(const struct prefix *sp, const struct prefix *ssrc_p, + afi_t afi, safi_t safi, struct vrf *vrf, enum static_install_states state) { struct static_vrf *svrf; @@ -169,7 +168,7 @@ static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi, if (!stable) return; - rn = srcdest_rnode_lookup(stable, sp, NULL); + rn = srcdest_rnode_lookup(stable, sp, (const struct prefix_ipv6 *)ssrc_p); if (!rn) return; si = rn->info; @@ -184,8 +183,8 @@ static void static_nht_mark_state_safi(struct prefix *sp, afi_t afi, route_unlock_node(rn); } -void static_nht_mark_state(struct prefix *sp, safi_t safi, vrf_id_t vrf_id, - enum static_install_states state) +void static_nht_mark_state(const struct prefix *sp, const struct prefix *ssrc_p, safi_t safi, + vrf_id_t vrf_id, enum static_install_states state) { struct vrf *vrf; @@ -198,5 +197,5 @@ void static_nht_mark_state(struct prefix *sp, safi_t safi, vrf_id_t vrf_id, if (!vrf || !vrf->info) return; - static_nht_mark_state_safi(sp, afi, safi, vrf, state); + static_nht_mark_state_safi(sp, ssrc_p, afi, safi, vrf, state); } diff --git a/staticd/static_nht.h b/staticd/static_nht.h index 74f4401e49..41ff30cd52 100644 --- a/staticd/static_nht.h +++ b/staticd/static_nht.h @@ -16,15 +16,14 @@ extern "C" { * us call this function to find the nexthop we are tracking so it * can be installed or removed. * - * sp -> The route we are looking at. If NULL then look at all - * routes. + * sp + ssrc_p -> The route we are looking at. If NULL then look at all routes. * nhp -> The nexthop that is being tracked. * nh_num -> number of valid nexthops. * afi -> The afi we are working in. * vrf_id -> The vrf the nexthop is in. */ -extern void static_nht_update(struct prefix *sp, struct prefix *nhp, - uint32_t nh_num, afi_t afi, safi_t safi, +extern void static_nht_update(const struct prefix *sp, const struct prefix *ssrc_p, + struct prefix *nhp, uint32_t nh_num, afi_t afi, safi_t safi, vrf_id_t vrf_id); /* @@ -35,11 +34,10 @@ extern void static_nht_reset_start(struct prefix *nhp, afi_t afi, safi_t safi, vrf_id_t nh_vrf_id); /* - * For the given prefix, sp, mark it as in a particular state + * For the given prefix, sp + ssrc_p, mark it as in a particular state */ -extern void static_nht_mark_state(struct prefix *sp, safi_t safi, - vrf_id_t vrf_id, - enum static_install_states state); +extern void static_nht_mark_state(const struct prefix *sp, const struct prefix *ssrc_p, safi_t safi, + vrf_id_t vrf_id, enum static_install_states state); /* * For the given nexthop, returns the string diff --git a/staticd/static_routes.c b/staticd/static_routes.c index cba38183bb..cbe1c3c8c0 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -33,10 +33,6 @@ void zebra_stable_node_cleanup(struct route_table *table, struct static_nexthop *nh; struct static_path *pn; struct static_route_info *si; - struct route_table *src_table; - struct route_node *src_node; - struct static_path *src_pn; - struct static_route_info *src_si; si = node->info; @@ -50,36 +46,6 @@ void zebra_stable_node_cleanup(struct route_table *table, static_path_list_del(&si->path_list, pn); XFREE(MTYPE_STATIC_PATH, pn); } - - /* clean up for dst table */ - src_table = srcdest_srcnode_table(node); - if (src_table) { - /* This means the route_node is part of the top - * hierarchy and refers to a destination prefix. - */ - for (src_node = route_top(src_table); src_node; - src_node = route_next(src_node)) { - src_si = src_node->info; - - frr_each_safe(static_path_list, - &src_si->path_list, src_pn) { - frr_each_safe(static_nexthop_list, - &src_pn->nexthop_list, - nh) { - static_nexthop_list_del( - &src_pn->nexthop_list, - nh); - XFREE(MTYPE_STATIC_NEXTHOP, nh); - } - static_path_list_del(&src_si->path_list, - src_pn); - XFREE(MTYPE_STATIC_PATH, src_pn); - } - - XFREE(MTYPE_STATIC_ROUTE, src_node->info); - } - } - XFREE(MTYPE_STATIC_ROUTE, node->info); } } @@ -124,28 +90,10 @@ struct route_node *static_add_route(afi_t afi, safi_t safi, struct prefix *p, return rn; } -/* To delete the srcnodes */ -static void static_del_src_route(struct route_node *rn) -{ - struct static_path *pn; - struct static_route_info *si; - - si = rn->info; - - frr_each_safe(static_path_list, &si->path_list, pn) { - static_del_path(pn); - } - - XFREE(MTYPE_STATIC_ROUTE, rn->info); - route_unlock_node(rn); -} - void static_del_route(struct route_node *rn) { struct static_path *pn; struct static_route_info *si; - struct route_table *src_table; - struct route_node *src_node; si = rn->info; @@ -153,17 +101,6 @@ void static_del_route(struct route_node *rn) static_del_path(pn); } - /* clean up for dst table */ - src_table = srcdest_srcnode_table(rn); - if (src_table) { - /* This means the route_node is part of the top hierarchy - * and refers to a destination prefix. - */ - for (src_node = route_top(src_table); src_node; - src_node = route_next(src_node)) { - static_del_src_route(src_node); - } - } XFREE(MTYPE_STATIC_ROUTE, rn->info); route_unlock_node(rn); } @@ -477,7 +414,7 @@ static void static_fixup_vrf(struct vrf *vrf, struct route_table *stable, struct static_path *pn; struct static_route_info *si; - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; @@ -517,7 +454,7 @@ static void static_enable_vrf(struct route_table *stable, afi_t afi, safi_t safi struct static_path *pn; struct static_route_info *si; - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; @@ -575,7 +512,7 @@ static void static_cleanup_vrf(struct vrf *vrf, struct route_table *stable, struct static_path *pn; struct static_route_info *si; - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; @@ -608,7 +545,7 @@ static void static_disable_vrf(struct route_table *stable, struct static_path *pn; struct static_route_info *si; - for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; diff --git a/staticd/static_vrf.c b/staticd/static_vrf.c index 710827a9ff..78bc30500b 100644 --- a/staticd/static_vrf.c +++ b/staticd/static_vrf.c @@ -51,10 +51,8 @@ struct static_vrf *static_vrf_alloc(const char *name) for (afi = AFI_IP; afi <= AFI_IP6; afi++) { for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { - if (afi == AFI_IP6) - table = srcdest_table_init(); - else - table = route_table_init(); + table = srcdest_table_init(); + table->cleanup = zebra_stable_node_cleanup; info = XCALLOC(MTYPE_STATIC_RTABLE_INFO, sizeof(struct stable_info)); @@ -63,7 +61,6 @@ struct static_vrf *static_vrf_alloc(const char *name) info->safi = safi; route_table_set_info(table, info); - table->cleanup = zebra_stable_node_cleanup; svrf->stable[afi][safi] = table; } } diff --git a/staticd/static_vty.c b/staticd/static_vty.c index 2fadc1f0d4..ed2805d3ea 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -79,7 +79,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) char xpath_seg[XPATH_MAXLEN]; char ab_xpath[XPATH_MAXLEN]; char buf_prefix[PREFIX_STRLEN]; - char buf_src_prefix[PREFIX_STRLEN] = {}; + char buf_src_prefix[PREFIX_STRLEN] = "::/0"; char buf_nh_type[PREFIX_STRLEN] = {}; char buf_tag[PREFIX_STRLEN]; uint8_t label_stack_id = 0; @@ -116,6 +116,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) } assert(!!str2prefix(args->prefix, &p)); + src = (struct prefix){ .family = p.family, .prefixlen = 0 }; switch (args->afi) { case AFI_IP: @@ -146,7 +147,7 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) return CMD_WARNING_CONFIG_FAILED; } - if (args->source) + if (src.prefixlen) prefix2str(&src, buf_src_prefix, sizeof(buf_src_prefix)); if (args->gateway) buf_gate_str = args->gateway; @@ -183,25 +184,10 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) static_get_nh_type(type, buf_nh_type, sizeof(buf_nh_type)); if (!args->delete) { - if (args->source) - snprintf(ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, - "frr-staticd:staticd", "staticd", args->vrf, - buf_prefix, - yang_afi_safi_value2identity(args->afi, - args->safi), - buf_src_prefix, table_id, buf_nh_type, - args->nexthop_vrf, buf_gate_str, - args->interface_name); - else - snprintf(ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, - "frr-staticd:staticd", "staticd", args->vrf, - buf_prefix, - yang_afi_safi_value2identity(args->afi, - args->safi), - table_id, buf_nh_type, args->nexthop_vrf, - buf_gate_str, args->interface_name); + snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, + "frr-staticd:staticd", "staticd", args->vrf, buf_prefix, buf_src_prefix, + yang_afi_safi_value2identity(args->afi, args->safi), table_id, buf_nh_type, + args->nexthop_vrf, buf_gate_str, args->interface_name); /* * If there's already the same nexthop but with a different @@ -218,22 +204,9 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) } /* route + path procesing */ - if (args->source) - snprintf(xpath_prefix, sizeof(xpath_prefix), - FRR_S_ROUTE_SRC_INFO_KEY_XPATH, - "frr-staticd:staticd", "staticd", args->vrf, - buf_prefix, - yang_afi_safi_value2identity(args->afi, - args->safi), - buf_src_prefix, table_id, distance); - else - snprintf(xpath_prefix, sizeof(xpath_prefix), - FRR_STATIC_ROUTE_INFO_KEY_XPATH, - "frr-staticd:staticd", "staticd", args->vrf, - buf_prefix, - yang_afi_safi_value2identity(args->afi, - args->safi), - table_id, distance); + snprintf(xpath_prefix, sizeof(xpath_prefix), FRR_STATIC_ROUTE_INFO_KEY_XPATH, + "frr-staticd:staticd", "staticd", args->vrf, buf_prefix, buf_src_prefix, + yang_afi_safi_value2identity(args->afi, args->safi), table_id, distance); nb_cli_enqueue_change(vty, xpath_prefix, NB_OP_CREATE, NULL); @@ -412,51 +385,18 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) if (orig_seg) XFREE(MTYPE_TMP, orig_seg); } else { - if (args->source) { - if (args->distance) - snprintf(ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH, - "frr-staticd:staticd", "staticd", - args->vrf, buf_prefix, - yang_afi_safi_value2identity( - args->afi, args->safi), - buf_src_prefix, table_id, distance, - buf_nh_type, args->nexthop_vrf, - buf_gate_str, args->interface_name); - else - snprintf( - ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, - "frr-staticd:staticd", "staticd", - args->vrf, buf_prefix, - yang_afi_safi_value2identity( - args->afi, args->safi), - buf_src_prefix, table_id, buf_nh_type, - args->nexthop_vrf, buf_gate_str, - args->interface_name); - } else { - if (args->distance) - snprintf(ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_NH_KEY_XPATH, - "frr-staticd:staticd", "staticd", - args->vrf, buf_prefix, - yang_afi_safi_value2identity( - args->afi, args->safi), - table_id, distance, buf_nh_type, - args->nexthop_vrf, buf_gate_str, - args->interface_name); - else - snprintf( - ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, - "frr-staticd:staticd", "staticd", - args->vrf, buf_prefix, - yang_afi_safi_value2identity( - args->afi, args->safi), - table_id, buf_nh_type, - args->nexthop_vrf, buf_gate_str, - args->interface_name); - } + if (args->distance) + snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_NH_KEY_XPATH, + "frr-staticd:staticd", "staticd", args->vrf, buf_prefix, + buf_src_prefix, yang_afi_safi_value2identity(args->afi, args->safi), + table_id, distance, buf_nh_type, args->nexthop_vrf, buf_gate_str, + args->interface_name); + else + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, "frr-staticd:staticd", + "staticd", args->vrf, buf_prefix, buf_src_prefix, + yang_afi_safi_value2identity(args->afi, args->safi), table_id, + buf_nh_type, args->nexthop_vrf, buf_gate_str, args->interface_name); dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath); if (!dnode) { @@ -1439,9 +1379,8 @@ static int srv6_seg_iter_cb(const struct lyd_node *dnode, void *arg) } static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, - const struct lyd_node *src, - const struct lyd_node *path, - const struct lyd_node *nexthop, bool show_defaults) + const struct lyd_node *path, const struct lyd_node *nexthop, + bool show_defaults) { const char *vrf; const char *afi_safi; @@ -1455,6 +1394,7 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, struct srv6_seg_iter seg_iter; const char *nexthop_vrf; uint32_t table_id; + struct prefix src_prefix; bool onlink; vrf = yang_dnode_get_string(route, "../../vrf"); @@ -1476,9 +1416,9 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, vty_out(vty, " %s", yang_dnode_get_string(route, "prefix")); - if (src) - vty_out(vty, " from %s", - yang_dnode_get_string(src, "src-prefix")); + yang_dnode_get_prefix(&src_prefix, route, "src-prefix"); + if (src_prefix.prefixlen) + vty_out(vty, " from %pFX", &src_prefix); nh_type = yang_dnode_get_enum(nexthop, "nh-type"); switch (nh_type) { @@ -1582,18 +1522,7 @@ static void static_nexthop_cli_show(struct vty *vty, const struct lyd_node *route = yang_dnode_get_parent(path, "route-list"); - nexthop_cli_show(vty, route, NULL, path, dnode, show_defaults); -} - -static void static_src_nexthop_cli_show(struct vty *vty, - const struct lyd_node *dnode, - bool show_defaults) -{ - const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list"); - const struct lyd_node *src = yang_dnode_get_parent(path, "src-list"); - const struct lyd_node *route = yang_dnode_get_parent(src, "route-list"); - - nexthop_cli_show(vty, route, src, path, dnode, show_defaults); + nexthop_cli_show(vty, route, path, dnode, show_defaults); } static int static_nexthop_cli_cmp(const struct lyd_node *dnode1, @@ -1658,6 +1587,8 @@ static int static_route_list_cli_cmp(const struct lyd_node *dnode1, afi_t afi1, afi2; safi_t safi1, safi2; struct prefix prefix1, prefix2; + struct prefix src_prefix1, src_prefix2; + int rv; afi_safi1 = yang_dnode_get_string(dnode1, "afi-safi"); yang_afi_safi_identity2value(afi_safi1, &afi1, &safi1); @@ -1673,19 +1604,13 @@ static int static_route_list_cli_cmp(const struct lyd_node *dnode1, yang_dnode_get_prefix(&prefix1, dnode1, "prefix"); yang_dnode_get_prefix(&prefix2, dnode2, "prefix"); + rv = prefix_cmp(&prefix1, &prefix2); + if (rv) + return rv; - return prefix_cmp(&prefix1, &prefix2); -} - -static int static_src_list_cli_cmp(const struct lyd_node *dnode1, - const struct lyd_node *dnode2) -{ - struct prefix prefix1, prefix2; - - yang_dnode_get_prefix(&prefix1, dnode1, "src-prefix"); - yang_dnode_get_prefix(&prefix2, dnode2, "src-prefix"); - - return prefix_cmp(&prefix1, &prefix2); + yang_dnode_get_prefix(&src_prefix1, dnode1, "src-prefix"); + yang_dnode_get_prefix(&src_prefix2, dnode2, "src-prefix"); + return prefix_cmp(&src_prefix1, &src_prefix2); } static int static_path_list_cli_cmp(const struct lyd_node *dnode1, @@ -1831,25 +1756,6 @@ const struct frr_yang_module_info frr_staticd_cli_info = { } }, { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list", - .cbs = { - .cli_cmp = static_src_list_cli_cmp, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list", - .cbs = { - .cli_cmp = static_path_list_cli_cmp, - } - }, - { - .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop", - .cbs = { - .cli_show = static_src_nexthop_cli_show, - .cli_cmp = static_nexthop_cli_cmp, - } - }, - { .xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/segment-routing", .cbs = { .cli_show = static_segment_routing_cli_show, diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index e87eaed008..057193aa08 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -132,35 +132,37 @@ static int static_ifp_down(struct interface *ifp) static int route_notify_owner(ZAPI_CALLBACK_ARGS) { - struct prefix p; + struct prefix p, src_p, *src_pp; enum zapi_route_notify_owner note; uint32_t table_id; safi_t safi; - if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, ¬e, NULL, - &safi)) + if (!zapi_route_notify_decode_srcdest(zclient->ibuf, &p, &src_p, &table_id, ¬e, NULL, + &safi)) return -1; + src_pp = src_p.prefixlen ? &src_p : NULL; + switch (note) { case ZAPI_ROUTE_FAIL_INSTALL: - static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED); + static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_NOT_INSTALLED); zlog_warn("%s: Route %pFX failed to install for table: %u", __func__, &p, table_id); break; case ZAPI_ROUTE_BETTER_ADMIN_WON: - static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED); + static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_NOT_INSTALLED); zlog_warn( "%s: Route %pFX over-ridden by better route for table: %u", __func__, &p, table_id); break; case ZAPI_ROUTE_INSTALLED: - static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED); + static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_INSTALLED); break; case ZAPI_ROUTE_REMOVED: - static_nht_mark_state(&p, safi, vrf_id, STATIC_NOT_INSTALLED); + static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_NOT_INSTALLED); break; case ZAPI_ROUTE_REMOVE_FAIL: - static_nht_mark_state(&p, safi, vrf_id, STATIC_INSTALLED); + static_nht_mark_state(&p, src_pp, safi, vrf_id, STATIC_INSTALLED); zlog_warn("%s: Route %pFX failure to remove for table: %u", __func__, &p, table_id); break; @@ -226,8 +228,8 @@ static void static_zebra_nexthop_update(struct vrf *vrf, struct prefix *matched, nhtd->nh_num = nhr->nexthop_num; static_nht_reset_start(matched, afi, nhr->safi, nhtd->nh_vrf_id); - static_nht_update(NULL, matched, nhr->nexthop_num, afi, - nhr->safi, nhtd->nh_vrf_id); + static_nht_update(NULL, NULL, matched, nhr->nexthop_num, afi, nhr->safi, + nhtd->nh_vrf_id); } else zlog_err("No nhtd?"); } @@ -312,10 +314,13 @@ void static_zebra_nht_register(struct static_nexthop *nh, bool reg) { struct static_path *pn = nh->pn; struct route_node *rn = pn->rn; + const struct prefix *p, *src_p; struct static_route_info *si = static_route_info_from_rnode(rn); struct static_nht_data *nhtd, lookup = {}; uint32_t cmd; + srcdest_rnode_prefixes(rn, &p, &src_p); + if (!static_zebra_nht_get_prefix(nh, &lookup.nh)) return; lookup.nh_vrf_id = nh->nh_vrf_id; @@ -351,8 +356,8 @@ void static_zebra_nht_register(struct static_nexthop *nh, bool reg) if (nh->state == STATIC_NOT_INSTALLED || nh->state == STATIC_SENT_TO_ZEBRA) nh->state = STATIC_START; - static_nht_update(&rn->p, &nhtd->nh, nhtd->nh_num, afi, - si->safi, nh->nh_vrf_id); + static_nht_update(p, src_p, &nhtd->nh, nhtd->nh_num, afi, si->safi, + nh->nh_vrf_id); return; } diff --git a/tests/topotests/bgp_table_direct_topo1/__init__.py b/tests/topotests/bgp_table_direct_topo1/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/__init__.py diff --git a/tests/topotests/bgp_table_direct_topo1/r1/frr.conf b/tests/topotests/bgp_table_direct_topo1/r1/frr.conf new file mode 100644 index 0000000000..c45e3456a4 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/r1/frr.conf @@ -0,0 +1,31 @@ +log commands +! +debug bgp zebra +debug zebra events +! +ip route 10.254.254.1/32 lo table 2000 +ip route 10.254.254.2/32 lo table 2000 +ip route 10.254.254.3/32 lo table 2000 +! +interface r1-eth0 + ip address 192.168.10.1/24 +! +interface r1-eth1 vrf blue + ip address 192.168.20.1/24 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.10.2 remote-as external + address-family ipv4 unicast + redistribute table-direct 2000 + exit-address-family +! +router bgp 65001 vrf blue + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.20.2 remote-as external + address-family ipv4 unicast + redistribute table-direct 2000 + exit-address-family +!
\ No newline at end of file diff --git a/tests/topotests/bgp_table_direct_topo1/r2/frr.conf b/tests/topotests/bgp_table_direct_topo1/r2/frr.conf new file mode 100644 index 0000000000..04787be0b3 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/r2/frr.conf @@ -0,0 +1,10 @@ +log commands +! +interface r2-eth0 + ip address 192.168.10.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.10.1 remote-as external +!
\ No newline at end of file diff --git a/tests/topotests/bgp_table_direct_topo1/r3/frr.conf b/tests/topotests/bgp_table_direct_topo1/r3/frr.conf new file mode 100644 index 0000000000..2530b28bfd --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/r3/frr.conf @@ -0,0 +1,10 @@ +log commands +! +interface r3-eth0 + ip address 192.168.20.2/24 +! +router bgp 65003 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.20.1 remote-as external +!
\ No newline at end of file diff --git a/tests/topotests/bgp_table_direct_topo1/test_bgp_table_direct_topo1.py b/tests/topotests/bgp_table_direct_topo1/test_bgp_table_direct_topo1.py new file mode 100644 index 0000000000..70257be3e7 --- /dev/null +++ b/tests/topotests/bgp_table_direct_topo1/test_bgp_table_direct_topo1.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_table_direct_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2025 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bgp_table_direct_topo1.py: Test the FRR PIM MSDP peer. +""" + +import os +import sys +import json +from functools import partial +import re +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest + +# Required to instantiate the topology builder class. +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +from lib.pim import McastTesterHelper + +pytestmark = [pytest.mark.bgpd, pytest.mark.pimd] + +app_helper = McastTesterHelper() + + +def build_topo(tgen): + """ + +----+ +----+ + | r1 | <-> | r2 | + +----+ +----+ + | + | +----+ + --------| r3 | + +----+ + """ + + # Create 3 routers + for routern in range(1, 4): + tgen.add_router(f"r{routern}") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for _, router in router_list.items(): + router.load_frr_config(os.path.join(CWD, f"{router.name}/frr.conf")) + + tgen.gears["r1"].run("ip link add blue type vrf table 10") + tgen.gears["r1"].run("ip link set blue up") + tgen.gears["r1"].run("ip link set r1-eth1 master blue") + + # Initialize all routers. + tgen.start_router() + + app_helper.init(tgen) + + +def teardown_module(): + "Teardown the pytest environment" + tgen = get_topogen() + app_helper.cleanup() + tgen.stop_topology() + + +def expect_bgp_route(router, iptype, route, missing=False): + "Wait until route is present on RIB for protocol." + if missing: + logger.info("waiting route {} go missing in {}".format(route, router)) + else: + logger.info("waiting route {} in {}".format(route, router)) + + tgen = get_topogen() + expected_output = {route: [{"protocol": "bgp"}]} + wait_time = 130 + if missing: + expected_output = {route: None} + wait_time = 5 + + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + "show {} route json".format(iptype), + expected_output + ) + + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = f'"{router}" convergence failure' + assert result is None, assertmsg + + +def test_bgp_convergence(): + "Wait for BGP protocol convergence" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + # Wait for R2 + expect_bgp_route("r2", "ip", "10.254.254.1/32") + expect_bgp_route("r2", "ip", "10.254.254.2/32") + expect_bgp_route("r2", "ip", "10.254.254.3/32") + + # Wait for R3 + expect_bgp_route("r3", "ip", "10.254.254.1/32") + expect_bgp_route("r3", "ip", "10.254.254.2/32") + expect_bgp_route("r3", "ip", "10.254.254.3/32") + + +def test_route_change_convergence(): + "Change routes in table 2000 to test zebra redistribution." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r1"].vtysh_cmd(""" + configure terminal + no ip route 10.254.254.2/32 lo table 2000 + ip route 10.254.254.10/32 lo table 2000 + """) + + # Check R2 + expect_bgp_route("r2", "ip", "10.254.254.2/32", missing=True) + expect_bgp_route("r2", "ip", "10.254.254.10/32") + + # Check R3 + expect_bgp_route("r3", "ip", "10.254.254.2/32", missing=True) + expect_bgp_route("r3", "ip", "10.254.254.10/32") + + +def test_configuration_removal_convergence(): + "Remove table direct configuration and check if routes went missing." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["r1"].vtysh_cmd(""" + configure terminal + router bgp 65001 + address-family ipv4 unicast + no redistribute table-direct 2000 + exit-address-family + exit + + router bgp 65001 vrf blue + address-family ipv4 unicast + no redistribute table-direct 2000 + exit-address-family + exit + """) + + # Check R2 + expect_bgp_route("r2", "ip", "10.254.254.1/32", missing=True) + expect_bgp_route("r2", "ip", "10.254.254.3/32", missing=True) + expect_bgp_route("r2", "ip", "10.254.254.10/32", missing=True) + + # Check R3 + expect_bgp_route("r3", "ip", "10.254.254.1/32", missing=True) + expect_bgp_route("r3", "ip", "10.254.254.3/32", missing=True) + expect_bgp_route("r3", "ip", "10.254.254.10/32", missing=True) + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_table_map/r1/frr.conf b/tests/topotests/bgp_table_map/r1/frr.conf new file mode 100644 index 0000000000..f74440c384 --- /dev/null +++ b/tests/topotests/bgp_table_map/r1/frr.conf @@ -0,0 +1,22 @@ +! +int r1-eth0 + ip address 10.255.0.1/24 +! +access-list AccList seq 5 permit 10.0.0.1/32 +! +route-map TableMap permit 10 + match ip address AccList +exit +! +router bgp 65001 + bgp router-id 10.255.0.1 + no bgp ebgp-requires-policy + neighbor 10.255.0.2 remote-as external + neighbor 10.255.0.2 timers 1 3 + neighbor 10.255.0.2 timers connect 1 + ! + address-family ipv4 unicast + table-map TableMap + exit-address-family +exit +! diff --git a/tests/topotests/bgp_table_map/r2/frr.conf b/tests/topotests/bgp_table_map/r2/frr.conf new file mode 100644 index 0000000000..4523fe49ea --- /dev/null +++ b/tests/topotests/bgp_table_map/r2/frr.conf @@ -0,0 +1,18 @@ +! +int r2-eth0 + ip address 10.255.0.2/24 +! +router bgp 65002 + bgp router-id 10.255.0.2 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 10.255.0.1 remote-as external + neighbor 10.255.0.1 timers 1 3 + neighbor 10.255.0.1 timers connect 1 + ! + address-family ipv4 unicast + network 10.0.0.1/32 + network 10.0.0.2/32 + exit-address-family +exit +! diff --git a/tests/topotests/bgp_table_map/test_bgp_table_map.py b/tests/topotests/bgp_table_map/test_bgp_table_map.py new file mode 100644 index 0000000000..b10680f741 --- /dev/null +++ b/tests/topotests/bgp_table_map/test_bgp_table_map.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +import functools, json, os, pytest, re, sys + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +from lib import topotest +from lib.topogen import Topogen, get_topogen + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config( + os.path.join(CWD, "{}/frr.conf".format(rname)) + ) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_table_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads( + r1.vtysh_cmd( "show bgp ipv4 unicast summary json") + ) + expected = { + "peers": { + "10.255.0.2": { + "remoteAs": 65002, + "state": "Established", + "peerState": "OK", + }, + }, + "totalPeers": 1, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_converge, + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Can't converge initial state" + + def _bgp_with_table_map(): + output = json.loads(r1.vtysh_cmd("show ip fib json")) + expected = { + "10.0.0.1/32": [], + "10.0.0.2/32": None, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_with_table_map, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Should contain only one of two shared networks" + + # + # Unset table-map + # + r1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + no table-map TableMap + """ + ) + + def _bgp_without_table_map(): + output = json.loads(r1.vtysh_cmd("show ip fib json")) + expected = { + "10.0.0.1/32": [], + "10.0.0.2/32": [], + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial( + _bgp_without_table_map, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Shouldn't contain both shared routes" + + # + # Reset table-map + # + r1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + table-map TableMap + """ + ) + + test_func = functools.partial( + _bgp_with_table_map, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "Should contain only one of two shared networks" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py b/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py index 9c1a23f54f..d17b4702f7 100644 --- a/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py +++ b/tests/topotests/isis_srv6_topo1/test_isis_srv6_topo1.py @@ -245,7 +245,7 @@ def check_ping6(name, dest_addr, expect_connected): if match not in output: return "ping fail" - match = "{} packet loss".format("0%" if expect_connected else "100%") + match = "{} packet loss".format(", 0%" if expect_connected else ", 100%") logger.info("[+] check {} {} {}".format(name, dest_addr, match)) tgen = get_topogen() func = functools.partial(_check, name, dest_addr, match) @@ -333,7 +333,7 @@ def test_ping_step1(): # Setup encap route on rt1, decap route on rt2 tgen.gears["rt1"].vtysh_cmd( - "sharp install seg6-routes fc00:0:9::1 nexthop-seg6 2001:db8:1::2 encap fc00:0:1:2:6:f00d:: 1" + "sharp install seg6-routes fc00:0:9::1 nexthop-seg6 2001:db8:1::2 encap fc00:0:2:6:f00d:: 1" ) tgen.gears["rt6"].vtysh_cmd( "sharp install seg6local-routes fc00:0:f00d:: nexthop-seg6local eth-dst End_DT6 254 1" @@ -443,7 +443,8 @@ def test_ping_step2(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - check_ping6("rt1", "fc00:0:9::1", False) + # ping should pass because route to fc00:0:2:6:f00d:: is still valid + check_ping6("rt1", "fc00:0:9::1", True) # @@ -643,7 +644,8 @@ def test_ping_step4(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - check_ping6("rt1", "fc00:0:9::1", False) + # ping should pass because route to fc00:0:2:6:f00d:: is still valid + check_ping6("rt1", "fc00:0:9::1", True) # @@ -838,7 +840,8 @@ def test_ping_step6(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - check_ping6("rt1", "fc00:0:9::1", False) + # ping should pass because route to fc00:0:2:6:f00d:: is still valid + check_ping6("rt1", "fc00:0:9::1", True) # @@ -1033,7 +1036,8 @@ def test_ping_step8(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) - check_ping6("rt1", "fc00:0:9::1", False) + # ping should pass because route to fc00:0:2:6:f00d:: is still valid + check_ping6("rt1", "fc00:0:9::1", True) # diff --git a/tests/topotests/mgmt_tests/test_yang_mgmt.py b/tests/topotests/mgmt_tests/test_yang_mgmt.py index 52f6ba4db7..7b74eab6b7 100644 --- a/tests/topotests/mgmt_tests/test_yang_mgmt.py +++ b/tests/topotests/mgmt_tests/test_yang_mgmt.py @@ -181,7 +181,7 @@ def test_mgmt_commit_check(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", "mgmt commit check", ] } @@ -194,7 +194,7 @@ def test_mgmt_commit_check(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", "mgmt commit check", ] } @@ -245,7 +245,7 @@ def test_mgmt_commit_apply(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", "mgmt commit apply", ] } @@ -258,7 +258,7 @@ def test_mgmt_commit_apply(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", "mgmt commit apply", ] } @@ -298,7 +298,7 @@ def test_mgmt_commit_abort(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.3/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.3/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", "mgmt commit abort", ] } @@ -350,7 +350,7 @@ def test_mgmt_delete_config(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", "mgmt commit apply", ] } @@ -381,7 +381,7 @@ def test_mgmt_delete_config(request): raw_config = { "r1": { "raw_config": [ - "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][afi-safi='frr-routing:ipv4-unicast']", + "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']", "mgmt commit apply", ] } @@ -657,7 +657,7 @@ def test_mgmt_chaos_stop_start_frr(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", "mgmt commit apply", ] } @@ -689,7 +689,7 @@ def test_mgmt_chaos_stop_start_frr(request): raw_config = { "r1": { "raw_config": [ - "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']", + "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']", "mgmt commit apply", ] } @@ -733,7 +733,7 @@ def test_mgmt_chaos_kill_daemon(request): raw_config = { "r1": { "raw_config": [ - "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][src-prefix='::/0'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", "mgmt commit apply", ] } diff --git a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py index 8d91826022..a32b82c7f4 100755 --- a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py +++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py @@ -638,12 +638,6 @@ def pre_config_for_source_dr_tests( "interfaceName": "r5-r4-eth1", "weight": 1, }, - { - "ip": "10.0.3.1", - "afi": "ipv4", - "interfaceName": "r5-r4-eth1", - "weight": 1, - }, ], } ] diff --git a/tests/topotests/ospf_prune_next_hop/r1/frr.conf b/tests/topotests/ospf_prune_next_hop/r1/frr.conf new file mode 100644 index 0000000000..130872e8d0 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r1/frr.conf @@ -0,0 +1,23 @@ +! +hostname r1 +ip forwarding +! +interface r1-eth0 + ip address 10.1.1.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r1-eth1 + ip address 10.1.2.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +! +router ospf + ospf router-id 1.1.1.1 + distance 20 + network 10.1.1.0/24 area 0 + network 10.1.2.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/r2/frr.conf b/tests/topotests/ospf_prune_next_hop/r2/frr.conf new file mode 100644 index 0000000000..4268aea857 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r2/frr.conf @@ -0,0 +1,23 @@ +! +hostname r2 +ip forwarding +! +interface r2-eth0 + ip address 10.1.1.2/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r2-eth1 + ip address 10.1.2.1/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +! +router ospf + ospf router-id 2.2.2.2 + distance 20 + network 10.1.1.0/24 area 0 + network 10.1.2.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/r3/frr.conf b/tests/topotests/ospf_prune_next_hop/r3/frr.conf new file mode 100644 index 0000000000..21d6506d7c --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r3/frr.conf @@ -0,0 +1,35 @@ +! +hostname r3 +ip forwarding +! +interface r3-eth0 + ip address 20.1.3.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r3-eth1 + ip address 10.1.3.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r3-eth2 + ip address 10.1.2.3/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +! +router ospf + ospf router-id 3.3.3.3 + distance 20 + network 10.1.2.0/24 area 0 + network 10.1.3.0/24 area 0 + network 20.1.3.0/24 area 1 + area 1 range 20.1.0.0/16 + redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r4/frr.conf b/tests/topotests/ospf_prune_next_hop/r4/frr.conf new file mode 100644 index 0000000000..e66e93e20c --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r4/frr.conf @@ -0,0 +1,34 @@ +! +hostname r4 +ip forwarding +! +interface r4-eth0 + ip address 20.1.4.4/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r4-eth1 + ip address 10.1.3.4/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r4-eth2 + ip address 10.1.2.4/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +router ospf + ospf router-id 4.4.4.4 + distance 20 + network 10.1.2.0/24 area 0 + network 10.1.3.0/24 area 0 + network 20.1.4.0/24 area 1 + area 1 range 20.1.0.0/16 + redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r5/frr.conf b/tests/topotests/ospf_prune_next_hop/r5/frr.conf new file mode 100644 index 0000000000..2d1dad9925 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r5/frr.conf @@ -0,0 +1,34 @@ +! +hostname r5 +ip forwarding +! +interface r5-eth0 + ip address 20.1.5.5/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r5-eth1 + ip address 10.1.3.5/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r5-eth2 + ip address 10.1.2.5/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +router ospf + ospf router-id 5.5.5.5 + distance 20 + network 10.1.2.0/24 area 0 + network 10.1.3.0/24 area 0 + network 20.1.5.0/24 area 1 + area 1 range 20.1.0.0/16 + redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r6/frr.conf b/tests/topotests/ospf_prune_next_hop/r6/frr.conf new file mode 100644 index 0000000000..f343ee7c35 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r6/frr.conf @@ -0,0 +1,34 @@ +! +hostname r6 +ip forwarding +! +interface r6-eth0 + ip address 20.1.6.6/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r6-eth1 + ip address 10.1.3.6/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +interface r6-eth2 + ip address 10.1.2.6/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +! +router ospf + ospf router-id 6.6.6.6 + distance 20 + network 10.1.2.0/24 area 0 + network 10.1.3.0/24 area 0 + network 20.1.6.0/24 area 1 + area 1 range 20.1.0.0/16 + redistribute static +! +! +ip route 100.100.100.100/32 Null0 diff --git a/tests/topotests/ospf_prune_next_hop/r7/frr.conf b/tests/topotests/ospf_prune_next_hop/r7/frr.conf new file mode 100644 index 0000000000..1eeb88c9d0 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r7/frr.conf @@ -0,0 +1,14 @@ +! +hostname r7 +ip forwarding +! +interface r7-eth0 + ip address 10.1.3.7/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +router ospf + ospf router-id 7.7.7.7 + distance 20 + network 10.1.3.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/r8/frr.conf b/tests/topotests/ospf_prune_next_hop/r8/frr.conf new file mode 100644 index 0000000000..d8facbc01f --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/r8/frr.conf @@ -0,0 +1,14 @@ +! +hostname r8 +ip forwarding +! +interface r8-eth0 + ip address 10.1.3.8/24 + ip ospf network broadcast + ip ospf hello-interval 1 + ip ospf dead-interval 10 +! +router ospf + ospf router-id 8.8.8.8 + distance 20 + network 10.1.3.0/24 area 0 diff --git a/tests/topotests/ospf_prune_next_hop/test_ospf_prune_next_hop.py b/tests/topotests/ospf_prune_next_hop/test_ospf_prune_next_hop.py new file mode 100644 index 0000000000..88aa6b2e36 --- /dev/null +++ b/tests/topotests/ospf_prune_next_hop/test_ospf_prune_next_hop.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_prune_next_hop +# +# Copyright (c) 2025 LabN Consulting +# Acee Lindem +# + +import os +import sys +from functools import partial +import pytest + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, get_topogen +from lib.topolog import logger + +from lib.common_config import ( + step, +) + + +""" +test_ospf_metric_propagation.py: Test OSPF/BGP metric propagation +""" + +TOPOLOGY = """ + 20.1.3.0 20.1.4.0 20.1.5.0 20.1.6.0 + eth0 | .3 eth0 | .4 eth0 | .5 eth0 | .6 + +--+-+ +--+-+ +--+-+ +--+-+ +10.1 3.0 | R3 | | R4 | | R5 | | R6 | + +-----+ | | |eth1 | |eth1 | | 10.1.3.0/24 + | | | | +---- | |--- + -+---+ + | +--+-+ +--+-+ +--+-+ +--+-+ | + | eth2 | .3 eth2 | .4 eth2 | .5 eth2 | | +eth0| | | | | | eth0 + +--+--+ ++-------+ Switch Network +---------++ +--+---+ + | R7 | | 10.1.2.0/24 | | R8 | + +-----+ +------------------------------------+ +------+ + eth1 | .2 + +--+--+ + | R2 | + +--+--+ + eth0 | .2 + 10.1.1.0/24 | + eth0 | .1 + +--+--+ + | R1 | + +-----+ + +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 8 routers + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("r4") + tgen.add_router("r5") + tgen.add_router("r6") + tgen.add_router("r7") + tgen.add_router("r8") + + # Interconect router 1, 2 (0) + switch = tgen.add_switch("s1-1-2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Add standalone networks to router 3 + switch = tgen.add_switch("s2-3") + switch.add_link(tgen.gears["r3"]) + + # Add standalone network to router 4 + switch = tgen.add_switch("s3-4") + switch.add_link(tgen.gears["r4"]) + + # Add standalone network to router 5 + switch = tgen.add_switch("s4-5") + switch.add_link(tgen.gears["r5"]) + + # Add standalone network to router 6 + switch = tgen.add_switch("s5-6") + switch.add_link(tgen.gears["r6"]) + + # Interconect routers 3, 4, 5, and 6 + switch = tgen.add_switch("s6-3") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r7"]) + switch = tgen.add_switch("s7-4") + switch.add_link(tgen.gears["r4"]) + switch = tgen.add_switch("s8-5") + switch.add_link(tgen.gears["r5"]) + switch = tgen.add_switch("s9-6") + switch.add_link(tgen.gears["r6"]) + switch.add_link(tgen.gears["r8"]) + + # Interconect routers 2, 3, 4, 5, and 6 + switch = tgen.add_switch("s10-lan") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r5"]) + switch.add_link(tgen.gears["r6"]) + + +def setup_module(mod): + logger.info("OSPF Prune Next Hops:\n {}".format(TOPOLOGY)) + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + # Starting Routers + router_list = tgen.routers() + + for rname, router in router_list.items(): + logger.info("Loading router %s" % rname) + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_intra_area_route_prune(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step("Test OSPF intra-area route 10.1.3.0/24 duplicate nexthops already pruned") + # Verify OSPF route 10.1.3.0/24 nexthops pruned already. + r1 = tgen.gears["r1"] + input_dict = { + "10.1.3.0/24": { + "routeType": "N", + "transit": True, + "cost": 30, + "area": "0.0.0.0", + "nexthops": [ + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "8.8.8.8"} + ], + } + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf route detail json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "OSPF Intra-Area route 10.1.3.0/24 mismatch on router r1" + assert result is None, assertmsg + + step("Test IP route 10.1.3.0/24 installed") + input_dict = { + "10.1.3.0/24": [ + { + "prefix": "10.1.3.0/24", + "prefixLen": 24, + "protocol": "ospf", + "vrfName": "default", + "distance": 20, + "metric": 30, + "installed": True, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "ip": "10.1.1.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": True, + "weight": 1, + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 10.1.3.0/24 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "IP route 10.1.3.0/24 mismatch on router r1" + assert result is None, assertmsg + + +def test_inter_area_route_prune(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step("Test OSPF inter-area route 20.1.0.0/16 duplicate nexthops installed") + # Verify OSPF route 20.1.0.0/16 duplication nexthops + r1 = tgen.gears["r1"] + input_dict = { + "20.1.0.0/16": { + "routeType": "N IA", + "cost": 30, + "area": "0.0.0.0", + "nexthops": [ + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "3.3.3.3"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "4.4.4.4"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "5.5.5.5"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "6.6.6.6"}, + ], + } + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf route detail json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "OSPF Inter-Area route 20.1.0.0/16 mismatch on router r1" + assert result is None, assertmsg + + step("Test IP route 10.1.3.0/24 installed with pruned next-hops") + input_dict = { + "20.1.0.0/16": [ + { + "prefix": "20.1.0.0/16", + "prefixLen": 16, + "protocol": "ospf", + "vrfName": "default", + "distance": 20, + "metric": 30, + "installed": True, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "ip": "10.1.1.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": True, + "weight": 1, + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 20.1.0.0/16 json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "IP route 20.1.1.0/24 mismatch on router r1" + assert result is None, assertmsg + + +def test_as_external_route_prune(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("Skipped because of router(s) failure") + + step("Test OSPF AS external route 100.100.100.100 duplicate nexthops installed") + # Verify OSPF route 20.1.0.0/16 duplication nexthops + r1 = tgen.gears["r1"] + input_dict = { + "100.100.100.100/32": { + "routeType": "N E2", + "cost": 20, + "type2cost": 20, + "tag": 0, + "nexthops": [ + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "3.3.3.3"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "4.4.4.4"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "5.5.5.5"}, + {"ip": "10.1.1.2", "via": "r1-eth0", "advertisedRouter": "6.6.6.6"}, + ], + } + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf route detail json", input_dict + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "OSPF AS external route 100.100.100.100/32 mismatch on router r1" + assert result is None, assertmsg + + step("Test IP route 100.100.100.100/32 installed with pruned next-hops") + input_dict = { + "100.100.100.100/32": [ + { + "prefix": "100.100.100.100/32", + "prefixLen": 32, + "protocol": "ospf", + "vrfName": "default", + "distance": 20, + "metric": 20, + "installed": True, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "ip": "10.1.1.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "active": True, + "weight": 1, + } + ], + } + ] + } + test_func = partial( + topotest.router_json_cmp, + r1, + "show ip route 100.100.100.100/32 json", + input_dict, + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "IP route 100.100.100.100/32 mismatch on router r1" + assert result is None, assertmsg + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/static_simple/test_static_simple.py b/tests/topotests/static_simple/test_static_simple.py index bb3580a1d8..afde58fbf7 100644 --- a/tests/topotests/static_simple/test_static_simple.py +++ b/tests/topotests/static_simple/test_static_simple.py @@ -61,6 +61,15 @@ def get_ip_networks(super_prefix, count): return tuple(network.subnets(count_log2))[0:count] +def get_src_networks(src_prefix, count, default=""): + if src_prefix is not None: + for net in get_ip_networks(src_prefix, count): + yield " from {}".format(net) + else: + for i in range(0, count): + yield default + + def enable_debug(router): router.vtysh_cmd("debug northbound callbacks configuration") @@ -70,7 +79,7 @@ def disable_debug(router): @retry(retry_timeout=30, initial_wait=0.1) -def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia): +def check_kernel(r1, super_prefix, src_prefix, count, add, is_blackhole, vrf, matchvia): network = ipaddress.ip_network(super_prefix) vrfstr = f" vrf {vrf}" if vrf else "" if network.version == 6: @@ -79,26 +88,30 @@ def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia): kernel = r1.run(f"ip -4 route show{vrfstr}") logger.debug("checking kernel routing table%s:\n%s", vrfstr, kernel) - for _, net in enumerate(get_ip_networks(super_prefix, count)): + for net, srcnet in zip( + get_ip_networks(super_prefix, count), get_src_networks(src_prefix, count) + ): + netfull = str(net) + srcnet if not add: - assert str(net) not in kernel + assert netfull + " nhid" not in kernel + assert netfull + " via" not in kernel continue if is_blackhole: - route = f"blackhole {str(net)} proto (static|196) metric 20" + route = f"blackhole {netfull}(?: dev lo)? proto (static|196) metric 20" else: route = ( - f"{str(net)}(?: nhid [0-9]+)? {matchvia} " - "proto (static|196) metric 20" + f"{netfull}(?: nhid [0-9]+)? {matchvia} proto (static|196) metric 20" ) assert re.search(route, kernel), f"Failed to find \n'{route}'\n in \n'{kernel}'" -def do_config( +def do_config_inner( r1, count, add=True, do_ipv6=False, + do_sadr=False, via=None, vrf=None, use_cli=False, @@ -109,11 +122,18 @@ def do_config( # # Set the route details # - - if vrf: - super_prefix = "2002::/48" if do_ipv6 else "20.0.0.0/8" + src_prefs = [None, None] + if do_ipv6 and do_sadr: + # intentionally using overlapping prefix + super_prefs = ["2001::/48", "2002::/48"] + src_prefs = ["2001:db8:1111::/48", "2001:db8:2222::/48"] + elif do_ipv6: + super_prefs = ["2001::/48", "2002::/48"] else: - super_prefix = "2001::/48" if do_ipv6 else "10.0.0.0/8" + super_prefs = ["10.0.0.0/8", "20.0.0.0/8"] + + super_prefix = super_prefs[1 if vrf else 0] + src_prefix = src_prefs[1 if vrf else 0] matchvia = "" if via == "blackhole": @@ -144,11 +164,13 @@ def do_config( if vrf: f.write("vrf {}\n".format(vrf)) - for _, net in enumerate(get_ip_networks(super_prefix, count)): + for net, srcnet in zip( + get_ip_networks(super_prefix, count), get_src_networks(src_prefix, count) + ): if add: - f.write("ip route {} {}\n".format(net, via)) + f.write("ip route {}{} {}\n".format(net, srcnet, via)) else: - f.write("no ip route {} {}\n".format(net, via)) + f.write("no ip route {}{} {}\n".format(net, srcnet, via)) # # Load config file. @@ -165,7 +187,9 @@ def do_config( # # Verify the results are in the kernel # - check_kernel(r1, super_prefix, count, add, via == "blackhole", vrf, matchvia) + check_kernel( + r1, super_prefix, src_prefix, count, add, via == "blackhole", vrf, matchvia + ) optyped = "added" if add else "removed" logger.debug( @@ -175,6 +199,12 @@ def do_config( ) +def do_config(*args, **kwargs): + do_config_inner(*args, do_ipv6=False, do_sadr=False, **kwargs) + do_config_inner(*args, do_ipv6=True, do_sadr=False, **kwargs) + do_config_inner(*args, do_ipv6=True, do_sadr=True, **kwargs) + + def guts(tgen, vrf, use_cli): if tgen.routers_have_failure(): pytest.skip(tgen.errors) @@ -183,20 +213,20 @@ def guts(tgen, vrf, use_cli): count = 10 step(f"add {count} via gateway", reset=True) - do_config(r1, count, True, False, vrf=vrf, use_cli=use_cli) + do_config(r1, count, True, vrf=vrf, use_cli=use_cli) step(f"remove {count} via gateway") - do_config(r1, count, False, False, vrf=vrf, use_cli=use_cli) + do_config(r1, count, False, vrf=vrf, use_cli=use_cli) via = f"lo-{vrf}" if vrf else "lo" step("add via loopback") - do_config(r1, 1, True, False, via=via, vrf=vrf, use_cli=use_cli) + do_config(r1, 1, True, via=via, vrf=vrf, use_cli=use_cli) step("remove via loopback") - do_config(r1, 1, False, False, via=via, vrf=vrf, use_cli=use_cli) + do_config(r1, 1, False, via=via, vrf=vrf, use_cli=use_cli) step("add via blackhole") - do_config(r1, 1, True, False, via="blackhole", vrf=vrf, use_cli=use_cli) + do_config(r1, 1, True, via="blackhole", vrf=vrf, use_cli=use_cli) step("remove via blackhole") - do_config(r1, 1, False, False, via="blackhole", vrf=vrf, use_cli=use_cli) + do_config(r1, 1, False, via="blackhole", vrf=vrf, use_cli=use_cli) def test_static_cli(tgen): diff --git a/tests/topotests/static_srv6_sids/expected_srv6_sids.json b/tests/topotests/static_srv6_sids/expected_srv6_sids.json index e1a2a16afe..5799d97988 100644 --- a/tests/topotests/static_srv6_sids/expected_srv6_sids.json +++ b/tests/topotests/static_srv6_sids/expected_srv6_sids.json @@ -1,4 +1,39 @@ { + "fcbb:bbbb:1::/48": [ + { + "prefix": "fcbb:bbbb:1::/48", + "prefixLen": 48, + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 9, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "sr0", + "active": true, + "weight": 1, + "seg6local": { + "action": "End" + }, + "seg6localContext": { + + } + } + ] + } + ], "fcbb:bbbb:1:fe10::/64": [ { "prefix": "fcbb:bbbb:1:fe10::/64", diff --git a/tests/topotests/static_srv6_sids/r1/frr.conf b/tests/topotests/static_srv6_sids/r1/frr.conf index 999e35c35b..b4904d9ac2 100644 --- a/tests/topotests/static_srv6_sids/r1/frr.conf +++ b/tests/topotests/static_srv6_sids/r1/frr.conf @@ -8,6 +8,7 @@ segment-routing ! ! static-sids + sid fcbb:bbbb:1::/48 locator MAIN behavior uN sid fcbb:bbbb:1:fe10::/64 locator MAIN behavior uDT4 vrf Vrf10 sid fcbb:bbbb:1:fe20::/64 locator MAIN behavior uDT6 vrf Vrf20 sid fcbb:bbbb:1:fe30::/64 locator MAIN behavior uDT46 vrf Vrf30 diff --git a/tests/topotests/v6_nexthop_group_recursive_resolution/r1/frr.conf b/tests/topotests/v6_nexthop_group_recursive_resolution/r1/frr.conf new file mode 100644 index 0000000000..f4da11af06 --- /dev/null +++ b/tests/topotests/v6_nexthop_group_recursive_resolution/r1/frr.conf @@ -0,0 +1,4 @@ +int r1-eth0 + ipv6 address fc00::1/64 + +ipv6 route 1::1/128 fc00::2 diff --git a/tests/topotests/v6_nexthop_group_recursive_resolution/test_v6_nexthop_group_recursive_resolution.py b/tests/topotests/v6_nexthop_group_recursive_resolution/test_v6_nexthop_group_recursive_resolution.py new file mode 100644 index 0000000000..587a951c85 --- /dev/null +++ b/tests/topotests/v6_nexthop_group_recursive_resolution/test_v6_nexthop_group_recursive_resolution.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2024 by Nvidia Corporation +# Donald Sharp +# + +""" +Check that the v6 nexthop recursive resolution works when it changes +""" + +import os +import sys +import json +import pytest +import functools + +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.common_config import step + +pytestmark = [pytest.mark.staticd] + + +def build_topo(tgen): + + tgen.add_router("r1") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)), + [(TopoRouter.RD_MGMTD, None), + (TopoRouter.RD_ZEBRA, None), + (TopoRouter.RD_STATIC, None), + (TopoRouter.RD_SHARP, None)]) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_recursive_v6_nexthop_generation(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Testing v6 nexthop resolution") + + #assert False + router = tgen.gears["r1"] + + def _v6_converge_1_1_initial(): + output = json.loads( + router.vtysh_cmd("show ipv6 route 1::1 json")) + + expected = { + "1::1/128":[ + { + "prefix":"1::1/128", + "prefixLen":128, + "protocol":"static", + "vrfName":"default", + "selected":True, + "destSelected":True, + "distance":1, + "metric":0, + "installed":True, + "table":254, + "nexthops":[ + { + "fib":True, + "ip":"fc00::2", + "afi":"ipv6", + "interfaceName":"r1-eth0", + "active":True, + "weight":1 + } + ] + } + ] + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_v6_converge_1_1_initial) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to install v6 1::1 route" + + router.vtysh_cmd("sharp install routes 2::2 nexthop 1::1 1") + router.vtysh_cmd("conf\nipv6 route 1::1/128 fc00::3\nno ipv6 route 1::1/128 fc00::2") + + def _v6_converge_1_1_post(): + output = json.loads( + router.vtysh_cmd("show ipv6 route 1::1 json")) + + expected = { + "1::1/128":[ + { + "prefix":"1::1/128", + "prefixLen":128, + "protocol":"static", + "vrfName":"default", + "selected":True, + "destSelected":True, + "distance":1, + "metric":0, + "installed":True, + "table":254, + "nexthops":[ + { + "fib":True, + "ip":"fc00::3", + "afi":"ipv6", + "interfaceName":"r1-eth0", + "active":True, + "weight":1 + } + ] + } + ] + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_v6_converge_1_1_post) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to change v6 1::1 route" + + router.vtysh_cmd("sharp install routes 2::2 nexthop 1::1 1") + + def _v6_change_2_2_post(): + output = json.loads( + router.vtysh_cmd("show ipv6 route 2::2 json")) + + expected = { + "2::2/128":[ + { + "prefix":"2::2/128", + "prefixLen":128, + "protocol":"sharp", + "vrfName":"default", + "selected":True, + "destSelected":True, + "distance":150, + "metric":0, + "installed":True, + "table":254, + "nexthops":[ + { + "fib":True, + "ip":"fc00::3", + "afi":"ipv6", + "interfaceName":"r1-eth0", + "active":True, + "weight":1 + } + ] + } + ] + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_v6_change_2_2_post) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see sharpd route correctly" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tools/frr-reload.py b/tools/frr-reload.py index a138e4e239..f124cae713 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -237,6 +237,14 @@ def get_normalized_interface_vrf(line): return line +def get_normalized_ebgp_multihop_line(line): + obj = re.search(r"(.*)ebgp-multihop\s+255", line) + if obj: + line = obj.group(1) + "ebgp-multihop" + + return line + + # This dictionary contains a tree of all commands that we know start a # new multi-line context. All other commands are treated either as # commands inside a multi-line context or as single-line contexts. This @@ -382,6 +390,9 @@ class Config(object): if ":" in line: line = get_normalized_mac_ip_line(line) + if "ebgp-multihop" in line: + line = get_normalized_ebgp_multihop_line(line) + # vrf static routes can be added in two ways. The old way is: # # "ip route x.x.x.x/x y.y.y.y vrf <vrfname>" diff --git a/yang/frr-staticd.yang b/yang/frr-staticd.yang index 904e2058e9..8d0e58c0a5 100644 --- a/yang/frr-staticd.yang +++ b/yang/frr-staticd.yang @@ -165,7 +165,7 @@ module frr-staticd { "Support for a 'staticd' pseudo-protocol instance consists of a list of routes."; list route-list { - key "prefix afi-safi"; + key "prefix src-prefix afi-safi"; description "List of staticd IP routes."; leaf prefix { @@ -173,6 +173,11 @@ module frr-staticd { description "IP prefix."; } + leaf src-prefix { + type inet:ipv6-prefix; + description + "IPv6 source prefix for dst-src routes"; + } leaf afi-safi { type identityref { base frr-rt:afi-safi-type; @@ -180,6 +185,12 @@ module frr-staticd { description "AFI-SAFI type."; } + /* note dst-src routes are semantically invalid in MRIB */ + must "afi-safi = 'frr-rt:ipv6-unicast' + or afi-safi = 'frr-rt:ipv6-labeled-unicast' + or afi-safi = 'frr-rt:l3vpn-ipv6-unicast' + or src-prefix = '::/0' + "; uses staticd-prefix-attributes { augment "path-list/frr-nexthops/nexthop" { @@ -194,17 +205,6 @@ module frr-staticd { } } } - - list src-list { - key "src-prefix"; - leaf src-prefix { - type inet:ipv6-prefix; - description - "IPv6 source prefix"; - } - - uses staticd-prefix-attributes; - } } container segment-routing { diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 66dc5b4b5f..9bf7e2cbb5 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -82,8 +82,8 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id) RNODE_FOREACH_RE (rn, newre) { if (CHECK_FLAG(newre->flags, ZEBRA_FLAG_SELECTED)) - zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, - client, rn, newre, false); + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn, + newre, NULL); } route_unlock_node(rn); @@ -91,6 +91,24 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id) } /* Redistribute routes. */ +static void redistribute_table_direct(struct zserv *client, int type, const struct route_node *rn, + const struct route_entry *re) +{ + struct redist_table_direct *table; + struct redist_proto *red; + struct listnode *node; + afi_t afi = family2afi(rn->p.family); + + red = &client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT]; + + for (ALL_LIST_ELEMENTS_RO(red->instances, node, table)) { + if (table->table_id != (int)re->table) + continue; + + zsend_redistribute_route(type, client, rn, re, &table->vrf_id); + } +} + static void zebra_redistribute(struct zserv *client, int type, unsigned short instance, struct zebra_vrf *zvrf, int afi) @@ -102,13 +120,9 @@ static void zebra_redistribute(struct zserv *client, int type, vrf_id_t vrf_id = zvrf_id(zvrf); if (type == ZEBRA_ROUTE_TABLE_DIRECT) { - if (vrf_id == VRF_DEFAULT) { - table = zebra_router_find_table(zvrf, instance, afi, - SAFI_UNICAST); - type = ZEBRA_ROUTE_ALL; - is_table_direct = true; - } else - return; + table = zebra_router_find_table(zvrf, instance, afi, SAFI_UNICAST); + type = ZEBRA_ROUTE_ALL; + is_table_direct = true; } else table = zebra_vrf_table(afi, SAFI_UNICAST, vrf_id); @@ -140,15 +154,20 @@ static void zebra_redistribute(struct zserv *client, int type, if (!zebra_check_addr(&rn->p)) continue; - zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, - client, rn, newre, is_table_direct); + if (is_table_direct) + redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn, + newre); + else + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn, + newre, NULL); } } /* - * Function to return a valid table id value if table-direct is used - * return 0 otherwise - * This function can be called only if zebra_redistribute_check returns TRUE + * Checks if the route entry can be used as table-direct or not. + * `table-direct` routes always belong to `VRF_DEFAULT` and has an table + * ID different than the VRF it belongs (example main VRF table is 254, + * so in order to be `table-direct` the route's table ID must be != 254). */ static bool zebra_redistribute_is_table_direct(const struct route_entry *re) { @@ -177,15 +196,14 @@ static bool zebra_redistribute_check(const struct route_node *rn, afi = family2afi(rn->p.family); zvrf = zebra_vrf_lookup_by_id(re->vrf_id); - if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table) { + if (zvrf->table_id != re->table) { + /* + * Routes with table ID different from VRFs can be used as + * `table-direct` if enabled. + */ if (re->table && - redist_check_instance(&client->mi_redist - [afi][ZEBRA_ROUTE_TABLE_DIRECT], - re->table)) { - /* table-direct redistribution only for route entries which - * are on the default vrf, and that have table id different - * from the default table. - */ + redist_table_direct_has_id(&client->mi_redist[afi][ZEBRA_ROUTE_TABLE_DIRECT], + re->table)) { return true; } return false; @@ -227,7 +245,6 @@ void redistribute_update(const struct route_node *rn, { struct listnode *node, *nnode; struct zserv *client; - bool is_table_direct; if (IS_ZEBRA_DEBUG_RIB) zlog_debug( @@ -242,7 +259,6 @@ void redistribute_update(const struct route_node *rn, return; } - for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { if (zebra_redistribute_check(rn, re, client)) { if (IS_ZEBRA_DEBUG_RIB) { @@ -253,15 +269,19 @@ void redistribute_update(const struct route_node *rn, re->vrf_id, re->table, re->type, re->distance, re->metric); } - is_table_direct = zebra_redistribute_is_table_direct(re); - zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, - client, rn, re, - is_table_direct); + if (zebra_redistribute_is_table_direct(re)) + redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_ADD, rn, + re); + else + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_ADD, client, rn, + re, NULL); } else if (zebra_redistribute_check(rn, prev_re, client)) { - is_table_direct = zebra_redistribute_is_table_direct(prev_re); - zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, - client, rn, prev_re, - is_table_direct); + if (zebra_redistribute_is_table_direct(prev_re)) + redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn, + prev_re); + else + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, rn, + prev_re, NULL); } } } @@ -281,7 +301,6 @@ void redistribute_delete(const struct route_node *rn, struct listnode *node, *nnode; struct zserv *client; vrf_id_t vrfid; - bool is_table_direct; if (old_re) vrfid = old_re->vrf_id; @@ -344,10 +363,12 @@ void redistribute_delete(const struct route_node *rn, * happy. */ assert(old_re); - is_table_direct = zebra_redistribute_is_table_direct(old_re); - zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, - client, rn, old_re, - is_table_direct); + if (zebra_redistribute_is_table_direct(old_re)) + redistribute_table_direct(client, ZEBRA_REDISTRIBUTE_ROUTE_DEL, rn, + old_re); + else + zsend_redistribute_route(ZEBRA_REDISTRIBUTE_ROUTE_DEL, client, rn, + old_re, NULL); } } } @@ -383,8 +404,16 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS) } if (instance) { - if (!redist_check_instance(&client->mi_redist[afi][type], - instance)) { + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + struct redist_table_direct table = { + .vrf_id = zvrf->vrf->vrf_id, + .table_id = instance, + }; + if (!redist_lookup_table_direct(&client->mi_redist[afi][type], &table)) { + redist_add_table_direct(&client->mi_redist[afi][type], &table); + zebra_redistribute(client, type, instance, zvrf, afi); + } + } else if (!redist_check_instance(&client->mi_redist[afi][type], instance)) { redist_add_instance(&client->mi_redist[afi][type], instance); zebra_redistribute(client, type, instance, zvrf, afi); @@ -443,7 +472,13 @@ void zebra_redistribute_delete(ZAPI_HANDLER_ARGS) * themselves should keep track of the received routes from zebra and * withdraw them when necessary. */ - if (instance) + if (type == ZEBRA_ROUTE_TABLE_DIRECT) { + struct redist_table_direct table = { + .vrf_id = zvrf->vrf->vrf_id, + .table_id = instance, + }; + redist_del_table_direct(&client->mi_redist[afi][type], &table); + } else if (instance) redist_del_instance(&client->mi_redist[afi][type], instance); else vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf)); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index b32882e858..d696b19859 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2588,10 +2588,10 @@ ssize_t netlink_route_multipath_msg_encode(int cmd, struct zebra_dplane_ctx *ctx } } - if ((!fpm && kernel_nexthops_supported() - && (!proto_nexthops_only() - || is_proto_nhg(dplane_ctx_get_nhe_id(ctx), 0))) - || (fpm && force_nhg)) { + if ((!fpm && kernel_nexthops_supported() && + (!proto_nexthops_only() || is_proto_nhg(dplane_ctx_get_nhe_id(ctx), 0)) && + (!src_p || !src_p->prefixlen)) || + (fpm && force_nhg)) { /* Kernel supports nexthop objects */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("%s: %pFX nhg_id is %u", __func__, p, diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index ab55998af0..e9d554ba3d 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -509,9 +509,8 @@ int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp) return zserv_send_message(client, s); } -int zsend_redistribute_route(int cmd, struct zserv *client, - const struct route_node *rn, - const struct route_entry *re, bool is_table_direct) +int zsend_redistribute_route(int cmd, struct zserv *client, const struct route_node *rn, + const struct route_entry *re, vrf_id_t *to_vrf) { struct zapi_route api; struct zapi_nexthop *api_nh; @@ -527,9 +526,10 @@ int zsend_redistribute_route(int cmd, struct zserv *client, api.vrf_id = re->vrf_id; api.type = re->type; api.safi = SAFI_UNICAST; - if (is_table_direct) { + if (to_vrf != NULL) { api.instance = re->table; api.type = ZEBRA_ROUTE_TABLE_DIRECT; + api.vrf_id = *to_vrf; } else api.instance = re->instance; api.flags = re->flags; @@ -598,7 +598,7 @@ int zsend_redistribute_route(int cmd, struct zserv *client, /* Attributes. */ SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); - if (is_table_direct) + if (to_vrf != NULL) api.distance = ZEBRA_TABLEDIRECT_DISTANCE_DEFAULT; else api.distance = re->distance; @@ -740,6 +740,10 @@ static int route_notify_internal(const struct route_node *rn, int type, struct zserv *client; struct stream *s; uint8_t blen; + const struct prefix *p, *src_p; + struct prefix src_dummy = {}; + + srcdest_rnode_prefixes(rn, &p, &src_p); client = zserv_find_client(type, instance); if (!client || !client->notify_owner) { @@ -771,9 +775,17 @@ static int route_notify_internal(const struct route_node *rn, int type, stream_putc(s, rn->p.family); - blen = prefix_blen(&rn->p); - stream_putc(s, rn->p.prefixlen); - stream_put(s, &rn->p.u.prefix, blen); + blen = prefix_blen(p); + stream_putc(s, p->prefixlen); + stream_put(s, &p->u.prefix, blen); + + if (!src_p) { + src_dummy.family = p->family; + src_p = &src_dummy; + } + blen = prefix_blen(src_p); + stream_putc(s, src_p->prefixlen); + stream_put(s, &src_p->u.prefix, blen); stream_putl(s, table_id); diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index a59ccc838b..29a5b69f18 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -51,10 +51,8 @@ extern void nbr_connected_delete_ipv6(struct interface *ifp, struct in6_addr *address); extern int zsend_interface_update(int cmd, struct zserv *client, struct interface *ifp); -extern int zsend_redistribute_route(int cmd, struct zserv *zclient, - const struct route_node *rn, - const struct route_entry *re, - bool is_table_direct); +extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct route_node *rn, + const struct route_entry *re, vrf_id_t *to_vrf); extern int zsend_router_id_update(struct zserv *zclient, afi_t afi, struct prefix *p, vrf_id_t vrf_id); diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 4f6bc02c6e..f5141c8f23 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -572,8 +572,7 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) /* Nexthops should be in-order, so we simply compare them in-place */ for (nexthop1 = nhe1->nhg.nexthop, nexthop2 = nhe2->nhg.nexthop; nexthop1 && nexthop2; - nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { - + nexthop1 = nexthop_next(nexthop1), nexthop2 = nexthop_next(nexthop2)) { if (!nhg_compare_nexthops(nexthop1, nexthop2)) return false; } @@ -608,8 +607,7 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2) for (nexthop1 = nhe1->backup_info->nhe->nhg.nexthop, nexthop2 = nhe2->backup_info->nhe->nhg.nexthop; nexthop1 && nexthop2; - nexthop1 = nexthop1->next, nexthop2 = nexthop2->next) { - + nexthop1 = nexthop_next(nexthop1), nexthop2 = nexthop_next(nexthop2)) { if (!nhg_compare_nexthops(nexthop1, nexthop2)) return false; } diff --git a/zebra/zebra_srv6.c b/zebra/zebra_srv6.c index 624f60e815..6d228c5e24 100644 --- a/zebra/zebra_srv6.c +++ b/zebra/zebra_srv6.c @@ -1547,9 +1547,26 @@ static int get_srv6_sid_explicit(struct zebra_srv6_sid **sid, } if (ctx->behavior == ZEBRA_SEG6_LOCAL_ACTION_END) { - zlog_err("%s: invalid SM request arguments: explicit SID allocation not allowed for End/uN behavior", - __func__); - return -1; + zctx = zebra_srv6_sid_ctx_alloc(); + zctx->ctx = *ctx; + + *sid = zebra_srv6_sid_alloc(zctx, sid_value, locator, block, sid_func, + SRV6_SID_ALLOC_MODE_EXPLICIT); + if (!(*sid)) { + flog_err(EC_ZEBRA_SM_CANNOT_ASSIGN_SID, + "%s: failed to create SRv6 SID %s (%pI6)", __func__, + srv6_sid_ctx2str(buf, sizeof(buf), ctx), sid_value); + return -1; + } + (*sid)->ctx = zctx; + zctx->sid = *sid; + listnode_add(srv6->sids, zctx); + + if (IS_ZEBRA_DEBUG_SRV6) + zlog_debug("%s: allocated explicit SRv6 SID %pI6 for context %s", __func__, + &(*sid)->value, srv6_sid_ctx2str(buf, sizeof(buf), ctx)); + + return 1; } /* Allocate an explicit SID function for the SID */ |
