OSPFd: Explicit Null option for Segment-Routing

Add new option to `segment-routing prefix` command to set the
Explcit Null flag in addition to the No-PHP flag. MPLS LFIB configuration
has been also updated to take into account the Explicit Null flag.

Signed-off-by: Olivier Dugeon <olivier.dugeon@orange.com>
This commit is contained in:
Olivier Dugeon 2020-07-08 20:12:19 +02:00
parent 6f751f1493
commit f786c4d7c9
9 changed files with 135 additions and 116 deletions

View File

@ -1099,13 +1099,15 @@ dataplane.
Fix the Maximum Stack Depth supported by the router. The value depend of the 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. 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] .. 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] .. 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 Set the Segment Routing index for the specified prefix. Note that, only
prefix with /32 corresponding to a loopback interface are currently prefix with /32 corresponding to a loopback interface are currently
supported. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR supported. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR
node to request to its neighbor to not pop the label. node to request to its neighbor to not pop the label. The 'explicit-null' means that
neighbor nodes must swap the incoming label by the MPLS Explicit Null label
before delivering the packet.
.. index:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json] .. index:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json]
.. clicmd:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json] .. clicmd:: show ip ospf database segment-routing <adv-router ADVROUTER|self-originate> [json]

View File

@ -641,9 +641,12 @@ static mpls_label_t index2label(uint32_t index, struct sr_block srgb)
mpls_label_t label; mpls_label_t label;
label = srgb.lower_bound + index; 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; return MPLS_INVALID_LABEL;
else } else
return label; return label;
} }
@ -750,6 +753,45 @@ static int compute_link_nhlfe(struct sr_link *srl)
return rc; 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 * 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 */ /* And store this information for later update */
srnext->neighbor = OspfSR.self; srnext->neighbor = OspfSR.self;
if (IPV4_ADDR_SAME(&srnext->adv_router, &srp->adv_router)) path->srni.nexthop = srnext;
path->srni.nexthop = NULL;
else
path->srni.nexthop = srnext;
/* /*
* SR Node could be known, but SRGB could be not initialize * 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->srgb.range_size, srnext->srgb.lower_bound,
&srnext->adv_router); &srnext->adv_router);
/* /* Compute Output Label with Nexthop SR Node SRGB */
* Compute Output Label with Nexthop SR Node SRGB or Implicit path->srni.label_out = sr_prefix_out_label(srp, srnext);
* 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);
osr_debug(" |- Computed new labels in: %u out: %u", osr_debug(" |- Computed new labels in: %u out: %u",
srp->label_in, path->srni.label_out); 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)) { for (ALL_LIST_ELEMENTS_RO(srp->route->paths, pnode, path)) {
/* Process only SID Index for next hop without PHP */ /* Process only SID Index for next hop without PHP */
if ((path->srni.nexthop == NULL) if ((path->srni.nexthop == srp->srn)
&& (!CHECK_FLAG(srp->flags, && (!CHECK_FLAG(srp->flags,
EXT_SUBTLV_PREFIX_SID_NPFLG))) EXT_SUBTLV_PREFIX_SID_NPFLG)))
continue; 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", " |- Update Node SID %pFX - %u for self SR Node",
(struct prefix *)&srp->prefv4, srp->sid); (struct prefix *)&srp->prefv4, srp->sid);
/* Install NHLFE if NO-PHP is requested */ /* Install SID if NO-PHP is set and not EXPLICIT-NULL */
if (CHECK_FLAG(srp->flags, if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)
EXT_SUBTLV_PREFIX_SID_NPFLG)) { && !CHECK_FLAG(srp->flags,
EXT_SUBTLV_PREFIX_SID_EFLG)) {
srp->label_in = index2label(srp->sid, srp->label_in = index2label(srp->sid,
OspfSR.self->srgb); OspfSR.self->srgb);
srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; 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, for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node,
srp)) { srp)) {
vty_out(vty, vty_out(vty,
" segment-routing prefix %s/%u index %u%s\n", " segment-routing prefix %s/%u "
"index %u",
inet_ntoa(srp->prefv4.prefix), inet_ntoa(srp->prefv4.prefix),
srp->prefv4.prefixlen, srp->sid, srp->prefv4.prefixlen, srp->sid);
CHECK_FLAG(srp->flags, if (CHECK_FLAG(srp->flags,
EXT_SUBTLV_PREFIX_SID_NPFLG) EXT_SUBTLV_PREFIX_SID_EFLG))
? " no-php-flag" 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, DEFUN (sr_prefix_sid,
sr_prefix_sid_cmd, 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 SR_STR
"Prefix SID\n" "Prefix SID\n"
"IPv4 Prefix as A.B.C.D/M\n" "IPv4 Prefix as A.B.C.D/M\n"
"SID index for this prefix in decimal (0-65535)\n" "SID index for this prefix in decimal (0-65535)\n"
"Index value inside SRGB (lower_bound < index < upper_bound)\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; int idx = 0;
struct prefix p; struct prefix p;
uint32_t index; uint32_t index;
struct listnode *node; struct listnode *node;
struct sr_prefix *srp, *new; struct sr_prefix *srp, *new = NULL;
struct interface *ifp; struct interface *ifp;
if (!ospf_sr_enabled(vty)) if (!ospf_sr_enabled(vty))
@ -2321,27 +2358,42 @@ DEFUN (sr_prefix_sid,
return CMD_WARNING_CONFIG_FAILED; 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)) { for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) {
if (srp->sid == index) { if (srp->sid == index) {
vty_out(vty, "Index %u is already used\n", index); if (prefix_same((struct prefix *)&srp->prefv4, &p)) {
return CMD_WARNING_CONFIG_FAILED; 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 */ /* Create new Extended Prefix to SRDB if not found */
new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); if (new == NULL) {
IPV4_ADDR_COPY(&new->prefv4.prefix, &p.u.prefix4); new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix));
new->prefv4.prefixlen = p.prefixlen; IPV4_ADDR_COPY(&new->prefv4.prefix, &p.u.prefix4);
new->prefv4.family = p.family; new->prefv4.prefixlen = p.prefixlen;
new->sid = index; new->prefv4.family = p.family;
new->type = LOCAL_SID; new->sid = index;
new->type = LOCAL_SID;
}
/* Set NO PHP flag if present and compute NHLFE */ /* Set NO PHP flag if present and compute NHLFE */
if (argv_find(argv, argc, "no-php-flag", &idx)) { if (argv_find(argv, argc, "no-php-flag", &idx)) {
SET_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG); 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->label_in = index2label(new->sid, OspfSR.self->srgb);
new->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; 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, osr_debug("SR (%s): Add new index %u to Prefix %pFX", __func__, index,
(struct prefix *)&new->prefv4); (struct prefix *)&new->prefv4);
@ -2369,27 +2421,16 @@ DEFUN (sr_prefix_sid,
} }
new->nhlfe.ifindex = ifp->ifindex; new->nhlfe.ifindex = ifp->ifindex;
/* Search if this prefix already exist */ /* Add this new SR Prefix if not already found */
for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { if (srp != new)
if ((IPV4_ADDR_SAME(&srp->prefv4.prefix, &p.u.prefix4)
&& srp->prefv4.prefixlen == p.prefixlen))
break;
else
srp = NULL;
}
/* Update or Add this new SR Prefix */
if (srp) {
listnode_delete(OspfSR.self->ext_prefix, srp);
listnode_add(OspfSR.self->ext_prefix, new); listnode_add(OspfSR.self->ext_prefix, new);
} else {
listnode_add(OspfSR.self->ext_prefix, new);
}
/* Install Prefix SID if SR is UP */ /* Install Prefix SID if SR is UP and a valid input label set */
if (OspfSR.status == SR_UP) if (OspfSR.status == SR_UP) {
ospf_zebra_update_prefix_sid(new); if (CHECK_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)
else && !CHECK_FLAG(new->flags, EXT_SUBTLV_PREFIX_SID_EFLG))
ospf_zebra_update_prefix_sid(new);
} else
return CMD_SUCCESS; return CMD_SUCCESS;
/* Finally, update Extended Prefix LSA id SR is UP */ /* Finally, update Extended Prefix LSA id SR is UP */
@ -2406,14 +2447,15 @@ DEFUN (sr_prefix_sid,
DEFUN (no_sr_prefix_sid, DEFUN (no_sr_prefix_sid,
no_sr_prefix_sid_cmd, 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 NO_STR
SR_STR SR_STR
"Prefix SID\n" "Prefix SID\n"
"IPv4 Prefix as A.B.C.D/M\n" "IPv4 Prefix as A.B.C.D/M\n"
"SID index for this prefix in decimal (0-65535)\n" "SID index for this prefix in decimal (0-65535)\n"
"Index value inside SRGB (lower_bound < index < upper_bound)\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; int idx = 0;
struct prefix p; struct prefix p;
@ -2468,8 +2510,9 @@ DEFUN (no_sr_prefix_sid,
osr_debug("SR (%s): Remove Prefix %pFX with index %u", __func__, osr_debug("SR (%s): Remove Prefix %pFX with index %u", __func__,
(struct prefix *)&srp->prefv4, srp->sid); (struct prefix *)&srp->prefv4, srp->sid);
/* Delete NHLFE if NO-PHP is set */ /* Delete NHLFE if NO-PHP is set and EXPLICIT NULL not set */
if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) 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); ospf_zebra_delete_prefix_sid(srp);
/* OK, all is clean, remove SRP from SRDB */ /* 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); snprintf(buf, size, "Pop(%u)", label_in);
break; break;
case MPLS_LABEL_IPV4_EXPLICIT_NULL: 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; break;
case MPLS_INVALID_LABEL: case MPLS_INVALID_LABEL:
snprintf(buf, size, "no-op."); snprintf(buf, size, "no-op.");

View File

@ -112,10 +112,10 @@
{ {
"prefix":"10.0.255.1\/32", "prefix":"10.0.255.1\/32",
"sid":100, "sid":100,
"inputLabel":20100, "inputLabel":0,
"prefixRoute":[ "prefixRoute":[
{ {
"outputLabel":3, "outputLabel":0,
"interface":"lo", "interface":"lo",
"nexthop":"10.0.255.1" "nexthop":"10.0.255.1"
} }

View File

@ -18,6 +18,6 @@ router ospf
segment-routing on segment-routing on
segment-routing node-msd 16 segment-routing node-msd 16
segment-routing global-block 20000 29999 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
! !

View File

@ -1,19 +1,4 @@
[ [
{
"inLabel":20100,
"installed":true,
"nexthops":[
{
"type":"SR (OSPF)",
"outLabel":3,
"outLabelStack":[
3
],
"distance":150,
"installed":true
}
]
},
{ {
"inLabel":20200, "inLabel":20200,
"installed":true, "installed":true,
@ -43,20 +28,13 @@
"nexthops":[ "nexthops":[
{ {
"type":"SR (OSPF)", "type":"SR (OSPF)",
"outLabel":8300, "outLabel":16300,
"outLabelStack":[ "outLabelStack":[
8300 16300
], ],
"distance":150, "distance":150,
"installed":true, "installed":true,
"nexthop":"10.0.1.2" "nexthop":"10.0.1.2"
},
{
"type":"SR (OSPF)",
"outLabel":16300,
"distance":150,
"installed":true,
"nexthop":"10.0.0.2"
} }
] ]
}, },
@ -66,20 +44,13 @@
"nexthops":[ "nexthops":[
{ {
"type":"SR (OSPF)", "type":"SR (OSPF)",
"outLabel":8400, "outLabel":16400,
"outLabelStack":[ "outLabelStack":[
8400 16400
], ],
"distance":150, "distance":150,
"installed":true, "installed":true,
"nexthop":"10.0.1.2" "nexthop":"10.0.1.2"
},
{
"type":"SR (OSPF)",
"outLabel":16400,
"distance":150,
"installed":true,
"nexthop":"10.0.0.2"
} }
] ]
}, },

View File

@ -166,12 +166,12 @@
"inputLabel":16100, "inputLabel":16100,
"prefixRoute":[ "prefixRoute":[
{ {
"outputLabel":20100, "outputLabel":0,
"interface":"r2-eth0", "interface":"r2-eth0",
"nexthop":"10.0.0.1" "nexthop":"10.0.0.1"
}, },
{ {
"outputLabel":20100, "outputLabel":0,
"interface":"r2-eth1", "interface":"r2-eth1",
"nexthop":"10.0.1.1" "nexthop":"10.0.1.1"
} }

View File

@ -5,9 +5,9 @@
"nexthops":[ "nexthops":[
{ {
"type":"SR (OSPF)", "type":"SR (OSPF)",
"outLabel":20100, "outLabel":0,
"outLabelStack":[ "outLabelStack":[
20100 0
], ],
"distance":150, "distance":150,
"installed":true, "installed":true,
@ -15,7 +15,7 @@
}, },
{ {
"type":"SR (OSPF)", "type":"SR (OSPF)",
"outLabel":20100, "outLabel":0,
"distance":150, "distance":150,
"installed":true, "installed":true,
"nexthop":"10.0.0.1" "nexthop":"10.0.0.1"

View File

@ -5,9 +5,9 @@
"nexthops":[ "nexthops":[
{ {
"type":"SR (OSPF)", "type":"SR (OSPF)",
"outLabel":8100, "outLabel":16100,
"outLabelStack":[ "outLabelStack":[
8100 16100
], ],
"distance":150, "distance":150,
"installed":true, "installed":true,
@ -37,9 +37,9 @@
"nexthops":[ "nexthops":[
{ {
"type":"SR (OSPF)", "type":"SR (OSPF)",
"outLabel":8400, "outLabel":16400,
"outLabelStack":[ "outLabelStack":[
8400 16400
], ],
"distance":150, "distance":150,
"installed":true, "installed":true,

View File

@ -5,9 +5,9 @@
"nexthops":[ "nexthops":[
{ {
"type":"SR (OSPF)", "type":"SR (OSPF)",
"outLabel":8100, "outLabel":16100,
"outLabelStack":[ "outLabelStack":[
8100 16100
], ],
"distance":150, "distance":150,
"installed":true, "installed":true,
@ -37,9 +37,9 @@
"nexthops":[ "nexthops":[
{ {
"type":"SR (OSPF)", "type":"SR (OSPF)",
"outLabel":8300, "outLabel":16300,
"outLabelStack":[ "outLabelStack":[
8300 16300
], ],
"distance":150, "distance":150,
"installed":true, "installed":true,