diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index ef3eb38510..19cafa9484 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -1099,13 +1099,15 @@ dataplane. Fix the Maximum Stack Depth supported by the router. The value depend of the MPLS dataplane. E.g. for Linux kernel, since version 4.13 it is 32. -.. index:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag] -.. clicmd:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag] +.. index:: [no] segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag|explicit-null] +.. clicmd:: [no] segment-routing prefix A.B.C.D/M [index (0-65535)|no-php-flag|explicit-null] Set the Segment Routing index for the specified prefix. Note that, only prefix with /32 corresponding to a loopback interface are currently supported. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR - node to request to its neighbor to not pop the label. + node to request to its neighbor to not pop the label. The 'explicit-null' means that + neighbor nodes must swap the incoming label by the MPLS Explicit Null label + before delivering the packet. .. index:: show ip ospf database segment-routing [json] .. clicmd:: show ip ospf database segment-routing [json] diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index ebc31a90f4..f6a190db80 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -641,9 +641,12 @@ static mpls_label_t index2label(uint32_t index, struct sr_block srgb) mpls_label_t label; label = srgb.lower_bound + index; - if (label > (srgb.lower_bound + srgb.range_size)) + if (label > (srgb.lower_bound + srgb.range_size)) { + flog_warn(EC_OSPF_SR_SID_OVERFLOW, + "%s: SID index %u falls outside SRGB range", + __func__, index); return MPLS_INVALID_LABEL; - else + } else return label; } @@ -750,6 +753,45 @@ static int compute_link_nhlfe(struct sr_link *srl) return rc; } +/** + * Compute output label for the given Prefix-SID. + * + * @param srp Segment Routing Prefix + * @param srnext Segment Routing nexthop node + * + * @return MPLS label or MPLS_INVALID_LABEL in case of error + */ +static mpls_label_t sr_prefix_out_label(const struct sr_prefix *srp, + const struct sr_node *srnext) +{ + /* Check if the nexthop SR Node is the last hop? */ + if (srnext == srp->srn) { + /* SR-Node doesn't request NO-PHP. Return Implicit NULL label */ + if (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) + return MPLS_LABEL_IMPLICIT_NULL; + + /* SR-Node requests Explicit NULL Label */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + return MPLS_LABEL_IPV4_EXPLICIT_NULL; + /* Fallthrough */ + } + + /* Return SID value as MPLS label if it is an Absolute SID */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG + | EXT_SUBTLV_PREFIX_SID_LFLG)) { + /* + * V/L SIDs have local significance, so only adjacent routers + * can use them (RFC8665 section #5) + */ + if (srp->srn != srnext) + return MPLS_INVALID_LABEL; + return srp->sid; + } + + /* Return MPLS label as SRGB lower bound + SID index as per RFC 8665 */ + return (index2label(srp->sid, srnext->srgb)); +} + /* * Compute NHLFE entry for Extended Prefix * @@ -800,10 +842,7 @@ static int compute_prefix_nhlfe(struct sr_prefix *srp) /* And store this information for later update */ srnext->neighbor = OspfSR.self; - if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router)) - path->srni.nexthop = NULL; - else - path->srni.nexthop = srnext; + path->srni.nexthop = srnext; /* * SR Node could be known, but SRGB could be not initialize @@ -818,18 +857,8 @@ static int compute_prefix_nhlfe(struct sr_prefix *srp) srnext->srgb.range_size, srnext->srgb.lower_bound, &srnext->adv_router); - /* - * Compute Output Label with Nexthop SR Node SRGB or Implicit - * Null label if next hop is the destination and request PHP - */ - if ((path->srni.nexthop == NULL) - && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))) - path->srni.label_out = MPLS_LABEL_IMPLICIT_NULL; - else if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) - path->srni.label_out = srp->sid; - else - path->srni.label_out = - index2label(srp->sid, srnext->srgb); + /* Compute Output Label with Nexthop SR Node SRGB */ + path->srni.label_out = sr_prefix_out_label(srp, srnext); osr_debug(" |- Computed new labels in: %u out: %u", srp->label_in, path->srni.label_out); @@ -1194,7 +1223,7 @@ static void update_out_nhlfe(struct hash_bucket *bucket, void *args) for (ALL_LIST_ELEMENTS_RO(srp->route->paths, pnode, path)) { /* Process only SID Index for next hop without PHP */ - if ((path->srni.nexthop == NULL) + if ((path->srni.nexthop == srp->srn) && (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG))) continue; @@ -1784,9 +1813,10 @@ void ospf_sr_update_local_prefix(struct interface *ifp, struct prefix *p) " |- Update Node SID %pFX - %u for self SR Node", (struct prefix *)&srp->prefv4, srp->sid); - /* Install NHLFE if NO-PHP is requested */ - if (CHECK_FLAG(srp->flags, - EXT_SUBTLV_PREFIX_SID_NPFLG)) { + /* Install SID if NO-PHP is set and not EXPLICIT-NULL */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(srp->flags, + EXT_SUBTLV_PREFIX_SID_EFLG)) { srp->label_in = index2label(srp->sid, OspfSR.self->srgb); srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; @@ -1913,13 +1943,19 @@ void ospf_sr_config_write_router(struct vty *vty) for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { vty_out(vty, - " segment-routing prefix %s/%u index %u%s\n", + " segment-routing prefix %s/%u " + "index %u", inet_ntoa(srp->prefv4.prefix), - srp->prefv4.prefixlen, srp->sid, - CHECK_FLAG(srp->flags, - EXT_SUBTLV_PREFIX_SID_NPFLG) - ? " no-php-flag" - : ""); + srp->prefv4.prefixlen, srp->sid); + if (CHECK_FLAG(srp->flags, + EXT_SUBTLV_PREFIX_SID_EFLG)) + vty_out(vty, " explicit-null\n"); + else if (CHECK_FLAG( + srp->flags, + EXT_SUBTLV_PREFIX_SID_NPFLG)) + vty_out(vty, " no-php-flag\n"); + else + vty_out(vty, "\n"); } } } @@ -2287,19 +2323,20 @@ DEFUN (no_sr_node_msd, DEFUN (sr_prefix_sid, sr_prefix_sid_cmd, - "segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag]", + "segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag|explicit-null]", SR_STR "Prefix SID\n" "IPv4 Prefix as A.B.C.D/M\n" "SID index for this prefix in decimal (0-65535)\n" "Index value inside SRGB (lower_bound < index < upper_bound)\n" - "Don't request Penultimate Hop Popping (PHP)\n") + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n") { int idx = 0; struct prefix p; uint32_t index; struct listnode *node; - struct sr_prefix *srp, *new; + struct sr_prefix *srp, *new = NULL; struct interface *ifp; if (!ospf_sr_enabled(vty)) @@ -2321,27 +2358,42 @@ DEFUN (sr_prefix_sid, return CMD_WARNING_CONFIG_FAILED; } - /* check that the index is not already used */ + /* Search for an existing Prefix-SID */ for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { if (srp->sid == index) { - vty_out(vty, "Index %u is already used\n", index); - return CMD_WARNING_CONFIG_FAILED; + if (prefix_same((struct prefix *)&srp->prefv4, &p)) { + new = srp; + break; + } else { + vty_out(vty, "Index %u is already used\n", + index); + return CMD_WARNING_CONFIG_FAILED; + } } } /* Create new Extended Prefix to SRDB if not found */ - new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); - IPV4_ADDR_COPY(&new->prefv4.prefix, &p.u.prefix4); - new->prefv4.prefixlen = p.prefixlen; - new->prefv4.family = p.family; - new->sid = index; - new->type = LOCAL_SID; + if (new == NULL) { + new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); + IPV4_ADDR_COPY(&new->prefv4.prefix, &p.u.prefix4); + new->prefv4.prefixlen = p.prefixlen; + new->prefv4.family = p.family; + new->sid = index; + new->type = LOCAL_SID; + } + /* Set NO PHP flag if present and compute NHLFE */ if (argv_find(argv, argc, "no-php-flag", &idx)) { SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG); + UNSET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_EFLG); new->label_in = index2label(new->sid, OspfSR.self->srgb); new->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; } + /* Set EXPLICIT NULL flag is present */ + if (argv_find(argv, argc, "explicit-null", &idx)) { + SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG); + SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_EFLG); + } osr_debug("SR (%s): Add new index %u to Prefix %pFX", __func__, index, (struct prefix *)&new->prefv4); @@ -2369,27 +2421,16 @@ DEFUN (sr_prefix_sid, } new->nhlfe.ifindex = ifp->ifindex; - /* Search if this prefix already exist */ - for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { - if ((IPV4_ADDR_SAME(&srp->prefv4.prefix, &p.u.prefix4) - && srp->prefv4.prefixlen == p.prefixlen)) - break; - else - srp = NULL; - } - - /* Update or Add this new SR Prefix */ - if (srp) { - listnode_delete(OspfSR.self->ext_prefix, srp); + /* Add this new SR Prefix if not already found */ + if (srp != new) listnode_add(OspfSR.self->ext_prefix, new); - } else { - listnode_add(OspfSR.self->ext_prefix, new); - } - /* Install Prefix SID if SR is UP */ - if (OspfSR.status == SR_UP) - ospf_zebra_update_prefix_sid(new); - else + /* Install Prefix SID if SR is UP and a valid input label set */ + if (OspfSR.status == SR_UP) { + if (CHECK_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + ospf_zebra_update_prefix_sid(new); + } else return CMD_SUCCESS; /* Finally, update Extended Prefix LSA id SR is UP */ @@ -2406,14 +2447,15 @@ DEFUN (sr_prefix_sid, DEFUN (no_sr_prefix_sid, no_sr_prefix_sid_cmd, - "no segment-routing prefix A.B.C.D/M [index (0-65535) no-php-flag]", + "no segment-routing prefix A.B.C.D/M [index (0-65535)|no-php-flag|explicit-null]", NO_STR SR_STR "Prefix SID\n" "IPv4 Prefix as A.B.C.D/M\n" "SID index for this prefix in decimal (0-65535)\n" "Index value inside SRGB (lower_bound < index < upper_bound)\n" - "Don't request Penultimate Hop Popping (PHP)\n") + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n") { int idx = 0; struct prefix p; @@ -2468,8 +2510,9 @@ DEFUN (no_sr_prefix_sid, osr_debug("SR (%s): Remove Prefix %pFX with index %u", __func__, (struct prefix *)&srp->prefv4, srp->sid); - /* Delete NHLFE if NO-PHP is set */ - if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) + /* Delete NHLFE if NO-PHP is set and EXPLICIT NULL not set */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) ospf_zebra_delete_prefix_sid(srp); /* OK, all is clean, remove SRP from SRDB */ @@ -2491,7 +2534,10 @@ static char *sr_op2str(char *buf, size_t size, mpls_label_t label_in, snprintf(buf, size, "Pop(%u)", label_in); break; case MPLS_LABEL_IPV4_EXPLICIT_NULL: - snprintf(buf, size, "Swap(%u, null)", label_in); + if (label_in == MPLS_LABEL_IPV4_EXPLICIT_NULL) + snprintf(buf, size, "no-op."); + else + snprintf(buf, size, "Swap(%u, null)", label_in); break; case MPLS_INVALID_LABEL: snprintf(buf, size, "no-op."); diff --git a/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json index 57bcbaeffc..952a26ed10 100644 --- a/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r1/ospf_srdb.json @@ -112,10 +112,10 @@ { "prefix":"10.0.255.1\/32", "sid":100, - "inputLabel":20100, + "inputLabel":0, "prefixRoute":[ { - "outputLabel":3, + "outputLabel":0, "interface":"lo", "nexthop":"10.0.255.1" } diff --git a/tests/topotests/ospf-sr-topo1/r1/ospfd.conf b/tests/topotests/ospf-sr-topo1/r1/ospfd.conf index f8fac2428c..73a272786c 100644 --- a/tests/topotests/ospf-sr-topo1/r1/ospfd.conf +++ b/tests/topotests/ospf-sr-topo1/r1/ospfd.conf @@ -18,6 +18,6 @@ router ospf segment-routing on segment-routing node-msd 16 segment-routing global-block 20000 29999 - segment-routing prefix 10.0.255.1/32 index 100 no-php-flag + segment-routing prefix 10.0.255.1/32 index 100 explicit-null ! diff --git a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json index fdf0886bcd..6c87596acb 100644 --- a/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r1/zebra_mpls.json @@ -1,19 +1,4 @@ [ - { - "inLabel":20100, - "installed":true, - "nexthops":[ - { - "type":"SR (OSPF)", - "outLabel":3, - "outLabelStack":[ - 3 - ], - "distance":150, - "installed":true - } - ] - }, { "inLabel":20200, "installed":true, @@ -43,20 +28,13 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8300, + "outLabel":16300, "outLabelStack":[ - 8300 + 16300 ], "distance":150, "installed":true, "nexthop":"10.0.1.2" - }, - { - "type":"SR (OSPF)", - "outLabel":16300, - "distance":150, - "installed":true, - "nexthop":"10.0.0.2" } ] }, @@ -66,20 +44,13 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8400, + "outLabel":16400, "outLabelStack":[ - 8400 + 16400 ], "distance":150, "installed":true, "nexthop":"10.0.1.2" - }, - { - "type":"SR (OSPF)", - "outLabel":16400, - "distance":150, - "installed":true, - "nexthop":"10.0.0.2" } ] }, diff --git a/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json b/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json index 6ea0f0a0cf..1de780d84e 100644 --- a/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json +++ b/tests/topotests/ospf-sr-topo1/r2/ospf_srdb.json @@ -166,12 +166,12 @@ "inputLabel":16100, "prefixRoute":[ { - "outputLabel":20100, + "outputLabel":0, "interface":"r2-eth0", "nexthop":"10.0.0.1" }, { - "outputLabel":20100, + "outputLabel":0, "interface":"r2-eth1", "nexthop":"10.0.1.1" } diff --git a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json index d343315f07..a885e88fc5 100644 --- a/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r2/zebra_mpls.json @@ -5,9 +5,9 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":20100, + "outLabel":0, "outLabelStack":[ - 20100 + 0 ], "distance":150, "installed":true, @@ -15,7 +15,7 @@ }, { "type":"SR (OSPF)", - "outLabel":20100, + "outLabel":0, "distance":150, "installed":true, "nexthop":"10.0.0.1" diff --git a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json index 3d036801d5..1b98ff4756 100644 --- a/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r3/zebra_mpls.json @@ -5,9 +5,9 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8100, + "outLabel":16100, "outLabelStack":[ - 8100 + 16100 ], "distance":150, "installed":true, @@ -37,9 +37,9 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8400, + "outLabel":16400, "outLabelStack":[ - 8400 + 16400 ], "distance":150, "installed":true, diff --git a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json index 86ad8721f8..b5758f29a0 100644 --- a/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json +++ b/tests/topotests/ospf-sr-topo1/r4/zebra_mpls.json @@ -5,9 +5,9 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8100, + "outLabel":16100, "outLabelStack":[ - 8100 + 16100 ], "distance":150, "installed":true, @@ -37,9 +37,9 @@ "nexthops":[ { "type":"SR (OSPF)", - "outLabel":8300, + "outLabel":16300, "outLabelStack":[ - 8300 + 16300 ], "distance":150, "installed":true,