summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRuss White <russ@riw.us>2021-03-30 16:28:37 +0200
committerGitHub <noreply@github.com>2021-03-30 16:28:37 +0200
commita2d351d19e60c771b02d4d103541e13a9f1b7402 (patch)
treeee86d91543d76e6d0b07be5fe69cc4b475066fdc
parentMerge pull request #8056 from jmmikkel/peertype (diff)
parentsharpd: Add Traffic Engineering Database (TED) (diff)
downloadfrr-a2d351d19e60c771b02d4d103541e13a9f1b7402.tar.xz
frr-a2d351d19e60c771b02d4d103541e13a9f1b7402.zip
Merge pull request #8137 from Orange-OpenSource/ospf_ls
ospfd: Add Link State support
-rw-r--r--doc/developer/link-state.rst238
-rw-r--r--doc/user/ospfd.rst21
-rw-r--r--doc/user/sharp.rst12
-rw-r--r--lib/link_state.c1425
-rw-r--r--lib/link_state.h405
-rw-r--r--lib/zclient.h2
-rw-r--r--ospfd/ospf_te.c2221
-rw-r--r--ospfd/ospf_te.h47
-rw-r--r--ospfd/ospf_zebra.c8
-rw-r--r--sharpd/sharp_globals.h3
-rw-r--r--sharpd/sharp_main.c2
-rw-r--r--sharpd/sharp_vty.c139
-rw-r--r--sharpd/sharp_zebra.c27
-rw-r--r--sharpd/sharp_zebra.h3
-rwxr-xr-xtests/topotests/ospf-te-topo1/__init__.py0
-rw-r--r--tests/topotests/ospf-te-topo1/r1/ospfd.conf23
-rw-r--r--tests/topotests/ospf-te-topo1/r1/zebra.conf21
-rw-r--r--tests/topotests/ospf-te-topo1/r2/ospfd.conf34
-rw-r--r--tests/topotests/ospf-te-topo1/r2/zebra.conf33
-rw-r--r--tests/topotests/ospf-te-topo1/r3/ospfd.conf24
-rw-r--r--tests/topotests/ospf-te-topo1/r3/zebra.conf22
-rw-r--r--tests/topotests/ospf-te-topo1/r4/ospfd.conf22
-rw-r--r--tests/topotests/ospf-te-topo1/r4/zebra.conf12
-rw-r--r--tests/topotests/ospf-te-topo1/reference/ted_step1.json577
-rw-r--r--tests/topotests/ospf-te-topo1/reference/ted_step2.json477
-rw-r--r--tests/topotests/ospf-te-topo1/reference/ted_step3.json409
-rw-r--r--tests/topotests/ospf-te-topo1/reference/ted_step4.json490
-rw-r--r--tests/topotests/ospf-te-topo1/reference/ted_step5.json614
-rw-r--r--tests/topotests/ospf-te-topo1/reference/ted_step6.json615
-rw-r--r--tests/topotests/ospf-te-topo1/reference/ted_step7.json456
-rw-r--r--tests/topotests/ospf-te-topo1/test_ospf_te_topo1.py314
31 files changed, 8246 insertions, 450 deletions
diff --git a/doc/developer/link-state.rst b/doc/developer/link-state.rst
index f1fc52966..1cbaf27ff 100644
--- a/doc/developer/link-state.rst
+++ b/doc/developer/link-state.rst
@@ -81,26 +81,47 @@ corresponds to a Link State information conveyed by the routing protocol.
Functions
^^^^^^^^^
-A set of functions is provided to create, delete and compare Link State Node:
+A set of functions is provided to create, delete and compare Link State
+Node, Atribute and Prefix:
.. c:function:: struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr router_id, struct in6_addr router6_id)
-.. c:function:: voidls_node_del(struct ls_node *node)
-.. c:function:: int ls_node_same(struct ls_node *n1, struct ls_node *n2)
+.. c:function:: struct ls_attributes *ls_attributes_new(struct ls_node_id adv, struct in_addr local, struct in6_addr local6, uint32_t local_id)
+.. c:function:: struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p)
-and Link State Attributes:
+ Create respectively a new Link State Node, Attribute or Prefix.
+ Structure is dynamically allocated. Link State Node ID (adv) is mandatory
+ and:
-.. c:function:: struct ls_attributes *ls_attributes_new(struct ls_node_id adv, struct in_addr local, struct in6_addr local6, uint32_t local_id)
+ - at least one of IPv4 or IPv6 must be provided for the router ID
+ (router_id or router6_id) for Node
+ - at least one of local, local6 or local_id must be provided for Attribute
+ - prefix is mandatory for Link State Prefix.
+
+.. c:function:: void ls_node_del(struct ls_node *node)
.. c:function:: void ls_attributes_del(struct ls_attributes *attr)
+.. c:function:: void ls_prefix_del(struct ls_prefix *pref)
+
+ Remove, respectively Link State Node, Attributes or Prefix.
+ Data structure is freed.
+
+.. c:function:: void ls_attributes_srlg_del(struct ls_attributes *attr)
+
+ Remove SRLGs attribute if defined. Data structure is freed.
+
+.. c:function:: int ls_node_same(struct ls_node *n1, struct ls_node *n2)
.. c:function:: int ls_attributes_same(struct ls_attributes *a1, struct ls_attributes *a2)
+.. c:function:: int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix*p2)
+
+ Check, respectively if two Link State Nodes, Attributes or Prefix are equal.
+ Note that these routines have the same return value sense as '==' (which is
+ different from a comparison).
-The low level API doesn't provide any particular functions for the Link State
-Prefix structure as this latter is simpler to manipulate.
Link State TED
--------------
This is the high level API that provides functions to create, update, delete a
-Link State Database to from a Traffic Engineering Database (TED).
+Link State Database to build a Traffic Engineering Database (TED).
Data Structures
^^^^^^^^^^^^^^^
@@ -143,35 +164,143 @@ A unique Key is used to identify both Vertices and Edges within the Graph.
.. c:type:: struct ls_prefix
.. c:type:: struct ls_ted
+TED stores Vertex, Edge and Subnet elements with a RB Tree structure.
+The Vertex key corresponds to the Router ID for OSPF and ISO System ID for
+IS-IS. The Edge key corresponds to the IPv4 address, the lowest 64 bits of
+the IPv6 address or the combination of the local & remote ID of the interface.
+The Subnet key corresponds to the Prefix address (v4 or v6).
-Functions
-^^^^^^^^^
+An additional status for Vertex, Edge and Subnet allows to determine the state
+of the element in the TED: UNSET, NEW, UPDATE, DELETE, SYNC, ORPHAN. Normal
+state is SYNC. NEW, UPDATE and DELETE are temporary state when element is
+processed. UNSET is normally never used and ORPHAN serves to identify elements
+that must be remove when TED is cleaning.
+
+Vertex, Edges and Subnets management functions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. c:function:: struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node)
+.. c:function:: struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes)
+.. c:function:: struct ls_subnet *ls_subnet_add(struct ls_ted *ted, struct ls_prefix *pref)
+
+ Add, respectively new Vertex, Edge or Subnet to the Link State Datebase.
+ Vertex, Edge or Subnet are created from, respectively the Link State Node,
+ Attribute or Prefix structure. Data structure are dynamically allocated.
+
.. c:function:: struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node)
+.. c:function:: struct ls_edge *ls_edge_update(struct ls_ted *ted, struct ls_attributes *attributes)
+.. c:function:: struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref)
+
+ Update, respectively Vertex, Edge or Subnet with, respectively the Link
+ State Node, Attribute or Prefix. A new data structure is created if no one
+ corresponds to the Link State Node, Attribute or Prefix. If element already
+ exists in the TED, its associated Link State information is replaced by the
+ new one if there are different and the old associated Link State information
+ is deleted and memory freed.
+
.. c:function:: void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex)
+.. c:function:: void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex)
+.. c:function:: void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge)
+.. c:function:: void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge)
+.. c:function:: void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet)
+.. c:function:: void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet)
+
+ Delete, respectively Link State Vertex, Edge or Subnet. Data structure are
+ freed but not the associated Link State information with the simple `_del()`
+ form of the function while the `_del_all()` version freed also associated
+ Link State information. TED is not modified if Vertex, Edge or Subnet is
+ NULL or not found in the Data Base. Note that references between Vertices,
+ Edges and Subnets are removed first.
+
.. c:function:: struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key)
.. c:function:: struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted, struct ls_node_id id)
-.. c:function:: int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2)
-.. c:function:: struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes)
-.. c:function:: struct ls_edge *ls_edge_update(struct ls_ted *ted, struct ls_attributes *attributes)
-.. c:function:: void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge)
+ Find Vertex in the TED by its unique key or its Link State Node ID.
+ Return Vertex if found, NULL otherwise.
+
.. c:function:: struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key)
.. c:function:: struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted, struct ls_attributes *attributes);
.. c:function:: struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted, struct ls_attributes *attributes);
-.. c:function:: struct ls_subnet *ls_subnet_add(struct ls_ted *ted, struct ls_prefix *pref)
-.. c:function:: void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet)
+ Find Edge in the Link State Data Base by its key, source or distination
+ (local IPv4 or IPv6 address or local ID) informations of the Link State
+ Attributes. Return Edge if found, NULL otherwise.
+
.. c:function:: struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix)
+ Find Subnet in the Link State Data Base by its key, i.e. the associated
+ prefix. Return Subnet if found, NULL otherwise.
+
+.. c:function:: int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2)
+.. c:function:: int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2)
+.. c:function:: int ls_subnet_same(struct ls_subnet *s1, struct ls_subnet *s2)
+
+ Check, respectively if two Vertices, Edges or Subnets are equal.
+ Note that these routines has the same return value sense as '=='
+ (which is different from a comparison).
+
+
+TED management functions
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Some helpers functions have been also provided to ease TED management:
+
.. c:function:: struct ls_ted *ls_ted_new(const uint32_t key, char *name, uint32_t asn)
+
+ Create a new Link State Data Base. Key must be different from 0.
+ Name could be NULL and AS number equal to 0 if unknown.
+
.. c:function:: void ls_ted_del(struct ls_ted *ted)
+.. c:function:: void ls_ted_del_all(struct ls_ted *ted)
+
+ Delete existing Link State Data Base. Vertices, Edges, and Subnets are not
+ removed with ls_ted_del() function while they are with ls_ted_del_all().
+
.. c:function:: void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, struct ls_edge *edge)
+
+ Connect Source and Destination Vertices by given Edge. Only non NULL source
+ and destination vertices are connected.
+
.. c:function:: void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source)
.. c:function:: void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, bool source)
+
+ Connect / Disconnect Link State Edge to the Link State Vertex which could be
+ a Source (source = true) or a Destination (source = false) Vertex.
+
.. c:function:: void ls_disconnect_edge(struct ls_edge *edge)
+ Disconnect Link State Edge from both Source and Destination Vertex.
+ Note that Edge is not removed but its status is marked as ORPHAN.
+
+.. c:function:: void ls_vertex_clean(struct ls_ted *ted, struct ls_vertex *vertex, struct zclient *zclient)
+
+ Clean Vertex structure by removing all Edges and Subnets marked as ORPHAN
+ from this vertex. Corresponding Link State Update message is sent if zclient
+ parameter is not NULL. Note that associated Link State Attribute and Prefix
+ are also removed and memory freed.
+
+.. c:function:: void ls_ted_clean(struct ls_ted *ted)
+
+ Clean Link State Data Base by removing all Vertices, Edges and SubNets
+ marked as ORPHAN. Note that associated Link State Node, Attributes and
+ Prefix are removed too.
+
+.. c:function:: void ls_show_vertex(struct ls_vertex *vertex, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_edge(struct ls_edeg *edge, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_vertices(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_edges(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_subnets(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose)
+.. c:function:: void ls_show_ted(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose)
+
+ Respectively, show Vertex, Edge, Subnet provided as parameter, all Vertices,
+ all Edges, all Subnets and the whole TED if not specified. Output could be
+ more detailed with verbose parameter for VTY output. If both JSON and VTY
+ output are specified, JSON takes precedence over VTY.
+
+.. c:function:: void ls_dump_ted(struct ls_ted *ted)
+
+ Dump TED information to the current logging output.
Link State Messages
-------------------
@@ -198,8 +327,8 @@ Figure 1 below, illustrates the ZAPI Opaque message exchange between a
message sequences are as follows:
- First, both *Producer* and *Consumer* must register to their respective ZAPI
- Opaque Message. **Link State Sync** for the *Producer* in order to receive
- Database synchronisation request from a *Consumer*. **Link State Update** for
+ Opaque Message: **Link State Sync** for the *Producer* in order to receive
+ Database synchronisation request from a *Consumer*, **Link State Update** for
the *Consumer* in order to received any Link State update from a *Producer*.
These register messages are stored by Zebra to determine to which daemon it
should redistribute the ZAPI messages it receives.
@@ -245,22 +374,22 @@ message sequences are as follows:
| | Request LS Sync | v \
| Request LS Sync |<----------------------------| |
|<-----------------------------| | Synchronistation
- | LS DB Sync | | Phase
- |----------------------------->| LS DB Sync | |
+ | LS DB Update | | Phase
+ |----------------------------->| LS DB Update | |
| |---------------------------->| |
- | LS DB Sync (cont'd) | | |
- |----------------------------->| LS DB Sync (cont'd) | |
+ | LS DB Update (cont'd) | | |
+ |----------------------------->| LS DB Update (cont'd) | |
| . |---------------------------->| |
| . | . | |
| . | . | |
- | LS DB Sync (end) | . | |
- |----------------------------->| LS DB Sync (end) | |
+ | LS DB Update (end) | . | |
+ |----------------------------->| LS DB Update (end) | |
| |---------------------------->| |
| | | /
: : :
: : :
- | LS Update | | \
- |----------------------------->| LS Update | |
+ | LS DB Update | | \
+ |----------------------------->| LS DB Update | |
| |---------------------------->| Update Phase
| | | |
: : : /
@@ -269,7 +398,7 @@ message sequences are as follows:
| | Unregister LS Update | |
| |<----------------------------| Deregister Phase
| | | |
- | LS Update | | |
+ | LS DB Update | | |
|----------------------------->| | |
| | | /
| | |
@@ -305,10 +434,65 @@ Opaque Link State type at once.
Functions
^^^^^^^^^
+.. c:function:: int ls_register(struct zclient *zclient, bool server)
+.. c:function:: int ls_unregister(struct zclient *zclient, bool server)
+
+ Register / Unregister daemon to received ZAPI Link State Opaque messages.
+ Server must be set to true for *Producer* and to false for *Consumer*.
+
+.. c:function:: int ls_request_sync(struct zclient *zclient)
+
+ Request initial Synchronisation to collect the whole Link State Database.
+
.. c:function:: struct ls_message *ls_parse_msg(struct stream *s)
+
+ Parse Link State Message from stream. Used this function once receiving a
+ new ZAPI Opaque message of type Link State.
+
+.. c:function:: void ls_delete_msg(struct ls_message *msg)
+
+ Delete existing message. Data structure is freed.
+
.. c:function:: int ls_send_msg(struct zclient *zclient, struct ls_message *msg, struct zapi_opaque_reg_info *dst)
+
+ Send Link State Message as new ZAPI Opaque message of type Link State.
+ If destination is not NULL, message is sent as Unicast otherwise it is
+ broadcast to all registered daemon.
+
.. c:function:: struct ls_message *ls_vertex2msg(struct ls_message *msg, struct ls_vertex *vertex)
.. c:function:: struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge)
.. c:function:: struct ls_message *ls_subnet2msg(struct ls_message *msg, struct ls_subnet *subnet)
+
+ Create respectively a new Link State Message from a Link State Vertex, Edge
+ or Subnet. If Link State Message is NULL, a new data structure is
+ dynamically allocated. Note that the Vertex, Edge and Subnet status is used
+ to determine the corresponding Link State Message event: ADD, UPDATE,
+ DELETE, SYNC.
+
+.. c:function:: int ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg)
+.. c:function:: int ls_msg2edge(struct ls_ted *ted, struct ls_message *msg)
+.. c:function:: int ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg)
+
+ Convert Link State Message respectively in Vertex, Edge or Subnet and
+ update the Link State Database accordingly to the message event: SYNC, ADD,
+ UPDATE or DELETE.
+
+.. c:function:: struct ls_element *ls_msg2ted(struct ls_ted *ted, struct ls_message *msg, bool delete)
+.. c:function:: struct ls_element *ls_stream2ted(struct ls_ted *ted, struct ls_message *msg, bool delete)
+
+ Convert Link State Message or Stream Buffer in a Link State element (Vertex,
+ Edge or Subnet) and update the Link State Database accordingly to the
+ message event: SYNC, ADD, UPDATE or DELETE. The function return the generic
+ structure ls_element that point to the Vertex, Edge or Subnet which has been
+ added, updated or synchronous in the database. Note that the delete boolean
+ parameter governs the action for the DELETE action: true, Link State Element
+ is removed from the database and NULL is return. If set to false, database
+ is not updated and the function sets the Link State Element status to
+ Delete and return the element for futur deletion by the calling function.
+
.. c:function:: int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, struct zapi_opaque_reg_info *dst)
+ Send all the content of the Link State Data Base to the given destination.
+ Link State content is sent is this order: Vertices, Edges then Subnet.
+ This function must be used when a daemon request a Link State Data Base
+ Synchronization.
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index af9a7844a..fc0229ba1 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -846,6 +846,13 @@ Traffic Engineering
flood in AREA <area-id> with Opaque Type-10, respectively in AS with Opaque
Type-11. In all case, Opaque-LSA TLV=6.
+.. index:: mpls-te export
+.. clicmd:: no mpls-te export
+
+ Export Traffic Engineering Data Base to other daemons through the ZAPI
+ Opaque Link State messages.
+
+.. index:: show ip ospf mpls-te interface
.. clicmd:: show ip ospf mpls-te interface
.. clicmd:: show ip ospf mpls-te interface INTERFACE
@@ -856,6 +863,20 @@ Traffic Engineering
Show Traffic Engineering router parameters.
+.. index:: show ip ospf mpls-te database [verbose|json]
+.. clicmd:: show ip ospf mpls-te database [verbose|json]
+
+.. index:: show ip ospf mpls-te database vertex [self-originate|adv-router ADV-ROUTER] [verbose|json]
+.. clicmd:: show ip ospf mpls-te database vertex [self-originate|adv-router ADV-ROUTER] [verbose|json]
+
+.. index:: show ip ospf mpls-te database edge [A.B.C.D] [verbose|json]
+.. clicmd:: show ip ospf mpls-te database edge [A.B.C.D] [verbose|json]
+
+.. index:: show ip ospf mpls-te database subnet [A.B.C.D/M] [verbose|json]
+.. clicmd:: show ip ospf mpls-te database subnet [A.B.C.D/M] [verbose|json]
+
+ Show Traffic Engineering Database
+
.. _router-information:
Router Information
diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst
index 9e83e4422..bef2748af 100644
--- a/doc/user/sharp.rst
+++ b/doc/user/sharp.rst
@@ -131,3 +131,15 @@ keyword. At present, no sharp commands will be preserved in the config.
Send an ARP/NDP request to trigger the addition of a neighbor in the ARP
table.
+
+.. clicmd:: sharp import-te
+
+ Import Traffic Engineering Database produce by OSPF or IS-IS.
+
+.. clicmd:: show sharp ted [verbose|json]
+
+.. clicmd:: show sharp ted [<vertex [A.B.C.D]|edge [A.B.C.D]|subnet [A.B.C.D/M]>] [verbose|json]
+
+ Show imported Traffic Engineering Data Base
+
+
diff --git a/lib/link_state.c b/lib/link_state.c
index 7f0d2a124..8606f8eb0 100644
--- a/lib/link_state.c
+++ b/lib/link_state.c
@@ -33,6 +33,9 @@
#include "vty.h"
#include "zclient.h"
#include "stream.h"
+#include "sbuf.h"
+#include "printfrr.h"
+#include <lib/json.h>
#include "link_state.h"
/* Link State Memory allocation */
@@ -46,7 +49,7 @@ struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid,
{
struct ls_node *new;
- if (adv.origin == NONE)
+ if (adv.origin == UNKNOWN)
return NULL;
new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node));
@@ -70,6 +73,9 @@ struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid,
void ls_node_del(struct ls_node *node)
{
+ if (!node)
+ return;
+
XFREE(MTYPE_LS_DB, node);
node = NULL;
}
@@ -111,7 +117,7 @@ struct ls_attributes *ls_attributes_new(struct ls_node_id adv,
{
struct ls_attributes *new;
- if (adv.origin == NONE)
+ if (adv.origin == UNKNOWN)
return NULL;
new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes));
@@ -139,7 +145,7 @@ struct ls_attributes *ls_attributes_new(struct ls_node_id adv,
return new;
}
-void ls_attributes_del(struct ls_attributes *attr)
+void ls_attributes_srlg_del(struct ls_attributes *attr)
{
if (!attr)
return;
@@ -147,6 +153,18 @@ void ls_attributes_del(struct ls_attributes *attr)
if (attr->srlgs)
XFREE(MTYPE_LS_DB, attr->srlgs);
+ attr->srlgs = NULL;
+ attr->srlg_len = 0;
+ UNSET_FLAG(attr->flags, LS_ATTR_SRLG);
+}
+
+void ls_attributes_del(struct ls_attributes *attr)
+{
+ if (!attr)
+ return;
+
+ ls_attributes_srlg_del(attr);
+
XFREE(MTYPE_LS_DB, attr);
attr = NULL;
}
@@ -179,75 +197,155 @@ int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2)
}
/**
- * Link State Vertices management functions
+ * Link State prefix management functions
*/
-struct ls_vertex *ls_vertex_new(struct ls_node *node)
+struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p)
{
- struct ls_vertex *new;
+ struct ls_prefix *new;
- if (node == NULL)
+ if (adv.origin == UNKNOWN)
return NULL;
- new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_vertex));
- new->node = node;
- new->incoming_edges = list_new();
- new->outgoing_edges = list_new();
- new->prefixes = list_new();
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes));
+ new->adv = adv;
+ new->pref = p;
return new;
}
-void ls_vertex_del(struct ls_vertex *vertex)
+void ls_prefix_del(struct ls_prefix *pref)
{
- if (vertex == NULL)
+ if (!pref)
return;
- list_delete_all_node(vertex->incoming_edges);
- list_delete_all_node(vertex->outgoing_edges);
- list_delete_all_node(vertex->prefixes);
- XFREE(MTYPE_LS_DB, vertex);
- vertex = NULL;
+ XFREE(MTYPE_LS_DB, pref);
+ pref = NULL;
}
+int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2)
+{
+ if ((p1 && !p2) || (!p1 && p2))
+ return 0;
+
+ if (p1 == p2)
+ return 1;
+
+ if (p1->flags != p2->flags)
+ return 0;
+
+ if (p1->adv.origin != p2->adv.origin)
+ return 0;
+
+ if (!memcmp(&p1->adv.id, &p2->adv.id, sizeof(struct ls_node_id)))
+ return 0;
+
+ /* Do we need to test individually each field, instead performing a
+ * global memcmp? There is a risk that an old value that is bit masked
+ * i.e. corresponding flag = 0, will result into a false negative
+ */
+ if (!memcmp(p1, p2, sizeof(struct ls_prefix)))
+ return 0;
+ else
+ return 1;
+}
+
+/**
+ * Link State Vertices management functions
+ */
struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node)
{
struct ls_vertex *new;
+ uint64_t key = 0;
if ((ted == NULL) || (node == NULL))
return NULL;
- new = ls_vertex_new(node);
- if (!new)
- return NULL;
-
/* set Key as the IPv4/Ipv6 Router ID or ISO System ID */
switch (node->adv.origin) {
case OSPFv2:
case STATIC:
case DIRECT:
- memcpy(&new->key, &node->adv.id.ip.addr, IPV4_MAX_BYTELEN);
+ key = ((uint64_t)ntohl(node->adv.id.ip.addr.s_addr))
+ & 0xffffffff;
break;
case ISIS_L1:
case ISIS_L2:
- memcpy(&new->key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN);
+ memcpy(&key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN);
break;
default:
- new->key = 0;
+ key = 0;
break;
}
- /* Remove Vertex if key is not set */
- if (new->key == 0) {
- ls_vertex_del(new);
+ /* Check that key is valid */
+ if (key == 0)
return NULL;
- }
- /* Add Vertex to TED */
+ /* Create Vertex and add it to the TED */
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_vertex));
+ if (!new)
+ return NULL;
+
+ new->key = key;
+ new->node = node;
+ new->status = NEW;
+ new->type = VERTEX;
+ new->incoming_edges = list_new();
+ new->incoming_edges->cmp = (int (*)(void *, void *))edge_cmp;
+ new->outgoing_edges = list_new();
+ new->outgoing_edges->cmp = (int (*)(void *, void *))edge_cmp;
+ new->prefixes = list_new();
+ new->prefixes->cmp = (int (*)(void *, void *))subnet_cmp;
vertices_add(&ted->vertices, new);
return new;
}
+void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex)
+{
+ struct listnode *node, *nnode;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+
+ if (!ted || !vertex)
+ return;
+
+ /* Remove outgoing Edges and list */
+ for (ALL_LIST_ELEMENTS(vertex->outgoing_edges, node, nnode, edge))
+ ls_edge_del_all(ted, edge);
+ list_delete(&vertex->outgoing_edges);
+
+ /* Disconnect incoming Edges and remove list */
+ for (ALL_LIST_ELEMENTS(vertex->incoming_edges, node, nnode, edge)) {
+ ls_disconnect(vertex, edge, false);
+ if (edge->source == NULL)
+ ls_edge_del_all(ted, edge);
+ }
+ list_delete(&vertex->incoming_edges);
+
+ /* Remove subnet and list */
+ for (ALL_LIST_ELEMENTS(vertex->prefixes, node, nnode, subnet))
+ ls_subnet_del_all(ted, subnet);
+ list_delete(&vertex->prefixes);
+
+ /* Then remove Vertex from Link State Data Base and free memory */
+ vertices_del(&ted->vertices, vertex);
+ XFREE(MTYPE_LS_DB, vertex);
+ vertex = NULL;
+}
+
+void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex)
+{
+ if (!ted || !vertex)
+ return;
+
+ /* First remove associated Link State Node */
+ ls_node_del(vertex->node);
+
+ /* Then, Vertex itself */
+ ls_vertex_del(ted, vertex);
+}
+
struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node)
{
struct ls_vertex *old;
@@ -261,49 +359,46 @@ struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node)
ls_node_del(old->node);
old->node = node;
}
+ old->status = UPDATE;
return old;
}
return ls_vertex_add(ted, node);
}
-void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex)
-{
- vertices_del(&ted->vertices, vertex);
- ls_vertex_del(vertex);
-}
-
struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key)
{
- struct ls_vertex node = {};
+ struct ls_vertex vertex = {};
if (key == 0)
return NULL;
- node.key = key;
- return vertices_find(&ted->vertices, &node);
+ vertex.key = key;
+ return vertices_find(&ted->vertices, &vertex);
}
struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted,
struct ls_node_id nid)
{
- struct ls_vertex node = {};
+ struct ls_vertex vertex = {};
+ vertex.key = 0;
switch (nid.origin) {
case OSPFv2:
case STATIC:
case DIRECT:
- memcpy(&node.key, &nid.id.ip.addr, IPV4_MAX_BYTELEN);
+ vertex.key =
+ ((uint64_t)ntohl(nid.id.ip.addr.s_addr)) & 0xffffffff;
break;
case ISIS_L1:
case ISIS_L2:
- memcpy(&node.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN);
+ memcpy(&vertex.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN);
break;
default:
return NULL;
}
- return vertices_find(&ted->vertices, &node);
+ return vertices_find(&ted->vertices, &vertex);
}
int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2)
@@ -323,6 +418,49 @@ int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2)
return ls_node_same(v1->node, v2->node);
}
+void ls_vertex_clean(struct ls_ted *ted, struct ls_vertex *vertex,
+ struct zclient *zclient)
+{
+ struct listnode *node, *nnode;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+ struct ls_message msg;
+
+ /* Remove Orphan Edge ... */
+ for (ALL_LIST_ELEMENTS(vertex->outgoing_edges, node, nnode, edge)) {
+ if (edge->status == ORPHAN) {
+ if (zclient) {
+ edge->status = DELETE;
+ ls_edge2msg(&msg, edge);
+ ls_send_msg(zclient, &msg, NULL);
+ }
+ ls_edge_del_all(ted, edge);
+ }
+ }
+ for (ALL_LIST_ELEMENTS(vertex->incoming_edges, node, nnode, edge)) {
+ if (edge->status == ORPHAN) {
+ if (zclient) {
+ edge->status = DELETE;
+ ls_edge2msg(&msg, edge);
+ ls_send_msg(zclient, &msg, NULL);
+ }
+ ls_edge_del_all(ted, edge);
+ }
+ }
+
+ /* ... and Subnet from the Vertex */
+ for (ALL_LIST_ELEMENTS(vertex->prefixes, node, nnode, subnet)) {
+ if (subnet->status == ORPHAN) {
+ if (zclient) {
+ subnet->status = DELETE;
+ ls_subnet2msg(&msg, subnet);
+ ls_send_msg(zclient, &msg, NULL);
+ }
+ ls_subnet_del_all(ted, subnet);
+ }
+ }
+}
+
/**
* Link State Edges management functions
*/
@@ -354,18 +492,18 @@ static void ls_edge_connect_to(struct ls_ted *ted, struct ls_edge *edge)
vertex = ls_vertex_add(ted, node);
}
/* and attach the edge as source to the vertex */
- listnode_add(vertex->outgoing_edges, edge);
+ listnode_add_sort_nodup(vertex->outgoing_edges, edge);
edge->source = vertex;
/* Then search if there is a reverse Edge */
dst = ls_find_edge_by_destination(ted, edge->attributes);
/* attach the destination edge to the vertex */
if (dst) {
- listnode_add(vertex->incoming_edges, dst);
+ listnode_add_sort_nodup(vertex->incoming_edges, dst);
dst->destination = vertex;
/* and destination vertex to this edge */
vertex = dst->source;
- listnode_add(vertex->incoming_edges, edge);
+ listnode_add_sort_nodup(vertex->incoming_edges, edge);
edge->destination = vertex;
}
}
@@ -374,37 +512,43 @@ struct ls_edge *ls_edge_add(struct ls_ted *ted,
struct ls_attributes *attributes)
{
struct ls_edge *new;
+ uint64_t key = 0;
if (attributes == NULL)
return NULL;
- new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge));
- new->attributes = attributes;
/* Key is the IPv4 local address */
if (!IPV4_NET0(attributes->standard.local.s_addr))
- new->key = ((uint64_t)attributes->standard.local.s_addr)
- & 0xffffffff;
+ key = ((uint64_t)ntohl(attributes->standard.local.s_addr))
+ & 0xffffffff;
/* or the IPv6 local address if IPv4 is not defined */
else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6))
- new->key = (uint64_t)(attributes->standard.local6.s6_addr32[0]
- & 0xffffffff)
- | ((uint64_t)attributes->standard.local6.s6_addr32[1]
- << 32);
+ key = (uint64_t)(attributes->standard.local6.s6_addr32[0]
+ & 0xffffffff)
+ | ((uint64_t)attributes->standard.local6.s6_addr32[1]
+ << 32);
/* of local identifier if no IP addresses are defined */
else if (attributes->standard.local_id != 0)
- new->key = (uint64_t)(
+ key = (uint64_t)(
(attributes->standard.local_id & 0xffffffff)
| ((uint64_t)attributes->standard.remote_id << 32));
- /* Remove Edge if key is not known */
- if (new->key == 0) {
- XFREE(MTYPE_LS_DB, new);
+ /* Check that key is valid */
+ if (key == 0)
+ return NULL;
+
+ /* Create Edge and add it to the TED */
+ new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge));
+ if (!new)
return NULL;
- }
+ new->attributes = attributes;
+ new->key = key;
+ new->status = NEW;
+ new->type = EDGE;
edges_add(&ted->edges, new);
- /* Finally, connect edge to vertices */
+ /* Finally, connect Edge to Vertices */
ls_edge_connect_to(ted, new);
return new;
@@ -429,9 +573,10 @@ struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted,
if (attributes == NULL)
return NULL;
+ edge.key = 0;
/* Key is the IPv4 local address */
if (!IPV4_NET0(attributes->standard.local.s_addr))
- edge.key = ((uint64_t)attributes->standard.local.s_addr)
+ edge.key = ((uint64_t)ntohl(attributes->standard.local.s_addr))
& 0xffffffff;
/* or the IPv6 local address if IPv4 is not defined */
else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6))
@@ -459,18 +604,19 @@ struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted,
if (attributes == NULL)
return NULL;
- /* Key is the IPv4 local address */
+ edge.key = 0;
+ /* Key is the IPv4 remote address */
if (!IPV4_NET0(attributes->standard.remote.s_addr))
- edge.key = ((uint64_t)attributes->standard.remote.s_addr)
+ edge.key = ((uint64_t)ntohl(attributes->standard.remote.s_addr))
& 0xffffffff;
- /* or the IPv6 local address if IPv4 is not defined */
+ /* or the IPv6 remote address if IPv4 is not defined */
else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.remote6))
edge.key =
(uint64_t)(attributes->standard.remote6.s6_addr32[0]
& 0xffffffff)
| ((uint64_t)attributes->standard.remote6.s6_addr32[1]
<< 32);
- /* of local identifier if no IP addresses are defined */
+ /* of remote identifier if no IP addresses are defined */
else if (attributes->standard.remote_id != 0)
edge.key = (uint64_t)(
(attributes->standard.remote_id & 0xffffffff)
@@ -498,6 +644,7 @@ struct ls_edge *ls_edge_update(struct ls_ted *ted,
ls_attributes_del(old->attributes);
old->attributes = attributes;
}
+ old->status = UPDATE;
return old;
}
@@ -505,15 +652,46 @@ struct ls_edge *ls_edge_update(struct ls_ted *ted,
return ls_edge_add(ted, attributes);
}
+int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2)
+{
+ if ((e1 && !e2) || (!e1 && e2))
+ return 0;
+
+ if (!e1 && !e2)
+ return 1;
+
+ if (e1->key != e2->key)
+ return 0;
+
+ if (e1->attributes == e2->attributes)
+ return 1;
+
+ return ls_attributes_same(e1->attributes, e2->attributes);
+}
+
void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge)
{
- /* Fist disconnect Edge */
+ if (!ted || !edge)
+ return;
+
+ /* Fist disconnect Edge from Vertices */
ls_disconnect_edge(edge);
/* Then remove it from the Data Base */
edges_del(&ted->edges, edge);
XFREE(MTYPE_LS_DB, edge);
}
+void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge)
+{
+ if (!ted || !edge)
+ return;
+
+ /* Remove associated Link State Attributes */
+ ls_attributes_del(edge->attributes);
+ /* Then Edge itself */
+ ls_edge_del(ted, edge);
+}
+
/**
* Link State Subnet Management functions.
*/
@@ -531,6 +709,8 @@ struct ls_subnet *ls_subnet_add(struct ls_ted *ted,
new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_subnet));
new->ls_pref = ls_pref;
new->key = ls_pref->pref;
+ new->status = NEW;
+ new->type = SUBNET;
/* Find Vertex */
vertex = ls_find_vertex_by_id(ted, ls_pref->adv);
@@ -541,19 +721,73 @@ struct ls_subnet *ls_subnet_add(struct ls_ted *ted,
}
/* And attach the subnet to the corresponding Vertex */
new->vertex = vertex;
- listnode_add(vertex->prefixes, new);
+ listnode_add_sort_nodup(vertex->prefixes, new);
subnets_add(&ted->subnets, new);
return new;
}
+struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref)
+{
+ struct ls_subnet *old;
+
+ if (pref == NULL)
+ return NULL;
+
+ old = ls_find_subnet(ted, pref->pref);
+ if (old) {
+ if (!ls_prefix_same(old->ls_pref, pref)) {
+ ls_prefix_del(old->ls_pref);
+ old->ls_pref = pref;
+ }
+ old->status = UPDATE;
+ return old;
+ }
+
+ return ls_subnet_add(ted, pref);
+}
+
+int ls_subnet_same(struct ls_subnet *s1, struct ls_subnet *s2)
+{
+ if ((s1 && !s2) || (!s1 && s2))
+ return 0;
+
+ if (!s1 && !s2)
+ return 1;
+
+ if (!prefix_same(&s1->key, &s2->key))
+ return 0;
+
+ if (s1->ls_pref == s2->ls_pref)
+ return 1;
+
+ return ls_prefix_same(s1->ls_pref, s2->ls_pref);
+}
+
void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet)
{
+ if (!ted || !subnet)
+ return;
+
+ /* First, disconnect Subnet from associated Vertex */
+ listnode_delete(subnet->vertex->prefixes, subnet);
+ /* Then delete Subnet */
subnets_del(&ted->subnets, subnet);
XFREE(MTYPE_LS_DB, subnet);
}
+void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet)
+{
+ if (!ted || !subnet)
+ return;
+
+ /* First, remove associated Link State Subnet */
+ ls_prefix_del(subnet->ls_pref);
+ /* Then, delete Subnet itself */
+ ls_subnet_del(ted, subnet);
+}
+
struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix)
{
struct ls_subnet subnet = {};
@@ -592,6 +826,11 @@ void ls_ted_del(struct ls_ted *ted)
if (ted == NULL)
return;
+ /* Check that TED is empty */
+ if (vertices_count(&ted->vertices) || edges_count(&ted->edges)
+ || subnets_count(&ted->subnets))
+ return;
+
/* Release RB Tree */
vertices_fini(&ted->vertices);
edges_fini(&ted->edges);
@@ -601,16 +840,63 @@ void ls_ted_del(struct ls_ted *ted)
ted = NULL;
}
+void ls_ted_del_all(struct ls_ted *ted)
+{
+ struct ls_vertex *vertex;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+
+ if (ted == NULL)
+ return;
+
+ /* First remove Vertices, Edges and Subnets and associated Link State */
+ frr_each (vertices, &ted->vertices, vertex)
+ ls_vertex_del_all(ted, vertex);
+ frr_each (edges, &ted->edges, edge)
+ ls_edge_del_all(ted, edge);
+ frr_each (subnets, &ted->subnets, subnet)
+ ls_subnet_del_all(ted, subnet);
+
+ /* then remove TED itself */
+ ls_ted_del(ted);
+}
+
+void ls_ted_clean(struct ls_ted *ted)
+{
+ struct ls_vertex *vertex;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+
+ if (ted == NULL)
+ return;
+
+ /* First, start with Vertices */
+ frr_each (vertices, &ted->vertices, vertex)
+ if (vertex->status == ORPHAN)
+ ls_vertex_del_all(ted, vertex);
+
+ /* Then Edges */
+ frr_each (edges, &ted->edges, edge)
+ if (edge->status == ORPHAN)
+ ls_edge_del_all(ted, edge);
+
+ /* and Subnets */
+ frr_each (subnets, &ted->subnets, subnet)
+ if (subnet->status == ORPHAN)
+ ls_subnet_del_all(ted, subnet);
+
+}
+
void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source)
{
if (vertex == NULL || edge == NULL)
return;
if (source) {
- listnode_add(vertex->outgoing_edges, edge);
+ listnode_add_sort_nodup(vertex->outgoing_edges, edge);
edge->source = vertex;
} else {
- listnode_add(vertex->incoming_edges, edge);
+ listnode_add_sort_nodup(vertex->incoming_edges, edge);
edge->destination = vertex;
}
}
@@ -640,11 +926,10 @@ void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst,
edge->destination = dst;
if (src != NULL)
- listnode_add(src->outgoing_edges, edge);
+ listnode_add_sort_nodup(src->outgoing_edges, edge);
if (dst != NULL)
- listnode_add(dst->incoming_edges, edge);
-
+ listnode_add_sort_nodup(dst->incoming_edges, edge);
}
void ls_disconnect_edge(struct ls_edge *edge)
@@ -654,12 +939,68 @@ void ls_disconnect_edge(struct ls_edge *edge)
ls_disconnect(edge->source, edge, true);
ls_disconnect(edge->destination, edge, false);
+
+ /* Mark this Edge as ORPHAN for future cleanup */
+ edge->status = ORPHAN;
}
/**
* Link State Message management functions
*/
+int ls_register(struct zclient *zclient, bool server)
+{
+ int rc;
+
+ if (server)
+ rc = zclient_register_opaque(zclient, LINK_STATE_SYNC);
+ else
+ rc = zclient_register_opaque(zclient, LINK_STATE_UPDATE);
+
+ return rc;
+}
+
+int ls_unregister(struct zclient *zclient, bool server)
+{
+ int rc;
+
+ if (server)
+ rc = zclient_unregister_opaque(zclient, LINK_STATE_SYNC);
+ else
+ rc = zclient_unregister_opaque(zclient, LINK_STATE_UPDATE);
+
+ return rc;
+}
+
+int ls_request_sync(struct zclient *zclient)
+{
+ struct stream *s;
+ uint16_t flags = 0;
+
+ /* Check buffer size */
+ if (STREAM_SIZE(zclient->obuf)
+ < (ZEBRA_HEADER_SIZE + 3 * sizeof(uint32_t)))
+ return -1;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
+
+ /* Set type and flags */
+ stream_putl(s, LINK_STATE_SYNC);
+ stream_putw(s, flags);
+ /* Send destination client info */
+ stream_putc(s, zclient->redist_default);
+ stream_putw(s, zclient->instance);
+ stream_putl(s, zclient->session_id);
+
+ /* Put length into the header at the start of the stream. */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
static struct ls_node *ls_parse_node(struct stream *s)
{
struct ls_node *node;
@@ -723,7 +1064,7 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s)
STREAM_GET(attr->name, s, len);
}
if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC))
- STREAM_GETL(s, attr->standard.metric);
+ STREAM_GETL(s, attr->metric);
if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC))
STREAM_GETL(s, attr->standard.te_metric);
if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP))
@@ -804,7 +1145,7 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s)
stream_failure:
zlog_err("LS(%s): Could not parse Link State Attributes. Abort!",
__func__);
- /* Clean memeory allocation */
+ /* Clean memory allocation */
if (attr->srlgs != NULL)
XFREE(MTYPE_LS_DB, attr->srlgs);
XFREE(MTYPE_LS_DB, attr);
@@ -947,7 +1288,7 @@ static int ls_format_attributes(struct stream *s, struct ls_attributes *attr)
stream_putc(s, '\0');
}
if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC))
- stream_putl(s, attr->standard.metric);
+ stream_putl(s, attr->metric);
if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC))
stream_putl(s, attr->standard.te_metric);
if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP))
@@ -1084,6 +1425,10 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg,
struct stream *s;
uint16_t flags = 0;
+ /* Check if we have a valid message */
+ if (msg->event == LS_MSG_EVENT_UNDEF)
+ return -1;
+
/* Check buffer size */
if (STREAM_SIZE(zclient->obuf) <
(ZEBRA_HEADER_SIZE + sizeof(uint32_t) + sizeof(msg)))
@@ -1094,7 +1439,7 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg,
zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
- /* Send sub-type, flags and destination for unicast message */
+ /* Set sub-type, flags and destination for unicast message */
stream_putl(s, LINK_STATE_UPDATE);
if (dst != NULL) {
SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST);
@@ -1103,8 +1448,9 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg,
stream_putc(s, dst->proto);
stream_putw(s, dst->instance);
stream_putl(s, dst->session_id);
- } else
+ } else {
stream_putw(s, flags);
+ }
/* Format Link State message */
if (ls_format_msg(s, msg) < 0) {
@@ -1128,8 +1474,25 @@ struct ls_message *ls_vertex2msg(struct ls_message *msg,
memset(msg, 0, sizeof(*msg));
msg->type = LS_MSG_TYPE_NODE;
+ switch (vertex->status) {
+ case NEW:
+ msg->event = LS_MSG_EVENT_ADD;
+ break;
+ case UPDATE:
+ msg->event = LS_MSG_EVENT_UPDATE;
+ break;
+ case DELETE:
+ msg->event = LS_MSG_EVENT_DELETE;
+ break;
+ case SYNC:
+ msg->event = LS_MSG_EVENT_SYNC;
+ break;
+ default:
+ msg->event = LS_MSG_EVENT_UNDEF;
+ break;
+ }
msg->data.node = vertex->node;
- msg->remote_id.origin = NONE;
+ msg->remote_id.origin = UNKNOWN;
return msg;
}
@@ -1143,11 +1506,28 @@ struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge)
memset(msg, 0, sizeof(*msg));
msg->type = LS_MSG_TYPE_ATTRIBUTES;
+ switch (edge->status) {
+ case NEW:
+ msg->event = LS_MSG_EVENT_ADD;
+ break;
+ case UPDATE:
+ msg->event = LS_MSG_EVENT_UPDATE;
+ break;
+ case DELETE:
+ msg->event = LS_MSG_EVENT_DELETE;
+ break;
+ case SYNC:
+ msg->event = LS_MSG_EVENT_SYNC;
+ break;
+ default:
+ msg->event = LS_MSG_EVENT_UNDEF;
+ break;
+ }
msg->data.attr = edge->attributes;
if (edge->destination != NULL)
msg->remote_id = edge->destination->node->adv;
else
- msg->remote_id.origin = NONE;
+ msg->remote_id.origin = UNKNOWN;
return msg;
}
@@ -1162,34 +1542,189 @@ struct ls_message *ls_subnet2msg(struct ls_message *msg,
memset(msg, 0, sizeof(*msg));
msg->type = LS_MSG_TYPE_PREFIX;
+ switch (subnet->status) {
+ case NEW:
+ msg->event = LS_MSG_EVENT_ADD;
+ break;
+ case UPDATE:
+ msg->event = LS_MSG_EVENT_UPDATE;
+ break;
+ case DELETE:
+ msg->event = LS_MSG_EVENT_DELETE;
+ break;
+ case SYNC:
+ msg->event = LS_MSG_EVENT_SYNC;
+ break;
+ default:
+ msg->event = LS_MSG_EVENT_UNDEF;
+ break;
+ }
msg->data.prefix = subnet->ls_pref;
- msg->remote_id.origin = NONE;
+ msg->remote_id.origin = UNKNOWN;
return msg;
}
-void ls_delete_msg(struct ls_message *msg)
+struct ls_vertex *ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg,
+ bool delete)
{
- if (msg == NULL)
- return;
+ struct ls_node *node = (struct ls_node *)msg->data.node;
+ struct ls_vertex *vertex = NULL;
+
+ switch (msg->event) {
+ case LS_MSG_EVENT_SYNC:
+ vertex = ls_vertex_add(ted, node);
+ if (vertex)
+ vertex->status = SYNC;
+ break;
+ case LS_MSG_EVENT_ADD:
+ vertex = ls_vertex_add(ted, node);
+ if (vertex)
+ vertex->status = NEW;
+ break;
+ case LS_MSG_EVENT_UPDATE:
+ vertex = ls_vertex_update(ted, node);
+ if (vertex)
+ vertex->status = UPDATE;
+ break;
+ case LS_MSG_EVENT_DELETE:
+ vertex = ls_find_vertex_by_id(ted, node->adv);
+ if (vertex) {
+ if (delete)
+ ls_vertex_del_all(ted, vertex);
+ else
+ vertex->status = DELETE;
+ }
+ break;
+ default:
+ vertex = NULL;
+ break;
+ }
+
+ return vertex;
+}
+
+struct ls_edge *ls_msg2edge(struct ls_ted *ted, struct ls_message *msg,
+ bool delete)
+{
+ struct ls_attributes *attr = (struct ls_attributes *)msg->data.attr;
+ struct ls_edge *edge = NULL;
+
+ switch (msg->event) {
+ case LS_MSG_EVENT_SYNC:
+ edge = ls_edge_add(ted, attr);
+ if (edge)
+ edge->status = SYNC;
+ break;
+ case LS_MSG_EVENT_ADD:
+ edge = ls_edge_add(ted, attr);
+ if (edge)
+ edge->status = NEW;
+ break;
+ case LS_MSG_EVENT_UPDATE:
+ edge = ls_edge_update(ted, attr);
+ if (edge)
+ edge->status = UPDATE;
+ break;
+ case LS_MSG_EVENT_DELETE:
+ edge = ls_find_edge_by_source(ted, attr);
+ if (edge) {
+ if (delete)
+ ls_edge_del_all(ted, edge);
+ else
+ edge->status = DELETE;
+ }
+ break;
+ default:
+ edge = NULL;
+ break;
+ }
+
+ return edge;
+}
+
+struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg,
+ bool delete)
+{
+ struct ls_prefix *pref = (struct ls_prefix *)msg->data.prefix;
+ struct ls_subnet *subnet = NULL;
+
+ switch (msg->event) {
+ case LS_MSG_EVENT_SYNC:
+ subnet = ls_subnet_add(ted, pref);
+ if (subnet)
+ subnet->status = SYNC;
+ break;
+ case LS_MSG_EVENT_ADD:
+ subnet = ls_subnet_add(ted, pref);
+ if (subnet)
+ subnet->status = NEW;
+ break;
+ case LS_MSG_EVENT_UPDATE:
+ subnet = ls_subnet_update(ted, pref);
+ if (subnet)
+ subnet->status = UPDATE;
+ break;
+ case LS_MSG_EVENT_DELETE:
+ subnet = ls_find_subnet(ted, pref->pref);
+ if (subnet) {
+ if (delete)
+ ls_subnet_del_all(ted, subnet);
+ else
+ subnet->status = DELETE;
+ }
+ break;
+ default:
+ subnet = NULL;
+ break;
+ }
+
+ return subnet;
+}
+
+struct ls_element *ls_msg2ted(struct ls_ted *ted, struct ls_message *msg,
+ bool delete)
+{
+ struct ls_element *lse = NULL;
switch (msg->type) {
case LS_MSG_TYPE_NODE:
- if (msg->data.node)
- XFREE(MTYPE_LS_DB, msg->data.node);
+ lse = (struct ls_element *)ls_msg2vertex(ted, msg, delete);
break;
case LS_MSG_TYPE_ATTRIBUTES:
- if (msg->data.attr)
- XFREE(MTYPE_LS_DB, msg->data.attr);
+ lse = (struct ls_element *)ls_msg2edge(ted, msg, delete);
break;
case LS_MSG_TYPE_PREFIX:
- if (msg->data.prefix)
- XFREE(MTYPE_LS_DB, msg->data.prefix);
+ lse = (struct ls_element *)ls_msg2subnet(ted, msg, delete);
break;
default:
+ lse = NULL;
break;
}
+ return lse;
+}
+
+struct ls_element *ls_stream2ted(struct ls_ted *ted, struct stream *s,
+ bool delete)
+{
+ struct ls_message *msg;
+ struct ls_element *lse = NULL;
+
+ msg = ls_parse_msg(s);
+ if (msg) {
+ lse = ls_msg2ted(ted, msg, delete);
+ ls_delete_msg(msg);
+ }
+
+ return lse;
+}
+
+void ls_delete_msg(struct ls_message *msg)
+{
+ if (msg == NULL)
+ return;
+
XFREE(MTYPE_LS_DB, msg);
}
@@ -1201,9 +1736,6 @@ int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient,
struct ls_subnet *subnet;
struct ls_message msg;
- /* Prepare message */
- msg.event = LS_MSG_EVENT_SYNC;
-
/* Loop TED, start sending Node, then Attributes and finally Prefix */
frr_each(vertices, &ted->vertices, vertex) {
ls_vertex2msg(&msg, vertex);
@@ -1220,33 +1752,696 @@ int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient,
return 0;
}
+/**
+ * Link State Show functions
+ */
+static const char *const origin2txt[] = {
+ "Unknown",
+ "ISIS_L1",
+ "ISIS_L2",
+ "OSPFv2",
+ "Direct",
+ "Static"
+};
+
+static const char *const type2txt[] = {
+ "Unknown",
+ "Standard",
+ "ABR",
+ "ASBR",
+ "Remote ASBR",
+ "Pseudo"
+};
+
+static const char *const status2txt[] = {
+ "Unknown",
+ "New",
+ "Update",
+ "Delete",
+ "Sync",
+ "Orphan"
+};
+
+static const char *ls_node_id_to_text(struct ls_node_id lnid, char *str,
+ size_t size)
+{
+ if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2) {
+ uint8_t *id;
+
+ id = lnid.id.iso.sys_id;
+ snprintfrr(str, size, "%02x%02x.%02x%02x.%02x%02x", id[0],
+ id[1], id[2], id[3], id[4], id[5]);
+ } else
+ snprintfrr(str, size, "%pI4", &lnid.id.ip.addr);
+
+ return str;
+}
+
+static void ls_show_vertex_vty(struct ls_vertex *vertex, struct vty *vty,
+ bool verbose)
+{
+ struct listnode *node;
+ struct ls_node *lsn;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+ struct sbuf sbuf;
+ uint32_t upper;
+
+ /* Sanity Check */
+ if (!vertex)
+ return;
+
+ lsn = vertex->node;
+
+ sbuf_init(&sbuf, NULL, 0);
+
+ sbuf_push(&sbuf, 2, "Vertex (%" PRIu64 "): %s", vertex->key, lsn->name);
+ sbuf_push(&sbuf, 0, "\tRouter Id: %pI4", &lsn->router_id);
+ sbuf_push(&sbuf, 0, "\tOrigin: %s", origin2txt[lsn->adv.origin]);
+ sbuf_push(&sbuf, 0, "\tStatus: %s\n", status2txt[vertex->status]);
+ if (!verbose) {
+ sbuf_push(
+ &sbuf, 0,
+ "\t%d Outgoing Edges, %d Incoming Edges, %d Subnets\n",
+ listcount(vertex->outgoing_edges),
+ listcount(vertex->incoming_edges),
+ listcount(vertex->prefixes));
+ goto end;
+ }
+
+ if (CHECK_FLAG(lsn->flags, LS_NODE_TYPE))
+ sbuf_push(&sbuf, 4, "Type: %s\n", type2txt[lsn->type]);
+ if (CHECK_FLAG(lsn->flags, LS_NODE_AS_NUMBER))
+ sbuf_push(&sbuf, 4, "AS number: %u\n", lsn->as_number);
+ if (CHECK_FLAG(lsn->flags, LS_NODE_SR)) {
+ sbuf_push(&sbuf, 4, "Segment Routing Capabilities:\n");
+ upper = lsn->srgb.lower_bound + lsn->srgb.range_size - 1;
+ sbuf_push(&sbuf, 8, "SRGB: [%d/%d]", lsn->srgb.lower_bound,
+ upper);
+ if (CHECK_FLAG(lsn->flags, LS_NODE_SRLB)) {
+ upper = lsn->srlb.lower_bound + lsn->srlb.range_size
+ - 1;
+ sbuf_push(&sbuf, 0, "\tSRLB: [%d/%d]",
+ lsn->srlb.lower_bound, upper);
+ }
+ sbuf_push(&sbuf, 0, "\tAlgo: ");
+ for (int i = 0; i < 2; i++) {
+ if (lsn->algo[i] == 255)
+ continue;
+
+ sbuf_push(&sbuf, 0,
+ lsn->algo[i] == 0 ? "SPF " : "S-SPF ");
+ }
+ if (CHECK_FLAG(lsn->flags, LS_NODE_MSD))
+ sbuf_push(&sbuf, 0, "\tMSD: %d", lsn->msd);
+ sbuf_push(&sbuf, 0, "\n");
+ }
+
+ sbuf_push(&sbuf, 4, "Outgoing Edges: %d\n",
+ listcount(vertex->outgoing_edges));
+ for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) {
+ if (edge->destination) {
+ lsn = edge->destination->node;
+ sbuf_push(&sbuf, 6, "To:\t%s(%pI4)", lsn->name,
+ &lsn->router_id);
+ } else {
+ sbuf_push(&sbuf, 6, "To:\t- (0.0.0.0)");
+ }
+ sbuf_push(&sbuf, 0, "\tLocal: %pI4\tRemote: %pI4\n",
+ &edge->attributes->standard.local,
+ &edge->attributes->standard.remote);
+ }
+
+ sbuf_push(&sbuf, 4, "Incoming Edges: %d\n",
+ listcount(vertex->incoming_edges));
+ for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, node, edge)) {
+ if (edge->source) {
+ lsn = edge->source->node;
+ sbuf_push(&sbuf, 6, "From:\t%s(%pI4)", lsn->name,
+ &lsn->router_id);
+ } else {
+ sbuf_push(&sbuf, 6, "From:\t- (0.0.0.0)");
+ }
+ sbuf_push(&sbuf, 0, "\tRemote: %pI4\tLocal: %pI4\n",
+ &edge->attributes->standard.local,
+ &edge->attributes->standard.remote);
+ }
+
+ sbuf_push(&sbuf, 4, "Subnets: %d\n", listcount(vertex->prefixes));
+ for (ALL_LIST_ELEMENTS_RO(vertex->prefixes, node, subnet))
+ sbuf_push(&sbuf, 6, "Prefix:\t%pFX\n", &subnet->key);
+
+end:
+ vty_out(vty, "%s\n", sbuf_buf(&sbuf));
+ sbuf_free(&sbuf);
+}
+
+static void ls_show_vertex_json(struct ls_vertex *vertex,
+ struct json_object *json)
+{
+ struct ls_node *lsn;
+ json_object *jsr, *jalgo, *jobj;
+ char buf[INET6_BUFSIZ];
+
+ /* Sanity Check */
+ if (!vertex)
+ return;
+
+ lsn = vertex->node;
+
+ json_object_int_add(json, "vertex-id", vertex->key);
+ json_object_string_add(json, "status", status2txt[vertex->status]);
+ json_object_string_add(json, "origin", origin2txt[lsn->adv.origin]);
+ if (CHECK_FLAG(lsn->flags, LS_NODE_NAME))
+ json_object_string_add(json, "name", lsn->name);
+ if (CHECK_FLAG(lsn->flags, LS_NODE_ROUTER_ID)) {
+ snprintfrr(buf, INET6_BUFSIZ, "%pI4", &lsn->router_id);
+ json_object_string_add(json, "router-id", buf);
+ }
+ if (CHECK_FLAG(lsn->flags, LS_NODE_ROUTER_ID6)) {
+ snprintfrr(buf, INET6_BUFSIZ, "%pI6", &lsn->router6_id);
+ json_object_string_add(json, "router-id-v6", buf);
+ }
+ if (CHECK_FLAG(lsn->flags, LS_NODE_TYPE))
+ json_object_string_add(json, "vertex-type",
+ type2txt[lsn->type]);
+ if (CHECK_FLAG(lsn->flags, LS_NODE_AS_NUMBER))
+ json_object_int_add(json, "asn", lsn->as_number);
+ if (CHECK_FLAG(lsn->flags, LS_NODE_SR)) {
+ jsr = json_object_new_object();
+ json_object_object_add(json, "segment-routing", jsr);
+ json_object_int_add(jsr, "srgb-size", lsn->srgb.range_size);
+ json_object_int_add(jsr, "srgb-lower", lsn->srgb.lower_bound);
+ jalgo = json_object_new_array();
+ json_object_object_add(jsr, "algorithms", jalgo);
+ for (int i = 0; i < 2; i++) {
+ if (lsn->algo[i] == 255)
+ continue;
+ jobj = json_object_new_object();
+
+ snprintfrr(buf, 2, "%u", i);
+ json_object_string_add(
+ jobj, buf, lsn->algo[i] == 0 ? "SPF" : "S-SPF");
+ json_object_array_add(jalgo, jobj);
+ }
+ if (CHECK_FLAG(lsn->flags, LS_NODE_SRLB)) {
+ json_object_int_add(jsr, "srlb-size",
+ lsn->srlb.range_size);
+ json_object_int_add(jsr, "srlb-lower",
+ lsn->srlb.lower_bound);
+ }
+ if (CHECK_FLAG(lsn->flags, LS_NODE_MSD))
+ json_object_int_add(jsr, "msd", lsn->msd);
+ }
+}
+
+void ls_show_vertex(struct ls_vertex *vertex, struct vty *vty,
+ struct json_object *json, bool verbose)
+{
+ if (json)
+ ls_show_vertex_json(vertex, json);
+ else if (vty)
+ ls_show_vertex_vty(vertex, vty, verbose);
+}
+
+void ls_show_vertices(struct ls_ted *ted, struct vty *vty,
+ struct json_object *json, bool verbose)
+{
+ struct ls_vertex *vertex;
+ json_object *jnodes, *jnode;
+
+ if (json) {
+ jnodes = json_object_new_array();
+ json_object_object_add(json, "vertices", jnodes);
+ frr_each (vertices, &ted->vertices, vertex) {
+ jnode = json_object_new_object();
+ ls_show_vertex(vertex, NULL, jnode, verbose);
+ json_object_array_add(jnodes, jnode);
+ }
+ } else if (vty) {
+ frr_each (vertices, &ted->vertices, vertex)
+ ls_show_vertex(vertex, vty, NULL, verbose);
+ }
+}
+
+static void ls_show_edge_vty(struct ls_edge *edge, struct vty *vty,
+ bool verbose)
+{
+ struct ls_attributes *attr;
+ struct sbuf sbuf;
+ char buf[INET6_BUFSIZ];
+
+ attr = edge->attributes;
+ sbuf_init(&sbuf, NULL, 0);
+
+ sbuf_push(&sbuf, 2, "Edge (%" PRIu64 "): ", edge->key);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
+ sbuf_push(&sbuf, 0, "%pI4", &attr->standard.local);
+ else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
+ sbuf_push(&sbuf, 0, "%pI6", &attr->standard.local6);
+ else
+ sbuf_push(&sbuf, 0, "%u/%u", attr->standard.local_id,
+ attr->standard.remote_id);
+ ls_node_id_to_text(attr->adv, buf, INET6_BUFSIZ);
+ sbuf_push(&sbuf, 0, "\tAdv. Vertex: %s", buf);
+ sbuf_push(&sbuf, 0, "\tMetric: %u", attr->metric);
+ sbuf_push(&sbuf, 0, "\tStatus: %s\n", status2txt[edge->status]);
+
+ if (!verbose)
+ goto end;
+
+ sbuf_push(&sbuf, 4, "Origin: %s\n", origin2txt[attr->adv.origin]);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NAME))
+ sbuf_push(&sbuf, 4, "Name: %s\n", attr->name);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC))
+ sbuf_push(&sbuf, 4, "TE Metric: %u\n",
+ attr->standard.te_metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP))
+ sbuf_push(&sbuf, 4, "Admin Group: 0x%x\n",
+ attr->standard.admin_group);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
+ sbuf_push(&sbuf, 4, "Local IPv4 address: %pI4\n",
+ &attr->standard.local);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR))
+ sbuf_push(&sbuf, 4, "Remote IPv4 address: %pI4\n",
+ &attr->standard.remote);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
+ sbuf_push(&sbuf, 4, "Local IPv6 address: %pI6\n",
+ &attr->standard.local6);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6))
+ sbuf_push(&sbuf, 4, "Remote IPv6 address: %pI6\n",
+ &attr->standard.remote6);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
+ sbuf_push(&sbuf, 4, "Local Identifier: %u\n",
+ attr->standard.local_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID))
+ sbuf_push(&sbuf, 4, "Remote Identifier: %u\n",
+ attr->standard.remote_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW))
+ sbuf_push(&sbuf, 4, "Maximum Bandwidth: %g (Bytes/s)\n",
+ attr->standard.max_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW))
+ sbuf_push(&sbuf, 4,
+ "Maximum Reservable Bandwidth: %g (Bytes/s)\n",
+ attr->standard.max_rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW)) {
+ sbuf_push(&sbuf, 4, "Unreserved Bandwidth per Class Type\n");
+ for (int i = 0; i < MAX_CLASS_TYPE; i += 2)
+ sbuf_push(&sbuf, 8,
+ "[%d]: %g (Bytes/sec)\t[%d]: %g (Bytes/s)\n",
+ i, attr->standard.unrsv_bw[i], i + 1,
+ attr->standard.unrsv_bw[i + 1]);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS))
+ sbuf_push(&sbuf, 4, "Remote AS: %u\n",
+ attr->standard.remote_as);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR))
+ sbuf_push(&sbuf, 4, "Remote ASBR IPv4 address: %pI4\n",
+ &attr->standard.remote_addr);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6))
+ sbuf_push(&sbuf, 4, "Remote ASBR IPv6 address: %pI6\n",
+ &attr->standard.remote_addr6);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY))
+ sbuf_push(&sbuf, 4, "Average Link Delay: %d (micro-sec)\n",
+ attr->extended.delay);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY))
+ sbuf_push(&sbuf, 4, "Min/Max Link Delay: %d/%d (micro-sec)\n",
+ attr->extended.min_delay, attr->extended.max_delay);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER))
+ sbuf_push(&sbuf, 4, "Delay Variation: %d (micro-sec)\n",
+ attr->extended.jitter);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS))
+ sbuf_push(&sbuf, 4, "Link Loss: %g (%%)\n",
+ (float)(attr->extended.pkt_loss * LOSS_PRECISION));
+ if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW))
+ sbuf_push(&sbuf, 4, "Available Bandwidth: %g (Bytes/s)\n",
+ attr->extended.ava_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW))
+ sbuf_push(&sbuf, 4, "Residual Bandwidth: %g (Bytes/s)\n",
+ attr->extended.rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW))
+ sbuf_push(&sbuf, 4, "Utilized Bandwidth: %g (Bytes/s)\n",
+ attr->extended.used_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) {
+ sbuf_push(&sbuf, 4, "Adjacency-SID: %u", attr->adj_sid[0].sid);
+ sbuf_push(&sbuf, 0, "\tFlags: 0x%x\tWeight: 0x%x\n",
+ attr->adj_sid[0].flags, attr->adj_sid[0].weight);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) {
+ sbuf_push(&sbuf, 4, "Bck. Adjacency-SID: %u",
+ attr->adj_sid[1].sid);
+ sbuf_push(&sbuf, 0, "\tFlags: 0x%x\tWeight: 0x%x\n",
+ attr->adj_sid[1].flags, attr->adj_sid[1].weight);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) {
+ sbuf_push(&sbuf, 4, "SRLGs: %d", attr->srlg_len);
+ for (int i = 1; i < attr->srlg_len; i++) {
+ if (i % 8)
+ sbuf_push(&sbuf, 8, "\n%u", attr->srlgs[i]);
+ else
+ sbuf_push(&sbuf, 8, ", %u", attr->srlgs[i]);
+ }
+ sbuf_push(&sbuf, 0, "\n");
+ }
+
+end:
+ vty_out(vty, "%s\n", sbuf_buf(&sbuf));
+ sbuf_free(&sbuf);
+}
+
+static void ls_show_edge_json(struct ls_edge *edge, struct json_object *json)
+{
+ struct ls_attributes *attr;
+ struct json_object *jte, *jbw, *jobj, *jsr = NULL, *jsrlg;
+ char buf[INET6_BUFSIZ];
+
+ attr = edge->attributes;
+
+ json_object_int_add(json, "edge-id", edge->key);
+ json_object_string_add(json, "status", status2txt[edge->status]);
+ json_object_string_add(json, "origin", origin2txt[attr->adv.origin]);
+ ls_node_id_to_text(attr->adv, buf, INET6_BUFSIZ);
+ json_object_string_add(json, "advertised-router", buf);
+ if (edge->source)
+ json_object_int_add(json, "local-vertex-id", edge->source->key);
+ if (edge->destination)
+ json_object_int_add(json, "remote-vertex-id",
+ edge->destination->key);
+ json_object_int_add(json, "metric", attr->metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NAME))
+ json_object_string_add(json, "name", attr->name);
+ jte = json_object_new_object();
+ json_object_object_add(json, "edge-attributes", jte);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC))
+ json_object_int_add(jte, "te-metric", attr->standard.te_metric);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP))
+ json_object_int_add(jte, "admin-group",
+ attr->standard.admin_group);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) {
+ snprintfrr(buf, INET6_BUFSIZ, "%pI4", &attr->standard.local);
+ json_object_string_add(jte, "local-address", buf);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) {
+ snprintfrr(buf, INET6_BUFSIZ, "%pI4", &attr->standard.remote);
+ json_object_string_add(jte, "remote-address", buf);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) {
+ snprintfrr(buf, INET6_BUFSIZ, "%pI6", &attr->standard.local6);
+ json_object_string_add(jte, "local-address-v6", buf);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) {
+ snprintfrr(buf, INET6_BUFSIZ, "%pI6", &attr->standard.remote6);
+ json_object_string_add(jte, "remote-address-v6", buf);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
+ json_object_int_add(jte, "local-identifier",
+ attr->standard.local_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID))
+ json_object_int_add(jte, "remote-identifier",
+ attr->standard.remote_id);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW))
+ json_object_double_add(jte, "max-link-bandwidth",
+ attr->standard.max_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW))
+ json_object_double_add(jte, "max-resv-link-bandwidth",
+ attr->standard.max_rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW)) {
+ jbw = json_object_new_array();
+ json_object_object_add(jte, "unreserved-bandwidth", jbw);
+ for (int i = 0; i < MAX_CLASS_TYPE; i++) {
+ jobj = json_object_new_object();
+ snprintfrr(buf, 13, "class-type-%u", i);
+ json_object_double_add(jobj, buf,
+ attr->standard.unrsv_bw[i]);
+ json_object_array_add(jbw, jobj);
+ }
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS))
+ json_object_int_add(jte, "remote-asn",
+ attr->standard.remote_as);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR)) {
+ snprintfrr(buf, INET6_BUFSIZ, "%pI4",
+ &attr->standard.remote_addr);
+ json_object_string_add(jte, "remote-as-address", buf);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6)) {
+ snprintfrr(buf, INET6_BUFSIZ, "%pI6",
+ &attr->standard.remote_addr6);
+ json_object_string_add(jte, "remote-as-address-v6", buf);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY))
+ json_object_int_add(jte, "delay", attr->extended.delay);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) {
+ json_object_int_add(jte, "min-delay", attr->extended.min_delay);
+ json_object_int_add(jte, "max-delay", attr->extended.max_delay);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER))
+ json_object_int_add(jte, "jitter", attr->extended.jitter);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS))
+ json_object_double_add(
+ jte, "loss", attr->extended.pkt_loss * LOSS_PRECISION);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW))
+ json_object_double_add(jte, "available-bandwidth",
+ attr->extended.ava_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW))
+ json_object_double_add(jte, "residual-bandwidth",
+ attr->extended.rsv_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW))
+ json_object_double_add(jte, "utilized-bandwidth",
+ attr->extended.used_bw);
+ if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) {
+ jsrlg = json_object_new_array();
+ json_object_object_add(jte, "srlgs", jsrlg);
+ for (int i = 1; i < attr->srlg_len; i++) {
+ jobj = json_object_new_object();
+ json_object_int_add(jobj, "srlg", attr->srlgs[i]);
+ json_object_array_add(jsrlg, jobj);
+ }
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) {
+ jsr = json_object_new_array();
+ json_object_object_add(json, "segment-routing", jsr);
+ jobj = json_object_new_object();
+ json_object_int_add(jobj, "adj-sid", attr->adj_sid[0].sid);
+ snprintfrr(buf, 6, "0x%x", attr->adj_sid[0].flags);
+ json_object_string_add(jobj, "flags", buf);
+ json_object_int_add(jobj, "weight", attr->adj_sid[0].weight);
+ json_object_array_add(jsr, jobj);
+ }
+ if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) {
+ if (!jsr) {
+ jsr = json_object_new_array();
+ json_object_object_add(json, "segment-routing", jsr);
+ }
+ jobj = json_object_new_object();
+ json_object_int_add(jobj, "adj-sid", attr->adj_sid[1].sid);
+ snprintfrr(buf, 6, "0x%x", attr->adj_sid[1].flags);
+ json_object_string_add(jobj, "flags", buf);
+ json_object_int_add(jobj, "weight", attr->adj_sid[1].weight);
+ json_object_array_add(jsr, jobj);
+ }
+}
+
+void ls_show_edge(struct ls_edge *edge, struct vty *vty,
+ struct json_object *json, bool verbose)
+{
+ /* Sanity Check */
+ if (!edge)
+ return;
+
+ if (json)
+ ls_show_edge_json(edge, json);
+ else if (vty)
+ ls_show_edge_vty(edge, vty, verbose);
+}
+
+void ls_show_edges(struct ls_ted *ted, struct vty *vty,
+ struct json_object *json, bool verbose)
+{
+ struct ls_edge *edge;
+ json_object *jedges, *jedge;
+
+ if (json) {
+ jedges = json_object_new_array();
+ json_object_object_add(json, "edges", jedges);
+ frr_each (edges, &ted->edges, edge) {
+ jedge = json_object_new_object();
+ ls_show_edge(edge, NULL, jedge, verbose);
+ json_object_array_add(jedges, jedge);
+ }
+ } else if (vty) {
+ frr_each (edges, &ted->edges, edge)
+ ls_show_edge(edge, vty, NULL, verbose);
+ }
+}
+
+static void ls_show_subnet_vty(struct ls_subnet *subnet, struct vty *vty,
+ bool verbose)
+{
+ struct ls_prefix *pref;
+ struct sbuf sbuf;
+ char buf[INET6_BUFSIZ];
+
+ pref = subnet->ls_pref;
+ sbuf_init(&sbuf, NULL, 0);
+
+ sbuf_push(&sbuf, 2, "Subnet: %pFX", &subnet->key);
+ ls_node_id_to_text(pref->adv, buf, INET6_BUFSIZ);
+ sbuf_push(&sbuf, 0, "\tAdv. Vertex: %s", buf);
+ sbuf_push(&sbuf, 0, "\tMetric: %d", pref->metric);
+ sbuf_push(&sbuf, 0, "\tStatus: %s\n", status2txt[subnet->status]);
+
+ if (!verbose)
+ goto end;
+
+ sbuf_push(&sbuf, 4, "Origin: %s\n", origin2txt[pref->adv.origin]);
+ if (CHECK_FLAG(pref->flags, LS_PREF_IGP_FLAG))
+ sbuf_push(&sbuf, 4, "Flags: %d\n", pref->igp_flag);
+
+ if (CHECK_FLAG(pref->flags, LS_PREF_ROUTE_TAG))
+ sbuf_push(&sbuf, 4, "Tag: %d\n", pref->route_tag);
+
+ if (CHECK_FLAG(pref->flags, LS_PREF_EXTENDED_TAG))
+ sbuf_push(&sbuf, 4, "Extended Tag: %" PRIu64 "\n",
+ pref->extended_tag);
+
+ if (CHECK_FLAG(pref->flags, LS_PREF_SR))
+ sbuf_push(&sbuf, 4, "SID: %d\tAlgorithm: %d\tFlags: 0x%x\n",
+ pref->sr.sid, pref->sr.algo, pref->sr.sid_flag);
+
+end:
+ vty_out(vty, "%s\n", sbuf_buf(&sbuf));
+ sbuf_free(&sbuf);
+}
+
+static void ls_show_subnet_json(struct ls_subnet *subnet,
+ struct json_object *json)
+{
+ struct ls_prefix *pref;
+ json_object *jsr;
+ char buf[INET6_BUFSIZ];
+
+ pref = subnet->ls_pref;
+
+ snprintfrr(buf, INET6_BUFSIZ, "%pFX", &subnet->key);
+ json_object_string_add(json, "subnet-id", buf);
+ json_object_string_add(json, "status", status2txt[subnet->status]);
+ json_object_string_add(json, "origin", origin2txt[pref->adv.origin]);
+ ls_node_id_to_text(pref->adv, buf, INET6_BUFSIZ);
+ json_object_string_add(json, "advertised-router", buf);
+ if (subnet->vertex)
+ json_object_int_add(json, "vertex-id", subnet->vertex->key);
+ json_object_int_add(json, "metric", pref->metric);
+ if (CHECK_FLAG(pref->flags, LS_PREF_IGP_FLAG)) {
+ snprintfrr(buf, INET6_BUFSIZ, "0x%x", pref->igp_flag);
+ json_object_string_add(json, "flags", buf);
+ }
+ if (CHECK_FLAG(pref->flags, LS_PREF_ROUTE_TAG))
+ json_object_int_add(json, "tag", pref->route_tag);
+ if (CHECK_FLAG(pref->flags, LS_PREF_EXTENDED_TAG))
+ json_object_int_add(json, "extended-tag", pref->extended_tag);
+ if (CHECK_FLAG(pref->flags, LS_PREF_SR)) {
+ jsr = json_object_new_object();
+ json_object_object_add(json, "segment-routing", jsr);
+ json_object_int_add(jsr, "pref-sid", pref->sr.sid);
+ json_object_int_add(jsr, "algo", pref->sr.algo);
+ snprintfrr(buf, INET6_BUFSIZ, "0x%x", pref->sr.sid_flag);
+ json_object_string_add(jsr, "flags", buf);
+ }
+}
+
+void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty,
+ struct json_object *json, bool verbose)
+{
+ /* Sanity Check */
+ if (!subnet)
+ return;
+
+ if (json)
+ ls_show_subnet_json(subnet, json);
+ else if (vty)
+ ls_show_subnet_vty(subnet, vty, verbose);
+}
+
+void ls_show_subnets(struct ls_ted *ted, struct vty *vty,
+ struct json_object *json, bool verbose)
+{
+ struct ls_subnet *subnet;
+ json_object *jsubs, *jsub;
+
+ if (json) {
+ jsubs = json_object_new_array();
+ json_object_object_add(json, "subnets", jsubs);
+ frr_each (subnets, &ted->subnets, subnet) {
+ jsub = json_object_new_object();
+ ls_show_subnet(subnet, NULL, jsub, verbose);
+ json_object_array_add(jsubs, jsub);
+ }
+ } else if (vty) {
+ frr_each (subnets, &ted->subnets, subnet)
+ ls_show_subnet(subnet, vty, NULL, verbose);
+ }
+}
+
+void ls_show_ted(struct ls_ted *ted, struct vty *vty, struct json_object *json,
+ bool verbose)
+{
+ json_object *jted;
+
+ if (json) {
+ jted = json_object_new_object();
+ json_object_object_add(json, "ted", jted);
+ json_object_string_add(jted, "name", ted->name);
+ json_object_int_add(jted, "key", ted->key);
+ json_object_int_add(jted, "verticesCount",
+ vertices_count(&ted->vertices));
+ json_object_int_add(jted, "edgesCount",
+ edges_count(&ted->edges));
+ json_object_int_add(jted, "subnetsCount",
+ subnets_count(&ted->subnets));
+ ls_show_vertices(ted, NULL, jted, verbose);
+ ls_show_edges(ted, NULL, jted, verbose);
+ ls_show_subnets(ted, NULL, jted, verbose);
+ return;
+ }
+
+ if (vty) {
+ vty_out(vty,
+ "\n\tTraffic Engineering Database: %s (key: %d)\n\n",
+ ted->name, ted->key);
+ ls_show_vertices(ted, vty, NULL, verbose);
+ ls_show_edges(ted, vty, NULL, verbose);
+ ls_show_subnets(ted, vty, NULL, verbose);
+ vty_out(vty,
+ "\n\tTotal: %zu Vertices, %zu Edges, %zu Subnets\n\n",
+ vertices_count(&ted->vertices),
+ edges_count(&ted->edges), subnets_count(&ted->subnets));
+ }
+}
+
void ls_dump_ted(struct ls_ted *ted)
{
struct ls_vertex *vertex;
struct ls_edge *edge;
struct ls_subnet *subnet;
- struct ls_message msg;
+ const struct in_addr inaddr_any = {.s_addr = INADDR_ANY};
zlog_debug("(%s) Ted init", __func__);
- /* Prepare message */
- msg.event = LS_MSG_EVENT_SYNC;
/* Loop TED, start printing Node, then Attributes and finally Prefix */
- frr_each(vertices, &ted->vertices, vertex) {
- ls_vertex2msg(&msg, vertex);
+ frr_each (vertices, &ted->vertices, vertex) {
zlog_debug(" Ted node (%s %pI4 %s)",
vertex->node->name[0] ? vertex->node->name
: "no name node",
&vertex->node->router_id,
- vertex->node->adv.origin == DIRECT ? "DIRECT"
- : "NO DIRECT");
+ origin2txt[vertex->node->adv.origin]);
struct listnode *lst_node;
struct ls_edge *vertex_edge;
for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node,
vertex_edge)) {
zlog_debug(
- " inc edge key:%"PRIu64"n attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
+ " inc edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
vertex_edge->key,
&vertex_edge->attributes->adv.id.ip.addr,
&vertex_edge->attributes->standard.local,
@@ -1255,29 +2450,25 @@ void ls_dump_ted(struct ls_ted *ted)
for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node,
vertex_edge)) {
zlog_debug(
- " out edge key:%"PRIu64" attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
+ " out edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
vertex_edge->key,
&vertex_edge->attributes->adv.id.ip.addr,
&vertex_edge->attributes->standard.local,
&vertex_edge->attributes->standard.remote);
}
}
- frr_each(edges, &ted->edges, edge) {
- ls_edge2msg(&msg, edge);
- zlog_debug(" Ted edge key:%"PRIu64" src:%s dst:%s",
- edge->key,
- edge->source ? edge->source->node->name
- : "no_source",
- edge->destination ? edge->destination->node->name
- : "no_dest");
+ frr_each (edges, &ted->edges, edge) {
+ zlog_debug(" Ted edge key:%" PRIu64 "src:%pI4 dst:%pI4", edge->key,
+ edge->source ? &edge->source->node->router_id
+ : &inaddr_any,
+ edge->destination
+ ? &edge->destination->node->router_id
+ : &inaddr_any);
}
- frr_each(subnets, &ted->subnets, subnet) {
- ls_subnet2msg(&msg, subnet);
- zlog_debug(
- " Ted subnet key:%pFX vertex:%pI4 pfx:%pFX",
- &subnet->key,
- &subnet->vertex->node->adv.id.ip.addr,
- &subnet->ls_pref->pref);
+ frr_each (subnets, &ted->subnets, subnet) {
+ zlog_debug(" Ted subnet key:%pFX vertex:%pI4",
+ &subnet->ls_pref->pref,
+ &subnet->vertex->node->adv.id.ip.addr);
}
zlog_debug("(%s) Ted end", __func__);
}
diff --git a/lib/link_state.h b/lib/link_state.h
index f9eb59b76..de116df89 100644
--- a/lib/link_state.h
+++ b/lib/link_state.h
@@ -53,20 +53,26 @@ extern "C" {
* id for OSPF and the ISO System id plus the IS-IS level for IS-IS.
*/
+/* external reference */
+struct zapi_opaque_reg_info;
+struct zclient;
+
/* Link State Common definitions */
#define MAX_NAME_LENGTH 256
#define ISO_SYS_ID_LEN 6
/* Type of Node */
enum ls_node_type {
+ NONE = 0, /* Unknown */
STANDARD, /* a P or PE node */
ABR, /* an Array Border Node */
ASBR, /* an Autonomous System Border Node */
- PSEUDO, /* a Pseudo Node */
+ RMT_ASBR, /* Remote ASBR */
+ PSEUDO /* a Pseudo Node */
};
/* Origin of the Link State information */
-enum ls_origin {NONE = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC};
+enum ls_origin { UNKNOWN = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC };
/**
* Link State Node Identifier as:
@@ -108,19 +114,17 @@ struct ls_node {
struct in_addr router_id; /* IPv4 Router ID */
struct in6_addr router6_id; /* IPv6 Router ID */
uint8_t node_flag; /* IS-IS or OSPF Node flag */
- enum node_type type; /* Type of Node */
+ enum ls_node_type type; /* Type of Node */
uint32_t as_number; /* Local or neighbor AS number */
- struct { /* Segment Routing Global Block */
+ struct ls_srgb { /* Segment Routing Global Block */
uint32_t lower_bound; /* MPLS label lower bound */
uint32_t range_size; /* MPLS label range size */
uint8_t flag; /* IS-IS SRGB flags */
} srgb;
-#define LS_NODE_SRGB_SIZE 9
- struct { /* Segment Routing Local Block */
+ struct ls_srlb { /* Segment Routing Local Block */
uint32_t lower_bound; /* MPLS label lower bound */
uint32_t range_size; /* MPLS label range size */
} srlb;
-#define LS_NODE_SRLB_SIZE 8
uint8_t algo[2]; /* Segment Routing Algorithms */
uint8_t msd; /* Maximum Stack Depth */
};
@@ -150,17 +154,17 @@ struct ls_node {
#define LS_ATTR_AVA_BW 0x00100000
#define LS_ATTR_RSV_BW 0x00200000
#define LS_ATTR_USE_BW 0x00400000
-#define LS_ATTR_ADJ_SID 0x00800000
-#define LS_ATTR_BCK_ADJ_SID 0x01000000
-#define LS_ATTR_SRLG 0x02000000
+#define LS_ATTR_ADJ_SID 0x01000000
+#define LS_ATTR_BCK_ADJ_SID 0x02000000
+#define LS_ATTR_SRLG 0x10000000
/* Link State Attributes */
struct ls_attributes {
uint32_t flags; /* Flag for parameters validity */
struct ls_node_id adv; /* Adv. Router of this Link State */
char name[MAX_NAME_LENGTH]; /* Name of the Edge. Could be null */
- struct { /* Standard TE metrics */
- uint32_t metric; /* IGP standard metric */
+ uint32_t metric; /* IGP standard metric */
+ struct ls_standard { /* Standard TE metrics */
uint32_t te_metric; /* Traffic Engineering metric */
uint32_t admin_group; /* Administrative Group */
struct in_addr local; /* Local IPv4 address */
@@ -176,8 +180,7 @@ struct ls_attributes {
struct in_addr remote_addr; /* Remote IPv4 address */
struct in6_addr remote_addr6; /* Remote IPv6 address */
} standard;
-#define LS_ATTR_STANDARD_SIZE 124
- struct { /* Extended TE Metrics */
+ struct ls_extended { /* Extended TE Metrics */
uint32_t delay; /* Unidirectional average delay */
uint32_t min_delay; /* Unidirectional minimum delay */
uint32_t max_delay; /* Unidirectional maximum delay */
@@ -187,8 +190,7 @@ struct ls_attributes {
float rsv_bw; /* Reserved Bandwidth */
float used_bw; /* Utilized Bandwidth */
} extended;
-#define LS_ATTR_EXTENDED_SIZE 32
- struct { /* (LAN)-Adjacency SID for OSPF */
+ struct ls_adjacency { /* (LAN)-Adjacency SID for OSPF */
uint32_t sid; /* SID as MPLS label or index */
uint8_t flags; /* Flags */
uint8_t weight; /* Administrative weight */
@@ -197,7 +199,6 @@ struct ls_attributes {
uint8_t sysid[ISO_SYS_ID_LEN]; /* or Sys-ID for ISIS */
} neighbor;
} adj_sid[2]; /* Primary & Backup (LAN)-Adj. SID */
-#define LS_ATTR_ADJ_SID_SIZE 120
uint32_t *srlgs; /* List of Shared Risk Link Group */
uint8_t srlg_len; /* number of SRLG in the list */
};
@@ -219,7 +220,7 @@ struct ls_prefix {
uint32_t route_tag; /* IGP Route Tag */
uint64_t extended_tag; /* IGP Extended Route Tag */
uint32_t metric; /* Route metric for this prefix */
- struct {
+ struct ls_sid {
uint32_t sid; /* Segment Routing ID */
uint8_t sid_flag; /* Segment Routing Flags */
uint8_t algo; /* Algorithm for Segment Routing */
@@ -273,9 +274,16 @@ extern struct ls_attributes *ls_attributes_new(struct ls_node_id adv,
uint32_t local_id);
/**
+ * Remove SRLGs from Link State Attributes if defined.
+ *
+ * @param attr Pointer to a valid Link State Attribute structure
+ */
+extern void ls_attributes_srlg_del(struct ls_attributes *attr);
+
+/**
* Remove Link State Attributes. Data structure is freed.
*
- * @param attr Pointer to a valid Link State Attribute structure
+ * @param attr Pointer to a valid Link State Attribute structure
*/
extern void ls_attributes_del(struct ls_attributes *attr);
@@ -292,6 +300,34 @@ extern int ls_attributes_same(struct ls_attributes *a1,
struct ls_attributes *a2);
/**
+ * Create a new Link State Prefix. Structure is dynamically allocated.
+ *
+ * @param adv Mandatory Link State Node ID i.e. advertise router ID
+ * @param p Mandatory Prefix
+ *
+ * @return New Link State Prefix
+ */
+extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p);
+
+/**
+ * Remove Link State Prefix. Data Structure is freed.
+ *
+ * @param pref Pointer to a valid Link State Attribute Prefix.
+ */
+extern void ls_prefix_del(struct ls_prefix *pref);
+
+/**
+ * Check if two Link State Prefix are equal. Note that this routine has the
+ * same return value sense as '==' (which is different from a comparison).
+ *
+ * @param p1 First Link State Prefix to be compare
+ * @param p2 Second Link State Prefix to be compare
+ *
+ * @return 1 if equal, 0 otherwise
+ */
+extern int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2);
+
+/**
* In addition a Graph model is defined as an overlay on top of link state
* database in order to ease Path Computation algorithm implementation.
* Denoted G(V, E), a graph is composed by a list of Vertices (V) which
@@ -323,9 +359,14 @@ extern int ls_attributes_same(struct ls_attributes *a1,
*
*/
+enum ls_status { UNSET = 0, NEW, UPDATE, DELETE, SYNC, ORPHAN };
+enum ls_type { GENERIC = 0, VERTEX, EDGE, SUBNET };
+
/* Link State Vertex structure */
PREDECL_RBTREE_UNIQ(vertices);
struct ls_vertex {
+ enum ls_type type; /* Link State Type */
+ enum ls_status status; /* Status of the Vertex in the TED */
struct vertices_item entry; /* Entry in RB Tree */
uint64_t key; /* Unique Key identifier */
struct ls_node *node; /* Link State Node */
@@ -337,6 +378,8 @@ struct ls_vertex {
/* Link State Edge structure */
PREDECL_RBTREE_UNIQ(edges);
struct ls_edge {
+ enum ls_type type; /* Link State Type */
+ enum ls_status status; /* Status of the Edge in the TED */
struct edges_item entry; /* Entry in RB tree */
uint64_t key; /* Unique Key identifier */
struct ls_attributes *attributes; /* Link State attributes */
@@ -347,10 +390,12 @@ struct ls_edge {
/* Link State Subnet structure */
PREDECL_RBTREE_UNIQ(subnets);
struct ls_subnet {
+ enum ls_type type; /* Link State Type */
+ enum ls_status status; /* Status of the Subnet in the TED */
struct subnets_item entry; /* Entry in RB tree */
struct prefix key; /* Unique Key identifier */
- struct ls_vertex *vertex; /* Back pointer to the Vertex owner */
struct ls_prefix *ls_pref; /* Link State Prefix */
+ struct ls_vertex *vertex; /* Back pointer to the Vertex owner */
};
/* Declaration of Vertices, Edges and Prefixes RB Trees */
@@ -386,37 +431,45 @@ struct ls_ted {
struct subnets_head subnets; /* List of Subnets */
};
+/* Generic Link State Element */
+struct ls_element {
+ enum ls_type type; /* Link State Element Type */
+ enum ls_status status; /* Link State Status in the TED */
+ void *data; /* Link State payload */
+};
+
/**
- * Create a new Link State Vertex structure and initialize is with the Link
- * State Node parameter.
+ * Add new vertex to the Link State DB. Vertex is created from the Link State
+ * Node. Vertex data structure is dynamically allocated.
*
+ * @param ted Traffic Engineering Database structure
* @param node Link State Node
*
- * @return New Vertex
+ * @return New Vertex or NULL in case of error
*/
-extern struct ls_vertex *ls_vertex_new(struct ls_node *node);
+extern struct ls_vertex *ls_vertex_add(struct ls_ted *ted,
+ struct ls_node *node);
/**
* Delete Link State Vertex. This function clean internal Vertex lists (incoming
- * and outgoing Link State Edge and Link State Subnet). Note that referenced
- * objects of the different lists (Edges & SubNet) are not removed as they could
- * be connected to other Vertices.
+ * and outgoing Link State Edge and Link State Subnet). Vertex Data structure
+ * is freed but not the Link State Node. Link State DB is not modified if Vertex
+ * is NULL or not found in the Data Base. Note that referenced to Link State
+ * Edges & SubNets are not removed as they could be connected to other Vertices.
*
+ * @param ted Traffic Engineering Database structure
* @param vertex Link State Vertex to be removed
*/
-extern void ls_vertex_del(struct ls_vertex *vertex);
+extern void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex);
/**
- * Add new vertex to the Link State DB. Vertex is created from the Link State
- * Node. Vertex data structure is dynamically allocated.
+ * Delete Link State Vertex as ls_vertex_del() but also removed associated
+ * Link State Node.
*
- * @param ted Traffic Engineering Database structure
- * @param node Link State Node
- *
- * @return New Vertex or NULL in case of error
+ * @param ted Traffic Engineering Database structure
+ * @param vertex Link State Vertex to be removed
*/
-extern struct ls_vertex *ls_vertex_add(struct ls_ted *ted,
- struct ls_node *node);
+extern void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex);
/**
* Update Vertex with the Link State Node. A new vertex is created if no one
@@ -431,15 +484,15 @@ extern struct ls_vertex *ls_vertex_update(struct ls_ted *ted,
struct ls_node *node);
/**
- * Remove Vertex from the Link State DB. Vertex Data structure is freed but
- * not the Link State Node. Link State DB is not modified if Vertex is NULL or
- * not found in the Data Base.
+ * Clean Vertex structure by removing all Edges and Subnets marked as ORPHAN
+ * from this vertex. Link State Update message is sent if zclient is not NULL.
*
* @param ted Link State Data Base
- * @param vertex Vertex to be removed
+ * @param vertex Link State Vertex to be cleaned
+ * @param zclient Reference to Zebra Client
*/
-extern void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex);
-
+extern void ls_vertex_clean(struct ls_ted *ted, struct ls_vertex *vertex,
+ struct zclient *zclient);
/**
* Find Vertex in the Link State DB by its unique key.
*
@@ -498,6 +551,17 @@ extern struct ls_edge *ls_edge_update(struct ls_ted *ted,
struct ls_attributes *attributes);
/**
+ * Check if two Edges are equal. Note that this routine has the same return
+ * value sense as '==' (which is different from a comparison).
+ *
+ * @param e1 First edge to compare
+ * @param e2 Second edge to compare
+ *
+ * @return 1 if equal, 0 otherwise
+ */
+extern int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2);
+
+/**
* Remove Edge from the Link State DB. Edge data structure is freed but not the
* Link State Attributes data structure. Link State DB is not modified if Edge
* is NULL or not found in the Data Base.
@@ -508,6 +572,15 @@ extern struct ls_edge *ls_edge_update(struct ls_ted *ted,
extern void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge);
/**
+ * Remove Edge and associated Link State Attributes from the Link State DB.
+ * Link State DB is not modified if Edge is NULL or not found.
+ *
+ * @param ted Link State Data Base
+ * @param edge Edge to be removed
+ */
+extern void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge);
+
+/**
* Find Edge in the Link State Data Base by Edge key.
*
* @param ted Link State Data Base
@@ -520,8 +593,7 @@ extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted,
/**
* Find Edge in the Link State Data Base by the source (local IPv4 or IPv6
- * address or local ID) informations of the Link
- * State Attributes
+ * address or local ID) informations of the Link State Attributes
*
* @param ted Link State Data Base
* @param attributes Link State Attributes
@@ -557,6 +629,29 @@ extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted,
struct ls_prefix *pref);
/**
+ * Update the Link State Prefix information of an existing Subnet. If there is
+ * no corresponding Subnet in the Link State Data Base, a new Subnet is created.
+ *
+ * @param ted Link State Data Base
+ * @param pref Link State Prefix
+ *
+ * @return Updated Link State Subnet, or NULL in case of error
+ */
+extern struct ls_subnet *ls_subnet_update(struct ls_ted *ted,
+ struct ls_prefix *pref);
+
+/**
+ * Check if two Subnets are equal. Note that this routine has the same return
+ * value sense as '==' (which is different from a comparison).
+ *
+ * @param s1 First subnet to compare
+ * @param s2 Second subnet to compare
+ *
+ * @return 1 if equal, 0 otherwise
+ */
+extern int ls_subnet_same(struct ls_subnet *s1, struct ls_subnet *s2);
+
+/**
* Remove Subnet from the Link State DB. Subnet data structure is freed but
* not the Link State prefix data structure. Link State DB is not modified
* if Subnet is NULL or not found in the Data Base.
@@ -567,6 +662,15 @@ extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted,
extern void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet);
/**
+ * Remove Subnet and the associated Link State Prefix from the Link State DB.
+ * Link State DB is not modified if Subnet is NULL or not found.
+ *
+ * @param ted Link State Data Base
+ * @param subnet Subnet to be removed
+ */
+extern void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet);
+
+/**
* Find Subnet in the Link State Data Base by prefix.
*
* @param ted Link State Data Base
@@ -582,7 +686,7 @@ extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted,
*
* @param key Unique key of the data base. Must be different from 0
* @param name Name of the data base (may be NULL)
- * @param asn AS Number for this data base. Must be different from 0
+ * @param asn AS Number for this data base. 0 if unknown
*
* @return New Link State Database or NULL in case of error
*/
@@ -590,13 +694,29 @@ extern struct ls_ted *ls_ted_new(const uint32_t key, const char *name,
uint32_t asn);
/**
- * Delete existing Link State Data Base.
+ * Delete existing Link State Data Base. Vertices, Edges, and Subnets are not
+ * removed.
*
* @param ted Link State Data Base
*/
extern void ls_ted_del(struct ls_ted *ted);
/**
+ * Delete all Link State Vertices, Edges and SubNets and the Link State DB.
+ *
+ * @param ted Link State Data Base
+ */
+extern void ls_ted_del_all(struct ls_ted *ted);
+
+/**
+ * Clean Link State Data Base by removing all Vertices, Edges and SubNets marked
+ * as ORPHAN.
+ *
+ * @param ted Link State Data Base
+ */
+extern void ls_ted_clean(struct ls_ted *ted);
+
+/**
* Connect Source and Destination Vertices by given Edge. Only non NULL source
* and destination vertices are connected.
*
@@ -657,6 +777,7 @@ extern void ls_disconnect_edge(struct ls_edge *edge);
*/
/* ZAPI Opaque Link State Message Event */
+#define LS_MSG_EVENT_UNDEF 0
#define LS_MSG_EVENT_SYNC 1
#define LS_MSG_EVENT_ADD 2
#define LS_MSG_EVENT_UPDATE 3
@@ -680,6 +801,35 @@ struct ls_message {
};
/**
+ * Register Link State daemon as a server or client for Zebra OPAQUE API.
+ *
+ * @param zclient Zebra client structure
+ * @param server Register daemon as a server (true) or as a client (false)
+ *
+ * @return 0 if success, -1 otherwise
+ */
+extern int ls_register(struct zclient *zclient, bool server);
+
+/**
+ * Unregister Link State daemon as a server or client for Zebra OPAQUE API.
+ *
+ * @param zclient Zebra client structure
+ * @param server Unregister daemon as a server (true) or as a client (false)
+ *
+ * @return 0 if success, -1 otherwise
+ */
+extern int ls_unregister(struct zclient *zclient, bool server);
+
+/**
+ * Send Link State SYNC message to request the complete Link State Database.
+ *
+ * @param zclient Zebra client
+ *
+ * @return 0 if success, -1 otherwise
+ */
+extern int ls_request_sync(struct zclient *zclient);
+
+/**
* Parse Link State Message from stream. Used this function once receiving a
* new ZAPI Opaque message of type Link State.
*
@@ -690,7 +840,7 @@ struct ls_message {
extern struct ls_message *ls_parse_msg(struct stream *s);
/**
- * Delete existing message, freeing all substructure.
+ * Delete existing message. Data structure is freed.
*
* @param msg Link state message to be deleted
*/
@@ -751,6 +901,81 @@ extern struct ls_message *ls_subnet2msg(struct ls_message *msg,
struct ls_subnet *subnet);
/**
+ * Convert Link State Message into Vertex and update TED accordingly to
+ * the message event: SYNC, ADD, UPDATE or DELETE.
+ *
+ * @param ted Link State Database
+ * @param msg Link State Message
+ * @param delete True to delete the Link State Vertex from the Database,
+ * False otherwise. If true, return value is NULL in case
+ * of deletion.
+ *
+ * @return Vertex if success, NULL otherwise or if Vertex is removed
+ */
+extern struct ls_vertex *ls_msg2vertex(struct ls_ted *ted,
+ struct ls_message *msg, bool delete);
+
+/**
+ * Convert Link State Message into Edge and update TED accordingly to
+ * the message event: SYNC, ADD, UPDATE or DELETE.
+ *
+ * @param ted Link State Database
+ * @param msg Link State Message
+ * @param delete True to delete the Link State Edge from the Database,
+ * False otherwise. If true, return value is NULL in case
+ * of deletion.
+ *
+ * @return Edge if success, NULL otherwise or if Edge is removed
+ */
+extern struct ls_edge *ls_msg2edge(struct ls_ted *ted, struct ls_message *msg,
+ bool delete);
+
+/**
+ * Convert Link State Message into Subnet and update TED accordingly to
+ * the message event: SYNC, ADD, UPDATE or DELETE.
+ *
+ * @param ted Link State Database
+ * @param msg Link State Message
+ * @param delete True to delete the Link State Subnet from the Database,
+ * False otherwise. If true, return value is NULL in case
+ * of deletion.
+ *
+ * @return Subnet if success, NULL otherwise or if Subnet is removed
+ */
+extern struct ls_subnet *ls_msg2subnet(struct ls_ted *ted,
+ struct ls_message *msg, bool delete);
+
+/**
+ * Convert Link State Message into Link State element (Vertex, Edge or Subnet)
+ * and update TED accordingly to the message event: SYNC, ADD, UPDATE or DELETE.
+ *
+ * @param ted Link State Database
+ * @param msg Link State Message
+ * @param delete True to delete the Link State Element from the Database,
+ * False otherwise. If true, return value is NULL in case
+ * of deletion.
+ *
+ * @return Element if success, NULL otherwise or if Element is removed
+ */
+extern struct ls_element *ls_msg2ted(struct ls_ted *ted, struct ls_message *msg,
+ bool delete);
+
+/**
+ * Convert stream buffer into Link State element (Vertex, Edge or Subnet) and
+ * update TED accordingly to the message event: SYNC, ADD, UPDATE or DELETE.
+ *
+ * @param ted Link State Database
+ * @param s Stream buffer
+ * @param delete True to delete the Link State Element from the Database,
+ * False otherwise. If true, return value is NULL in case
+ * of deletion.
+ *
+ * @return Element if success, NULL otherwise or if Element is removed
+ */
+extern struct ls_element *ls_stream2ted(struct ls_ted *ted, struct stream *s,
+ bool delete);
+
+/**
* Send all the content of the Link State Data Base to the given destination.
* Link State content is sent is this order: Vertices, Edges, Subnet.
* This function must be used when a daemon request a Link State Data Base
@@ -765,6 +990,92 @@ extern struct ls_message *ls_subnet2msg(struct ls_message *msg,
extern int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient,
struct zapi_opaque_reg_info *dst);
+struct json_object;
+struct vty;
+/**
+ * Show Link State Vertex information. If both vty and json are specified,
+ * Json format output supersedes standard vty output.
+ *
+ * @param vertex Link State Vertex to show. Must not be NULL
+ * @param vty Pointer to vty output, could be NULL
+ * @param json Pointer to json output, could be NULL
+ * @param verbose Set to true for more detail
+ */
+extern void ls_show_vertex(struct ls_vertex *vertex, struct vty *vty,
+ struct json_object *json, bool verbose);
+
+/**
+ * Show all Link State Vertices information. If both vty and json are specified,
+ * Json format output supersedes standard vty output.
+ *
+ * @param ted Link State Data Base. Must not be NULL
+ * @param vty Pointer to vty output, could be NULL
+ * @param json Pointer to json output, could be NULL
+ * @param verbose Set to true for more detail
+ */
+extern void ls_show_vertices(struct ls_ted *ted, struct vty *vty,
+ struct json_object *json, bool verbose);
+
+/**
+ * Show Link State Edge information. If both vty and json are specified,
+ * Json format output supersedes standard vty output.
+ *
+ * @param edge Link State Edge to show. Must not be NULL
+ * @param vty Pointer to vty output, could be NULL
+ * @param json Pointer to json output, could be NULL
+ * @param verbose Set to true for more detail
+ */
+extern void ls_show_edge(struct ls_edge *edge, struct vty *vty,
+ struct json_object *json, bool verbose);
+
+/**
+ * Show all Link State Edges information. If both vty and json are specified,
+ * Json format output supersedes standard vty output.
+ *
+ * @param ted Link State Data Base. Must not be NULL
+ * @param vty Pointer to vty output, could be NULL
+ * @param json Pointer to json output, could be NULL
+ * @param verbose Set to true for more detail
+ */
+extern void ls_show_edges(struct ls_ted *ted, struct vty *vty,
+ struct json_object *json, bool verbose);
+
+/**
+ * Show Link State Subnets information. If both vty and json are specified,
+ * Json format output supersedes standard vty output.
+ *
+ * @param subnet Link State Subnet to show. Must not be NULL
+ * @param vty Pointer to vty output, could be NULL
+ * @param json Pointer to json output, could be NULL
+ * @param verbose Set to true for more detail
+ */
+extern void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty,
+ struct json_object *json, bool verbose);
+
+/**
+ * Show all Link State Subnet information. If both vty and json are specified,
+ * Json format output supersedes standard vty output.
+ *
+ * @param ted Link State Data Base. Must not be NULL
+ * @param vty Pointer to vty output, could be NULL
+ * @param json Pointer to json output, could be NULL
+ * @param verbose Set to true for more detail
+ */
+extern void ls_show_subnets(struct ls_ted *ted, struct vty *vty,
+ struct json_object *json, bool verbose);
+
+/**
+ * Show Link State Data Base information. If both vty and json are specified,
+ * Json format output supersedes standard vty output.
+ *
+ * @param ted Link State Data Base to show. Must not be NULL
+ * @param vty Pointer to vty output, could be NULL
+ * @param json Pointer to json output, could be NULL
+ * @param verbose Set to true for more detail
+ */
+extern void ls_show_ted(struct ls_ted *ted, struct vty *vty,
+ struct json_object *json, bool verbose);
+
/**
* Dump all Link State Data Base elements for debugging purposes
*
diff --git a/lib/zclient.h b/lib/zclient.h
index 5b2298c42..bd952ea1e 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -1134,7 +1134,7 @@ int zapi_opaque_reg_decode(struct stream *msg,
*/
enum zapi_opaque_registry {
/* Request link-state database dump, at restart for example */
- LINK_STATE_REQUEST = 1,
+ LINK_STATE_SYNC = 1,
/* Update containing link-state db info */
LINK_STATE_UPDATE = 2,
/* Request LDP-SYNC state from LDP */
diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c
index 17ff9a1a4..f2842538a 100644
--- a/ospfd/ospf_te.c
+++ b/ospfd/ospf_te.c
@@ -43,6 +43,9 @@
#include "hash.h"
#include "sockunion.h" /* for inet_aton() */
#include "network.h"
+#include "link_state.h"
+#include "zclient.h"
+#include "printfrr.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.h"
@@ -60,6 +63,9 @@
#include "ospfd/ospf_ase.h"
#include "ospfd/ospf_zebra.h"
#include "ospfd/ospf_te.h"
+#include "ospfd/ospf_sr.h"
+#include "ospfd/ospf_ri.h"
+#include "ospfd/ospf_ext.h"
#include "ospfd/ospf_vty.h"
#include "ospfd/ospf_errors.h"
@@ -71,6 +77,7 @@ struct ospf_mpls_te OspfMplsTE;
static const char *const mode2text[] = {"Off", "AS", "Area"};
+
/*------------------------------------------------------------------------*
* Followings are initialize/terminate functions for MPLS-TE handling.
*------------------------------------------------------------------------*/
@@ -82,8 +89,11 @@ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_status);
static void ospf_mpls_te_config_write_router(struct vty *vty);
static void ospf_mpls_te_show_info(struct vty *vty, struct ospf_lsa *lsa);
static int ospf_mpls_te_lsa_originate_area(void *arg);
-static int ospf_mpls_te_lsa_originate_as(void *arg);
+static int ospf_mpls_te_lsa_inter_as_as(void *arg);
+static int ospf_mpls_te_lsa_inter_as_area(void *arg);
static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa);
+static int ospf_mpls_te_lsa_update(struct ospf_lsa *lsa);
+static int ospf_mpls_te_lsa_delete(struct ospf_lsa *lsa);
static void del_mpls_te_link(void *val);
static void ospf_mpls_te_register_vty(void);
@@ -92,79 +102,72 @@ int ospf_mpls_te_init(void)
{
int rc;
+ /* Register Opaque AREA LSA Type 1 for Traffic Engineering */
rc = ospf_register_opaque_functab(
- OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA,
- ospf_mpls_te_new_if, ospf_mpls_te_del_if,
- ospf_mpls_te_ism_change, ospf_mpls_te_nsm_change,
+ OSPF_OPAQUE_AREA_LSA,
+ OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA,
+ ospf_mpls_te_new_if,
+ ospf_mpls_te_del_if,
+ ospf_mpls_te_ism_change,
+ ospf_mpls_te_nsm_change,
ospf_mpls_te_config_write_router,
- NULL, /*ospf_mpls_te_config_write_if */
+ NULL, /* ospf_mpls_te_config_write_if */
NULL, /* ospf_mpls_te_config_write_debug */
ospf_mpls_te_show_info, ospf_mpls_te_lsa_originate_area,
- ospf_mpls_te_lsa_refresh, NULL, /* ospf_mpls_te_new_lsa_hook */
- NULL /* ospf_mpls_te_del_lsa_hook */);
+ ospf_mpls_te_lsa_refresh,
+ ospf_mpls_te_lsa_update, /* ospf_mpls_te_new_lsa_hook */
+ ospf_mpls_te_lsa_delete /* ospf_mpls_te_del_lsa_hook */);
if (rc != 0) {
flog_warn(
EC_OSPF_OPAQUE_REGISTRATION,
- "ospf_mpls_te_init: Failed to register Traffic Engineering functions");
+ "MPLS-TE (%s): Failed to register Traffic Engineering functions",
+ __func__);
return rc;
}
- memset(&OspfMplsTE, 0, sizeof(struct ospf_mpls_te));
- OspfMplsTE.enabled = false;
- OspfMplsTE.inter_as = Off;
- OspfMplsTE.iflist = list_new();
- OspfMplsTE.iflist->del = del_mpls_te_link;
-
- ospf_mpls_te_register_vty();
-
- return rc;
-}
-
-/* Additional register for RFC5392 support */
-static int ospf_mpls_te_register(enum inter_as_mode mode)
-{
- int rc = 0;
- uint8_t scope;
-
- if (OspfMplsTE.inter_as != Off)
+ /*
+ * Wee need also to register Opaque LSA Type 6 i.e. Inter-AS RFC5392 for
+ * both AREA and AS at least to have the possibility to call the show()
+ * function when looking to the opaque LSA of the OSPF database.
+ */
+ rc = ospf_register_opaque_functab(OSPF_OPAQUE_AREA_LSA,
+ OPAQUE_TYPE_INTER_AS_LSA, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ ospf_mpls_te_show_info,
+ ospf_mpls_te_lsa_inter_as_area,
+ ospf_mpls_te_lsa_refresh, NULL, NULL);
+ if (rc != 0) {
+ flog_warn(
+ EC_OSPF_OPAQUE_REGISTRATION,
+ "MPLS-TE (%s): Failed to register Inter-AS with Area scope",
+ __func__);
return rc;
+ }
- if (mode == AS)
- scope = OSPF_OPAQUE_AS_LSA;
- else
- scope = OSPF_OPAQUE_AREA_LSA;
-
- rc = ospf_register_opaque_functab(scope, OPAQUE_TYPE_INTER_AS_LSA, NULL,
+ rc = ospf_register_opaque_functab(OSPF_OPAQUE_AS_LSA,
+ OPAQUE_TYPE_INTER_AS_LSA, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
ospf_mpls_te_show_info,
- ospf_mpls_te_lsa_originate_as,
+ ospf_mpls_te_lsa_inter_as_as,
ospf_mpls_te_lsa_refresh, NULL, NULL);
-
if (rc != 0) {
flog_warn(
EC_OSPF_OPAQUE_REGISTRATION,
- "ospf_router_info_init: Failed to register Inter-AS functions");
+ "MPLS-TE (%s): Failed to register Inter-AS with AS scope",
+ __func__);
return rc;
}
- return rc;
-}
-
-static int ospf_mpls_te_unregister(void)
-{
- uint8_t scope;
-
- if (OspfMplsTE.inter_as == Off)
- return 0;
-
- if (OspfMplsTE.inter_as == AS)
- scope = OSPF_OPAQUE_AS_LSA;
- else
- scope = OSPF_OPAQUE_AREA_LSA;
+ memset(&OspfMplsTE, 0, sizeof(struct ospf_mpls_te));
+ OspfMplsTE.enabled = false;
+ OspfMplsTE.export = false;
+ OspfMplsTE.inter_as = Off;
+ OspfMplsTE.iflist = list_new();
+ OspfMplsTE.iflist->del = del_mpls_te_link;
- ospf_delete_opaque_functab(scope, OPAQUE_TYPE_INTER_AS_LSA);
+ ospf_mpls_te_register_vty();
- return 0;
+ return rc;
}
void ospf_mpls_te_term(void)
@@ -173,28 +176,28 @@ void ospf_mpls_te_term(void)
ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA,
OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA);
+ ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA,
+ OPAQUE_TYPE_INTER_AS_LSA);
+ ospf_delete_opaque_functab(OSPF_OPAQUE_AS_LSA,
+ OPAQUE_TYPE_INTER_AS_LSA);
OspfMplsTE.enabled = false;
-
- ospf_mpls_te_unregister();
OspfMplsTE.inter_as = Off;
+ OspfMplsTE.export = false;
return;
}
void ospf_mpls_te_finish(void)
{
- // list_delete_all_node(OspfMplsTE.iflist);
-
OspfMplsTE.enabled = false;
- ospf_mpls_te_unregister();
OspfMplsTE.inter_as = Off;
+ OspfMplsTE.export = false;
}
/*------------------------------------------------------------------------*
* Followings are control functions for MPLS-TE parameters management.
*------------------------------------------------------------------------*/
-
static void del_mpls_te_link(void *val)
{
XFREE(MTYPE_OSPF_MPLS_TE, val);
@@ -235,8 +238,7 @@ static struct mpls_te_link *lookup_linkparams_by_instance(struct ospf_lsa *lsa)
if (lp->instance == key)
return lp;
- zlog_info("lookup_linkparams_by_instance: Entry not found: key(%x)",
- key);
+ ote_debug("MPLS-TE (%s): Entry not found: key(%x)", __func__, key);
return NULL;
}
@@ -484,6 +486,13 @@ static void set_linkparams_inter_as(struct mpls_te_link *lp,
lp->ras.header.type = htons(TE_LINK_SUBTLV_RAS);
lp->ras.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE);
lp->ras.value = htonl(as);
+
+ /* Set Type & Flooding flag accordingly */
+ lp->type = INTER_AS;
+ if (OspfMplsTE.inter_as == AS)
+ SET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS);
+ else
+ UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS);
}
static void unset_linkparams_inter_as(struct mpls_te_link *lp)
@@ -497,6 +506,10 @@ static void unset_linkparams_inter_as(struct mpls_te_link *lp)
lp->ras.header.type = htons(0);
lp->ras.header.length = htons(0);
lp->ras.value = htonl(0);
+
+ /* Reset Type & Flooding flag accordingly */
+ lp->type = STD_TE;
+ UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS);
}
void set_linkparams_llri(struct mpls_te_link *lp, uint32_t local,
@@ -606,15 +619,15 @@ static void update_linkparams(struct mpls_te_link *lp)
/* Get the Interface structure */
if ((ifp = lp->ifp) == NULL) {
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "OSPF MPLS-TE: Abort update TE parameters: no interface associated to Link Parameters");
+ ote_debug(
+ "MPLS-TE (%s): Abort update TE parameters: no interface associated to Link Parameters",
+ __func__);
return;
}
if (!HAS_LINK_PARAMS(ifp)) {
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "OSPF MPLS-TE: Abort update TE parameters: no Link Parameters for interface");
+ ote_debug(
+ "MPLS-TE (%s): Abort update TE parameters: no Link Parameters for interface",
+ __func__);
return;
}
@@ -688,17 +701,18 @@ static void update_linkparams(struct mpls_te_link *lp)
/* Flush LSA if it engaged and was previously a STD_TE one */
if (IS_STD_TE(lp->type)
&& CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) {
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "OSPF MPLS-TE Update IF: Switch from Standard LSA to INTER-AS for %s[%d/%d]",
- ifp->name, lp->flags, lp->type);
+ ote_debug(
+ "MPLS-TE (%s): Update IF: Switch from Standard LSA to INTER-AS for %s[%d/%d]",
+ __func__, ifp->name, lp->flags, lp->type);
ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA);
/* Then, switch it to INTER-AS */
- if (OspfMplsTE.inter_as == AS)
- lp->flags = INTER_AS | FLOOD_AS;
- else {
- lp->flags = INTER_AS | FLOOD_AREA;
+ if (OspfMplsTE.inter_as == AS) {
+ lp->type = INTER_AS;
+ SET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS);
+ } else {
+ lp->type = INTER_AS;
+ UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS);
lp->area = ospf_area_lookup_by_area_id(
ospf_lookup_by_vrf_id(VRF_DEFAULT),
OspfMplsTE.interas_areaid);
@@ -707,10 +721,9 @@ static void update_linkparams(struct mpls_te_link *lp)
set_linkparams_inter_as(lp, ifp->link_params->rmt_ip,
ifp->link_params->rmt_as);
} else {
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "OSPF MPLS-TE Update IF: Switch from INTER-AS LSA to Standard for %s[%d/%d]",
- ifp->name, lp->flags, lp->type);
+ ote_debug(
+ "MPLS-TE (%s): Update IF: Switch from INTER-AS LSA to Standard for %s[%d/%d]",
+ __func__, ifp->name, lp->flags, lp->type);
/* reset inter-as TE params */
/* Flush LSA if it engaged and was previously an INTER_AS one */
@@ -718,7 +731,8 @@ static void update_linkparams(struct mpls_te_link *lp)
&& CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) {
ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA);
/* Then, switch it to Standard TE */
- lp->flags = STD_TE | FLOOD_AREA;
+ lp->flags = STD_TE;
+ UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS);
}
unset_linkparams_inter_as(lp);
}
@@ -730,10 +744,8 @@ static void initialize_linkparams(struct mpls_te_link *lp)
struct ospf_interface *oi = NULL;
struct route_node *rn;
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "MPLS-TE(initialize_linkparams) Initialize Link Parameters for interface %s",
- ifp->name);
+ ote_debug("MPLS-TE (%s): Initialize Link Parameters for interface %s",
+ __func__, ifp->name);
/* Search OSPF Interface parameters for this interface */
for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) {
@@ -746,10 +758,9 @@ static void initialize_linkparams(struct mpls_te_link *lp)
}
if ((oi == NULL) || (oi->ifp != ifp)) {
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "MPLS-TE(initialize_linkparams) Could not find corresponding OSPF Interface for %s",
- ifp->name);
+ ote_debug(
+ "MPLS-TE (%s): Could not find corresponding OSPF Interface for %s",
+ __func__, ifp->name);
return;
}
@@ -780,21 +791,20 @@ static int is_mandated_params_set(struct mpls_te_link *lp)
int rc = 0;
if (ntohs(OspfMplsTE.router_addr.header.type) == 0) {
- flog_warn(
- EC_OSPF_TE_UNEXPECTED,
- "MPLS-TE(is_mandated_params_set) Missing Router Address");
+ flog_warn(EC_OSPF_TE_UNEXPECTED,
+ "MPLS-TE (%s): Missing Router Address", __func__);
return rc;
}
if (ntohs(lp->link_type.header.type) == 0) {
flog_warn(EC_OSPF_TE_UNEXPECTED,
- "MPLS-TE(is_mandated_params_set) Missing Link Type");
+ "MPLS-TE (%s): Missing Link Type", __func__);
return rc;
}
if (!IS_INTER_AS(lp->type) && (ntohs(lp->link_id.header.type) == 0)) {
- flog_warn(EC_OSPF_TE_UNEXPECTED,
- "MPLS-TE(is_mandated_params_set) Missing Link ID");
+ flog_warn(EC_OSPF_TE_UNEXPECTED, "MPLS-TE (%s) Missing Link ID",
+ __func__);
return rc;
}
@@ -810,10 +820,9 @@ static int ospf_mpls_te_new_if(struct interface *ifp)
{
struct mpls_te_link *new;
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "MPLS-TE(ospf_mpls_te_new_if) Add new %s interface %s to MPLS-TE list",
- ifp->link_params ? "Active" : "Inactive", ifp->name);
+ ote_debug("MPLS-TE (%s): Add new %s interface %s to MPLS-TE list",
+ __func__, ifp->link_params ? "Active" : "Inactive",
+ ifp->name);
if (lookup_linkparams_by_ifp(ifp) != NULL)
return 0;
@@ -826,7 +835,7 @@ static int ospf_mpls_te_new_if(struct interface *ifp)
* active */
/* This default behavior will be adapted with call to
* ospf_mpls_te_update_if() */
- new->type = STD_TE | FLOOD_AREA;
+ new->type = STD_TE;
new->flags = LPFLG_LSA_INACTIVE;
/* Initialize Link Parameters from Interface */
@@ -838,10 +847,8 @@ static int ospf_mpls_te_new_if(struct interface *ifp)
/* Add Link Parameters structure to the list */
listnode_add(OspfMplsTE.iflist, new);
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "OSPF MPLS-TE New IF: Add new LP context for %s[%d/%d]",
- ifp->name, new->flags, new->type);
+ ote_debug("MPLS-TE (%s): Add new LP context for %s[%d/%d]", __func__,
+ ifp->name, new->flags, new->type);
/* Schedule Opaque-LSA refresh. */ /* XXX */
return 0;
@@ -874,17 +881,15 @@ void ospf_mpls_te_update_if(struct interface *ifp)
{
struct mpls_te_link *lp;
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "OSPF MPLS-TE: Update LSA parameters for interface %s [%s]",
- ifp->name, HAS_LINK_PARAMS(ifp) ? "ON" : "OFF");
+ ote_debug("MPLS-TE (%s): Update LSA parameters for interface %s [%s]",
+ __func__, ifp->name, HAS_LINK_PARAMS(ifp) ? "ON" : "OFF");
/* Get Link context from interface */
if ((lp = lookup_linkparams_by_ifp(ifp)) == NULL) {
flog_warn(
EC_OSPF_TE_UNEXPECTED,
- "OSPF MPLS-TE Update: Did not find Link Parameters context for interface %s",
- ifp->name);
+ "MPLS-TE (%s): Did not find Link Parameters context for interface %s",
+ __func__, ifp->name);
return;
}
@@ -949,9 +954,6 @@ static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state)
/* Keep Area information in combination with linkparams. */
lp->area = oi->area;
- /* Keep interface MPLS-TE status */
- lp->flags = HAS_LINK_PARAMS(oi->ifp);
-
switch (oi->state) {
case ISM_PointToPoint:
case ISM_DROther:
@@ -962,17 +964,22 @@ static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state)
set_linkparams_lclif_ipaddr(lp, oi->address->u.prefix4);
break;
- default:
- /* State is undefined: Flush LSA if engaged */
- if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED))
+ case ISM_Down:
+ /* Interface goes Down: Flush LSA if engaged */
+ if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) {
+ ote_debug(
+ "MPLS-TE (%s): Interface %s goes down: flush LSA",
+ __func__, IF_NAME(oi));
ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA);
+ return;
+ }
+ break;
+ default:
break;
}
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "MPLS-TE(%s): Update Link parameters for interface %s",
- __func__, IF_NAME(oi));
+ ote_debug("MPLS-TE (%s): Update Link parameters for interface %s",
+ __func__, IF_NAME(oi));
return;
}
@@ -1009,12 +1016,21 @@ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state)
return;
}
+ /* Flush TE Opaque LSA if Neighbor State goes Down or Deleted */
+ if (OspfMplsTE.enabled
+ && (nbr->state == NSM_Down || nbr->state == NSM_Deleted)) {
+ if (CHECK_FLAG(lp->flags, EXT_LPFLG_LSA_ENGAGED)) {
+ ote_debug(
+ "MPLS-TE (%s): Interface %s goes down: flush LSA",
+ __func__, IF_NAME(oi));
+ ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA);
+ }
+ return;
+ }
+
/* Keep Area information in combination with SR info. */
lp->area = oi->area;
- /* Keep interface MPLS-TE status */
- lp->flags = HAS_LINK_PARAMS(oi->ifp);
-
/*
* The Link ID is identical to the contents of the Link ID field
* in the Router LSA for these link types.
@@ -1034,18 +1050,22 @@ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state)
set_linkparams_link_id(lp, DR(oi));
break;
- default:
- /* State is undefined: Flush LSA if engaged */
- if (OspfMplsTE.enabled &&
- CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED))
+ case ISM_Down:
+ /* State goes Down: Flush LSA if engaged */
+ if (OspfMplsTE.enabled
+ && CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) {
+ ote_debug(
+ "MPLS-TE (%s): Interface %s goes down: flush LSA",
+ __func__, IF_NAME(oi));
ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA);
+ }
return;
+ default:
+ break;
}
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "MPLS-TE (%s): Add Link-ID %pI4 for interface %s ",
- __func__, &lp->link_id.value, oi->ifp->name);
+ ote_debug("MPLS-TE (%s): Add Link-ID %pI4 for interface %s ", __func__,
+ &lp->link_id.value, oi->ifp->name);
/* Try to Schedule LSA */
if (OspfMplsTE.enabled) {
@@ -1058,7 +1078,7 @@ static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state)
}
/*------------------------------------------------------------------------*
- * Followings are OSPF protocol processing functions for MPLS-TE.
+ * Followings are OSPF protocol processing functions for MPLS-TE LSA.
*------------------------------------------------------------------------*/
static void build_tlv_header(struct stream *s, struct tlv_header *tlvh)
@@ -1119,11 +1139,12 @@ static void build_link_tlv(struct stream *s, struct mpls_te_link *lp)
static void ospf_mpls_te_lsa_body_set(struct stream *s, struct mpls_te_link *lp)
{
/*
- * The router address TLV is type 1, and ...
- * It must appear in exactly one
- * Traffic Engineering LSA originated by a router.
+ * The router address TLV is type 1, and ... It must appear in exactly
+ * one Traffic Engineering LSA originated by a router but not in
+ * Inter-AS TLV.
*/
- build_router_tlv(s);
+ if (!IS_INTER_AS(lp->type))
+ build_router_tlv(s);
/*
* Only one Link TLV shall be carried in each LSA, allowing for fine
@@ -1154,7 +1175,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf,
/* Set opaque-LSA header fields depending of the type of RFC */
if (IS_INTER_AS(lp->type)) {
- if (IS_FLOOD_AS(lp->type)) {
+ if (IS_FLOOD_AS(lp->flags)) {
/* Enable AS external as we flood Inter-AS with Opaque
* Type 11
*/
@@ -1186,10 +1207,9 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf,
area->ospf->router_id);
}
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
- zlog_debug(
- "LSA[Type%d:%pI4]: Create an Opaque-LSA/MPLS-TE instance",
- lsa_type, &lsa_id);
+ ote_debug(
+ "MPLS-TE (%s): LSA[Type%d:%pI4]: Create an Opaque-LSA/MPLS-TE instance",
+ __func__, lsa_type, &lsa_id);
/* Set opaque-LSA body fields. */
ospf_mpls_te_lsa_body_set(s, lp);
@@ -1221,16 +1241,15 @@ static int ospf_mpls_te_lsa_originate1(struct ospf_area *area,
/* Create new Opaque-LSA/MPLS-TE instance. */
new = ospf_mpls_te_lsa_new(area->ospf, area, lp);
if (new == NULL) {
- flog_warn(
- EC_OSPF_TE_UNEXPECTED,
- "ospf_mpls_te_lsa_originate1: ospf_mpls_te_lsa_new() ?");
+ flog_warn(EC_OSPF_TE_UNEXPECTED,
+ "MPLS-TE (%s): ospf_mpls_te_lsa_new() ?", __func__);
return rc;
}
/* Install this LSA into LSDB. */
if (ospf_lsa_install(area->ospf, NULL /*oi*/, new) == NULL) {
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
- "ospf_mpls_te_lsa_originate1: ospf_lsa_install() ?");
+ "MPLS-TE (%s): ospf_lsa_install() ?", __func__);
ospf_lsa_unlock(&new);
return rc;
}
@@ -1243,13 +1262,12 @@ static int ospf_mpls_te_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)) {
- zlog_debug(
- "LSA[Type%d:%pI4]: Originate Opaque-LSA/MPLS-TE: Area(%pI4), Link(%s)",
- new->data->type, &new->data->id, &area->area_id,
- lp->ifp->name);
+ ote_debug(
+ "MPLS-TE (%s): LSA[Type%d:%pI4]: Originate Opaque-LSA/MPLS-TE: Area(%pI4), Link(%s)",
+ __func__, new->data->type, &new->data->id, &area->area_id,
+ lp->ifp->name);
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
ospf_lsa_header_dump(new->data);
- }
rc = 0;
return rc;
@@ -1263,8 +1281,7 @@ static int ospf_mpls_te_lsa_originate_area(void *arg)
int rc = -1;
if (!OspfMplsTE.enabled) {
- zlog_info(
- "ospf_mpls_te_lsa_originate_area: MPLS-TE is disabled now.");
+ ote_debug("MPLS-TE (%s): MPLS-TE is disabled now.", __func__);
rc = 0; /* This is not an error case. */
return rc;
}
@@ -1272,7 +1289,7 @@ static int ospf_mpls_te_lsa_originate_area(void *arg)
for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) {
/* Process only enabled LSA with area scope flooding */
if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE)
- || IS_FLOOD_AS(lp->type))
+ || IS_FLOOD_AS(lp->flags))
continue;
if (lp->area == NULL)
@@ -1284,26 +1301,26 @@ static int ospf_mpls_te_lsa_originate_area(void *arg)
if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) {
if (CHECK_FLAG(lp->flags, LPFLG_LSA_FORCED_REFRESH)) {
UNSET_FLAG(lp->flags, LPFLG_LSA_FORCED_REFRESH);
- zlog_info(
- "OSPF MPLS-TE (ospf_mpls_te_lsa_originate_area): Refresh instead of Originate");
+ ote_debug(
+ "MPLS-TE (%s): Refresh instead of Originate",
+ __func__);
ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA);
}
continue;
}
if (!is_mandated_params_set(lp)) {
- zlog_info(
- "ospf_mpls_te_lsa_originate_area: Link(%s) lacks some mandated MPLS-TE parameters.",
- lp->ifp ? lp->ifp->name : "?");
+ ote_debug(
+ "MPLS-TE (%s): Link(%s) lacks some mandated MPLS-TE parameters.",
+ __func__, lp->ifp ? lp->ifp->name : "?");
continue;
}
/* Ok, let's try to originate an LSA for this area and Link. */
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "MPLS-TE(ospf_mpls_te_lsa_originate_area) Let's finally reoriginate the LSA %d through the Area %pI4 for Link %s",
- lp->instance, &area->area_id,
- lp->ifp ? lp->ifp->name : "?");
+ ote_debug(
+ "MPLS-TE (%s): Let's finally reoriginate the LSA %d through the Area %pI4 for Link %s",
+ __func__, lp->instance, &area->area_id,
+ lp->ifp ? lp->ifp->name : "?");
if (ospf_mpls_te_lsa_originate1(area, lp) != 0)
return rc;
}
@@ -1321,9 +1338,9 @@ static int ospf_mpls_te_lsa_originate2(struct ospf *top,
/* Create new Opaque-LSA/Inter-AS instance. */
new = ospf_mpls_te_lsa_new(top, NULL, lp);
if (new == NULL) {
- flog_warn(
- EC_OSPF_LSA_UNEXPECTED,
- "ospf_mpls_te_lsa_originate2: ospf_router_info_lsa_new() ?");
+ flog_warn(EC_OSPF_LSA_UNEXPECTED,
+ "MPLS-TE (%s): ospf_router_info_lsa_new() ?",
+ __func__);
return rc;
}
new->vrf_id = top->vrf_id;
@@ -1331,7 +1348,7 @@ static int ospf_mpls_te_lsa_originate2(struct ospf *top,
/* Install this LSA into LSDB. */
if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
- "ospf_mpls_te_lsa_originate2: ospf_lsa_install() ?");
+ "MPLS-TE (%s): ospf_lsa_install() ?", __func__);
ospf_lsa_unlock(&new);
return rc;
}
@@ -1344,12 +1361,12 @@ static int ospf_mpls_te_lsa_originate2(struct ospf *top,
/* Flood new LSA through AS. */
ospf_flood_through_as(top, NULL /*nbr */, new);
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
- zlog_debug(
- "LSA[Type%d:%pI4]: Originate Opaque-LSA/MPLS-TE Inter-AS",
- new->data->type, &new->data->id);
+ ote_debug(
+ "MPLS-TE (%s): LSA[Type%d:%pI4]: Originate Opaque-LSA/MPLS-TE Inter-AS",
+ __func__, new->data->type, &new->data->id);
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
ospf_lsa_header_dump(new->data);
- }
+
rc = 0;
return rc;
@@ -1364,8 +1381,8 @@ static int ospf_mpls_te_lsa_originate_as(void *arg)
int rc = -1;
if ((!OspfMplsTE.enabled) || (OspfMplsTE.inter_as == Off)) {
- zlog_info(
- "ospf_mpls_te_lsa_originate_as: MPLS-TE Inter-AS is disabled for now.");
+ ote_debug("MPLS-TE (%s): Inter-AS is disabled for now",
+ __func__);
rc = 0; /* This is not an error case. */
return rc;
}
@@ -1373,6 +1390,7 @@ static int ospf_mpls_te_lsa_originate_as(void *arg)
for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) {
/* Process only enabled INTER_AS Links or Pseudo-Links */
if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE)
+ || !CHECK_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS)
|| !IS_INTER_AS(lp->type))
continue;
@@ -1387,20 +1405,19 @@ static int ospf_mpls_te_lsa_originate_as(void *arg)
if (!is_mandated_params_set(lp)) {
flog_warn(
EC_OSPF_TE_UNEXPECTED,
- "ospf_mpls_te_lsa_originate_as: Link(%s) lacks some mandated MPLS-TE parameters.",
- lp->ifp ? lp->ifp->name : "?");
+ "MPLS-TE (%s): Link(%s) lacks some mandated MPLS-TE parameters.",
+ __func__, lp->ifp ? lp->ifp->name : "?");
continue;
}
/* Ok, let's try to originate an LSA for this AS and Link. */
- if (IS_DEBUG_OSPF_TE)
- zlog_debug(
- "MPLS-TE(ospf_mpls_te_lsa_originate_as) Let's finally re-originate the Inter-AS LSA %d through the %s for Link %s",
- lp->instance,
- IS_FLOOD_AS(lp->type) ? "AS" : "Area",
- lp->ifp ? lp->ifp->name : "Unknown");
+ ote_debug(
+ "MPLS-TE (%s): Let's finally re-originate the Inter-AS LSA %d through the %s for Link %s",
+ __func__, lp->instance,
+ IS_FLOOD_AS(lp->flags) ? "AS" : "Area",
+ lp->ifp ? lp->ifp->name : "Unknown");
- if (IS_FLOOD_AS(lp->type)) {
+ if (IS_FLOOD_AS(lp->flags)) {
top = (struct ospf *)arg;
ospf_mpls_te_lsa_originate2(top, lp);
} else {
@@ -1413,6 +1430,30 @@ static int ospf_mpls_te_lsa_originate_as(void *arg)
return rc;
}
+/*
+ * As Inter-AS LSA must be registered with both AREA and AS flooding, and
+ * because all origination callback functions are call (disregarding the Opaque
+ * LSA type and Flooding scope) it is necessary to determine which flooding
+ * scope is associated with the LSA origination as parameter is of type void and
+ * must be cast to struct *ospf for AS flooding and to struct *ospf_area for
+ * Area flooding.
+ */
+static int ospf_mpls_te_lsa_inter_as_as(void *arg)
+{
+ if (OspfMplsTE.inter_as == AS)
+ return ospf_mpls_te_lsa_originate_as(arg);
+ else
+ return 0;
+}
+
+static int ospf_mpls_te_lsa_inter_as_area(void *arg)
+{
+ if (OspfMplsTE.inter_as == Area)
+ return ospf_mpls_te_lsa_originate_area(arg);
+ else
+ return 0;
+}
+
static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa)
{
struct mpls_te_link *lp;
@@ -1426,7 +1467,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa)
* change.
* It seems a slip among routers in the routing domain.
*/
- zlog_info("ospf_mpls_te_lsa_refresh: MPLS-TE is disabled now.");
+ ote_debug("MPLS-TE (%s): MPLS-TE is disabled now", __func__);
lsa->data->ls_age =
htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */
}
@@ -1434,7 +1475,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa)
/* At first, resolve lsa/lp relationship. */
if ((lp = lookup_linkparams_by_instance(lsa)) == NULL) {
flog_warn(EC_OSPF_TE_UNEXPECTED,
- "ospf_mpls_te_lsa_refresh: Invalid parameter?");
+ "MPLS-TE (%s): Invalid parameter?", __func__);
lsa->data->ls_age =
htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */
ospf_opaque_lsa_flush_schedule(lsa);
@@ -1443,9 +1484,8 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa)
/* Check if lp was not disable in the interval */
if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE)) {
- flog_warn(
- EC_OSPF_TE_UNEXPECTED,
- "ospf_mpls_te_lsa_refresh: lp was disabled: Flush it!");
+ flog_warn(EC_OSPF_TE_UNEXPECTED,
+ "MPLS-TE (%s): lp was disabled: Flush it!", __func__);
lsa->data->ls_age =
htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */
}
@@ -1461,7 +1501,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa)
new = ospf_mpls_te_lsa_new(top, area, lp);
if (new == NULL) {
flog_warn(EC_OSPF_TE_UNEXPECTED,
- "ospf_mpls_te_lsa_refresh: ospf_mpls_te_lsa_new() ?");
+ "MPLS-TE (%s): ospf_mpls_te_lsa_new() ?", __func__);
return NULL;
}
new->data->ls_seqnum = lsa_seqnum_increment(lsa);
@@ -1475,24 +1515,23 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa)
if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) {
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
- "ospf_mpls_te_lsa_refresh: ospf_lsa_install() ?");
+ "MPLS-TE (%s): ospf_lsa_install() ?", __func__);
ospf_lsa_unlock(&new);
return NULL;
}
/* Flood updated LSA through AS or Area depending of the RFC of the link
*/
- if (IS_FLOOD_AS(lp->type))
+ if (IS_FLOOD_AS(lp->flags))
ospf_flood_through_as(top, NULL, new);
else
ospf_flood_through_area(area, NULL /*nbr*/, new);
/* Debug logging. */
- if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
- zlog_debug("LSA[Type%d:%pI4]: Refresh Opaque-LSA/MPLS-TE",
- new->data->type, &new->data->id);
+ ote_debug("MPLS-TE (%s): LSA[Type%d:%pI4]: Refresh Opaque-LSA/MPLS-TE",
+ __func__, new->data->type, &new->data->id);
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
ospf_lsa_header_dump(new->data);
- }
return new;
}
@@ -1509,14 +1548,19 @@ void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode)
top = ospf_lookup_by_vrf_id(VRF_DEFAULT);
/* Check if the pseudo link is ready to flood */
- if (!(CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE))
- || !(IS_FLOOD_AREA(lp->type) || IS_FLOOD_AS(lp->type))) {
+ if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE))
return;
- }
+
+ ote_debug("MPLS-TE (%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" : "",
+ lp->ifp ? lp->ifp->name : "-");
lsa.area = lp->area;
lsa.data = &lsah;
- if (IS_FLOOD_AS(lp->type)) {
+ if (IS_FLOOD_AS(lp->flags)) {
lsah.type = OSPF_OPAQUE_AS_LSA;
tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_INTER_AS_LSA, lp->instance);
lsah.id.s_addr = htonl(tmp);
@@ -1531,7 +1575,8 @@ void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode)
if (lp->area == NULL) {
flog_warn(
EC_OSPF_TE_UNEXPECTED,
- "MPLS-TE(ospf_mpls_te_lsa_schedule) Area context is null. Abort !");
+ "MPLS-TE (%s): Area context is null. Abort !",
+ __func__);
return;
}
tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_INTER_AS_LSA,
@@ -1545,14 +1590,11 @@ void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode)
switch (opcode) {
case REORIGINATE_THIS_LSA:
- if (IS_FLOOD_AS(lp->type)) {
+ if (IS_FLOOD_AS(lp->flags)) {
ospf_opaque_lsa_reoriginate_schedule(
(void *)top, OSPF_OPAQUE_AS_LSA,
OPAQUE_TYPE_INTER_AS_LSA);
- break;
- }
-
- if (IS_FLOOD_AREA(lp->type)) {
+ } else {
if (IS_INTER_AS(lp->type))
ospf_opaque_lsa_reoriginate_schedule(
(void *)lp->area, OSPF_OPAQUE_AREA_LSA,
@@ -1561,7 +1603,6 @@ void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode)
ospf_opaque_lsa_reoriginate_schedule(
(void *)lp->area, OSPF_OPAQUE_AREA_LSA,
OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA);
- break;
}
break;
case REFRESH_THIS_LSA:
@@ -1574,14 +1615,1532 @@ void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode)
break;
default:
flog_warn(EC_OSPF_TE_UNEXPECTED,
- "ospf_mpls_te_lsa_schedule: Unknown opcode (%u)",
+ "MPLS-TE (%s): Unknown opcode (%u)", __func__,
opcode);
break;
}
+}
- return;
+/**
+ * ------------------------------------------------------
+ * Followings are Link State Data Base control functions.
+ * ------------------------------------------------------
+ */
+
+/**
+ * Get Vertex from TED by the router which advertised the LSA. A new Vertex and
+ * associated Link State Node are created if Vertex is not found.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return Link State Vertex
+ */
+static struct ls_vertex *get_vertex(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct ls_node_id lnid;
+ struct ls_node *lnode;
+ struct ls_vertex *vertex;
+
+ /* Sanity Check */
+ if (!ted || !lsa || !lsa->data || !lsa->area)
+ return NULL;
+
+ /* Search if a Link State Vertex already exist */
+ lnid.origin = OSPFv2;
+ lnid.id.ip.addr = lsa->data->adv_router;
+ lnid.id.ip.area_id = lsa->area->area_id;
+ vertex = ls_find_vertex_by_id(ted, lnid);
+
+ /* Create Node & Vertex in the Link State Date Base if not found */
+ if (!vertex) {
+ const struct in_addr inaddr_any = {.s_addr = INADDR_ANY};
+
+ lnode = ls_node_new(lnid, inaddr_any, in6addr_any);
+ snprintfrr(lnode->name, MAX_NAME_LENGTH, "%pI4",
+ &lnid.id.ip.addr);
+ vertex = ls_vertex_add(ted, lnode);
+ }
+
+ if (IS_LSA_SELF(lsa))
+ ted->self = vertex;
+
+ return vertex;
+}
+
+/**
+ * Get Edge from TED by Link State Attribute ID. A new Edge and associated Link
+ * State Attributes are created if not found.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param adv Link State Node ID of router which advertised Edge
+ * @param link_id Link State Attribute ID
+ *
+ * @return Link State Edge
+ */
+static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_node_id adv,
+ struct in_addr link_id)
+{
+ uint64_t key;
+ struct ls_edge *edge;
+ struct ls_attributes *attr;
+
+ /* Search Edge that corresponds to the Link ID */
+ key = ((uint64_t)ntohl(link_id.s_addr)) & 0xffffffff;
+ edge = ls_find_edge_by_key(ted, key);
+
+ /* Create new one if not exist */
+ if (!edge) {
+ attr = ls_attributes_new(adv, link_id, in6addr_any, 0);
+ edge = ls_edge_add(ted, attr);
+ }
+
+ return edge;
}
+/**
+ * Export Link State information to consumer daemon through ZAPI Link State
+ * Opaque Message.
+ *
+ * @param type Type of Link State Element i.e. Vertex, Edge or Subnet
+ * @param link_state Pointer to Link State Vertex, Edge or Subnet
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_export(uint8_t type, void *link_state)
+{
+ struct ls_message msg = {};
+ int rc = 0;
+
+ if (!OspfMplsTE.export)
+ return rc;
+
+ switch (type) {
+ case LS_MSG_TYPE_NODE:
+ ls_vertex2msg(&msg, (struct ls_vertex *)link_state);
+ rc = ls_send_msg(zclient, &msg, NULL);
+ break;
+ case LS_MSG_TYPE_ATTRIBUTES:
+ ls_edge2msg(&msg, (struct ls_edge *)link_state);
+ rc = ls_send_msg(zclient, &msg, NULL);
+ break;
+ case LS_MSG_TYPE_PREFIX:
+ ls_subnet2msg(&msg, (struct ls_subnet *)link_state);
+ rc = ls_send_msg(zclient, &msg, NULL);
+ break;
+ default:
+ rc = -1;
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * Update Link State Edge & Attributes from the given Link State Attributes ID
+ * and metric. This function is called when parsing Router LSA.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param vertex Vertex where the Edge is attached as source
+ * @param link_data Link State Edge ID
+ * @param metric Standard metric attached to this Edge
+ */
+static void ospf_te_update_link(struct ls_ted *ted, struct ls_vertex *vertex,
+ struct in_addr link_data, uint8_t metric)
+{
+ struct ls_edge *edge;
+ struct ls_attributes *attr;
+
+ /* Sanity check */
+ if (!ted || !vertex || !vertex->node)
+ return;
+
+ /* Get Corresponding Edge from Link State Data Base */
+ edge = get_edge(ted, vertex->node->adv, link_data);
+ attr = edge->attributes;
+
+ /* re-attached edge to vertex if needed */
+ if (!edge->source)
+ edge->source = vertex;
+
+ /* Check if it is just an LSA refresh */
+ if ((CHECK_FLAG(attr->flags, LS_ATTR_METRIC)
+ && (attr->metric == metric))) {
+ edge->status = SYNC;
+ return;
+ }
+
+ /* Update metric value */
+ attr->metric = metric;
+ SET_FLAG(attr->flags, LS_ATTR_METRIC);
+ if (edge->status != NEW)
+ edge->status = UPDATE;
+
+ ote_debug(" |- %s Edge %pI4 with metric %d",
+ edge->status == NEW ? "Add" : "Update", &attr->standard.local,
+ attr->metric);
+
+ /* Export Link State Edge */
+ ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge);
+ edge->status = SYNC;
+}
+
+/**
+ * Update Link State Subnet & Prefix from the given prefix and metric. This
+ * function is called when parsing Router LSA.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param vertex Vertex where the Edge is attached as source
+ * @param p Prefix associated to the Subnet
+ * @param metric Standard metric attached to this Edge
+ */
+static void ospf_te_update_subnet(struct ls_ted *ted, struct ls_vertex *vertex,
+ struct prefix p, uint8_t metric)
+{
+ struct ls_subnet *subnet;
+ struct ls_prefix *ls_pref;
+
+ /* Search if there is a Subnet for this prefix */
+ subnet = ls_find_subnet(ted, p);
+
+ /* If found a Subnet, check if it is attached to this Vertex */
+ if (subnet) {
+ /* Re-attach the subnet to the vertex if necessary */
+ if (subnet->vertex != vertex) {
+ subnet->vertex = vertex;
+ listnode_add_sort_nodup(vertex->prefixes, subnet);
+ }
+ /* Check if it is a simple refresh */
+ ls_pref = subnet->ls_pref;
+ if ((CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC))
+ && (ls_pref->metric == metric)) {
+ subnet->status = SYNC;
+ return;
+ }
+ ls_pref->metric = metric;
+ SET_FLAG(ls_pref->flags, LS_PREF_METRIC);
+ subnet->status = UPDATE;
+ } else {
+ /* Create new Link State Prefix */
+ ls_pref = ls_prefix_new(vertex->node->adv, p);
+ ls_pref->metric = metric;
+ SET_FLAG(ls_pref->flags, LS_PREF_METRIC);
+ /* and add it to the TED */
+ subnet = ls_subnet_add(ted, ls_pref);
+ }
+
+ ote_debug(" |- %s subnet %pFX with metric %d",
+ subnet->status == NEW ? "Add" : "Update", &subnet->key,
+ ls_pref->metric);
+
+ /* Export Link State Subnet */
+ ospf_te_export(LS_MSG_TYPE_PREFIX, subnet);
+ subnet->status = SYNC;
+}
+
+/**
+ * Delete Subnet that correspond to the given IPv4 address and export deletion
+ * information before removal. Prefix length is fixed to IPV4_MAX_PREFIXLEN.
+ *
+ * @param ted Links State Database
+ * @param addr IPv4 address
+ */
+static void ospf_te_delete_subnet(struct ls_ted *ted, struct in_addr addr)
+{
+ struct prefix p;
+ struct ls_subnet *subnet;
+
+ /* Search subnet that correspond to the address/32 as prefix */
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_PREFIXLEN;
+ p.u.prefix4 = addr;
+ subnet = ls_find_subnet(ted, p);
+
+ /* Remove subnet if found */
+ if (subnet) {
+ subnet->status = DELETE;
+ ospf_te_export(LS_MSG_TYPE_PREFIX, subnet);
+ ls_subnet_del_all(ted, subnet);
+ }
+}
+
+/**
+ * Parse Router LSA. This function will create or update corresponding Vertex,
+ * Edge and Subnet. It also remove Edge and Subnet if they are marked as Orphan
+ * once Router LSA is parsed.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct router_lsa *rl;
+ enum ls_node_type type;
+ struct ls_vertex *vertex;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+ struct listnode *node;
+ int len;
+
+ /* Sanity Check */
+ if (!ted || !lsa || !lsa->data)
+ return -1;
+
+ ote_debug("MPLS-TE (%s): Parse Router LSA[%pI4] from Router[%pI4]",
+ __func__, &lsa->data->id, &lsa->data->adv_router);
+
+ /* Get vertex from LSA Advertise Router ID */
+ vertex = get_vertex(ted, lsa);
+
+ /* Set Node type information if it has changed */
+ rl = (struct router_lsa *)lsa->data;
+ if (IS_ROUTER_LSA_VIRTUAL(rl))
+ type = PSEUDO;
+ else if (IS_ROUTER_LSA_EXTERNAL(rl))
+ type = ASBR;
+ else if (IS_ROUTER_LSA_BORDER(rl))
+ type = ABR;
+ else
+ type = STANDARD;
+
+ if (vertex->status == NEW) {
+ vertex->node->type = type;
+ SET_FLAG(vertex->node->flags, LS_NODE_TYPE);
+ } else if (vertex->node->type != type) {
+ vertex->node->type = type;
+ vertex->status = UPDATE;
+ }
+
+ /* Check if Vertex has been modified */
+ if (vertex->status != SYNC) {
+ ote_debug(" |- %s Vertex %pI4",
+ vertex->status == NEW ? "Add" : "Update",
+ &vertex->node->router_id);
+
+ /* Vertex is out of sync: export it */
+ ospf_te_export(LS_MSG_TYPE_NODE, vertex);
+ vertex->status = SYNC;
+ }
+
+ /* Mark outgoing Edge and Subnet as ORPHAN to detect deletion */
+ for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge))
+ edge->status = ORPHAN;
+
+ for (ALL_LIST_ELEMENTS_RO(vertex->prefixes, node, subnet))
+ subnet->status = ORPHAN;
+
+ /* Then, process Link Information */
+ len = ntohs(rl->header.length) - 4;
+ for (int i = 0; i < ntohs(rl->links) && len > 0; len -= 12, i++) {
+ struct prefix p;
+ uint32_t metric;
+
+ switch (rl->link[i].type) {
+ case LSA_LINK_TYPE_POINTOPOINT:
+ ospf_te_update_link(ted, vertex, rl->link[i].link_data,
+ ntohs(rl->link[i].metric));
+ /* Add corresponding subnet */
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_PREFIXLEN;
+ p.u.prefix4 = rl->link[i].link_data;
+ metric = ntohs(rl->link[i].metric);
+ ospf_te_update_subnet(ted, vertex, p, metric);
+ break;
+ case LSA_LINK_TYPE_STUB:
+ /* Keep only /32 prefix */
+ p.prefixlen = ip_masklen(rl->link[i].link_data);
+ if (p.prefixlen == IPV4_MAX_PREFIXLEN) {
+ p.family = AF_INET;
+ p.u.prefix4 = rl->link[i].link_id;
+ metric = ntohs(rl->link[i].metric);
+ ospf_te_update_subnet(ted, vertex, p, metric);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ /* Clean remaining Orphan Edges or Subnets */
+ if (OspfMplsTE.export)
+ ls_vertex_clean(ted, vertex, zclient);
+ else
+ ls_vertex_clean(ted, vertex, NULL);
+
+ return 0;
+}
+
+/**
+ * Delete Vertex, Edge and Subnet associated to this Router LSA. This function
+ * is called when the router received such LSA with MAX_AGE (Flush) or when the
+ * router stop OSPF.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_delete_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct ls_node_id lnid;
+ struct ls_vertex *vertex;
+
+ /* Sanity Check */
+ if (!ted || !lsa || !lsa->data)
+ return -1;
+
+ /* Search Vertex that corresponds to this LSA */
+ lnid.origin = OSPFv2;
+ lnid.id.ip.addr = lsa->data->adv_router;
+ lnid.id.ip.area_id = lsa->area->area_id;
+ vertex = ls_find_vertex_by_id(ted, lnid);
+ if (!vertex)
+ return -1;
+
+ ote_debug("MPLS-TE (%s): Delete Vertex %pI4 from Router LSA[%pI4]",
+ __func__, &vertex->node->router_id, &lsa->data->id);
+
+ /* Export deleted vertex ... */
+ vertex->status = DELETE;
+ ospf_te_export(LS_MSG_TYPE_NODE, vertex);
+
+ /* ... and remove Node & Vertex from Link State Date Base */
+ ls_vertex_del_all(ted, vertex);
+
+ return 0;
+}
+
+/**
+ * Create or update Remote Vertex that corresponds to the remote ASBR of the
+ * foreign network if Edge is associated to an Inter-AS LSA (Type 6).
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param edge Link State Edge
+ */
+static void ospf_te_update_remote_asbr(struct ls_ted *ted, struct ls_edge *edge)
+{
+ struct ls_node_id lnid;
+ struct ls_vertex *vertex;
+ struct ls_node *lnode;
+ struct ls_attributes *attr;
+ struct prefix p;
+
+ /* Sanity Check */
+ if (!ted || !edge)
+ return;
+
+ /* Search if a Link State Vertex already exist */
+ attr = edge->attributes;
+ lnid.origin = OSPFv2;
+ lnid.id.ip.addr = attr->standard.remote_addr;
+ lnid.id.ip.area_id = attr->adv.id.ip.area_id;
+ vertex = ls_find_vertex_by_id(ted, lnid);
+
+ /* Create Node & Vertex in the Link State Date Base if not found */
+ if (!vertex) {
+ const struct in_addr inaddr_any = {.s_addr = INADDR_ANY};
+
+ lnode = ls_node_new(lnid, inaddr_any, in6addr_any);
+ snprintfrr(lnode->name, MAX_NAME_LENGTH, "%pI4",
+ &lnid.id.ip.addr);
+ vertex = ls_vertex_add(ted, lnode);
+ }
+
+ /* Update Node information */
+ lnode = vertex->node;
+ if (CHECK_FLAG(lnode->flags, LS_NODE_TYPE)) {
+ if (lnode->type != RMT_ASBR) {
+ lnode->type = RMT_ASBR;
+ if (vertex->status != NEW)
+ vertex->status = UPDATE;
+ }
+ } else {
+ lnode->type = RMT_ASBR;
+ SET_FLAG(lnode->flags, LS_NODE_TYPE);
+ if (vertex->status != NEW)
+ vertex->status = UPDATE;
+ }
+ if (CHECK_FLAG(lnode->flags, LS_NODE_AS_NUMBER)) {
+ if (lnode->as_number != attr->standard.remote_as) {
+ lnode->as_number = attr->standard.remote_as;
+ if (vertex->status != NEW)
+ vertex->status = UPDATE;
+ }
+ } else {
+ lnode->as_number = attr->standard.remote_as;
+ SET_FLAG(lnode->flags, LS_NODE_AS_NUMBER);
+ if (vertex->status != NEW)
+ vertex->status = UPDATE;
+ }
+
+ /* Export Link State Vertex if needed */
+ if (vertex->status == NEW || vertex->status == UPDATE) {
+ ote_debug(" |- %s Remote Vertex %pI4 for AS %u",
+ vertex->status == NEW ? "Add" : "Update",
+ &lnode->router_id, lnode->as_number);
+ ospf_te_export(LS_MSG_TYPE_NODE, vertex);
+ vertex->status = SYNC;
+ }
+
+ /* Update corresponding Subnets */
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_PREFIXLEN;
+ p.u.prefix4 = attr->standard.local;
+ ospf_te_update_subnet(ted, edge->source, p, attr->standard.te_metric);
+
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_PREFIXLEN;
+ p.u.prefix4 = attr->standard.remote_addr;
+ ospf_te_update_subnet(ted, vertex, p, attr->standard.te_metric);
+
+ /* Connect Edge to the remote Vertex */
+ if (edge->destination == NULL) {
+ edge->destination = vertex;
+ listnode_add_sort_nodup(vertex->incoming_edges, edge);
+ }
+
+ /* Finally set type to ASBR the node that advertised this Edge ... */
+ vertex = edge->source;
+ lnode = vertex->node;
+ if (CHECK_FLAG(lnode->flags, LS_NODE_TYPE)) {
+ if (lnode->type != ASBR) {
+ lnode->type = ASBR;
+ if (vertex->status != NEW)
+ vertex->status = UPDATE;
+ }
+ } else {
+ lnode->type = ASBR;
+ SET_FLAG(lnode->flags, LS_NODE_TYPE);
+ if (vertex->status != NEW)
+ vertex->status = UPDATE;
+ }
+
+ /* ... and Export it if needed */
+ if (vertex->status == NEW || vertex->status == UPDATE) {
+ ospf_te_export(LS_MSG_TYPE_NODE, vertex);
+ vertex->status = SYNC;
+ }
+}
+
+/**
+ * Parse Opaque Traffic Engineering LSA (Type 1) TLVs and create or update the
+ * corresponding Link State Edge and Attributes. Vertex connections are also
+ * updated if needed based on the remote IP address of the Edge and existing
+ * reverse Edge.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_parse_te(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct ls_edge *edge;
+ struct ls_vertex *vertex;
+ struct ls_attributes *old, attr = {};
+ struct tlv_header *tlvh;
+ void *value;
+ uint16_t len, sum;
+ uint8_t lsa_id;
+
+ /* Initialize Attribute */
+ attr.adv.origin = OSPFv2;
+ attr.adv.id.ip.addr = lsa->data->adv_router;
+ if (lsa->data->type != OSPF_OPAQUE_AS_LSA)
+ attr.adv.id.ip.area_id = lsa->area->area_id;
+
+ /* Initialize TLV browsing */
+ tlvh = TLV_HDR_TOP(lsa->data);
+
+ /* Skip TE Router-ID if present */
+ if (ntohs(tlvh->type) == TE_TLV_ROUTER_ADDR)
+ tlvh = TLV_HDR_NEXT(tlvh);
+
+ /* Check if we have a TE Link TLV */
+ len = TLV_BODY_SIZE(tlvh);
+ if ((len == 0) || (ntohs(tlvh->type) != TE_TLV_LINK))
+ return 0;
+
+ sum = 0;
+ /* Browse sub-TLV and fulfill Link State Attributes */
+ for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) {
+ uint32_t val32, tab32[2];
+ float valf, tabf[8];
+ struct in_addr addr;
+
+ value = TLV_DATA(tlvh);
+ switch (ntohs(tlvh->type)) {
+ case TE_LINK_SUBTLV_LCLIF_IPADDR:
+ memcpy(&addr, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.standard.local = addr;
+ SET_FLAG(attr.flags, LS_ATTR_LOCAL_ADDR);
+ break;
+ case TE_LINK_SUBTLV_RMTIF_IPADDR:
+ memcpy(&addr, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.standard.remote = addr;
+ SET_FLAG(attr.flags, LS_ATTR_NEIGH_ADDR);
+ break;
+ case TE_LINK_SUBTLV_TE_METRIC:
+ memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.standard.te_metric = ntohl(val32);
+ SET_FLAG(attr.flags, LS_ATTR_TE_METRIC);
+ break;
+ case TE_LINK_SUBTLV_MAX_BW:
+ memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.standard.max_bw = ntohf(valf);
+ SET_FLAG(attr.flags, LS_ATTR_MAX_BW);
+ break;
+ case TE_LINK_SUBTLV_MAX_RSV_BW:
+ memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.standard.max_rsv_bw = ntohf(valf);
+ SET_FLAG(attr.flags, LS_ATTR_MAX_RSV_BW);
+ break;
+ case TE_LINK_SUBTLV_UNRSV_BW:
+ memcpy(tabf, value, TE_LINK_SUBTLV_UNRSV_SIZE);
+ for (int i = 0; i < MAX_CLASS_TYPE; i++)
+ attr.standard.unrsv_bw[i] = ntohf(tabf[i]);
+ SET_FLAG(attr.flags, LS_ATTR_UNRSV_BW);
+ break;
+ case TE_LINK_SUBTLV_RSC_CLSCLR:
+ memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.standard.admin_group = ntohl(val32);
+ SET_FLAG(attr.flags, LS_ATTR_ADM_GRP);
+ break;
+ case TE_LINK_SUBTLV_LLRI:
+ memcpy(tab32, value, TE_LINK_SUBTLV_LLRI_SIZE);
+ attr.standard.local_id = ntohl(tab32[0]);
+ attr.standard.remote_id = ntohl(tab32[1]);
+ SET_FLAG(attr.flags, LS_ATTR_LOCAL_ID);
+ SET_FLAG(attr.flags, LS_ATTR_NEIGH_ID);
+ break;
+ case TE_LINK_SUBTLV_RIP:
+ memcpy(&addr, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.standard.remote_addr = addr;
+ SET_FLAG(attr.flags, LS_ATTR_REMOTE_ADDR);
+ break;
+ case TE_LINK_SUBTLV_RAS:
+ memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.standard.remote_as = ntohl(val32);
+ SET_FLAG(attr.flags, LS_ATTR_REMOTE_AS);
+ break;
+ case TE_LINK_SUBTLV_AV_DELAY:
+ memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.extended.delay = ntohl(val32);
+ SET_FLAG(attr.flags, LS_ATTR_DELAY);
+ break;
+ case TE_LINK_SUBTLV_MM_DELAY:
+ memcpy(tab32, value, TE_LINK_SUBTLV_MM_DELAY_SIZE);
+ attr.extended.min_delay = ntohl(tab32[0]);
+ attr.extended.max_delay = ntohl(tab32[1]);
+ SET_FLAG(attr.flags, LS_ATTR_MIN_MAX_DELAY);
+ break;
+ case TE_LINK_SUBTLV_DELAY_VAR:
+ memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.extended.jitter = ntohl(val32);
+ SET_FLAG(attr.flags, LS_ATTR_JITTER);
+ break;
+ case TE_LINK_SUBTLV_PKT_LOSS:
+ memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.extended.pkt_loss = ntohl(val32);
+ SET_FLAG(attr.flags, LS_ATTR_PACKET_LOSS);
+ break;
+ case TE_LINK_SUBTLV_RES_BW:
+ memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.extended.rsv_bw = ntohf(valf);
+ SET_FLAG(attr.flags, LS_ATTR_RSV_BW);
+ break;
+ case TE_LINK_SUBTLV_AVA_BW:
+ memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.extended.ava_bw = ntohf(valf);
+ SET_FLAG(attr.flags, LS_ATTR_AVA_BW);
+ break;
+ case TE_LINK_SUBTLV_USE_BW:
+ memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE);
+ attr.extended.used_bw = ntohf(valf);
+ SET_FLAG(attr.flags, LS_ATTR_USE_BW);
+ break;
+ default:
+ break;
+ }
+ sum += TLV_SIZE(tlvh);
+ }
+
+ /* Get corresponding Edge from Link State Data Base */
+ edge = get_edge(ted, attr.adv, attr.standard.local);
+ old = edge->attributes;
+
+ ote_debug(" |- Process Traffic Engineering LSA %pI4 for Edge %pI4",
+ &lsa->data->id, &attr.standard.local);
+
+ /* Update standard fields */
+ len = sizeof(struct ls_standard);
+ if ((attr.flags & 0x0FFFF) == (old->flags & 0x0FFFF)) {
+ if (memcmp(&attr.standard, &old->standard, len) != 0) {
+ memcpy(&old->standard, &attr.standard, len);
+ if (edge->status != NEW)
+ edge->status = UPDATE;
+ }
+ } else {
+ memcpy(&old->standard, &attr.standard, len);
+ old->flags |= attr.flags & 0x0FFFF;
+ if (edge->status != NEW)
+ edge->status = UPDATE;
+ }
+ /* Update extended fields */
+ len = sizeof(struct ls_extended);
+ if ((attr.flags & 0x0FF0000) == (old->flags & 0x0FF0000)) {
+ if (memcmp(&attr.extended, &old->extended, len) != 0) {
+ memcpy(&old->extended, &attr.extended, len);
+ if (edge->status != NEW)
+ edge->status = UPDATE;
+ }
+ } else {
+ memcpy(&old->extended, &attr.extended, len);
+ old->flags |= attr.flags & 0x0FF0000;
+ if (edge->status != NEW)
+ edge->status = UPDATE;
+ }
+
+ /* If LSA is an Opaque Inter-AS, Add Node and Subnet */
+ lsa_id = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr));
+ if (lsa_id == OPAQUE_TYPE_INTER_AS_LSA)
+ ospf_te_update_remote_asbr(ted, edge);
+
+ /* Update remote Link if remote IP addr is known */
+ if (CHECK_FLAG(old->flags, LS_ATTR_NEIGH_ADDR)) {
+ struct ls_edge *dst;
+
+ dst = ls_find_edge_by_destination(ted, old);
+ /* Attach remote link if not set */
+ if (dst && edge->source && dst->destination == NULL) {
+ vertex = edge->source;
+ if (vertex->incoming_edges)
+ listnode_add_sort_nodup(vertex->incoming_edges,
+ dst);
+ dst->destination = vertex;
+ }
+ /* and destination vertex to this edge */
+ if (dst && dst->source && edge->destination == NULL) {
+ vertex = dst->source;
+ if (vertex->incoming_edges)
+ listnode_add_sort_nodup(vertex->incoming_edges,
+ edge);
+ edge->destination = vertex;
+ }
+ }
+
+ /* Export Link State Edge if needed */
+ if (edge->status == NEW || edge->status == UPDATE) {
+ ote_debug(" |- %s TE info. for Edge %pI4",
+ edge->status == NEW ? "Add" : "Update",
+ &edge->attributes->standard.local);
+
+ ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge);
+ edge->status = SYNC;
+ }
+
+ return 0;
+}
+
+/**
+ * Delete Link State Attributes information that correspond to the Opaque
+ * Traffic Engineering LSA (Type 1) TLVs. Note that the Edge is not removed.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct ls_edge *edge;
+ struct ls_attributes *attr;
+ struct tlv_header *tlvh;
+ struct in_addr addr;
+ uint64_t key = 0;
+ uint16_t len, sum;
+ uint8_t lsa_id;
+
+ /* Initialize TLV browsing */
+ tlvh = TLV_HDR_TOP(lsa->data);
+ /* Skip Router TE ID if present */
+ if (ntohs(tlvh->type) == TE_TLV_ROUTER_ADDR)
+ tlvh = TLV_HDR_NEXT(tlvh);
+ len = TLV_BODY_SIZE(tlvh);
+ sum = 0;
+
+ /* Browse sub-TLV to find Link ID */
+ for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) {
+ if (ntohs(tlvh->type) == TE_LINK_SUBTLV_LCLIF_IPADDR) {
+ memcpy(&addr, TLV_DATA(tlvh), TE_LINK_SUBTLV_DEF_SIZE);
+ key = ((uint64_t)ntohl(addr.s_addr)) & 0xffffffff;
+ break;
+ }
+ sum += TLV_SIZE(tlvh);
+ }
+ if (key == 0)
+ return 0;
+
+ /* Search Edge that corresponds to the Link ID */
+ edge = ls_find_edge_by_key(ted, key);
+ if (!edge || !edge->attributes)
+ return 0;
+ attr = edge->attributes;
+
+ /* First, remove Remote ASBR and associated Edge & Subnet if any */
+ lsa_id = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr));
+ if (lsa_id == OPAQUE_TYPE_INTER_AS_LSA) {
+ ote_debug(" |- Delete remote ASBR, Edge and Subnet");
+
+ if (edge->destination) {
+ edge->destination->status = DELETE;
+ ospf_te_export(LS_MSG_TYPE_NODE, edge->destination);
+ ls_vertex_del_all(ted, edge->destination);
+ }
+
+ ospf_te_delete_subnet(ted, attr->standard.local);
+
+ edge->status = DELETE;
+ ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge);
+ ls_edge_del_all(ted, edge);
+
+ return 0;
+ }
+
+ ote_debug(" |- Delete TE info. for Edge %pI4",
+ &edge->attributes->standard.local);
+
+ /* Remove Link State Attributes TE information */
+ memset(&attr->standard, 0, sizeof(struct ls_standard));
+ attr->flags &= 0x0FFFF;
+ memset(&attr->extended, 0, sizeof(struct ls_extended));
+ attr->flags &= 0x0FF0000;
+ ls_attributes_srlg_del(attr);
+
+ /* Export Edge that has been updated */
+ if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)
+ || CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) {
+ edge->status = UPDATE;
+ ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge);
+ edge->status = SYNC;
+ } else {
+ /* Remove completely the Edge if Segment Routing is not set */
+ ospf_te_delete_subnet(ted, attr->standard.local);
+ edge->status = DELETE;
+ ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge);
+ ls_edge_del_all(ted, edge);
+ }
+
+ return 0;
+}
+
+/**
+ * Parse Opaque Router Information LSA (Type 4) TLVs and update the
+ * corresponding Link State Vertex with these information (Segment Routing).
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_parse_ri(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct ls_vertex *vertex;
+ struct ls_node *node;
+ struct lsa_header *lsah = lsa->data;
+ struct tlv_header *tlvh;
+ uint16_t len = 0, sum = 0;
+
+ /* Get vertex / Node from LSA Advertised Router ID */
+ vertex = get_vertex(ted, lsa);
+ node = vertex->node;
+
+ ote_debug(" |- Process Router Information LSA %pI4 for Vertex %pI4",
+ &lsa->data->id, &node->router_id);
+
+ /* Initialize TLV browsing */
+ len = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE;
+ for (tlvh = TLV_HDR_TOP(lsah); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) {
+ struct ri_sr_tlv_sr_algorithm *algo;
+ struct ri_sr_tlv_sid_label_range *range;
+ struct ri_sr_tlv_node_msd *msd;
+ uint32_t size, lower;
+
+ switch (ntohs(tlvh->type)) {
+ case RI_SR_TLV_SR_ALGORITHM:
+ algo = (struct ri_sr_tlv_sr_algorithm *)tlvh;
+
+ for (int i = 0; i < ntohs(algo->header.length); i++) {
+ if (CHECK_FLAG(node->flags, LS_NODE_SR)
+ && (node->algo[i] == algo->value[i]))
+ continue;
+
+ node->algo[i] = algo->value[i];
+ SET_FLAG(node->flags, LS_NODE_SR);
+ if (vertex->status != NEW)
+ vertex->status = UPDATE;
+ }
+
+ /* Reset other Algorithms */
+ for (int i = ntohs(algo->header.length); i < 2; i++) {
+ if (vertex->status != NEW
+ && node->algo[i] != SR_ALGORITHM_UNSET)
+ vertex->status = UPDATE;
+ node->algo[i] = SR_ALGORITHM_UNSET;
+ }
+
+ break;
+
+ case RI_SR_TLV_SRGB_LABEL_RANGE:
+ range = (struct ri_sr_tlv_sid_label_range *)tlvh;
+ size = GET_RANGE_SIZE(ntohl(range->size));
+ lower = GET_LABEL(ntohl(range->lower.value));
+ if ((CHECK_FLAG(node->flags, LS_NODE_SR))
+ && ((node->srgb.range_size == size)
+ && (node->srgb.lower_bound == lower)))
+ break;
+
+ node->srgb.range_size = size;
+ node->srgb.lower_bound = lower;
+ SET_FLAG(node->flags, LS_NODE_SR);
+ if (vertex->status != NEW)
+ vertex->status = UPDATE;
+
+ break;
+
+ case RI_SR_TLV_SRLB_LABEL_RANGE:
+ range = (struct ri_sr_tlv_sid_label_range *)tlvh;
+ size = GET_RANGE_SIZE(ntohl(range->size));
+ lower = GET_LABEL(ntohl(range->lower.value));
+ if ((CHECK_FLAG(node->flags, LS_NODE_SRLB))
+ && ((node->srlb.range_size == size)
+ && (node->srlb.lower_bound == lower)))
+ break;
+
+ node->srlb.range_size = size;
+ node->srlb.lower_bound = lower;
+ SET_FLAG(node->flags, LS_NODE_SRLB);
+ if (vertex->status != NEW)
+ vertex->status = UPDATE;
+
+ break;
+
+ case RI_SR_TLV_NODE_MSD:
+ msd = (struct ri_sr_tlv_node_msd *)tlvh;
+ if ((CHECK_FLAG(node->flags, LS_NODE_MSD))
+ && (node->msd == msd->value))
+ break;
+
+ node->msd = msd->value;
+ SET_FLAG(node->flags, LS_NODE_MSD);
+ if (vertex->status != NEW)
+ vertex->status = UPDATE;
+
+ break;
+
+ default:
+ break;
+ }
+ sum += TLV_SIZE(tlvh);
+ }
+
+ /* Vertex has been created or updated: export it */
+ if (vertex->status == NEW || vertex->status == UPDATE) {
+ ote_debug(" |- %s SR info - SRGB[%d/%d] for Vertex %pI4",
+ vertex->status == NEW ? "Add" : "Update",
+ vertex->node->srgb.lower_bound,
+ vertex->node->srgb.range_size,
+ &vertex->node->router_id);
+
+ ospf_te_export(LS_MSG_TYPE_NODE, vertex);
+ vertex->status = SYNC;
+ }
+
+ return 0;
+}
+
+/**
+ * Delete Link State Node information (Segment Routing) that correspond to the
+ * Opaque Router Information LSA (Type 4) TLVs. Note that the Vertex is not
+ * removed.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_delete_ri(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct ls_node_id lnid;
+ struct ls_vertex *vertex;
+ struct ls_node *node;
+
+ /* Search if a Link State Vertex already exist */
+ lnid.origin = OSPFv2;
+ lnid.id.ip.addr = lsa->data->adv_router;
+ lnid.id.ip.area_id = lsa->area->area_id;
+ vertex = ls_find_vertex_by_id(ted, lnid);
+ if (!vertex)
+ return -1;
+
+ /* Remove Segment Routing Information if any */
+ node = vertex->node;
+ UNSET_FLAG(node->flags, LS_NODE_SR);
+ memset(&node->srgb, 0, sizeof(struct ls_srgb));
+ node->algo[0] = SR_ALGORITHM_UNSET;
+ node->algo[1] = SR_ALGORITHM_UNSET;
+ UNSET_FLAG(node->flags, LS_NODE_SRLB);
+ memset(&node->srlb, 0, sizeof(struct ls_srlb));
+ UNSET_FLAG(node->flags, LS_NODE_MSD);
+ node->msd = 0;
+ vertex->status = UPDATE;
+
+ ote_debug(" |- Delete SR info. for Vertex %pI4",
+ &vertex->node->router_id);
+
+ /* Vertex has been updated: export it */
+ ospf_te_export(LS_MSG_TYPE_NODE, vertex);
+ vertex->status = SYNC;
+
+ return 0;
+}
+
+/**
+ * Parse Opaque Extended Prefix LSA (Type 7) TLVs and update the corresponding
+ * Link State Subnet with these information (Segment Routing ID).
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_parse_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct ls_node_id lnid;
+ struct ls_subnet *subnet;
+ struct ls_prefix *ls_pref;
+ struct prefix pref;
+ struct ext_tlv_prefix *ext;
+ struct ext_subtlv_prefix_sid *pref_sid;
+ uint32_t label;
+
+ /* Get corresponding Subnet from Link State Data Base */
+ ext = (struct ext_tlv_prefix *)TLV_HDR_TOP(lsa->data);
+ pref.family = AF_INET;
+ pref.prefixlen = ext->pref_length;
+ pref.u.prefix4 = ext->address;
+ subnet = ls_find_subnet(ted, pref);
+
+ /* Create new Link State Prefix if not found */
+ if (!subnet) {
+ lnid.origin = OSPFv2;
+ lnid.id.ip.addr = lsa->data->adv_router;
+ lnid.id.ip.area_id = lsa->area->area_id;
+ ls_pref = ls_prefix_new(lnid, pref);
+ /* and add it to the TED */
+ subnet = ls_subnet_add(ted, ls_pref);
+ }
+
+ ote_debug(" |- Process Extended Prefix LSA %pI4 for subnet %pFX",
+ &lsa->data->id, &pref);
+
+ /* Initialize TLV browsing */
+ ls_pref = subnet->ls_pref;
+ pref_sid = (struct ext_subtlv_prefix_sid *)((char *)(ext) + TLV_HDR_SIZE
+ + EXT_TLV_PREFIX_SIZE);
+ label = CHECK_FLAG(pref_sid->flags, EXT_SUBTLV_PREFIX_SID_VFLG)
+ ? GET_LABEL(ntohl(pref_sid->value))
+ : ntohl(pref_sid->value);
+
+ /* Check if it is a simple refresh */
+ if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)
+ && ls_pref->sr.algo == pref_sid->algorithm
+ && ls_pref->sr.sid_flag == pref_sid->flags
+ && ls_pref->sr.sid == label)
+ return 0;
+
+ /* Fulfill SR information */
+ ls_pref->sr.algo = pref_sid->algorithm;
+ ls_pref->sr.sid_flag = pref_sid->flags;
+ ls_pref->sr.sid = label;
+ SET_FLAG(ls_pref->flags, LS_PREF_SR);
+ if (subnet->status != NEW)
+ subnet->status = UPDATE;
+
+ /* Export Subnet if needed */
+ if (subnet->status == NEW || subnet->status == UPDATE) {
+ ote_debug(" |- %s SID %d to subnet %pFX",
+ subnet->status == NEW ? "Add" : "Update",
+ ls_pref->sr.sid, &ls_pref->pref);
+
+ ospf_te_export(LS_MSG_TYPE_PREFIX, subnet);
+ subnet->status = SYNC;
+ }
+
+ return 0;
+}
+
+/**
+ * Delete Link State Subnet information (Segment Routing ID) that correspond to
+ * the Opaque Extended Prefix LSA (Type 7) TLVs. Note that the Subnet is not
+ * removed.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_delete_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct ls_subnet *subnet;
+ struct ls_prefix *ls_pref;
+ struct prefix pref;
+ struct ext_tlv_prefix *ext;
+
+ /* Get corresponding Subnet from Link State Data Base */
+ ext = (struct ext_tlv_prefix *)TLV_HDR_TOP(lsa->data);
+ pref.family = AF_INET;
+ pref.prefixlen = ext->pref_length;
+ pref.u.prefix4 = ext->address;
+ subnet = ls_find_subnet(ted, pref);
+
+ /* Check if there is a corresponding subnet */
+ if (!subnet)
+ return -1;
+
+ ote_debug(" |- Delete SID %d to subnet %pFX", subnet->ls_pref->sr.sid,
+ &subnet->ls_pref->pref);
+
+ /* Remove Segment Routing information */
+ ls_pref = subnet->ls_pref;
+ UNSET_FLAG(ls_pref->flags, LS_PREF_SR);
+ memset(&ls_pref->sr, 0, sizeof(struct ls_sid));
+ subnet->status = UPDATE;
+
+ /* Subnet has been updated: export it */
+ ospf_te_export(LS_MSG_TYPE_PREFIX, subnet);
+ subnet->status = SYNC;
+
+ return 0;
+}
+
+/**
+ * Parse Opaque Extended Link LSA (Type 8) TLVs and update the corresponding
+ * Link State Edge with these information (Segment Routing Adjacency).
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_parse_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct ls_node_id lnid;
+ struct tlv_header *tlvh;
+ struct ext_tlv_link *ext;
+ struct ls_edge *edge;
+ struct ls_attributes *atr;
+ uint16_t len = 0, sum = 0, i;
+ uint32_t label;
+
+ /* Get corresponding Edge from Link State Data Base */
+ lnid.origin = OSPFv2;
+ lnid.id.ip.addr = lsa->data->adv_router;
+ lnid.id.ip.area_id = lsa->area->area_id;
+ ext = (struct ext_tlv_link *)TLV_HDR_TOP(lsa->data);
+ edge = get_edge(ted, lnid, ext->link_data);
+ atr = edge->attributes;
+
+ ote_debug(" |- Process Extended Link LSA %pI4 for edge %pI4",
+ &lsa->data->id, &edge->attributes->standard.local);
+
+ /* Initialize TLV browsing */
+ len = TLV_BODY_SIZE(&ext->header);
+ tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE
+ + EXT_TLV_LINK_SIZE);
+ for (; sum < len; tlvh = TLV_HDR_NEXT(tlvh)) {
+ struct ext_subtlv_adj_sid *adj;
+ struct ext_subtlv_lan_adj_sid *ladj;
+ struct ext_subtlv_rmt_itf_addr *rmt;
+
+ switch (ntohs(tlvh->type)) {
+ case EXT_SUBTLV_ADJ_SID:
+ adj = (struct ext_subtlv_adj_sid *)tlvh;
+ label = CHECK_FLAG(adj->flags,
+ EXT_SUBTLV_LINK_ADJ_SID_VFLG)
+ ? GET_LABEL(ntohl(adj->value))
+ : ntohl(adj->value);
+ i = CHECK_FLAG(adj->flags,
+ EXT_SUBTLV_LINK_ADJ_SID_BFLG) ? 1 : 0;
+ if (((i && CHECK_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID))
+ || (!i && CHECK_FLAG(atr->flags, LS_ATTR_ADJ_SID)))
+ && atr->adj_sid[i].flags == adj->flags
+ && atr->adj_sid[i].sid == label
+ && atr->adj_sid[i].weight == adj->weight)
+ break;
+
+ atr->adj_sid[i].flags = adj->flags;
+ atr->adj_sid[i].sid = label;
+ atr->adj_sid[i].weight = adj->weight;
+ if (i == 0)
+ SET_FLAG(atr->flags, LS_ATTR_ADJ_SID);
+ else
+ SET_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID);
+ if (edge->status != NEW)
+ edge->status = UPDATE;
+
+ break;
+ case EXT_SUBTLV_LAN_ADJ_SID:
+ ladj = (struct ext_subtlv_lan_adj_sid *)tlvh;
+ label = CHECK_FLAG(ladj->flags,
+ EXT_SUBTLV_LINK_ADJ_SID_VFLG)
+ ? GET_LABEL(ntohl(ladj->value))
+ : ntohl(ladj->value);
+ i = CHECK_FLAG(ladj->flags,
+ EXT_SUBTLV_LINK_ADJ_SID_BFLG) ? 1 : 0;
+ if (((i && CHECK_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID))
+ || (!i && CHECK_FLAG(atr->flags, LS_ATTR_ADJ_SID)))
+ && atr->adj_sid[i].flags == ladj->flags
+ && atr->adj_sid[i].sid == label
+ && atr->adj_sid[i].weight == ladj->weight
+ && IPV4_ADDR_SAME(&atr->adj_sid[1].neighbor.addr,
+ &ladj->neighbor_id))
+ break;
+
+ atr->adj_sid[i].flags = ladj->flags;
+ atr->adj_sid[i].sid = label;
+ atr->adj_sid[i].weight = ladj->weight;
+ atr->adj_sid[i].neighbor.addr = ladj->neighbor_id;
+ if (i == 0)
+ SET_FLAG(atr->flags, LS_ATTR_ADJ_SID);
+ else
+ SET_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID);
+ if (edge->status != NEW)
+ edge->status = UPDATE;
+
+ break;
+ case EXT_SUBTLV_RMT_ITF_ADDR:
+ rmt = (struct ext_subtlv_rmt_itf_addr *)tlvh;
+ if (CHECK_FLAG(atr->flags, LS_ATTR_NEIGH_ADDR)
+ && IPV4_ADDR_SAME(&atr->standard.remote,
+ &rmt->value))
+ break;
+
+ atr->standard.remote = rmt->value;
+ SET_FLAG(atr->flags, LS_ATTR_NEIGH_ADDR);
+ if (edge->status != NEW)
+ edge->status = UPDATE;
+
+ break;
+ default:
+ break;
+ }
+ sum += TLV_SIZE(tlvh);
+ }
+
+ /* Export Link State Edge if needed */
+ if (edge->status == NEW || edge->status == UPDATE) {
+ ote_debug(" |- %s Adj-SID %d & %d to edge %pI4",
+ edge->status == NEW ? "Add" : "Update",
+ edge->attributes->adj_sid[0].sid,
+ edge->attributes->adj_sid[1].sid,
+ &edge->attributes->standard.local);
+
+ ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge);
+ edge->status = SYNC;
+ }
+
+ return 0;
+}
+
+/**
+ * Delete Link State Edge information (Segment Routing Adjacency) that
+ * correspond to the Opaque Extended Link LSA (Type 8) TLVs. Note that the Edge
+ * is not removed.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_delete_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ struct ls_edge *edge;
+ struct ls_attributes *atr;
+ struct ext_tlv_link *ext;
+ uint64_t key;
+
+ /* Search for corresponding Edge from Link State Data Base */
+ ext = (struct ext_tlv_link *)TLV_HDR_TOP(lsa->data);
+ key = ((uint64_t)ntohl(ext->link_data.s_addr)) & 0xffffffff;
+ edge = ls_find_edge_by_key(ted, key);
+
+ /* Check if there is a corresponding Edge */
+ if (!edge)
+ return -1;
+
+ ote_debug(" |- Delete Adj-SID %d to edge %pI4",
+ edge->attributes->adj_sid[0].sid,
+ &edge->attributes->standard.local);
+
+ /* Remove Segment Routing information */
+ atr = edge->attributes;
+ UNSET_FLAG(atr->flags, LS_ATTR_ADJ_SID);
+ UNSET_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID);
+ memset(atr->adj_sid, 0, 2 * sizeof(struct ls_sid));
+ edge->status = UPDATE;
+
+ /* Edge has been updated: export it */
+ ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge);
+ edge->status = SYNC;
+
+ return 0;
+}
+
+/**
+ * Parse Opaque LSA Type and call corresponding parser.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_parse_opaque_lsa(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ uint8_t key = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr));
+ int rc = -1;
+
+ ote_debug("MPLS-TE (%s): Parse Opaque LSA[%pI4] from Router[%pI4]",
+ __func__, &lsa->data->id, &lsa->data->adv_router);
+
+ switch (key) {
+ case OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA:
+ case OPAQUE_TYPE_INTER_AS_LSA:
+ rc = ospf_te_parse_te(ted, lsa);
+ break;
+ case OPAQUE_TYPE_ROUTER_INFORMATION_LSA:
+ rc = ospf_te_parse_ri(ted, lsa);
+ break;
+ case OPAQUE_TYPE_EXTENDED_PREFIX_LSA:
+ rc = ospf_te_parse_ext_pref(ted, lsa);
+ break;
+ case OPAQUE_TYPE_EXTENDED_LINK_LSA:
+ rc = ospf_te_parse_ext_link(ted, lsa);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * Parse Opaque LSA Type and call corresponding deletion function.
+ *
+ * @param ted Link State Traffic Engineering Database
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_te_delete_opaque_lsa(struct ls_ted *ted, struct ospf_lsa *lsa)
+{
+ uint8_t key = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr));
+ int rc = -1;
+
+ ote_debug("MPLS-TE (%s): Parse Opaque LSA[%pI4] from Router[%pI4]",
+ __func__, &lsa->data->id, &lsa->data->adv_router);
+
+ switch (key) {
+ case OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA:
+ case OPAQUE_TYPE_INTER_AS_LSA:
+ rc = ospf_te_delete_te(ted, lsa);
+ break;
+ case OPAQUE_TYPE_ROUTER_INFORMATION_LSA:
+ rc = ospf_te_delete_ri(ted, lsa);
+ break;
+ case OPAQUE_TYPE_EXTENDED_PREFIX_LSA:
+ rc = ospf_te_delete_ext_pref(ted, lsa);
+ break;
+ case OPAQUE_TYPE_EXTENDED_LINK_LSA:
+ rc = ospf_te_delete_ext_link(ted, lsa);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * Update Traffic Engineering Database Elements that correspond to the received
+ * OSPF LSA. If LSA age is equal to MAX_AGE, call deletion function instead.
+ *
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_mpls_te_lsa_update(struct ospf_lsa *lsa)
+{
+
+ uint8_t rc;
+
+ /* Check that MPLS-TE is active */
+ if (!OspfMplsTE.enabled || !OspfMplsTE.ted)
+ return 0;
+
+ /* Sanity Check */
+ if (lsa == NULL) {
+ flog_warn(EC_OSPF_LSA_NULL, "TE (%s): Abort! LSA is NULL",
+ __func__);
+ return -1;
+ }
+
+ /* If LSA is MAX_AGE, remove corresponding Link State element */
+ if (IS_LSA_MAXAGE(lsa)) {
+ switch (lsa->data->type) {
+ case OSPF_ROUTER_LSA:
+ rc = ospf_te_delete_router_lsa(OspfMplsTE.ted, lsa);
+ break;
+ case OSPF_OPAQUE_AREA_LSA:
+ case OSPF_OPAQUE_AS_LSA:
+ rc = ospf_te_delete_opaque_lsa(OspfMplsTE.ted, lsa);
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ } else {
+ /* Parse LSA to Update corresponding Link State element */
+ switch (lsa->data->type) {
+ case OSPF_ROUTER_LSA:
+ rc = ospf_te_parse_router_lsa(OspfMplsTE.ted, lsa);
+ break;
+ case OSPF_OPAQUE_AREA_LSA:
+ case OSPF_OPAQUE_AS_LSA:
+ rc = ospf_te_parse_opaque_lsa(OspfMplsTE.ted, lsa);
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Delete Traffic Engineering Database element from OSPF LSA. This function
+ * process only self LSA (i.e. advertised by the router) which reach MAX_AGE
+ * as LSA deleted by neighbor routers are Flushed (i.e. advertised with
+ * age == MAX_AGE) and processed by ospf_mpls_te_lsa_update() function.
+ *
+ * @param lsa OSPF Link State Advertisement
+ *
+ * @return 0 if success, -1 otherwise
+ */
+static int ospf_mpls_te_lsa_delete(struct ospf_lsa *lsa)
+{
+
+ uint8_t rc;
+
+ /* Check that MPLS-TE is active */
+ if (!OspfMplsTE.enabled || !OspfMplsTE.ted)
+ return 0;
+
+ /* Sanity Check */
+ if (lsa == NULL) {
+ flog_warn(EC_OSPF_LSA_NULL, "TE (%s): Abort! LSA is NULL",
+ __func__);
+ return -1;
+ }
+
+ /*
+ * Process only self LSAs that reach MAX_AGE. Indeed, when the router
+ * need to update or refresh an LSA, it first removes the old LSA from
+ * the LSDB and then insert the new one. Thus, to avoid removing
+ * corresponding Link State element and loosing some parameters
+ * instead of just updating it, only self LSAs that reach MAX_AGE are
+ * processed here. Other LSAs are processed by ospf_mpls_te_lsa_update()
+ * and eventually removed when LSA age is MAX_AGE i.e. LSA is flushed
+ * by the originator.
+ */
+ if (!IS_LSA_SELF(lsa) || !IS_LSA_MAXAGE(lsa))
+ return 0;
+
+ /* Parse Link State information */
+ switch (lsa->data->type) {
+ case OSPF_ROUTER_LSA:
+ rc = ospf_te_delete_router_lsa(OspfMplsTE.ted, lsa);
+ break;
+ case OSPF_OPAQUE_AREA_LSA:
+ case OSPF_OPAQUE_AS_LSA:
+ rc = ospf_te_delete_opaque_lsa(OspfMplsTE.ted, lsa);
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * Send the whole Link State Traffic Engineering Database to the consumer that
+ * request it through a ZAPI Link State Synchronous Opaque Message.
+ *
+ * @param info ZAPI Opaque message
+ *
+ * @return 0 if success, -1 otherwise
+ */
+int ospf_te_sync_ted(struct zapi_opaque_reg_info dst)
+{
+ int rc = -1;
+
+ /* Check that MPLS-TE and TE distribution are enabled */
+ if (!OspfMplsTE.enabled || !OspfMplsTE.export)
+ return rc;
+
+ rc = ls_sync_ted(OspfMplsTE.ted, zclient, &dst);
+
+ return rc;
+}
+
+/**
+ * Initialize Traffic Engineering Database from the various OSPF Link State
+ * Database (LSDB).
+ *
+ * @param ted Link State Traffice Engineering Database
+ * @param ospf OSPF main structure
+ */
+static void ospf_te_init_ted(struct ls_ted *ted, struct ospf *ospf)
+{
+ struct listnode *node, *nnode;
+ struct route_node *rn;
+ struct ospf_area *area;
+ struct ospf_lsa *lsa;
+
+ /* Iterate over all areas. */
+ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) {
+ if (!area->lsdb)
+ continue;
+
+ /* Parse all Router LSAs from the area LSDB */
+ LSDB_LOOP (ROUTER_LSDB(area), rn, lsa)
+ ospf_te_parse_router_lsa(ted, lsa);
+
+ /* Parse all Opaque LSAs from the area LSDB */
+ LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa)
+ ospf_te_parse_opaque_lsa(ted, lsa);
+ }
+
+ /* Parse AS-external opaque LSAs from OSPF LSDB */
+ if (ospf->lsdb) {
+ LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa)
+ ospf_te_parse_opaque_lsa(ted, lsa);
+ }
+
+}
/*------------------------------------------------------------------------*
* Followings are vty session control functions.
@@ -2157,14 +3716,15 @@ static void ospf_mpls_te_config_write_router(struct vty *vty)
vty_out(vty, " mpls-te on\n");
vty_out(vty, " mpls-te router-address %pI4\n",
&OspfMplsTE.router_addr.value);
- }
-
- if (OspfMplsTE.inter_as == AS)
- vty_out(vty, " mpls-te inter-as as\n");
- if (OspfMplsTE.inter_as == Area)
- vty_out(vty, " mpls-te inter-as area %pI4 \n",
- &OspfMplsTE.interas_areaid);
+ if (OspfMplsTE.inter_as == AS)
+ vty_out(vty, " mpls-te inter-as as\n");
+ if (OspfMplsTE.inter_as == Area)
+ vty_out(vty, " mpls-te inter-as area %pI4 \n",
+ &OspfMplsTE.interas_areaid);
+ if (OspfMplsTE.export)
+ vty_out(vty, " mpls-te export\n");
+ }
return;
}
@@ -2185,8 +3745,7 @@ DEFUN (ospf_mpls_te_on,
if (OspfMplsTE.enabled)
return CMD_SUCCESS;
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("MPLS-TE: OFF -> ON");
+ ote_debug("MPLS-TE: OFF -> ON");
OspfMplsTE.enabled = true;
@@ -2204,6 +3763,14 @@ DEFUN (ospf_mpls_te_on,
}
}
+ /* Create TED and initialize it */
+ OspfMplsTE.ted = ls_ted_new(1, "OSPF", 0);
+ if (!OspfMplsTE.ted) {
+ vty_out(vty, "Unable to create Link State Data Base\n");
+ return CMD_WARNING;
+ }
+ ospf_te_init_ted(OspfMplsTE.ted, ospf);
+
return CMD_SUCCESS;
}
@@ -2221,11 +3788,13 @@ DEFUN (no_ospf_mpls_te,
if (!OspfMplsTE.enabled)
return CMD_SUCCESS;
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("MPLS-TE: ON -> OFF");
+ ote_debug("MPLS-TE: ON -> OFF");
+ /* Remove TED */
+ ls_ted_del_all(OspfMplsTE.ted);
OspfMplsTE.enabled = false;
+ /* Flush all TE Opaque LSAs */
for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp))
if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED))
ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA);
@@ -2235,16 +3804,11 @@ DEFUN (no_ospf_mpls_te,
* This is to avoid having an inter-as value different from
* Off when mpls-te gets restarted (after being removed)
*/
- if (OspfMplsTE.inter_as != Off) {
- /* Deregister the Callbacks for Inter-AS support */
- ospf_mpls_te_unregister();
- OspfMplsTE.inter_as = Off;
- }
+ OspfMplsTE.inter_as = Off;
return CMD_SUCCESS;
}
-
DEFUN (ospf_mpls_te_router_addr,
ospf_mpls_te_router_addr_cmd,
"mpls-te router-address A.B.C.D",
@@ -2274,7 +3838,7 @@ DEFUN (ospf_mpls_te_router_addr,
return CMD_SUCCESS;
for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) {
- if ((lp->area == NULL) || IS_FLOOD_AS(lp->type))
+ if ((lp->area == NULL) || IS_FLOOD_AS(lp->flags))
continue;
if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) {
@@ -2284,7 +3848,7 @@ DEFUN (ospf_mpls_te_router_addr,
}
for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) {
- if ((lp->area == NULL) || IS_FLOOD_AS(lp->type))
+ if ((lp->area == NULL) || IS_FLOOD_AS(lp->flags))
continue;
if (need_to_reoriginate)
@@ -2324,18 +3888,9 @@ static int set_inter_as_mode(struct vty *vty, const char *mode_name,
return CMD_WARNING;
}
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug(
- "MPLS-TE: Inter-AS enable with %s flooding support",
- mode2text[mode]);
-
- /* Register new callbacks regarding the flooding scope (AS or
- * Area) */
- if (ospf_mpls_te_register(mode) < 0) {
- vty_out(vty,
- "Internal error: Unable to register Inter-AS functions\n");
- return CMD_WARNING;
- }
+ ote_debug(
+ "MPLS-TE (%s): Inter-AS enable with %s flooding support",
+ __func__, mode2text[mode]);
/* Enable mode and re-originate LSA if needed */
if ((OspfMplsTE.inter_as == Off)
@@ -2346,9 +3901,11 @@ static int set_inter_as_mode(struct vty *vty, const char *mode_name,
lp)) {
if (IS_INTER_AS(lp->type)) {
if (mode == AS)
- lp->type |= FLOOD_AS;
+ SET_FLAG(lp->flags,
+ LPFLG_LSA_FLOOD_AS);
else
- lp->type |= FLOOD_AREA;
+ UNSET_FLAG(lp->flags,
+ LPFLG_LSA_FLOOD_AS);
ospf_mpls_te_lsa_schedule(
lp, REORIGINATE_THIS_LSA);
}
@@ -2401,8 +3958,7 @@ DEFUN (no_ospf_mpls_te_inter_as,
struct listnode *node, *nnode;
struct mpls_te_link *lp;
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("MPLS-TE: Inter-AS support OFF");
+ ote_debug("MPLS-TE: Inter-AS support OFF");
if ((OspfMplsTE.enabled) && (OspfMplsTE.inter_as != Off)) {
/* Flush all Inter-AS LSA */
@@ -2411,14 +3967,55 @@ DEFUN (no_ospf_mpls_te_inter_as,
&& CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED))
ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA);
- /* Deregister the Callbacks for Inter-AS support */
- ospf_mpls_te_unregister();
OspfMplsTE.inter_as = Off;
}
return CMD_SUCCESS;
}
+DEFUN (ospf_mpls_te_export,
+ ospf_mpls_te_export_cmd,
+ "mpls-te export",
+ MPLS_TE_STR
+ "Export the MPLS-TE information as Link State\n")
+{
+
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ if (OspfMplsTE.enabled) {
+ if (ls_register(zclient, true) != 0) {
+ vty_out(vty, "Unable to register Link State\n");
+ return CMD_WARNING;
+ }
+ OspfMplsTE.export = true;
+ } else {
+ vty_out(vty, "mpls-te has not been turned on\n");
+ return CMD_WARNING;
+ }
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (no_ospf_mpls_te_export,
+ no_ospf_mpls_te_export_cmd,
+ "no mpls-te export",
+ NO_STR
+ MPLS_TE_STR
+ "Stop export of the MPLS-TE information as Link State\n")
+{
+
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ if (OspfMplsTE.export) {
+ if (ls_unregister(zclient, true) != 0) {
+ vty_out(vty, "Unable to unregister Link State\n");
+ return CMD_WARNING;
+ }
+ OspfMplsTE.export = false;
+ }
+ return CMD_SUCCESS;
+}
+
DEFUN (show_ip_ospf_mpls_te_router,
show_ip_ospf_mpls_te_router_cmd,
"show ip ospf mpls-te router",
@@ -2435,7 +4032,9 @@ DEFUN (show_ip_ospf_mpls_te_router,
show_vty_router_addr(vty,
&OspfMplsTE.router_addr.header);
else
- vty_out(vty, " N/A\n");
+ vty_out(vty, " Router address is not set\n");
+ vty_out(vty, " Link State distribution is %s\n",
+ OspfMplsTE.export ? "Active" : "Inactive");
}
return CMD_SUCCESS;
}
@@ -2592,10 +4191,142 @@ DEFUN (show_ip_ospf_mpls_te_link,
return CMD_SUCCESS;
}
+DEFUN (show_ip_ospf_mpls_te_db,
+ show_ip_ospf_mpls_te_db_cmd,
+ "show ip ospf mpls-te database [<vertex [<self-originate|adv-router A.B.C.D>]|edge [A.B.C.D]|subnet [A.B.C.D/M]>] [verbose|json]",
+ SHOW_STR
+ IP_STR
+ OSPF_STR
+ "MPLS-TE information\n"
+ "MPLS-TE database\n"
+ "MPLS-TE Vertex\n"
+ "Self-originated MPLS-TE router\n"
+ "Advertised MPLS-TE router\n"
+ "MPLS-TE router ID (as an IP address)\n"
+ "MPLS-TE Edge\n"
+ "MPLS-TE Edge ID (as an IP address)\n"
+ "MPLS-TE Subnet\n"
+ "MPLS-TE Subnet ID (as an IP prefix)\n"
+ "Verbose output\n"
+ JSON_STR)
+{
+ int idx = 0;
+ struct in_addr ip_addr;
+ struct prefix pref;
+ struct ls_vertex *vertex;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+ uint64_t key;
+ bool verbose = false;
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL;
+
+ if (!OspfMplsTE.enabled || !OspfMplsTE.ted) {
+ vty_out(vty, "MPLS-TE database is not enabled\n");
+ return CMD_WARNING;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+
+ if (argv[argc - 1]->arg && strmatch(argv[argc - 1]->text, "verbose"))
+ verbose = true;
+
+ idx = 5;
+ if (argv_find(argv, argc, "vertex", &idx)) {
+ /* Show Vertex */
+ if (argv_find(argv, argc, "self-originate", &idx))
+ vertex = OspfMplsTE.ted->self;
+ else if (argv_find(argv, argc, "adv-router", &idx)) {
+ if (!inet_aton(argv[idx + 1]->arg, &ip_addr)) {
+ vty_out(vty,
+ "Specified Router ID %s is invalid\n",
+ argv[idx + 1]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Get the Vertex from the Link State Database */
+ key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff;
+ vertex = ls_find_vertex_by_key(OspfMplsTE.ted, key);
+ if (!vertex) {
+ vty_out(vty, "No vertex found for ID %pI4\n",
+ &ip_addr);
+ return CMD_WARNING;
+ }
+ } else
+ vertex = NULL;
+
+ if (vertex)
+ ls_show_vertex(vertex, vty, json, verbose);
+ else
+ ls_show_vertices(OspfMplsTE.ted, vty, json, verbose);
+
+ } else if (argv_find(argv, argc, "edge", &idx)) {
+ /* Show Edge */
+ if (argv_find(argv, argc, "A.B.C.D", &idx)) {
+ if (!inet_aton(argv[idx]->arg, &ip_addr)) {
+ vty_out(vty,
+ "Specified Edge ID %s is invalid\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Get the Edge from the Link State Database */
+ key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff;
+ edge = ls_find_edge_by_key(OspfMplsTE.ted, key);
+ if (!edge) {
+ vty_out(vty, "No edge found for ID %pI4\n",
+ &ip_addr);
+ return CMD_WARNING;
+ }
+ } else
+ edge = NULL;
+
+ if (edge)
+ ls_show_edge(edge, vty, json, verbose);
+ else
+ ls_show_edges(OspfMplsTE.ted, vty, json, verbose);
+
+ } else if (argv_find(argv, argc, "subnet", &idx)) {
+ /* Show Subnet */
+ if (argv_find(argv, argc, "A.B.C.D/M", &idx)) {
+ if (!str2prefix(argv[idx]->arg, &pref)) {
+ vty_out(vty, "Invalid prefix format %s\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Get the Subnet from the Link State Database */
+ subnet = ls_find_subnet(OspfMplsTE.ted, pref);
+ if (!subnet) {
+ vty_out(vty, "No subnet found for ID %pFX\n",
+ &pref);
+ return CMD_WARNING;
+ }
+ } else
+ subnet = NULL;
+
+ if (subnet)
+ ls_show_subnet(subnet, vty, json, verbose);
+ else
+ ls_show_subnets(OspfMplsTE.ted, vty, json, verbose);
+
+ } else {
+ /* Show the complete TED */
+ ls_show_ted(OspfMplsTE.ted, vty, json, verbose);
+ }
+
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return CMD_SUCCESS;
+}
+
static void ospf_mpls_te_register_vty(void)
{
install_element(VIEW_NODE, &show_ip_ospf_mpls_te_router_cmd);
install_element(VIEW_NODE, &show_ip_ospf_mpls_te_link_cmd);
+ install_element(VIEW_NODE, &show_ip_ospf_mpls_te_db_cmd);
install_element(OSPF_NODE, &ospf_mpls_te_on_cmd);
install_element(OSPF_NODE, &no_ospf_mpls_te_cmd);
@@ -2603,6 +4334,8 @@ static void ospf_mpls_te_register_vty(void)
install_element(OSPF_NODE, &ospf_mpls_te_inter_as_cmd);
install_element(OSPF_NODE, &ospf_mpls_te_inter_as_area_cmd);
install_element(OSPF_NODE, &no_ospf_mpls_te_inter_as_cmd);
+ install_element(OSPF_NODE, &ospf_mpls_te_export_cmd);
+ install_element(OSPF_NODE, &no_ospf_mpls_te_export_cmd);
return;
}
diff --git a/ospfd/ospf_te.h b/ospfd/ospf_te.h
index 06c17f07f..fc9ca2078 100644
--- a/ospfd/ospf_te.h
+++ b/ospfd/ospf_te.h
@@ -74,25 +74,29 @@
#define GMPLS 0x02
#define INTER_AS 0x04
#define PSEUDO_TE 0x08
-#define FLOOD_AREA 0x10
-#define FLOOD_AS 0x20
-#define EMULATED 0x80
+#define EMULATED 0x10
-#define IS_STD_TE(x) (x & STD_TE)
+#define IS_STD_TE(x) (x & STD_TE)
#define IS_PSEUDO_TE(x) (x & PSEUDO_TE)
#define IS_INTER_AS(x) (x & INTER_AS)
#define IS_EMULATED(x) (x & EMULATED)
-#define IS_FLOOD_AREA(x) (x & FLOOD_AREA)
-#define IS_FLOOD_AS(x) (x & FLOOD_AS)
-#define IS_INTER_AS_EMU(x) (x & INTER_AS & EMULATED)
-#define IS_INTER_AS_AS(x) (x & INTER_AS & FLOOD_AS)
/* Flags to manage TE Link LSA */
-#define LPFLG_LSA_INACTIVE 0x0
-#define LPFLG_LSA_ACTIVE 0x1
-#define LPFLG_LSA_ENGAGED 0x2
-#define LPFLG_LOOKUP_DONE 0x4
-#define LPFLG_LSA_FORCED_REFRESH 0x8
+#define LPFLG_LSA_INACTIVE 0x00
+#define LPFLG_LSA_ACTIVE 0x01
+#define LPFLG_LSA_ENGAGED 0x02
+#define LPFLG_LOOKUP_DONE 0x04
+#define LPFLG_LSA_FORCED_REFRESH 0x08
+#define LPFLG_LSA_FLOOD_AS 0x10
+
+#define IS_FLOOD_AS(x) (x & LPFLG_LSA_FLOOD_AS)
+
+/* Macro to log debug message */
+#define ote_debug(...) \
+ do { \
+ if (IS_DEBUG_OSPF_TE) \
+ zlog_debug(__VA_ARGS__); \
+ } while (0)
/*
* Following section defines TLV body parts.
@@ -336,9 +340,13 @@ struct te_link_subtlv {
/* Following structure are internal use only. */
struct ospf_mpls_te {
- /* Status of MPLS-TE: enable or disbale */
+ /* Status of MPLS-TE: enable or disable */
bool enabled;
+ /* Traffic Engineering Database i.e. Link State */
+ struct ls_ted *ted;
+ bool export;
+
/* RFC5392 */
enum inter_as_mode inter_as;
struct in_addr interas_areaid;
@@ -414,4 +422,15 @@ extern void set_linkparams_llri(struct mpls_te_link *, uint32_t, uint32_t);
extern void set_linkparams_lrrid(struct mpls_te_link *, struct in_addr,
struct in_addr);
+struct zapi_opaque_reg_info;
+/**
+ * Call when a client send a Link State Sync message. In turn, OSPF will send
+ * the contain of the Link State Data base.
+ *
+ * @param info ZAPI Opaque message information
+ *
+ * @return 0 on success, -1 otherwise
+ */
+extern int ospf_te_sync_ted(struct zapi_opaque_reg_info dst);
+
#endif /* _ZEBRA_OSPF_MPLS_TE_H */
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 56b2f8d66..15a95162d 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -2043,6 +2043,7 @@ static int ospf_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
struct zapi_opaque_msg info;
struct ldp_igp_sync_if_state state;
struct ldp_igp_sync_announce announce;
+ struct zapi_opaque_reg_info dst;
int ret = 0;
s = zclient->ibuf;
@@ -2051,6 +2052,13 @@ static int ospf_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
return -1;
switch (info.type) {
+ case LINK_STATE_SYNC:
+ STREAM_GETC(s, dst.proto);
+ STREAM_GETW(s, dst.instance);
+ STREAM_GETL(s, dst.session_id);
+ dst.type = LINK_STATE_SYNC;
+ ret = ospf_te_sync_ted(dst);
+ break;
case LDP_IGP_SYNC_IF_STATE_UPDATE:
STREAM_GET(&state, s, sizeof(state));
ret = ospf_ldp_sync_state_update(state);
diff --git a/sharpd/sharp_globals.h b/sharpd/sharp_globals.h
index ecb7053fd..0b3776cd9 100644
--- a/sharpd/sharp_globals.h
+++ b/sharpd/sharp_globals.h
@@ -55,6 +55,9 @@ struct sharp_global {
/* The list of nexthops that we are watching and data about them */
struct list *nhs;
+
+ /* Traffic Engineering Database */
+ struct ls_ted *ted;
};
extern struct sharp_global sg;
diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c
index a1216247c..e93db34ff 100644
--- a/sharpd/sharp_main.c
+++ b/sharpd/sharp_main.c
@@ -43,6 +43,7 @@
#include "libfrr.h"
#include "routemap.h"
#include "nexthop_group.h"
+#include "link_state.h"
#include "sharp_zebra.h"
#include "sharp_vty.h"
@@ -138,6 +139,7 @@ static void sharp_global_init(void)
{
memset(&sg, 0, sizeof(sg));
sg.nhs = list_new();
+ sg.ted = NULL;
}
static void sharp_start_configuration(void)
diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c
index 940415b06..002336616 100644
--- a/sharpd/sharp_vty.c
+++ b/sharpd/sharp_vty.c
@@ -29,6 +29,7 @@
#include "vrf.h"
#include "zclient.h"
#include "nexthop_group.h"
+#include "link_state.h"
#include "sharpd/sharp_globals.h"
#include "sharpd/sharp_zebra.h"
@@ -700,6 +701,142 @@ DEFPY (neigh_discover,
return CMD_SUCCESS;
}
+DEFPY (import_te,
+ import_te_cmd,
+ "sharp import-te",
+ SHARP_STR
+ "Import Traffic Engineering\n")
+{
+ sg.ted = ls_ted_new(1, "Sharp", 0);
+ sharp_zebra_register_te();
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_sharp_ted,
+ show_sharp_ted_cmd,
+ "show sharp ted [<vertex [A.B.C.D]|edge [A.B.C.D]|subnet [A.B.C.D/M]>] [verbose|json]",
+ SHOW_STR
+ SHARP_STR
+ "Traffic Engineering Database\n"
+ "MPLS-TE Vertex\n"
+ "MPLS-TE router ID (as an IP address)\n"
+ "MPLS-TE Edge\n"
+ "MPLS-TE Edge ID (as an IP address)\n"
+ "MPLS-TE Subnet\n"
+ "MPLS-TE Subnet ID (as an IP prefix)\n"
+ "Verbose output\n"
+ JSON_STR)
+{
+ int idx = 0;
+ struct in_addr ip_addr;
+ struct prefix pref;
+ struct ls_vertex *vertex;
+ struct ls_edge *edge;
+ struct ls_subnet *subnet;
+ uint64_t key;
+ bool verbose = false;
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL;
+
+ if (sg.ted == NULL) {
+ vty_out(vty, "MPLS-TE import is not enabled\n");
+ return CMD_WARNING;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+
+ if (argv[argc - 1]->arg && strmatch(argv[argc - 1]->text, "verbose"))
+ verbose = true;
+
+ if (argv_find(argv, argc, "vertex", &idx)) {
+ /* Show Vertex */
+ if (argv_find(argv, argc, "A.B.C.D", &idx)) {
+ if (!inet_aton(argv[idx + 1]->arg, &ip_addr)) {
+ vty_out(vty,
+ "Specified Router ID %s is invalid\n",
+ argv[idx + 1]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Get the Vertex from the Link State Database */
+ key = ((uint64_t)ip_addr.s_addr) & 0xffffffff;
+ vertex = ls_find_vertex_by_key(sg.ted, key);
+ if (!vertex) {
+ vty_out(vty, "No vertex found for ID %pI4\n",
+ &ip_addr);
+ return CMD_WARNING;
+ }
+ } else
+ vertex = NULL;
+
+ if (vertex)
+ ls_show_vertex(vertex, vty, json, verbose);
+ else
+ ls_show_vertices(sg.ted, vty, json, verbose);
+
+ } else if (argv_find(argv, argc, "edge", &idx)) {
+ /* Show Edge */
+ if (argv_find(argv, argc, "A.B.C.D", &idx)) {
+ if (!inet_aton(argv[idx]->arg, &ip_addr)) {
+ vty_out(vty,
+ "Specified Edge ID %s is invalid\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Get the Edge from the Link State Database */
+ key = ((uint64_t)ip_addr.s_addr) & 0xffffffff;
+ edge = ls_find_edge_by_key(sg.ted, key);
+ if (!edge) {
+ vty_out(vty, "No edge found for ID %pI4\n",
+ &ip_addr);
+ return CMD_WARNING;
+ }
+ } else
+ edge = NULL;
+
+ if (edge)
+ ls_show_edge(edge, vty, json, verbose);
+ else
+ ls_show_edges(sg.ted, vty, json, verbose);
+
+ } else if (argv_find(argv, argc, "subnet", &idx)) {
+ /* Show Subnet */
+ if (argv_find(argv, argc, "A.B.C.D/M", &idx)) {
+ if (!str2prefix(argv[idx]->arg, &pref)) {
+ vty_out(vty, "Invalid prefix format %s\n",
+ argv[idx]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* Get the Subnet from the Link State Database */
+ subnet = ls_find_subnet(sg.ted, pref);
+ if (!subnet) {
+ vty_out(vty, "No subnet found for ID %pFX\n",
+ &pref);
+ return CMD_WARNING;
+ }
+ } else
+ subnet = NULL;
+
+ if (subnet)
+ ls_show_subnet(subnet, vty, json, verbose);
+ else
+ ls_show_subnets(sg.ted, vty, json, verbose);
+
+ } else {
+ /* Show the complete TED */
+ ls_show_ted(sg.ted, vty, json, verbose);
+ }
+
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return CMD_SUCCESS;
+}
+
void sharp_vty_init(void)
{
install_element(ENABLE_NODE, &install_routes_data_dump_cmd);
@@ -718,8 +855,10 @@ void sharp_vty_init(void)
install_element(ENABLE_NODE, &send_opaque_unicast_cmd);
install_element(ENABLE_NODE, &send_opaque_reg_cmd);
install_element(ENABLE_NODE, &neigh_discover_cmd);
+ install_element(ENABLE_NODE, &import_te_cmd);
install_element(ENABLE_NODE, &show_debugging_sharpd_cmd);
+ install_element(ENABLE_NODE, &show_sharp_ted_cmd);
return;
}
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
index 73bbaf0bc..0f2c63404 100644
--- a/sharpd/sharp_zebra.c
+++ b/sharpd/sharp_zebra.c
@@ -30,6 +30,7 @@
#include "zclient.h"
#include "nexthop.h"
#include "nexthop_group.h"
+#include "link_state.h"
#include "sharp_globals.h"
#include "sharp_nht.h"
@@ -769,11 +770,15 @@ int sharp_zclient_delete(uint32_t session_id)
return 0;
}
+static const char *const type2txt[] = { "Generic", "Vertex", "Edge", "Subnet" };
+static const char *const status2txt[] = { "Unknown", "New", "Update",
+ "Delete", "Sync", "Orphan"};
/* Handler for opaque messages */
static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
struct zapi_opaque_msg info;
+ struct ls_element *lse;
s = zclient->ibuf;
@@ -783,6 +788,18 @@ static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS)
zlog_debug("%s: [%u] received opaque type %u", __func__,
zclient->session_id, info.type);
+ if (info.type == LINK_STATE_UPDATE) {
+ lse = ls_stream2ted(sg.ted, s, false);
+ if (lse)
+ zlog_debug(" |- Got %s %s from Link State Database",
+ status2txt[lse->status],
+ type2txt[lse->type]);
+ else
+ zlog_debug(
+ "%s: Error to convert Stream into Link State",
+ __func__);
+ }
+
return 0;
}
@@ -853,6 +870,16 @@ void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance,
}
+/* Link State registration */
+void sharp_zebra_register_te(void)
+{
+ /* First register to received Link State Update messages */
+ zclient_register_opaque(zclient, LINK_STATE_UPDATE);
+
+ /* Then, request initial TED with SYNC message */
+ ls_request_sync(zclient);
+}
+
void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p)
{
zclient_send_neigh_discovery_req(zclient, ifp, p);
diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h
index e7247f537..ffddb9e78 100644
--- a/sharpd/sharp_zebra.h
+++ b/sharpd/sharp_zebra.h
@@ -60,4 +60,7 @@ void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance,
extern void sharp_zebra_send_arp(const struct interface *ifp,
const struct prefix *p);
+/* Register Link State Opaque messages */
+extern void sharp_zebra_register_te(void);
+
#endif
diff --git a/tests/topotests/ospf-te-topo1/__init__.py b/tests/topotests/ospf-te-topo1/__init__.py
new file mode 100755
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/__init__.py
diff --git a/tests/topotests/ospf-te-topo1/r1/ospfd.conf b/tests/topotests/ospf-te-topo1/r1/ospfd.conf
new file mode 100644
index 000000000..312dd2697
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/r1/ospfd.conf
@@ -0,0 +1,23 @@
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface r1-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface r1-eth1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.255.1
+ capability opaque
+ mpls-te on
+ mpls-te router-address 10.0.255.1
+!
+
diff --git a/tests/topotests/ospf-te-topo1/r1/zebra.conf b/tests/topotests/ospf-te-topo1/r1/zebra.conf
new file mode 100644
index 000000000..7c5dc3ffe
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/r1/zebra.conf
@@ -0,0 +1,21 @@
+!
+interface lo
+ ip address 10.0.255.1/32
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+ link-params
+ metric 20
+ delay 10000
+ ava-bw 1.25e+08
+ enable
+ exit-link-params
+!
+interface r1-eth1
+ ip address 10.0.1.1/24
+ link-params
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf-te-topo1/r2/ospfd.conf b/tests/topotests/ospf-te-topo1/r2/ospfd.conf
new file mode 100644
index 000000000..e9c3f65bc
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/r2/ospfd.conf
@@ -0,0 +1,34 @@
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface r2-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface r2-eth1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface r2-eth2
+ ip ospf network point-to-point
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface r2-eth3
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.255.2
+ capability opaque
+ mpls-te on
+ mpls-te router-address 10.0.255.2
+!
diff --git a/tests/topotests/ospf-te-topo1/r2/zebra.conf b/tests/topotests/ospf-te-topo1/r2/zebra.conf
new file mode 100644
index 000000000..69e10191f
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/r2/zebra.conf
@@ -0,0 +1,33 @@
+!
+interface lo
+ ip address 10.0.255.2/32
+!
+interface r2-eth0
+ ip address 10.0.0.2/24
+ link-params
+ enable
+ exit-link-params
+!
+interface r2-eth1
+ ip address 10.0.1.2/24
+ link-params
+ enable
+ exit-link-params
+!
+interface r2-eth2
+ ip address 10.0.3.2/24
+ link-params
+ enable
+ exit-link-params
+!
+interface r2-eth3
+ ip address 10.0.4.2/24
+ link-params
+ metric 30
+ delay 25000
+ use-bw 1.25e+8
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf-te-topo1/r3/ospfd.conf b/tests/topotests/ospf-te-topo1/r3/ospfd.conf
new file mode 100644
index 000000000..caa5f1e1e
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/r3/ospfd.conf
@@ -0,0 +1,24 @@
+!
+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
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface r3-eth1
+ ip ospf network point-to-point
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+!
+router ospf
+ ospf router-id 10.0.255.3
+ capability opaque
+ mpls-te on
+ mpls-te router-address 10.0.255.3
+ mpls-te inter-as as
+!
diff --git a/tests/topotests/ospf-te-topo1/r3/zebra.conf b/tests/topotests/ospf-te-topo1/r3/zebra.conf
new file mode 100644
index 000000000..4cf907708
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/r3/zebra.conf
@@ -0,0 +1,22 @@
+!
+interface lo
+ ip address 10.0.255.3/32
+!
+interface r3-eth0
+ ip address 10.0.3.1/24
+ link-params
+ enable
+ admin-grp 0x20
+ exit-link-params
+!
+interface r3-eth1
+ ip address 10.0.5.1/24
+ link-params
+ enable
+ metric 10
+ delay 50000
+ neighbor 10.0.255.5 as 65535
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf-te-topo1/r4/ospfd.conf b/tests/topotests/ospf-te-topo1/r4/ospfd.conf
new file mode 100644
index 000000000..e45467315
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/r4/ospfd.conf
@@ -0,0 +1,22 @@
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface r4-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+!
+router ospf
+ ospf router-id 10.0.255.4
+ capability opaque
+ mpls-te on
+ mpls-te router-address 10.0.255.4
+ segment-routing on
+ segment-routing local-block 5000 5999
+ segment-routing global-block 10000 19999
+ segment-routing node-msd 12
+ segment-routing prefix 10.0.255.4/32 index 400 no-php-flag
+!
diff --git a/tests/topotests/ospf-te-topo1/r4/zebra.conf b/tests/topotests/ospf-te-topo1/r4/zebra.conf
new file mode 100644
index 000000000..18c003b23
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/r4/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ip address 10.0.255.4/32
+!
+interface r4-eth0
+ ip address 10.0.4.1/24
+ link-params
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step1.json b/tests/topotests/ospf-te-topo1/reference/ted_step1.json
new file mode 100644
index 000000000..9624292cc
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/reference/ted_step1.json
@@ -0,0 +1,577 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":5,
+ "edgesCount":9,
+ "subnetsCount":14,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ },
+ {
+ "vertex-id":167837445,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.5",
+ "vertex-type":"Remote ASBR",
+ "asn":65535
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167773441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837445,
+ "metric":0,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address":"10.0.5.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "remote-asn":65535,
+ "remote-as-address":"10.0.255.5",
+ "delay":50000
+ }
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.5.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.5\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.5",
+ "vertex-id":167837445,
+ "metric":10
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step2.json b/tests/topotests/ospf-te-topo1/reference/ted_step2.json
new file mode 100644
index 000000000..623d1dc7e
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/reference/ted_step2.json
@@ -0,0 +1,477 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":5,
+ "edgesCount":7,
+ "subnetsCount":12,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ },
+ {
+ "vertex-id":167837445,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.5",
+ "vertex-type":"Remote ASBR",
+ "asn":65535
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167773441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837445,
+ "metric":0,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address":"10.0.5.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "remote-asn":65535,
+ "remote-as-address":"10.0.255.5",
+ "delay":50000
+ }
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.5.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.5\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.5",
+ "vertex-id":167837445,
+ "metric":10
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step3.json b/tests/topotests/ospf-te-topo1/reference/ted_step3.json
new file mode 100644
index 000000000..117011a43
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/reference/ted_step3.json
@@ -0,0 +1,409 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":6,
+ "subnetsCount":10,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step4.json b/tests/topotests/ospf-te-topo1/reference/ted_step4.json
new file mode 100644
index 000000000..5c2dee1e4
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/reference/ted_step4.json
@@ -0,0 +1,490 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":6,
+ "subnetsCount":10,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":4000,
+ "srgb-lower":20000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":15000
+ }
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":8000,
+ "srgb-lower":16000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":2000,
+ "srlb-lower":5000,
+ "msd":16
+ }
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5005,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5004,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":10,
+ "algo":0,
+ "flags":"0x0"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":20,
+ "algo":0,
+ "flags":"0x50"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step5.json b/tests/topotests/ospf-te-topo1/reference/ted_step5.json
new file mode 100644
index 000000000..47e747f3c
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/reference/ted_step5.json
@@ -0,0 +1,614 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":8,
+ "subnetsCount":12,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":4000,
+ "srgb-lower":20000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":15000
+ }
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":8000,
+ "srgb-lower":16000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":2000,
+ "srlb-lower":5000,
+ "msd":16
+ }
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5007,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5006,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5005,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5004,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":10,
+ "algo":0,
+ "flags":"0x0"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":20,
+ "algo":0,
+ "flags":"0x50"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step6.json b/tests/topotests/ospf-te-topo1/reference/ted_step6.json
new file mode 100644
index 000000000..74bd83fbd
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/reference/ted_step6.json
@@ -0,0 +1,615 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":8,
+ "subnetsCount":12,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":4000,
+ "srgb-lower":20000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":15000
+ }
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":8000,
+ "srgb-lower":16000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":2000,
+ "srlb-lower":5000,
+ "msd":16
+ }
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5007,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5006,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":20000,
+ "jitter":10000
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5005,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5004,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":10,
+ "algo":0,
+ "flags":"0x0"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":20,
+ "algo":0,
+ "flags":"0x50"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf-te-topo1/reference/ted_step7.json b/tests/topotests/ospf-te-topo1/reference/ted_step7.json
new file mode 100644
index 000000000..1cea9f045
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/reference/ted_step7.json
@@ -0,0 +1,456 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":3,
+ "edgesCount":6,
+ "subnetsCount":9,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":4000,
+ "srgb-lower":20000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":15000
+ }
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":8000,
+ "srgb-lower":16000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":2000,
+ "srlb-lower":5000,
+ "msd":16
+ }
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5007,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5006,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":0,
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":10,
+ "algo":0,
+ "flags":"0x0"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":20,
+ "algo":0,
+ "flags":"0x50"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf-te-topo1/test_ospf_te_topo1.py b/tests/topotests/ospf-te-topo1/test_ospf_te_topo1.py
new file mode 100644
index 000000000..1d69f5d69
--- /dev/null
+++ b/tests/topotests/ospf-te-topo1/test_ospf_te_topo1.py
@@ -0,0 +1,314 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_te_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Orange
+# Author: Olivier Dugeon <olivier.dugeon@orange.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_ospf_te_topo1.py: Test the FRR OSPF with Traffic Engineering.
+
+ +------------+
+ | |
+ | R1 |
+ | 10.0.225.1 |
+ | |
+ +------------+
+ r1-eth0| |r1-eth1
+ | |
+ 10.0.0.0/24| |10.0.1.0/24
+ | |
+ r2-eth0| |r2-eth1
+ +------------+ +------------+
+ | | | |
+ | R2 |r2-eth2 r3-eth0| R3 |
+ | 10.0.255.2 +------------------+ 10.0.255.3 |
+ | | 10.0.3.0/24 | |
+ +------------+ +------+-----+
+ r2-eth3| r3-eth1|
+ | |
+ 10.0.4.0/24| 10.0.5.0/24|
+ | |
+ r4-eth0| V
+ +------------+ ASBR 10.0.255.5
+ | |
+ | R4 |
+ | 10.0.255.4 |
+ | |
+ +------------+
+
+"""
+
+import os
+import sys
+import json
+from functools import partial
+
+# 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
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# and Finally pytest
+import pytest
+
+pytestmark = [pytest.mark.ospfd]
+
+class OspfTeTopo(Topo):
+ "Test topology builder"
+
+ def build(self):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ # 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("s3")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 4 and 2
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconnect router 3 with next AS
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ logger.info("\n\n---- Starting OSPF TE tests ----\n")
+
+ tgen = Topogen(OspfTeTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module():
+ "Teardown the pytest environment"
+
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+ logger.info("\n\n---- OSPF TE tests End ----\n")
+
+
+def compare_ted_json_output(tgen, rname, fileref):
+ "Compare TED JSON output"
+
+ logger.info('Comparing router "%s" TED output', rname)
+
+ filename = "{}/reference/{}".format(CWD, fileref)
+ expected = json.loads(open(filename).read())
+ command = "show ip ospf mpls-te database json"
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = '"{}" TED JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def setup_testcase(msg):
+ "Setup test case"
+
+ logger.info(msg)
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ return tgen
+
+
+# Note that all routers must discover the same Network Topology, so the same TED.
+
+def test_step1():
+ "Step1: Check initial topology"
+
+ tgen = setup_testcase("Step1: test initial OSPF TE Data Base")
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step1.json")
+
+
+def test_step2():
+ "Step2: Shutdown interface between r1 and r2 and verify that \
+ corresponding Edges are removed from the TED on all routers "
+
+ tgen = setup_testcase("Step2: Shutdown interface between r1 & r2")
+
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "interface r1-eth1" -c "shutdown"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "interface r2-eth1" -c "shutdown"'
+ )
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step2.json")
+
+
+def test_step3():
+ "Step3: Disable Inter-AS on r3 and verify that corresponding Edge and \
+ remote ASBR are removed from the TED on all routers"
+
+ tgen = setup_testcase("Step3: Disable Inter-AS on r3")
+
+ tgen.net["r3"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "no mpls-te inter-as"'
+ )
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step3.json")
+
+
+def test_step4():
+ "Step4: Enable Segment Routing on r1 and r2 and verify that corresponding \
+ Edges are updated with Adjacency SID and Subnets with Prefix SID in the \
+ TED on all routers"
+
+ tgen = setup_testcase("Step4: Enable Segment Routing on r1 & r2")
+
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing on"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing global-block 20000 23999"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 10.0.255.1/32 index 10"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing on"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing node-msd 16"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing local-block 5000 6999"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 10.0.255.2/32 index 20 explicit-null"'
+ )
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step4.json")
+
+
+def test_step5():
+ "Step5: Re-enable interface between r1 & r2 and verify that corresponding \
+ Edges are added in the TED on all routers"
+
+ tgen = setup_testcase("Step5: Re-enable interface between r1 & r2")
+
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "interface r1-eth1" -c "no shutdown"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "interface r2-eth1" -c "no shutdown"'
+ )
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step5.json")
+
+
+def test_step6():
+ "Step6: Set delay and jitter for interface r4-eth0 on r4, remove use-bw \
+ for interface r2-eth3 on r2 and verify that corresponding Edges are \
+ updated in the TED on all routers"
+
+ tgen = setup_testcase("Step6: Modify link parameters on r2 & r4")
+
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "interface r2-eth3" -c "link-params" -c "no use-bw"'
+ )
+ tgen.net["r4"].cmd(
+ 'vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay 20000"'
+ )
+ tgen.net["r4"].cmd(
+ 'vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay-variation 10000"'
+ )
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step6.json")
+
+
+def test_step7():
+ "Step7: Disable OSPF on r4 and verify that corresponding Vertex, Edges and \
+ Subnets are removed from the TED on all remaining routers"
+
+ tgen = setup_testcase("Step7: Disable OSPF on r4")
+
+ tgen.net["r4"].cmd(
+ 'vtysh -c "conf t" -c "no router ospf"'
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ compare_ted_json_output(tgen, rname, "ted_step7.json")
+
+
+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))