diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index bda7ed89a4c2..391d42fba156 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -2340,6 +2340,56 @@ static int lsp_handle_adj_state_change(struct isis_adjacency *adj) return 0; } +/* + * Iterate over all SRv6 locator TLVs + */ +int isis_lsp_iterate_srv6_locator(struct isis_lsp *lsp, uint16_t mtid, + lsp_ip_reach_iter_cb cb, void *arg) +{ + bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id); + struct isis_lsp *frag; + struct listnode *node; + + if (lsp->hdr.seqno == 0 || lsp->hdr.rem_lifetime == 0) + return LSP_ITER_CONTINUE; + + /* Parse LSP */ + if (lsp->tlvs) { + if (!pseudo_lsp) { + struct isis_item_list *srv6_locator_reachs; + struct isis_srv6_locator_tlv *r; + + srv6_locator_reachs = + isis_lookup_mt_items(&lsp->tlvs->srv6_locator, + mtid); + + for (r = srv6_locator_reachs + ? (struct isis_srv6_locator_tlv *) + srv6_locator_reachs->head + : NULL; + r; r = r->next) { + if ((*cb)((struct prefix *)&r->prefix, + r->metric, false /* ignore */, + r->subtlvs, arg) == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + } + } + + /* Parse LSP fragments if it is not a fragment itself */ + if (!LSP_FRAGMENT(lsp->hdr.lsp_id)) + for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) { + if (!frag->tlvs) + continue; + + if (isis_lsp_iterate_srv6_locator(frag, mtid, cb, + arg) == LSP_ITER_STOP) + return LSP_ITER_STOP; + } + + return LSP_ITER_CONTINUE; +} + /* * Iterate over all IP reachability TLVs in a LSP (all fragments) of the given * address-family and MT-ID. diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index 3839a9504c05..15db88b02f36 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -143,6 +143,8 @@ int isis_lsp_iterate_ip_reach(struct isis_lsp *lsp, int family, uint16_t mtid, lsp_ip_reach_iter_cb cb, void *arg); int isis_lsp_iterate_is_reach(struct isis_lsp *lsp, uint16_t mtid, lsp_is_reach_iter_cb cb, void *arg); +int isis_lsp_iterate_srv6_locator(struct isis_lsp *lsp, uint16_t mtid, + lsp_ip_reach_iter_cb cb, void *arg); #define lsp_flood(lsp, circuit) \ _lsp_flood((lsp), (circuit), __func__, __FILE__, __LINE__) diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 3683f7455821..bb73a4425774 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -1258,8 +1258,11 @@ static int lsp_to_subnet_cb(const struct prefix *prefix, uint32_t metric, if (!args || !prefix) return LSP_ITER_CONTINUE; - te_debug(" |- Process Extended %s Reachability %pFX", - prefix->family == AF_INET ? "IP" : "IPv6", prefix); + if (args->srv6_locator) + te_debug(" |- Process SRv6 Locator %pFX", prefix); + else + te_debug(" |- Process Extended %s Reachability %pFX", + prefix->family == AF_INET ? "IP" : "IPv6", prefix); vertex = args->vertex; @@ -1386,6 +1389,38 @@ static int lsp_to_subnet_cb(const struct prefix *prefix, uint32_t metric, } } + /* Update SRv6 SID and locator if any */ + if (subtlvs && subtlvs->srv6_end_sids.count != 0) { + struct isis_srv6_end_sid_subtlv *psid; + struct ls_srv6_sid sr = {}; + + psid = (struct isis_srv6_end_sid_subtlv *) + subtlvs->srv6_end_sids.head; + sr.behavior = psid->behavior; + sr.flags = psid->flags; + memcpy(&sr.sid, &psid->sid, sizeof(struct in6_addr)); + + if (!CHECK_FLAG(ls_pref->flags, LS_PREF_SRV6) || + memcmp(&ls_pref->srv6, &sr, sizeof(struct ls_srv6_sid))) { + memcpy(&ls_pref->srv6, &sr, sizeof(struct ls_srv6_sid)); + SET_FLAG(ls_pref->flags, LS_PREF_SRV6); + if (subnet->status != NEW) + subnet->status = UPDATE; + } else { + if (subnet->status == ORPHAN) + subnet->status = SYNC; + } + } else { + if (CHECK_FLAG(ls_pref->flags, LS_PREF_SRV6)) { + UNSET_FLAG(ls_pref->flags, LS_PREF_SRV6); + if (subnet->status != NEW) + subnet->status = UPDATE; + } else { + if (subnet->status == ORPHAN) + subnet->status = SYNC; + } + } + /* Update status and Export Link State Edge if needed */ if (subnet->status != SYNC) { if (args->export) @@ -1454,12 +1489,18 @@ static void isis_te_parse_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp) &args); /* Process all Extended IP (v4 & v6) in LSP (all fragments) */ + args.srv6_locator = false; isis_lsp_iterate_ip_reach(lsp, AF_INET, ISIS_MT_IPV4_UNICAST, lsp_to_subnet_cb, &args); isis_lsp_iterate_ip_reach(lsp, AF_INET6, ISIS_MT_IPV6_UNICAST, lsp_to_subnet_cb, &args); isis_lsp_iterate_ip_reach(lsp, AF_INET6, ISIS_MT_IPV4_UNICAST, lsp_to_subnet_cb, &args); + args.srv6_locator = true; + isis_lsp_iterate_srv6_locator(lsp, ISIS_MT_STANDARD, lsp_to_subnet_cb, + &args); + isis_lsp_iterate_srv6_locator(lsp, ISIS_MT_IPV6_UNICAST, + lsp_to_subnet_cb, &args); /* Clean remaining Orphan Edges or Subnets */ if (IS_EXPORT_TE(mta)) diff --git a/isisd/isis_te.h b/isisd/isis_te.h index bf1dc2b9bbbd..532a24e7e406 100644 --- a/isisd/isis_te.h +++ b/isisd/isis_te.h @@ -103,6 +103,7 @@ struct isis_te_args { struct ls_ted *ted; struct ls_vertex *vertex; bool export; + bool srv6_locator; }; enum lsp_event { LSP_UNKNOWN, LSP_ADD, LSP_UPD, LSP_DEL, LSP_INC, LSP_TICK }; diff --git a/lib/link_state.c b/lib/link_state.c index c758b7f57517..3d96c75f6d1e 100644 --- a/lib/link_state.c +++ b/lib/link_state.c @@ -414,6 +414,13 @@ int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2) || (p1->sr.sid_flag != p2->sr.sid_flag)) return 0; } + if (CHECK_FLAG(p1->flags, LS_PREF_SRV6)) { + if (memcmp(&p1->srv6.sid, &p2->srv6.sid, + sizeof(struct in6_addr)) || + (p1->srv6.flags != p2->srv6.flags) || + (p1->srv6.behavior != p2->srv6.behavior)) + return 0; + } /* OK, p1 & p2 are equal */ return 1; @@ -1388,6 +1395,11 @@ static struct ls_prefix *ls_parse_prefix(struct stream *s) STREAM_GETC(s, ls_pref->sr.sid_flag); STREAM_GETC(s, ls_pref->sr.algo); } + if (CHECK_FLAG(ls_pref->flags, LS_PREF_SRV6)) { + STREAM_GET(&ls_pref->srv6.sid, s, sizeof(struct in6_addr)); + STREAM_GETW(s, ls_pref->srv6.behavior); + STREAM_GETC(s, ls_pref->srv6.flags); + } return ls_pref; @@ -1632,6 +1644,11 @@ static int ls_format_prefix(struct stream *s, struct ls_prefix *ls_pref) stream_putc(s, ls_pref->sr.sid_flag); stream_putc(s, ls_pref->sr.algo); } + if (CHECK_FLAG(ls_pref->flags, LS_PREF_SRV6)) { + stream_put(s, &ls_pref->srv6.sid, sizeof(struct in6_addr)); + stream_putw(s, ls_pref->srv6.behavior); + stream_putc(s, ls_pref->srv6.flags); + } return 0; } @@ -2748,6 +2765,13 @@ static void ls_show_subnet_vty(struct ls_subnet *subnet, struct vty *vty, sbuf_push(&sbuf, 4, "SID: %d\tAlgorithm: %d\tFlags: 0x%x\n", pref->sr.sid, pref->sr.algo, pref->sr.sid_flag); + if (CHECK_FLAG(pref->flags, LS_PREF_SRV6)) + sbuf_push(&sbuf, 4, + "SIDv6: %pI6\tEndpoint behavior: %s\tFlags: 0x%x\n", + &pref->srv6.sid, + seg6local_action2str(pref->srv6.behavior), + pref->srv6.flags); + end: vty_out(vty, "%s\n", sbuf_buf(&sbuf)); sbuf_free(&sbuf); @@ -2757,7 +2781,7 @@ static void ls_show_subnet_json(struct ls_subnet *subnet, struct json_object *json) { struct ls_prefix *pref; - json_object *jsr; + json_object *jsr, *jsrv6; char buf[INET6_BUFSIZ]; pref = subnet->ls_pref; @@ -2787,6 +2811,16 @@ static void ls_show_subnet_json(struct ls_subnet *subnet, snprintfrr(buf, INET6_BUFSIZ, "0x%x", pref->sr.sid_flag); json_object_string_add(jsr, "flags", buf); } + if (CHECK_FLAG(pref->flags, LS_PREF_SRV6)) { + jsrv6 = json_object_new_object(); + json_object_object_add(json, "segment-routing-ipv6", jsrv6); + snprintfrr(buf, INET6_BUFSIZ, "%pI6", &pref->srv6.sid); + json_object_string_add(jsrv6, "sid", buf); + json_object_string_add(jsrv6, "behavior", + seg6local_action2str(pref->srv6.behavior)); + snprintfrr(buf, INET6_BUFSIZ, "0x%x", pref->srv6.flags); + json_object_string_add(jsrv6, "flags", buf); + } } void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty, diff --git a/lib/link_state.h b/lib/link_state.h index d819c20db763..c54e2ec6d904 100644 --- a/lib/link_state.h +++ b/lib/link_state.h @@ -243,6 +243,7 @@ struct ls_attributes { #define LS_PREF_EXTENDED_TAG 0x04 #define LS_PREF_METRIC 0x08 #define LS_PREF_SR 0x10 +#define LS_PREF_SRV6 0x20 /* Link State Prefix */ struct ls_prefix { @@ -258,6 +259,11 @@ struct ls_prefix { uint8_t sid_flag; /* Segment Routing Flags */ uint8_t algo; /* Algorithm for Segment Routing */ } sr; + struct ls_srv6_sid { + struct in6_addr sid; /* Segment Routing ID */ + uint16_t behavior; /* Endpoint behavior bound to the SID */ + uint8_t flags; /* Flags */ + } srv6; }; /**