mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-05 08:23:56 +00:00
Merge pull request #6182 from Orange-OpenSource/dev_ospf_sr
OSPF: Add ECMP support to Segment Routing
This commit is contained in:
commit
993db04388
251
ospfd/ospf_ext.c
251
ospfd/ospf_ext.c
@ -443,6 +443,32 @@ static void set_rmt_itf_addr(struct ext_itf *exti, struct in_addr rmtif)
|
||||
exti->rmt_itf_addr.value = rmtif;
|
||||
}
|
||||
|
||||
/* Delete Extended LSA */
|
||||
static void ospf_extended_lsa_delete(struct ext_itf *exti)
|
||||
{
|
||||
|
||||
/* Process only Active Extended Prefix/Link LSA */
|
||||
if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))
|
||||
return;
|
||||
|
||||
osr_debug("EXT (%s): Disable %s%s%s-SID on interface %s", __func__,
|
||||
exti->stype == PREF_SID ? "Prefix" : "",
|
||||
exti->stype == ADJ_SID ? "Adjacency" : "",
|
||||
exti->stype == LAN_ADJ_SID ? "LAN-Adjacency" : "",
|
||||
exti->ifp->name);
|
||||
|
||||
/* Flush LSA if already engaged */
|
||||
if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
|
||||
ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA);
|
||||
UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED);
|
||||
}
|
||||
|
||||
/* De-activate this Extended Prefix/Link and remove corresponding
|
||||
* Segment-Routing Prefix-SID or (LAN)-ADJ-SID */
|
||||
exti->flags = EXT_LPFLG_LSA_INACTIVE;
|
||||
ospf_sr_ext_itf_delete(exti);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update Extended prefix SID index for Loopback interface type
|
||||
*
|
||||
@ -465,12 +491,8 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index,
|
||||
return rc;
|
||||
|
||||
if (p != NULL) {
|
||||
if (IS_DEBUG_OSPF_SR)
|
||||
zlog_debug(
|
||||
"EXT (%s): Schedule new prefix %s/%u with "
|
||||
"index %u on interface %s",
|
||||
__func__, inet_ntoa(p->prefix), p->prefixlen,
|
||||
index, ifp->name);
|
||||
osr_debug("EXT (%s): Schedule new prefix %pFX with index %u "
|
||||
"on interface %s", __func__, p, index, ifp->name);
|
||||
|
||||
/* Set first Extended Prefix then the Prefix SID information */
|
||||
set_ext_prefix(exti, OSPF_PATH_INTRA_AREA, EXT_TLV_PREF_NFLG,
|
||||
@ -488,9 +510,8 @@ uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index,
|
||||
exti, REORIGINATE_THIS_LSA);
|
||||
}
|
||||
} else {
|
||||
if (IS_DEBUG_OSPF_SR)
|
||||
zlog_debug("EXT (%s): Remove prefix for interface %s",
|
||||
__func__, ifp->name);
|
||||
osr_debug("EXT (%s): Remove prefix for interface %s", __func__,
|
||||
ifp->name);
|
||||
|
||||
if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) {
|
||||
ospf_ext_pref_lsa_schedule(exti, FLUSH_THIS_LSA);
|
||||
@ -513,15 +534,15 @@ void ospf_ext_update_sr(bool enable)
|
||||
struct listnode *node;
|
||||
struct ext_itf *exti;
|
||||
|
||||
if (IS_DEBUG_OSPF_SR)
|
||||
zlog_debug("EXT (%s): %s Extended LSAs for Segment Routing ",
|
||||
__func__, enable ? "Enable" : "Disable");
|
||||
osr_debug("EXT (%s): %s Extended LSAs for Segment Routing ", __func__,
|
||||
enable ? "Enable" : "Disable");
|
||||
|
||||
if (enable) {
|
||||
OspfEXT.enabled = true;
|
||||
|
||||
/* Refresh LSAs if already engaged or originate */
|
||||
for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
|
||||
/* Skip inactive Extended Link */
|
||||
if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))
|
||||
continue;
|
||||
|
||||
@ -532,17 +553,15 @@ void ospf_ext_update_sr(bool enable)
|
||||
REORIGINATE_THIS_LSA);
|
||||
}
|
||||
} else {
|
||||
/* Start by Flushing engaged LSAs */
|
||||
for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
|
||||
if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED))
|
||||
ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA);
|
||||
exti->flags = EXT_LPFLG_LSA_INACTIVE;
|
||||
}
|
||||
/* Start by Removing Extended LSA */
|
||||
for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti))
|
||||
ospf_extended_lsa_delete(exti);
|
||||
|
||||
/* And then disable Extended Link/Prefix */
|
||||
OspfEXT.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* -----------------------------------------------------------------------
|
||||
* Followings are callback functions against generic Opaque-LSAs handling
|
||||
@ -580,10 +599,11 @@ static int ospf_ext_link_del_if(struct interface *ifp)
|
||||
|
||||
exti = lookup_ext_by_ifp(ifp);
|
||||
if (exti != NULL) {
|
||||
struct list *iflist = OspfEXT.iflist;
|
||||
/* Flush LSA and remove Adjacency SID */
|
||||
ospf_extended_lsa_delete(exti);
|
||||
|
||||
/* Dequeue listnode entry from the list. */
|
||||
listnode_delete(iflist, exti);
|
||||
listnode_delete(OspfEXT.iflist, exti);
|
||||
|
||||
XFREE(MTYPE_OSPF_EXT_PARAMS, exti);
|
||||
|
||||
@ -616,6 +636,7 @@ static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status)
|
||||
|
||||
/* Reset Extended information if ospf interface goes Down */
|
||||
if (oi->state == ISM_Down) {
|
||||
ospf_extended_lsa_delete(exti);
|
||||
exti->area = NULL;
|
||||
exti->flags = EXT_LPFLG_LSA_INACTIVE;
|
||||
return;
|
||||
@ -629,12 +650,12 @@ static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status)
|
||||
exti->instance = get_ext_pref_instance_value();
|
||||
exti->area = oi->area;
|
||||
|
||||
zlog_debug("EXT (%s): Set Prefix SID to interface %s ",
|
||||
__func__, oi->ifp->name);
|
||||
osr_debug("EXT (%s): Set Prefix SID to interface %s ",
|
||||
__func__, oi->ifp->name);
|
||||
|
||||
/* Complete SRDB if the interface belongs to a Prefix */
|
||||
if (OspfEXT.enabled)
|
||||
ospf_sr_update_prefix(oi->ifp, oi->address);
|
||||
ospf_sr_update_local_prefix(oi->ifp, oi->address);
|
||||
} else {
|
||||
/* Determine if interface is related to Adj. or LAN Adj. SID */
|
||||
if (oi->state == ISM_DR)
|
||||
@ -650,9 +671,9 @@ static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status)
|
||||
* Note: Adjacency SID information are completed when ospf
|
||||
* adjacency become up see ospf_ext_link_nsm_change()
|
||||
*/
|
||||
zlog_debug("EXT (%s): Set %sAdjacency SID for interface %s ",
|
||||
__func__, exti->stype == ADJ_SID ? "" : "LAN-",
|
||||
oi->ifp->name);
|
||||
osr_debug("EXT (%s): Set %sAdjacency SID for interface %s ",
|
||||
__func__, exti->stype == ADJ_SID ? "" : "LAN-",
|
||||
oi->ifp->name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,8 +687,8 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
|
||||
struct ext_itf *exti;
|
||||
uint32_t label;
|
||||
|
||||
/* Process Neighbor only when its state is NSM Full */
|
||||
if (nbr->state != NSM_Full)
|
||||
/* Process Link only when neighbor old or new state is NSM Full */
|
||||
if (nbr->state != NSM_Full && old_status != NSM_Full)
|
||||
return;
|
||||
|
||||
/* Get interface information for Segment Routing */
|
||||
@ -679,6 +700,23 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check that we have a valid area and ospf context */
|
||||
if (oi->area == NULL || oi->area->ospf == NULL) {
|
||||
flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED,
|
||||
"EXT (%s): Cannot refer to OSPF from OI(%s)",
|
||||
__func__, IF_NAME(oi));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remove Extended Link if Neighbor State goes Down or Deleted */
|
||||
if (nbr->state == NSM_Down || nbr->state == NSM_Deleted) {
|
||||
ospf_extended_lsa_delete(exti);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Keep Area information in combination with SR info. */
|
||||
exti->area = oi->area;
|
||||
|
||||
/* Process only Adjacency/LAN SID */
|
||||
if (exti->stype == PREF_SID)
|
||||
return;
|
||||
@ -742,11 +780,9 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_DEBUG_OSPF_SR)
|
||||
zlog_debug(
|
||||
"EXT (%s): Complete %sAdjacency SID for interface %s ",
|
||||
__func__, exti->stype == ADJ_SID ? "" : "LAN-",
|
||||
oi->ifp->name);
|
||||
osr_debug("EXT (%s): Complete %sAdjacency SID for interface %s ",
|
||||
__func__, exti->stype == ADJ_SID ? "" : "LAN-",
|
||||
oi->ifp->name);
|
||||
|
||||
/* flood this links params if everything is ok */
|
||||
SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE);
|
||||
@ -756,6 +792,9 @@ static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status)
|
||||
else
|
||||
ospf_ext_link_lsa_schedule(exti, REORIGINATE_THIS_LSA);
|
||||
}
|
||||
|
||||
/* Finally install (LAN)Adjacency-SID in the SRDB */
|
||||
ospf_sr_ext_itf_add(exti);
|
||||
}
|
||||
|
||||
/* Callbacks to handle Extended Link Segment Routing LSA information */
|
||||
@ -778,6 +817,10 @@ static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa)
|
||||
!= OPAQUE_TYPE_EXTENDED_LINK_LSA)
|
||||
return 0;
|
||||
|
||||
/* Check if it is not my LSA */
|
||||
if (IS_LSA_SELF(lsa))
|
||||
return 0;
|
||||
|
||||
/* Check if Extended is enable */
|
||||
if (!OspfEXT.enabled)
|
||||
return 0;
|
||||
@ -962,12 +1005,10 @@ static struct ospf_lsa *ospf_ext_pref_lsa_new(struct ospf_area *area,
|
||||
/* Set opaque-LSA header fields. */
|
||||
lsa_header_set(s, options, lsa_type, lsa_id, router_id);
|
||||
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
|
||||
zlog_debug(
|
||||
"EXT (%s): LSA[Type%u:%s]: Create an Opaque-LSA "
|
||||
"Extended Prefix Opaque LSA instance",
|
||||
__func__, lsa_type, inet_ntoa(lsa_id));
|
||||
|
||||
osr_debug(
|
||||
"EXT (%s): LSA[Type%u:%pI4]: Create an Opaque-LSA Extended "
|
||||
"Prefix Opaque LSA instance",
|
||||
__func__, lsa_type, &lsa_id);
|
||||
|
||||
/* Set opaque-LSA body fields. */
|
||||
ospf_ext_pref_lsa_body_set(s, exti);
|
||||
@ -1022,11 +1063,10 @@ static struct ospf_lsa *ospf_ext_link_lsa_new(struct ospf_area *area,
|
||||
tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance);
|
||||
lsa_id.s_addr = htonl(tmp);
|
||||
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
|
||||
zlog_debug(
|
||||
"EXT (%s) LSA[Type%u:%s]: Create an Opaque-LSA "
|
||||
"Extended Link Opaque LSA instance",
|
||||
__func__, lsa_type, inet_ntoa(lsa_id));
|
||||
osr_debug(
|
||||
"EXT (%s) LSA[Type%u:%pI4]: Create an Opaque-LSA Extended "
|
||||
"Link Opaque LSA instance",
|
||||
__func__, lsa_type, &lsa_id);
|
||||
|
||||
/* Set opaque-LSA header fields. */
|
||||
lsa_header_set(s, options, lsa_type, lsa_id, area->ospf->router_id);
|
||||
@ -1089,17 +1129,13 @@ static int ospf_ext_pref_lsa_originate1(struct ospf_area *area,
|
||||
/* Flood new LSA through area. */
|
||||
ospf_flood_through_area(area, NULL /*nbr */, new);
|
||||
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
|
||||
char area_id[INET_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET, &area->area_id, area_id, sizeof(area_id));
|
||||
zlog_debug(
|
||||
"EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA "
|
||||
"Extended Prefix Opaque LSA: Area(%s), Link(%s)",
|
||||
__func__, new->data->type, inet_ntoa(new->data->id),
|
||||
area_id, exti->ifp->name);
|
||||
osr_debug(
|
||||
"EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA"
|
||||
"Extended Prefix Opaque LSA: Area(%pI4), Link(%s)",
|
||||
__func__, new->data->type, &new->data->id,
|
||||
&area->area_id, exti->ifp->name);
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
|
||||
ospf_lsa_header_dump(new->data);
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
@ -1141,17 +1177,13 @@ static int ospf_ext_link_lsa_originate1(struct ospf_area *area,
|
||||
/* Flood new LSA through area. */
|
||||
ospf_flood_through_area(area, NULL /*nbr */, new);
|
||||
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
|
||||
char area_id[INET_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET, &area->area_id, area_id, sizeof(area_id));
|
||||
zlog_debug(
|
||||
"EXT (%s): LSA[Type%u:%s]: Originate Opaque-LSA "
|
||||
"Extended Link Opaque LSA: Area(%s), Link(%s)",
|
||||
__func__, new->data->type, inet_ntoa(new->data->id),
|
||||
area_id, exti->ifp->name);
|
||||
osr_debug(
|
||||
"EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA "
|
||||
"Extended Link Opaque LSA: Area(%pI4), Link(%s)",
|
||||
__func__, new->data->type, &new->data->id,
|
||||
&area->area_id, exti->ifp->name);
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
|
||||
ospf_lsa_header_dump(new->data);
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
@ -1168,15 +1200,13 @@ static int ospf_ext_pref_lsa_originate(void *arg)
|
||||
|
||||
if (!OspfEXT.enabled) {
|
||||
zlog_info(
|
||||
"EXT (%s): Segment Routing "
|
||||
"functionality is Disabled now",
|
||||
"EXT (%s): Segment Routing functionality is Disabled now",
|
||||
__func__);
|
||||
rc = 0; /* This is not an error case. */
|
||||
return rc;
|
||||
}
|
||||
if (IS_DEBUG_OSPF_SR)
|
||||
zlog_debug("EXT (%s): Start Originate Prefix LSA for area %s",
|
||||
__func__, inet_ntoa(area->area_id));
|
||||
osr_debug("EXT (%s): Start Originate Prefix LSA for area %pI4",
|
||||
__func__, &area->area_id);
|
||||
|
||||
/* Check if Extended Prefix Opaque LSA is already engaged */
|
||||
for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) {
|
||||
@ -1206,12 +1236,10 @@ static int ospf_ext_pref_lsa_originate(void *arg)
|
||||
}
|
||||
|
||||
/* Ok, let's try to originate an LSA */
|
||||
if (IS_DEBUG_OSPF_SR)
|
||||
zlog_debug(
|
||||
"EXT (%s): Let's finally reoriginate the "
|
||||
"LSA 7.0.0.%u for Itf %s",
|
||||
__func__, exti->instance,
|
||||
exti->ifp ? exti->ifp->name : "");
|
||||
osr_debug(
|
||||
"EXT (%s): Let's finally re-originate the LSA 7.0.0.%u "
|
||||
"for Itf %s", __func__, exti->instance,
|
||||
exti->ifp ? exti->ifp->name : "");
|
||||
ospf_ext_pref_lsa_originate1(area, exti);
|
||||
}
|
||||
|
||||
@ -1229,8 +1257,7 @@ static int ospf_ext_link_lsa_originate(void *arg)
|
||||
|
||||
if (!OspfEXT.enabled) {
|
||||
zlog_info(
|
||||
"EXT (%s): Segment Routing "
|
||||
"functionality is Disabled now",
|
||||
"EXT (%s): Segment Routing functionality is Disabled now",
|
||||
__func__);
|
||||
rc = 0; /* This is not an error case. */
|
||||
return rc;
|
||||
@ -1242,6 +1269,10 @@ static int ospf_ext_link_lsa_originate(void *arg)
|
||||
if (exti->stype == PREF_SID)
|
||||
continue;
|
||||
|
||||
/* Skip Inactive Extended Link */
|
||||
if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))
|
||||
continue;
|
||||
|
||||
/* Process only Extended Link with valid Area ID */
|
||||
if ((exti->area == NULL)
|
||||
|| (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id)))
|
||||
@ -1268,13 +1299,11 @@ static int ospf_ext_link_lsa_originate(void *arg)
|
||||
}
|
||||
|
||||
/* Ok, let's try to originate an LSA */
|
||||
if (IS_DEBUG_OSPF_SR)
|
||||
zlog_debug(
|
||||
"EXT (%s): Let's finally reoriginate the "
|
||||
"LSA 8.0.0.%u for Itf %s through the Area %s",
|
||||
__func__, exti->instance,
|
||||
exti->ifp ? exti->ifp->name : "-",
|
||||
inet_ntoa(area->area_id));
|
||||
osr_debug(
|
||||
"EXT (%s): Let's finally reoriginate the LSA 8.0.0.%u "
|
||||
"for Itf %s through the Area %pI4", __func__,
|
||||
exti->instance, exti->ifp ? exti->ifp->name : "-",
|
||||
&area->area_id);
|
||||
ospf_ext_link_lsa_originate1(area, exti);
|
||||
}
|
||||
|
||||
@ -1297,8 +1326,7 @@ static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa)
|
||||
* It seems a slip among routers in the routing domain.
|
||||
*/
|
||||
zlog_info(
|
||||
"EXT (%s): Segment Routing functionality is "
|
||||
"Disabled",
|
||||
"EXT (%s): Segment Routing functionality is Disabled",
|
||||
__func__);
|
||||
/* Flush it anyway. */
|
||||
lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
|
||||
@ -1362,12 +1390,11 @@ static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa)
|
||||
ospf_flood_through_area(area, NULL /*nbr */, new);
|
||||
|
||||
/* Debug logging. */
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
|
||||
zlog_debug(
|
||||
"EXT (%s): LSA[Type%u:%s] Refresh Extended Prefix LSA",
|
||||
__func__, new->data->type, inet_ntoa(new->data->id));
|
||||
osr_debug("EXT (%s): LSA[Type%u:%pI4] Refresh Extended Prefix LSA",
|
||||
__func__, new->data->type, &new->data->id);
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
|
||||
ospf_lsa_header_dump(new->data);
|
||||
}
|
||||
|
||||
|
||||
return new;
|
||||
}
|
||||
@ -1438,12 +1465,10 @@ static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa)
|
||||
ospf_flood_through_area(area, NULL /*nbr */, new);
|
||||
|
||||
/* Debug logging. */
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
|
||||
zlog_debug(
|
||||
"EXT (%s): LSA[Type%u:%s]: Refresh Extended Link LSA",
|
||||
__func__, new->data->type, inet_ntoa(new->data->id));
|
||||
osr_debug("EXT (%s): LSA[Type%u:%pI4]: Refresh Extended Link LSA",
|
||||
__func__, new->data->type, &new->data->id);
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
|
||||
ospf_lsa_header_dump(new->data);
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
@ -1468,18 +1493,17 @@ static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti,
|
||||
if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)))
|
||||
return;
|
||||
|
||||
zlog_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
|
||||
opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
|
||||
opcode == REFRESH_THIS_LSA ? "Refresh" : "",
|
||||
opcode == FLUSH_THIS_LSA ? "Flush" : "",
|
||||
exti->ifp ? exti->ifp->name : "-");
|
||||
osr_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
|
||||
opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
|
||||
opcode == REFRESH_THIS_LSA ? "Refresh" : "",
|
||||
opcode == FLUSH_THIS_LSA ? "Flush" : "",
|
||||
exti->ifp ? exti->ifp->name : "-");
|
||||
|
||||
/* Verify Area */
|
||||
if (exti->area == NULL) {
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
|
||||
zlog_debug(
|
||||
"EXT (%s): Area is not yet set. Try to use Backbone Area",
|
||||
__func__);
|
||||
osr_debug(
|
||||
"EXT (%s): Area is not yet set. Try to use Backbone Area",
|
||||
__func__);
|
||||
|
||||
top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
|
||||
struct in_addr backbone = {.s_addr = INADDR_ANY};
|
||||
@ -1533,18 +1557,17 @@ static void ospf_ext_link_lsa_schedule(struct ext_itf *exti,
|
||||
if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)))
|
||||
return;
|
||||
|
||||
zlog_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
|
||||
opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
|
||||
opcode == REFRESH_THIS_LSA ? "Refresh" : "",
|
||||
opcode == FLUSH_THIS_LSA ? "Flush" : "",
|
||||
exti->ifp ? exti->ifp->name : "-");
|
||||
osr_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__,
|
||||
opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "",
|
||||
opcode == REFRESH_THIS_LSA ? "Refresh" : "",
|
||||
opcode == FLUSH_THIS_LSA ? "Flush" : "",
|
||||
exti->ifp ? exti->ifp->name : "-");
|
||||
|
||||
/* Verify Area */
|
||||
if (exti->area == NULL) {
|
||||
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
|
||||
zlog_debug(
|
||||
"EXT (%s): Area is not yet set. Try to use Backbone Area",
|
||||
__func__);
|
||||
osr_debug(
|
||||
"EXT (%s): Area is not yet set. Try to use Backbone Area",
|
||||
__func__);
|
||||
|
||||
top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
|
||||
struct in_addr backbone = {.s_addr = INADDR_ANY};
|
||||
|
@ -33,12 +33,24 @@
|
||||
#define OSPF_PATH_TYPE2_EXTERNAL 4
|
||||
#define OSPF_PATH_MAX 5
|
||||
|
||||
/* Segment Routing information to complement ospf_path structure */
|
||||
struct sr_nexthop_info {
|
||||
/* Output label associated to this route */
|
||||
mpls_label_t label_out;
|
||||
/*
|
||||
* Pointer to SR Node which is the next hop for this route
|
||||
* or NULL if next hop is the destination of the prefix
|
||||
*/
|
||||
struct sr_node *nexthop;
|
||||
};
|
||||
|
||||
/* OSPF Path. */
|
||||
struct ospf_path {
|
||||
struct in_addr nexthop;
|
||||
struct in_addr adv_router;
|
||||
ifindex_t ifindex;
|
||||
unsigned char unnumbered;
|
||||
struct sr_nexthop_info srni;
|
||||
};
|
||||
|
||||
/* Below is the structure linked to every
|
||||
|
@ -1369,7 +1369,7 @@ static int ospf_spf_calculate_timer(struct thread *thread)
|
||||
abr_time = monotime_since(&start_time, NULL);
|
||||
|
||||
/* Schedule Segment Routing update */
|
||||
ospf_sr_update_timer_add(ospf);
|
||||
ospf_sr_update_task(ospf);
|
||||
|
||||
total_spf_time =
|
||||
monotime_since(&spf_start_time, &ospf->ts_spf_duration);
|
||||
|
1103
ospfd/ospf_sr.c
1103
ospfd/ospf_sr.c
File diff suppressed because it is too large
Load Diff
@ -51,6 +51,17 @@
|
||||
#define SID_INDEX 4
|
||||
#define SID_INDEX_SIZE(U) (U)
|
||||
|
||||
/* Macro to log debug message */
|
||||
#define osr_debug(...) \
|
||||
do { \
|
||||
if (IS_DEBUG_OSPF_SR) \
|
||||
zlog_debug(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/* Macro to check if SR Prefix has no valid route */
|
||||
#define IS_NO_ROUTE(srp) ((srp->route == NULL) || (srp->route->paths == NULL) \
|
||||
|| list_isempty(srp->route->paths))
|
||||
|
||||
/* SID/Label Sub TLV - section 2.1 */
|
||||
#define SUBTLV_SID_LABEL 1
|
||||
#define SUBTLV_SID_LABEL_SIZE 8
|
||||
@ -180,16 +191,13 @@ struct sr_srgb {
|
||||
};
|
||||
|
||||
/* SID type to make difference between loopback interfaces and others */
|
||||
enum sid_type { PREF_SID, ADJ_SID, LAN_ADJ_SID };
|
||||
enum sid_type { PREF_SID, LOCAL_SID, ADJ_SID, LAN_ADJ_SID };
|
||||
|
||||
/* Structure aggregating all OSPF Segment Routing information for the node */
|
||||
struct ospf_sr_db {
|
||||
/* Status of Segment Routing: enable or disable */
|
||||
bool enabled;
|
||||
|
||||
/* Ongoing Update following an OSPF SPF */
|
||||
bool update;
|
||||
|
||||
/* Flooding Scope: Area = 10 or AS = 11 */
|
||||
uint8_t scope;
|
||||
|
||||
@ -237,7 +245,6 @@ struct sr_node {
|
||||
|
||||
/* Segment Routing - NHLFE info: support IPv4 Only */
|
||||
struct sr_nhlfe {
|
||||
struct prefix_ipv4 prefv4;
|
||||
struct in_addr nexthop;
|
||||
ifindex_t ifindex;
|
||||
mpls_label_t label_in;
|
||||
@ -251,6 +258,9 @@ struct sr_link {
|
||||
/* 24-bit Opaque-ID field value according to RFC 7684 specification */
|
||||
uint32_t instance;
|
||||
|
||||
/* Interface address */
|
||||
struct in_addr itf_addr;
|
||||
|
||||
/* Flags to manage this link parameters. */
|
||||
uint8_t flags[2];
|
||||
|
||||
@ -258,7 +268,7 @@ struct sr_link {
|
||||
uint32_t sid[2];
|
||||
enum sid_type type;
|
||||
|
||||
/* SR NHLFE for this link */
|
||||
/* SR NHLFE (Primary + Backup) for this link */
|
||||
struct sr_nhlfe nhlfe[2];
|
||||
|
||||
/* Back pointer to SR Node which advertise this Link */
|
||||
@ -271,6 +281,9 @@ struct sr_prefix {
|
||||
/* 24-bit Opaque-ID field value according to RFC 7684 specification */
|
||||
uint32_t instance;
|
||||
|
||||
/* Prefix itself */
|
||||
struct prefix_ipv4 prefv4;
|
||||
|
||||
/* Flags to manage this prefix parameters. */
|
||||
uint8_t flags;
|
||||
|
||||
@ -278,17 +291,17 @@ struct sr_prefix {
|
||||
uint32_t sid;
|
||||
enum sid_type type;
|
||||
|
||||
/* SR NHLFE for this prefix */
|
||||
/* Incoming label for this prefix */
|
||||
mpls_label_t label_in;
|
||||
|
||||
/* Back pointer to OSPF Route for remote prefix */
|
||||
struct ospf_route *route;
|
||||
|
||||
/* NHLFE for local prefix */
|
||||
struct sr_nhlfe nhlfe;
|
||||
|
||||
/* Back pointer to SR Node which advertise this Prefix */
|
||||
struct sr_node *srn;
|
||||
|
||||
/*
|
||||
* Pointer to SR Node which is the next hop for this Prefix
|
||||
* or NULL if next hop is the destination of the prefix
|
||||
*/
|
||||
struct sr_node *nexthop;
|
||||
};
|
||||
|
||||
/* Prototypes definition */
|
||||
@ -303,10 +316,15 @@ extern void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa);
|
||||
extern void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa);
|
||||
extern void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa);
|
||||
extern void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa);
|
||||
/* Segment Routing Extending Link management */
|
||||
struct ext_itf;
|
||||
extern void ospf_sr_ext_itf_add(struct ext_itf *exti);
|
||||
extern void ospf_sr_ext_itf_delete(struct ext_itf *exti);
|
||||
/* Segment Routing configuration functions */
|
||||
extern uint32_t get_ext_link_label_value(void);
|
||||
extern void ospf_sr_config_write_router(struct vty *vty);
|
||||
extern void ospf_sr_update_prefix(struct interface *ifp, struct prefix *p);
|
||||
extern void ospf_sr_update_local_prefix(struct interface *ifp,
|
||||
struct prefix *p);
|
||||
/* Segment Routing re-routing function */
|
||||
extern void ospf_sr_update_timer_add(struct ospf *ospf);
|
||||
extern void ospf_sr_update_task(struct ospf *ospf);
|
||||
#endif /* _FRR_OSPF_SR_H */
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "ospfd/ospf_nsm.h"
|
||||
#include "ospfd/ospf_zebra.h"
|
||||
#include "ospfd/ospf_te.h"
|
||||
#include "ospfd/ospf_sr.h"
|
||||
|
||||
DEFINE_MTYPE_STATIC(OSPFD, OSPF_EXTERNAL, "OSPF External route table")
|
||||
DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute")
|
||||
@ -415,6 +416,119 @@ void ospf_external_del(struct ospf *ospf, uint8_t type, unsigned short instance)
|
||||
}
|
||||
}
|
||||
|
||||
/* Update NHLFE for Prefix SID */
|
||||
void ospf_zebra_update_prefix_sid(const struct sr_prefix *srp)
|
||||
{
|
||||
struct zapi_labels zl;
|
||||
struct zapi_nexthop *znh;
|
||||
struct listnode *node;
|
||||
struct ospf_path *path;
|
||||
|
||||
osr_debug("SR (%s): Update Labels %u for Prefix %pFX", __func__,
|
||||
srp->label_in, (struct prefix *)&srp->prefv4);
|
||||
|
||||
/* Prepare message. */
|
||||
memset(&zl, 0, sizeof(zl));
|
||||
zl.type = ZEBRA_LSP_OSPF_SR;
|
||||
zl.local_label = srp->label_in;
|
||||
|
||||
switch (srp->type) {
|
||||
case LOCAL_SID:
|
||||
/* Set Label for local Prefix */
|
||||
znh = &zl.nexthops[zl.nexthop_num++];
|
||||
znh->type = NEXTHOP_TYPE_IFINDEX;
|
||||
znh->ifindex = srp->nhlfe.ifindex;
|
||||
znh->label_num = 1;
|
||||
znh->labels[0] = srp->nhlfe.label_out;
|
||||
break;
|
||||
|
||||
case PREF_SID:
|
||||
/* Update route in the RIB too. */
|
||||
SET_FLAG(zl.message, ZAPI_LABELS_FTN);
|
||||
zl.route.prefix.u.prefix4 = srp->prefv4.prefix;
|
||||
zl.route.prefix.prefixlen = srp->prefv4.prefixlen;
|
||||
zl.route.prefix.family = srp->prefv4.family;
|
||||
zl.route.type = ZEBRA_ROUTE_OSPF;
|
||||
zl.route.instance = 0;
|
||||
|
||||
/* Check that SRP contains at least one valid path */
|
||||
if (srp->route == NULL) {
|
||||
return;
|
||||
}
|
||||
for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) {
|
||||
if (path->srni.label_out == MPLS_INVALID_LABEL)
|
||||
continue;
|
||||
|
||||
if (zl.nexthop_num >= MULTIPATH_NUM)
|
||||
break;
|
||||
|
||||
znh = &zl.nexthops[zl.nexthop_num++];
|
||||
znh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
|
||||
znh->gate.ipv4 = path->nexthop;
|
||||
znh->ifindex = path->ifindex;
|
||||
znh->label_num = 1;
|
||||
znh->labels[0] = path->srni.label_out;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Finally, send message to zebra. */
|
||||
(void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_REPLACE, &zl);
|
||||
}
|
||||
|
||||
/* Remove NHLFE for Prefix-SID */
|
||||
void ospf_zebra_delete_prefix_sid(const struct sr_prefix *srp)
|
||||
{
|
||||
struct zapi_labels zl;
|
||||
|
||||
osr_debug("SR (%s): Delete Labels %u for Prefix %pFX", __func__,
|
||||
srp->label_in, (struct prefix *)&srp->prefv4);
|
||||
|
||||
/* Prepare message. */
|
||||
memset(&zl, 0, sizeof(zl));
|
||||
zl.type = ZEBRA_LSP_OSPF_SR;
|
||||
zl.local_label = srp->label_in;
|
||||
|
||||
if (srp->type == PREF_SID) {
|
||||
/* Update route in the RIB too */
|
||||
SET_FLAG(zl.message, ZAPI_LABELS_FTN);
|
||||
zl.route.prefix.u.prefix4 = srp->prefv4.prefix;
|
||||
zl.route.prefix.prefixlen = srp->prefv4.prefixlen;
|
||||
zl.route.prefix.family = srp->prefv4.family;
|
||||
zl.route.type = ZEBRA_ROUTE_OSPF;
|
||||
zl.route.instance = 0;
|
||||
}
|
||||
|
||||
/* Send message to zebra. */
|
||||
(void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_DELETE, &zl);
|
||||
}
|
||||
|
||||
/* Send MPLS Label entry to Zebra for installation or deletion */
|
||||
void ospf_zebra_send_adjacency_sid(int cmd, struct sr_nhlfe nhlfe)
|
||||
{
|
||||
struct zapi_labels zl;
|
||||
struct zapi_nexthop *znh;
|
||||
|
||||
osr_debug("SR (%s): %s Labels %u/%u for Adjacency via %u", __func__,
|
||||
cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete",
|
||||
nhlfe.label_in, nhlfe.label_out, nhlfe.ifindex);
|
||||
|
||||
memset(&zl, 0, sizeof(zl));
|
||||
zl.type = ZEBRA_LSP_OSPF_SR;
|
||||
zl.local_label = nhlfe.label_in;
|
||||
zl.nexthop_num = 1;
|
||||
znh = &zl.nexthops[0];
|
||||
znh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
|
||||
znh->gate.ipv4 = nhlfe.nexthop;
|
||||
znh->ifindex = nhlfe.ifindex;
|
||||
znh->label_num = 1;
|
||||
znh->labels[0] = nhlfe.label_out;
|
||||
|
||||
(void)zebra_send_mpls_labels(zclient, cmd, &zl);
|
||||
}
|
||||
|
||||
struct ospf_redist *ospf_redist_lookup(struct ospf *ospf, uint8_t type,
|
||||
unsigned short instance)
|
||||
{
|
||||
@ -1351,6 +1465,7 @@ void ospf_zebra_vrf_deregister(struct ospf *ospf)
|
||||
zclient_send_dereg_requests(zclient, ospf->vrf_id);
|
||||
}
|
||||
}
|
||||
|
||||
static void ospf_zebra_connected(struct zclient *zclient)
|
||||
{
|
||||
/* Send the client registration */
|
||||
|
@ -63,6 +63,13 @@ extern struct ospf_external *ospf_external_lookup(struct ospf *, uint8_t,
|
||||
unsigned short);
|
||||
extern struct ospf_external *ospf_external_add(struct ospf *, uint8_t,
|
||||
unsigned short);
|
||||
|
||||
struct sr_prefix;
|
||||
struct sr_nhlfe;
|
||||
extern void ospf_zebra_update_prefix_sid(const struct sr_prefix *srp);
|
||||
extern void ospf_zebra_delete_prefix_sid(const struct sr_prefix *srp);
|
||||
extern void ospf_zebra_send_adjacency_sid(int cmd, struct sr_nhlfe nhlfe);
|
||||
|
||||
extern void ospf_external_del(struct ospf *, uint8_t, unsigned short);
|
||||
extern struct ospf_redist *ospf_redist_lookup(struct ospf *, uint8_t,
|
||||
unsigned short);
|
||||
@ -70,7 +77,6 @@ extern struct ospf_redist *ospf_redist_add(struct ospf *, uint8_t,
|
||||
unsigned short);
|
||||
extern void ospf_redist_del(struct ospf *, uint8_t, unsigned short);
|
||||
|
||||
|
||||
extern int ospf_redistribute_set(struct ospf *, int, unsigned short, int, int);
|
||||
extern int ospf_redistribute_unset(struct ospf *, int, unsigned short);
|
||||
extern int ospf_redistribute_default_set(struct ospf *, int, int, int);
|
||||
|
@ -15,9 +15,18 @@
|
||||
"prefix":"10.0.255.2\/32",
|
||||
"sid":200,
|
||||
"inputLabel":20200,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r1-eth0",
|
||||
"nexthop":"10.0.1.2"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":3,
|
||||
"interface":"r1-eth0",
|
||||
"nexthop":"10.0.0.2"
|
||||
},
|
||||
{
|
||||
"outputLabel":3,
|
||||
"interface":"r1-eth1",
|
||||
"nexthop":"10.0.1.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -36,9 +45,18 @@
|
||||
"prefix":"10.0.255.4\/32",
|
||||
"sid":400,
|
||||
"inputLabel":20400,
|
||||
"outputLabel":"8400",
|
||||
"interface":"r1-eth0",
|
||||
"nexthop":"10.0.1.2"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":8400,
|
||||
"interface":"r1-eth0",
|
||||
"nexthop":"10.0.0.2"
|
||||
},
|
||||
{
|
||||
"outputLabel":8400,
|
||||
"interface":"r1-eth1",
|
||||
"nexthop":"10.0.1.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -47,19 +65,24 @@
|
||||
"srgbSize":10000,
|
||||
"srgbLabel":10000,
|
||||
"algorithms":[
|
||||
{
|
||||
"0":"SPF"
|
||||
}
|
||||
],
|
||||
"nodeMsd":8,
|
||||
"extendedPrefix":[
|
||||
{
|
||||
"prefix":"10.0.255.3\/32",
|
||||
"sid":300,
|
||||
"inputLabel":20300,
|
||||
"outputLabel":"8300",
|
||||
"interface":"r1-eth0",
|
||||
"nexthop":"10.0.1.2"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":8300,
|
||||
"interface":"r1-eth0",
|
||||
"nexthop":"10.0.0.2"
|
||||
},
|
||||
{
|
||||
"outputLabel":8300,
|
||||
"interface":"r1-eth1",
|
||||
"nexthop":"10.0.1.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -78,26 +101,46 @@
|
||||
"prefix":"10.0.255.1\/32",
|
||||
"sid":100,
|
||||
"inputLabel":20100,
|
||||
"outputLabel":"pop",
|
||||
"interface":"lo",
|
||||
"nexthop":"10.0.255.1"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":3,
|
||||
"interface":"lo",
|
||||
"nexthop":"10.0.255.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"extendedLink":[
|
||||
{
|
||||
"prefix":"10.0.1.1\/32",
|
||||
"sid":50001,
|
||||
"inputLabel":50001,
|
||||
"outputLabel":"pop",
|
||||
"prefix":"10.0.0.1\/32",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r1-eth0",
|
||||
"nexthop":"10.0.0.2"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.0.1\/32",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r1-eth0",
|
||||
"nexthop":"10.0.0.2"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.1.1\/32",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r1-eth1",
|
||||
"nexthop":"10.0.1.2"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.1.1\/32",
|
||||
"sid":50000,
|
||||
"inputLabel":50000,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r1-eth0",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r1-eth1",
|
||||
"nexthop":"10.0.1.2"
|
||||
}
|
||||
]
|
||||
|
@ -3,6 +3,11 @@ interface lo
|
||||
ip ospf area 0.0.0.0
|
||||
!
|
||||
interface r1-eth0
|
||||
ip ospf network point-to-point
|
||||
ip ospf area 0.0.0.0
|
||||
!
|
||||
interface r1-eth1
|
||||
ip ospf network point-to-point
|
||||
ip ospf area 0.0.0.0
|
||||
!
|
||||
router ospf
|
||||
|
@ -3,6 +3,9 @@ interface lo
|
||||
ip address 10.0.255.1/32
|
||||
!
|
||||
interface r1-eth0
|
||||
ip address 10.0.0.1/24
|
||||
!
|
||||
interface r1-eth1
|
||||
ip address 10.0.1.1/24
|
||||
!
|
||||
ip forwarding
|
||||
|
@ -7,8 +7,7 @@
|
||||
"type":"SR (OSPF)",
|
||||
"outLabel":3,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.255.1"
|
||||
"installed":true
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -22,6 +21,13 @@
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.1.2"
|
||||
},
|
||||
{
|
||||
"type":"SR (OSPF)",
|
||||
"outLabel":3,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.0.2"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -35,6 +41,13 @@
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.1.2"
|
||||
},
|
||||
{
|
||||
"type":"SR (OSPF)",
|
||||
"outLabel":8300,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.0.2"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -48,11 +61,44 @@
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.1.2"
|
||||
},
|
||||
{
|
||||
"type":"SR (OSPF)",
|
||||
"outLabel":8400,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.0.2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"50000":{
|
||||
"inLabel":50000,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
"type":"SR (OSPF)",
|
||||
"outLabel":3,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.0.2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
"type":"SR (OSPF)",
|
||||
"outLabel":3,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.0.2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
@ -64,8 +110,8 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"50001":{
|
||||
"inLabel":50001,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
|
@ -15,59 +15,79 @@
|
||||
"prefix":"10.0.255.2\/32",
|
||||
"sid":200,
|
||||
"inputLabel":0,
|
||||
"outputLabel":"0",
|
||||
"interface":"lo",
|
||||
"nexthop":"10.0.255.2"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":0,
|
||||
"interface":"lo",
|
||||
"nexthop":"10.0.255.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"extendedLink":[
|
||||
{
|
||||
"prefix":"10.0.4.2\/32",
|
||||
"sid":50001,
|
||||
"inputLabel":50001,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r2-eth2",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r2-eth3",
|
||||
"nexthop":"10.0.4.1"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.4.2\/32",
|
||||
"sid":50000,
|
||||
"inputLabel":50000,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r2-eth2",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r2-eth3",
|
||||
"nexthop":"10.0.4.1"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.3.2\/32",
|
||||
"sid":50003,
|
||||
"inputLabel":50003,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r2-eth1",
|
||||
"nexthop":"10.0.3.1"
|
||||
"prefix":"10.0.0.2\/32",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r2-eth0",
|
||||
"nexthop":"10.0.0.1"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.3.2\/32",
|
||||
"sid":50002,
|
||||
"inputLabel":50002,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r2-eth1",
|
||||
"nexthop":"10.0.3.1"
|
||||
"prefix":"10.0.0.2\/32",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r2-eth0",
|
||||
"nexthop":"10.0.0.1"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.1.2\/32",
|
||||
"sid":50005,
|
||||
"inputLabel":50005,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r2-eth0",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r2-eth1",
|
||||
"nexthop":"10.0.1.1"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.1.2\/32",
|
||||
"sid":50004,
|
||||
"inputLabel":50004,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r2-eth0",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r2-eth1",
|
||||
"nexthop":"10.0.1.1"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.3.2\/32",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r2-eth2",
|
||||
"nexthop":"10.0.3.1"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.3.2\/32",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r2-eth2",
|
||||
"nexthop":"10.0.3.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -76,19 +96,19 @@
|
||||
"srgbSize":10000,
|
||||
"srgbLabel":10000,
|
||||
"algorithms":[
|
||||
{
|
||||
"0":"SPF"
|
||||
}
|
||||
],
|
||||
"nodeMsd":12,
|
||||
"extendedPrefix":[
|
||||
{
|
||||
"prefix":"10.0.255.4\/32",
|
||||
"sid":400,
|
||||
"inputLabel":8400,
|
||||
"outputLabel":"10400",
|
||||
"interface":"r2-eth2",
|
||||
"nexthop":"10.0.4.1"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":10400,
|
||||
"interface":"r2-eth3",
|
||||
"nexthop":"10.0.4.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -97,19 +117,19 @@
|
||||
"srgbSize":10000,
|
||||
"srgbLabel":10000,
|
||||
"algorithms":[
|
||||
{
|
||||
"0":"SPF"
|
||||
}
|
||||
],
|
||||
"nodeMsd":8,
|
||||
"extendedPrefix":[
|
||||
{
|
||||
"prefix":"10.0.255.3\/32",
|
||||
"sid":300,
|
||||
"inputLabel":8300,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r2-eth1",
|
||||
"nexthop":"10.0.3.1"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":3,
|
||||
"interface":"r2-eth2",
|
||||
"nexthop":"10.0.3.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -118,19 +138,24 @@
|
||||
"srgbSize":10000,
|
||||
"srgbLabel":20000,
|
||||
"algorithms":[
|
||||
{
|
||||
"0":"SPF"
|
||||
}
|
||||
],
|
||||
"nodeMsd":16,
|
||||
"extendedPrefix":[
|
||||
{
|
||||
"prefix":"10.0.255.1\/32",
|
||||
"sid":100,
|
||||
"inputLabel":8100,
|
||||
"outputLabel":"20100",
|
||||
"interface":"r2-eth0",
|
||||
"nexthop":"10.0.1.1"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":20100,
|
||||
"interface":"r2-eth0",
|
||||
"nexthop":"10.0.0.1"
|
||||
},
|
||||
{
|
||||
"outputLabel":20100,
|
||||
"interface":"r2-eth1",
|
||||
"nexthop":"10.0.1.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ interface lo
|
||||
ip ospf area 0.0.0.0
|
||||
!
|
||||
interface r2-eth0
|
||||
ip ospf network point-to-point
|
||||
ip ospf area 0.0.0.0
|
||||
!
|
||||
interface r2-eth1
|
||||
@ -12,6 +13,9 @@ interface r2-eth1
|
||||
ip ospf area 0.0.0.0
|
||||
!
|
||||
interface r2-eth2
|
||||
ip ospf area 0.0.0.0
|
||||
!
|
||||
interface r2-eth3
|
||||
ip ospf network point-to-point
|
||||
ip ospf area 0.0.0.0
|
||||
!
|
||||
|
@ -3,12 +3,15 @@ interface lo
|
||||
ip address 10.0.255.2/32
|
||||
!
|
||||
interface r2-eth0
|
||||
ip address 10.0.1.2/24
|
||||
ip address 10.0.0.2/24
|
||||
!
|
||||
interface r2-eth1
|
||||
ip address 10.0.3.2/24
|
||||
ip address 10.0.1.2/24
|
||||
!
|
||||
interface r2-eth2
|
||||
ip address 10.0.3.2/24
|
||||
!
|
||||
interface r2-eth3
|
||||
ip address 10.0.4.2/24
|
||||
!
|
||||
ip forwarding
|
||||
|
@ -9,6 +9,13 @@
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.1.1"
|
||||
},
|
||||
{
|
||||
"type":"SR (OSPF)",
|
||||
"outLabel":20100,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.0.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -38,8 +45,8 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"50000":{
|
||||
"inLabel":50000,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
@ -51,8 +58,8 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"50001":{
|
||||
"inLabel":50001,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
@ -64,8 +71,8 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"50002":{
|
||||
"inLabel":50002,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
@ -73,12 +80,12 @@
|
||||
"outLabel":3,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.3.1"
|
||||
"nexthop":"10.0.0.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"50003":{
|
||||
"inLabel":50003,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
@ -86,12 +93,12 @@
|
||||
"outLabel":3,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.3.1"
|
||||
"nexthop":"10.0.0.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"50004":{
|
||||
"inLabel":50004,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
@ -103,8 +110,8 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"50005":{
|
||||
"inLabel":50005,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
@ -115,5 +122,31 @@
|
||||
"nexthop":"10.0.1.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
"type":"SR (OSPF)",
|
||||
"outLabel":3,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.3.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
"type":"SR (OSPF)",
|
||||
"outLabel":3,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.3.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,13 @@
|
||||
"prefix":"10.0.255.2\/32",
|
||||
"sid":200,
|
||||
"inputLabel":10200,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r3-eth0",
|
||||
"nexthop":"10.0.3.2"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":3,
|
||||
"interface":"r3-eth0",
|
||||
"nexthop":"10.0.3.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -36,9 +40,13 @@
|
||||
"prefix":"10.0.255.4\/32",
|
||||
"sid":400,
|
||||
"inputLabel":10400,
|
||||
"outputLabel":"8400",
|
||||
"interface":"r3-eth0",
|
||||
"nexthop":"10.0.3.2"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":8400,
|
||||
"interface":"r3-eth0",
|
||||
"nexthop":"10.0.3.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -57,25 +65,29 @@
|
||||
"prefix":"10.0.255.3\/32",
|
||||
"sid":300,
|
||||
"inputLabel":0,
|
||||
"outputLabel":"0",
|
||||
"interface":"lo",
|
||||
"nexthop":"10.0.255.3"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":0,
|
||||
"interface":"lo",
|
||||
"nexthop":"10.0.255.3"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"extendedLink":[
|
||||
{
|
||||
"prefix":"10.0.3.1\/32",
|
||||
"sid":50001,
|
||||
"inputLabel":50001,
|
||||
"outputLabel":"pop",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r3-eth0",
|
||||
"nexthop":"10.0.3.2"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.3.1\/32",
|
||||
"sid":50000,
|
||||
"inputLabel":50000,
|
||||
"outputLabel":"pop",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r3-eth0",
|
||||
"nexthop":"10.0.3.2"
|
||||
}
|
||||
@ -96,9 +108,13 @@
|
||||
"prefix":"10.0.255.1\/32",
|
||||
"sid":100,
|
||||
"inputLabel":10100,
|
||||
"outputLabel":"8100",
|
||||
"interface":"r3-eth0",
|
||||
"nexthop":"10.0.3.2"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":8100,
|
||||
"interface":"r3-eth0",
|
||||
"nexthop":"10.0.3.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ interface lo
|
||||
ip ospf area 0.0.0.0
|
||||
!
|
||||
interface r3-eth0
|
||||
ip ospf network point-to-point
|
||||
ip ospf area 0.0.0.0
|
||||
!
|
||||
!
|
||||
|
@ -38,8 +38,8 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"50000":{
|
||||
"inLabel":50000,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
@ -51,8 +51,8 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"50001":{
|
||||
"inLabel":50001,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
|
@ -6,18 +6,19 @@
|
||||
"srgbSize":20000,
|
||||
"srgbLabel":8000,
|
||||
"algorithms":[
|
||||
{
|
||||
"0":"SPF"
|
||||
}
|
||||
],
|
||||
"extendedPrefix":[
|
||||
{
|
||||
"prefix":"10.0.255.2\/32",
|
||||
"sid":200,
|
||||
"inputLabel":10200,
|
||||
"outputLabel":"pop",
|
||||
"interface":"r4-eth0",
|
||||
"nexthop":"10.0.4.2"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":3,
|
||||
"interface":"r4-eth0",
|
||||
"nexthop":"10.0.4.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -36,25 +37,29 @@
|
||||
"prefix":"10.0.255.4\/32",
|
||||
"sid":400,
|
||||
"inputLabel":10400,
|
||||
"outputLabel":"pop",
|
||||
"interface":"lo",
|
||||
"nexthop":"10.0.255.4"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":3,
|
||||
"interface":"lo",
|
||||
"nexthop":"10.0.255.4"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"extendedLink":[
|
||||
{
|
||||
"prefix":"10.0.4.1\/32",
|
||||
"sid":50001,
|
||||
"inputLabel":50001,
|
||||
"outputLabel":"pop",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r4-eth0",
|
||||
"nexthop":"10.0.4.2"
|
||||
},
|
||||
{
|
||||
"prefix":"10.0.4.1\/32",
|
||||
"sid":50000,
|
||||
"inputLabel":50000,
|
||||
"outputLabel":"pop",
|
||||
"sid":"XX",
|
||||
"inputLabel":"XX",
|
||||
"outputLabel":3,
|
||||
"interface":"r4-eth0",
|
||||
"nexthop":"10.0.4.2"
|
||||
}
|
||||
@ -65,19 +70,19 @@
|
||||
"srgbSize":10000,
|
||||
"srgbLabel":10000,
|
||||
"algorithms":[
|
||||
{
|
||||
"0":"SPF"
|
||||
}
|
||||
],
|
||||
"nodeMsd":8,
|
||||
"extendedPrefix":[
|
||||
{
|
||||
"prefix":"10.0.255.3\/32",
|
||||
"sid":300,
|
||||
"inputLabel":10300,
|
||||
"outputLabel":"8300",
|
||||
"interface":"r4-eth0",
|
||||
"nexthop":"10.0.4.2"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":8300,
|
||||
"interface":"r4-eth0",
|
||||
"nexthop":"10.0.4.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -86,19 +91,19 @@
|
||||
"srgbSize":10000,
|
||||
"srgbLabel":20000,
|
||||
"algorithms":[
|
||||
{
|
||||
"0":"SPF"
|
||||
}
|
||||
],
|
||||
"nodeMsd":16,
|
||||
"extendedPrefix":[
|
||||
{
|
||||
"prefix":"10.0.255.1\/32",
|
||||
"sid":100,
|
||||
"inputLabel":10100,
|
||||
"outputLabel":"8100",
|
||||
"interface":"r4-eth0",
|
||||
"nexthop":"10.0.4.2"
|
||||
"prefixRoute":[
|
||||
{
|
||||
"outputLabel":8100,
|
||||
"interface":"r4-eth0",
|
||||
"nexthop":"10.0.4.2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -46,13 +46,12 @@
|
||||
"type":"SR (OSPF)",
|
||||
"outLabel":3,
|
||||
"distance":150,
|
||||
"installed":true,
|
||||
"nexthop":"10.0.255.4"
|
||||
"installed":true
|
||||
}
|
||||
]
|
||||
},
|
||||
"50000":{
|
||||
"inLabel":50000,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
@ -64,8 +63,8 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"50001":{
|
||||
"inLabel":50001,
|
||||
"XX":{
|
||||
"inLabel":"XX",
|
||||
"installed":true,
|
||||
"nexthops":[
|
||||
{
|
||||
|
@ -27,6 +27,7 @@ test_ospf_sr_topo1.py: Test the FRR OSPF routing daemon with Segment Routing.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from functools import partial
|
||||
|
||||
@ -62,20 +63,23 @@ class OspfSrTopo(Topo):
|
||||
for routern in range(1, 5):
|
||||
tgen.add_router("r{}".format(routern))
|
||||
|
||||
# Interconect router 1 and 2
|
||||
switch = tgen.add_switch("s1")
|
||||
switch.add_link(tgen.gears["r1"])
|
||||
switch.add_link(tgen.gears["r2"])
|
||||
# Interconect router 1 and 2 with 2 links
|
||||
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['r2'])
|
||||
|
||||
# Interconect router 3 and 2
|
||||
switch = tgen.add_switch("s2")
|
||||
switch.add_link(tgen.gears["r3"])
|
||||
switch.add_link(tgen.gears["r2"])
|
||||
switch = tgen.add_switch('s3')
|
||||
switch.add_link(tgen.gears['r3'])
|
||||
switch.add_link(tgen.gears['r2'])
|
||||
|
||||
# Interconect router 4 and 2
|
||||
switch = tgen.add_switch("s3")
|
||||
switch.add_link(tgen.gears["r4"])
|
||||
switch.add_link(tgen.gears["r2"])
|
||||
switch = tgen.add_switch('s4')
|
||||
switch.add_link(tgen.gears['r4'])
|
||||
switch.add_link(tgen.gears['r2'])
|
||||
|
||||
|
||||
def setup_module(mod):
|
||||
@ -130,6 +134,9 @@ def compare_ospf_srdb(rname, expected):
|
||||
"""
|
||||
tgen = get_topogen()
|
||||
current = tgen.gears[rname].vtysh_cmd("show ip ospf database segment-routing json")
|
||||
# Filter Adjacency SID allocation
|
||||
current = re.sub(r'"sid":5000[0-9],', '"sid":"XX",', current)
|
||||
current = re.sub(r'"inputLabel":5000[0-9],', '"inputLabel":"XX",', current)
|
||||
return topotest.difflines(
|
||||
current, expected, title1="Current output", title2="Expected output"
|
||||
)
|
||||
@ -142,6 +149,9 @@ def compare_mpls_table(rname, expected):
|
||||
"""
|
||||
tgen = get_topogen()
|
||||
current = tgen.gears[rname].vtysh_cmd("show mpls table json")
|
||||
# Filter Adjacency SID allocation
|
||||
current = re.sub(r'"5000[0-9]":', '"XX":', current)
|
||||
current = re.sub(r'"inLabel":5000[0-9],', '"inLabel":"XX",', current)
|
||||
return topotest.difflines(
|
||||
current, expected, title1="Current output", title2="Expected output"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user