]> git.puffer.fish Git - mirror/frr.git/commitdiff
lib: Add Link State Database 6810/head
authorOlivier Dugeon <olivier.dugeon@orange.com>
Tue, 26 May 2020 12:51:02 +0000 (14:51 +0200)
committerOlivier Dugeon <olivier.dugeon@orange.com>
Mon, 4 Jan 2021 17:19:26 +0000 (18:19 +0100)
Define new models for Link State Database a.k.a TED
and functions to manipulate the new database as well as exchange Link State
information through ZAPI Opaque message.

Signed-off-by: Olivier Dugeon <olivier.dugeon@orange.com>
doc/developer/link-state.rst [new file with mode: 0644]
lib/link_state.c [new file with mode: 0644]
lib/link_state.h [new file with mode: 0644]
lib/subdir.am

diff --git a/doc/developer/link-state.rst b/doc/developer/link-state.rst
new file mode 100644 (file)
index 0000000..f1fc529
--- /dev/null
@@ -0,0 +1,314 @@
+Link State API Documentation
+============================
+
+Introduction
+------------
+
+The Link State (LS) API aims to provide a set of structures and functions to
+build and manage a Traffic Engineering Database for the various FRR daemons.
+This API has been designed for several use cases:
+
+- BGP Link State (BGP-LS): where BGP protocol need to collect the link state
+  information from the routing daemons (IS-IS and/or OSPF) to implement RFC7572
+- Path Computation Element (PCE): where path computation algorithms are based
+  on Traffic Engineering Database
+- ReSerVation Protocol (RSVP): where signaling need to know the Traffic
+  Engineering topology of the network in order to determine the path of
+  RSVP tunnels
+
+Architecture
+------------
+
+The main requirements from the various uses cases are as follow:
+
+- Provides a set of data model and function to ease Link State information
+  manipulation (storage, serialize, parse ...)
+- Ease and normalize Link State information exchange between FRR daemons
+- Provides database structure for Traffic Engineering Database (TED)
+
+To ease Link State understanding, FRR daemons have been classified into two
+categories:
+
+- **Consumer**: Daemons that consume Link State information e.g. BGPd
+- **Producer**: Daemons that are able to collect Link State information and
+  send them to consumer daemons e.g. OSPFd IS-ISd
+
+Zebra daemon, and more precisely, the ZAPI message is used to convey the Link
+State information between *producer* and *consumer*, but, Zebra acts as a
+simple pass through and does not store any Link State information. A new ZAPI
+**Opaque** message has been design for that purpose.
+
+Each consumer and producer daemons are free to store or not Link State data and
+organise the information following the Traffic Engineering Database model
+provided by the API or any other data structure e.g. Hash, RB-tree ...
+
+Link State API
+--------------
+
+This is the low level API that allows any daemons manipulate the Link State
+elements that are stored in the Link State Database.
+
+Data structures
+^^^^^^^^^^^^^^^
+
+3 types of Link State structure have been defined:
+
+.. c:type:: struct ls_node
+
+   that groups all information related to a node
+
+.. c:type:: struct ls_attributes
+
+   that groups all information related to a link
+
+.. c:type:: struct ls_prefix
+
+   that groups all information related to a prefix
+
+These 3 types of structures are those handled by BGP-LS (see RFC7752) and
+suitable to describe a Traffic Engineering topology.
+
+Each structure, in addition to the specific parameters, embed the node
+identifier which advertises the Link State and a bit mask as flags to
+indicates which parameters are valid i.e. for which the value is valid and
+corresponds to a Link State information conveyed by the routing protocol.
+
+.. c:type:: struct ls_node_id
+
+   defines the Node identifier as router ID IPv4 address plus the area ID for
+   OSPF or the ISO System ID plus the IS-IS level for IS-IS.
+
+Functions
+^^^^^^^^^
+
+A set of functions is provided to create, delete and compare Link State Node:
+
+.. 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)
+
+and Link State Attributes:
+
+.. 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:: void ls_attributes_del(struct ls_attributes *attr)
+.. c:function:: int ls_attributes_same(struct ls_attributes *a1, struct ls_attributes *a2)
+
+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).
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+The Traffic Engineering is modeled as a Graph in order to ease Path Computation
+algorithm implementation. Denoted **G(V, E)**, a graph is composed by a list of
+**Vertices (V)** which represents the network Node and a list of **Edges (E)**
+which represents Link. An additional list of **prefixes (P)** is also added and
+also attached to the *Vertex (V)* which advertise it.
+
+*Vertex (V)* contains the list of outgoing *Edges (E)* that connect this Vertex
+with its direct neighbors and the list of incoming *Edges (E)* that connect
+the direct neighbors to this Vertex. Indeed, the *Edge (E)* is unidirectional,
+thus, it is necessary to add 2 Edges to model a bidirectional relation between
+2 Vertices. Finally, the *Vertex (V)* contains a pointer to the corresponding
+Link State Node.
+
+*Edge (E)* contains the source and destination Vertex that this Edge
+is connecting and a pointer to the corresponding Link State Attributes.
+
+A unique Key is used to identify both Vertices and Edges within the Graph.
+
+
+::
+
+          --------------     ---------------------------    --------------
+          | Connected  |---->| Connected Edge Va to Vb |--->| Connected  |
+      --->|  Vertex    |     ---------------------------    |  Vertex    |---->
+          |            |                                    |            |
+          | - Key (Va) |                                    | - Key (Vb) |
+      <---| - Vertex   |     ---------------------------    | - Vertex   |<----
+          |            |<----| Connected Edge Vb to Va |<---|            |
+          --------------     ---------------------------    --------------
+
+
+4 data structures have been defined to implement the Graph model:
+
+.. c:type:: struct ls_vertex
+.. c:type:: struct ls_edge
+.. c:type:: struct ls_prefix
+.. c:type:: struct ls_ted
+
+
+Functions
+^^^^^^^^^
+
+.. c:function:: struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node)
+.. c:function:: struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node)
+.. c:function:: void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex)
+.. 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)
+.. 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)
+.. c:function:: struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix)
+
+.. c:function:: struct ls_ted *ls_ted_new(const uint32_t key, char *name, uint32_t asn)
+.. c:function:: void ls_ted_del(struct ls_ted *ted)
+.. c:function:: void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, struct ls_edge *edge)
+.. 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)
+.. c:function:: void ls_disconnect_edge(struct ls_edge *edge)
+
+
+Link State Messages
+-------------------
+
+This part of the API provides functions and data structure to ease the
+communication between the *Producer* and *Consumer* daemons.
+
+Communications principles
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Recent ZAPI Opaque Message is used to exchange Link State data between daemons.
+For that purpose, Link State API provides new functions to serialize and parse
+Link State information through the ZAPI Opaque message. A dedicated flag,
+named ZAPI_OPAQUE_FLAG_UNICAST, allows daemons to send a unicast or a multicast
+Opaque message and is used as follow for the Link State exchange:
+
+- Multicast: To send data update to all daemons that have subscribed to the
+  Link State Update message
+- Unicast: To send initial Link State information from a particular daemon. All
+  data are send only to the daemon that request Link State Synchronisatio
+
+Figure 1 below, illustrates the ZAPI Opaque message exchange between a
+*Producer* (an IGP like OSPF or IS-IS) and a *Consumer* (e.g. BGP). The
+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
+  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.
+- Then, the *Consumer* sends a **Link State Synchronistation** request with the
+  Multicast method in order to receive the complete Link State Database from a
+  *Producer*. ZEBRA daemon forwards this message to any *Producer* daemons that
+  previously registered to this message. If no *Producer* has yet registered,
+  the request is lost. Thus, if the *Consumer* receives no response whithin a
+  given timer, it means that no *Producer* are available right now. So, the
+  *Consumer* must send the same request until it receives a Link State Database
+  Synchronistation message. This behaviour is necessary as we can't control in
+  which order daemons are started. It is up to the *Consumer* daemon to fix the
+  timeout and the number of retry.
+- When a *Producer* receives a **Link State Synchronisation** request, it
+  starts sending all elements of its own Link State Database through the
+  **Link State Database Synchronisation** message. These messages are send with
+  the Unicast method to avoid flooding other daemons with these elements. ZEBRA
+  layer ensures to forward the message to the right daemon.
+- When a *Producer* update its Link State Database, it automatically sends a
+  **Link State Update** message with the Multicast method. In turn, ZEBRA
+  daemon forwards the message to all *Consumer* daemons that previously
+  registered to this message. if no daemon is registered, the message is lost.
+- A daemon could unregister from the ZAPI Opaque message registry at any time.
+  In this case, the ZEBRA daemon stops to forward any messages it receives to
+  this daemon, even if it was previously converns.
+
+::
+
+       IGP                           ZEBRA                        Consumer
+    (OSPF/IS-IS)               (ZAPI Opaque Thread)              (e.g. BGP)
+        |                              |                             |           \
+        |                              |      Register LS Update     |            |
+        |                              |<----------------------------|   Register Phase
+        |                              |                             |            |
+        |                              |      Request LS Sync        |            |
+        |                              |<----------------------------|            |
+        :                              :                             :  A         |
+        |    Register LS Sync          |                             |  |         |
+        |----------------------------->|                             |  |        /
+        :                              :                             :  |TimeOut
+        :                              :                             :  |
+        |                              |                             |  |
+        |                              |      Request LS Sync        |  v        \
+        |    Request LS Sync           |<----------------------------|            |
+        |<-----------------------------|                             |   Synchronistation
+        |    LS DB Sync                |                             |           Phase
+        |----------------------------->|      LS DB Sync             |            |
+        |                              |---------------------------->|            |
+        |    LS DB Sync (cont'd)       |                             |            |
+        |----------------------------->|      LS DB Sync (cont'd)    |            |
+        |            .                 |---------------------------->|            |
+        |            .                 |             .               |            |
+        |            .                 |             .               |            |
+        |    LS DB Sync (end)          |             .               |            |
+        |----------------------------->|      LS DB Sync (end)       |            |
+        |                              |---------------------------->|            |
+        |                              |                             |           /
+        :                              :                             :
+        :                              :                             :
+        |    LS Update                 |                             |           \
+        |----------------------------->|      LS Update              |            |
+        |                              |---------------------------->|      Update Phase
+        |                              |                             |            |
+        :                              :                             :           /
+        :                              :                             :
+        |                              |                             |           \
+        |                              |      Unregister LS Update   |            |
+        |                              |<----------------------------|      Deregister Phase
+        |                              |                             |            |
+        |    LS Update                 |                             |            |
+        |----------------------------->|                             |            |
+        |                              |                             |           /
+        |                              |                             |
+
+        Figure 1: Link State messages exchange
+
+
+Data Structures
+^^^^^^^^^^^^^^^
+
+The Link State Message is defined to convey Link State parameters from
+the routing protocol (OSPF or IS-IS) to other daemons e.g. BGP.
+
+.. c:type:: struct ls_message
+
+The structure is composed of:
+
+- Event of the message:
+
+  - Sync: Send the whole LS DB following a request
+  - Add: Send the a new Link State element
+  - Update: Send an update of an existing Link State element
+  - Delete: Indicate that the given Link State element is removed
+
+- Type of Link State element: Node, Attribute or Prefix
+- Remote node id when known
+- Data: Node, Attributes or Prefix
+
+A Link State Message can carry only one Link State Element (Node, Attributes
+of Prefix) at once, and only one Link State Message is sent through ZAPI
+Opaque Link State type at once.
+
+Functions
+^^^^^^^^^
+
+.. c:function:: struct ls_message *ls_parse_msg(struct stream *s)
+.. c:function:: int ls_send_msg(struct zclient *zclient, struct ls_message *msg, struct zapi_opaque_reg_info *dst)
+.. 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)
+.. c:function:: int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, struct zapi_opaque_reg_info *dst)
+
diff --git a/lib/link_state.c b/lib/link_state.c
new file mode 100644 (file)
index 0000000..f8fdda6
--- /dev/null
@@ -0,0 +1,1284 @@
+/*
+ * Link State Database - link_state.c
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2020 Orange http://www.orange.com
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "if.h"
+#include "linklist.h"
+#include "log.h"
+#include "command.h"
+#include "termtable.h"
+#include "memory.h"
+#include "prefix.h"
+#include "table.h"
+#include "vty.h"
+#include "zclient.h"
+#include "stream.h"
+#include "link_state.h"
+
+/* Link State Memory allocation */
+DEFINE_MTYPE_STATIC(LIB, LS_DB, "Link State Database")
+
+/**
+ *  Link State Node management functions
+ */
+struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid,
+                           struct in6_addr rid6)
+{
+       struct ls_node *new;
+
+       if (adv.origin == NONE)
+               return NULL;
+
+       new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node));
+       new->adv = adv;
+       if (!IPV4_NET0(rid.s_addr)) {
+               new->router_id = rid;
+               SET_FLAG(new->flags, LS_NODE_ROUTER_ID);
+       } else {
+               if (adv.origin == OSPFv2 || adv.origin == STATIC
+                   || adv.origin == DIRECT) {
+                       new->router_id = adv.id.ip.addr;
+                       SET_FLAG(new->flags, LS_NODE_ROUTER_ID);
+               }
+       }
+       if (!IN6_IS_ADDR_UNSPECIFIED(&rid6)) {
+               new->router6_id = rid6;
+               SET_FLAG(new->flags, LS_NODE_ROUTER_ID6);
+       }
+       return new;
+}
+
+void ls_node_del(struct ls_node *node)
+{
+       XFREE(MTYPE_LS_DB, node);
+       node = NULL;
+}
+
+int ls_node_same(struct ls_node *n1, struct ls_node *n2)
+{
+       if ((n1 && !n2) || (!n1 && n2))
+               return 0;
+
+       if (n1 == n2)
+               return 1;
+
+       if (n1->flags != n2->flags)
+               return 0;
+
+       if (n1->adv.origin != n2->adv.origin)
+               return 0;
+
+       if (!memcmp(&n1->adv.id, &n2->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(n1, n2, sizeof(struct ls_node)))
+               return 0;
+       else
+               return 1;
+}
+
+/**
+ *  Link State Attributes management functions
+ */
+struct ls_attributes *ls_attributes_new(struct ls_node_id adv,
+                                       struct in_addr local,
+                                       struct in6_addr local6,
+                                       uint32_t local_id)
+{
+       struct ls_attributes *new;
+
+       if (adv.origin == NONE)
+               return NULL;
+
+       new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes));
+       new->adv = adv;
+       if (!IPV4_NET0(local.s_addr)) {
+               new->standard.local = local;
+               SET_FLAG(new->flags, LS_ATTR_LOCAL_ADDR);
+       }
+       if (!IN6_IS_ADDR_UNSPECIFIED(&local6)) {
+               new->standard.local6 = local6;
+               SET_FLAG(new->flags, LS_ATTR_LOCAL_ADDR6);
+       }
+       if (local_id != 0) {
+               new->standard.local_id = local_id;
+               SET_FLAG(new->flags, LS_ATTR_LOCAL_ID);
+       }
+
+       /* Check that almost one identifier is set */
+       if (!CHECK_FLAG(new->flags, LS_ATTR_LOCAL_ADDR | LS_ATTR_LOCAL_ADDR6
+           | LS_ATTR_LOCAL_ID)) {
+               XFREE(MTYPE_LS_DB, new);
+               return NULL;
+       }
+
+       return new;
+}
+
+void ls_attributes_del(struct ls_attributes *attr)
+{
+       if (!attr)
+               return;
+
+       if (attr->srlgs)
+               XFREE(MTYPE_LS_DB, attr->srlgs);
+
+       XFREE(MTYPE_LS_DB, attr);
+       attr = NULL;
+}
+
+int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2)
+{
+       if ((l1 && !l2) || (!l1 && l2))
+               return 0;
+
+       if (l1 == l2)
+               return 1;
+
+       if (l1->flags != l2->flags)
+               return 0;
+
+       if (l1->adv.origin != l2->adv.origin)
+               return 0;
+
+       if (!memcmp(&l1->adv.id, &l2->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(l1, l2, sizeof(struct ls_attributes)))
+               return 0;
+       else
+               return 1;
+}
+
+/**
+ *  Link State Vertices management functions
+ */
+struct ls_vertex *ls_vertex_new(struct ls_node *node)
+{
+       struct ls_vertex *new;
+
+       if (node == NULL)
+               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();
+
+       return new;
+}
+
+void ls_vertex_del(struct ls_vertex *vertex)
+{
+       if (vertex == NULL)
+               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;
+}
+
+struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node)
+{
+       struct ls_vertex *new;
+
+       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);
+               break;
+       case ISIS_L1:
+       case ISIS_L2:
+               memcpy(&new->key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN);
+               break;
+       default:
+               new->key = 0;
+               break;
+       }
+
+       /* Remove Vertex if key is not set */
+       if (new->key == 0) {
+               ls_vertex_del(new);
+               return NULL;
+       }
+
+       /* Add Vertex to TED */
+       vertices_add(&ted->vertices, new);
+
+       return new;
+}
+
+struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node)
+{
+       struct ls_vertex *old;
+
+       if (node == NULL)
+               return NULL;
+
+       old = ls_find_vertex_by_id(ted, node->adv);
+       if (old) {
+               if (!ls_node_same(old->node, node)) {
+                       ls_node_del(old->node);
+                       old->node = node;
+               }
+               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 = {};
+
+       if (key == 0)
+               return NULL;
+
+       node.key = key;
+       return vertices_find(&ted->vertices, &node);
+}
+
+struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted,
+                                      struct ls_node_id nid)
+{
+       struct ls_vertex node = {};
+
+       switch (nid.origin) {
+       case OSPFv2:
+       case STATIC:
+       case DIRECT:
+               memcpy(&node.key, &nid.id.ip.addr, IPV4_MAX_BYTELEN);
+               break;
+       case ISIS_L1:
+       case ISIS_L2:
+               memcpy(&node.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN);
+               break;
+       default:
+               return NULL;
+       }
+
+       return vertices_find(&ted->vertices, &node);
+}
+
+int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2)
+{
+       if ((v1 && !v2) || (!v1 && v2))
+               return 0;
+
+       if (!v1 && !v2)
+               return 1;
+
+       if (v1->key != v2->key)
+               return 0;
+
+       if (v1->node == v2->node)
+               return 1;
+
+       return ls_node_same(v1->node, v2->node);
+}
+
+/**
+ * Link State Edges management functions
+ */
+
+/**
+ * This function allows to connect the Edge to the vertices present in the TED.
+ * A temporary vertex that corresponds to the source of this Edge i.e. the
+ * advertised router, is created if not found in the Data Base. If a Edge that
+ * corresponds to the reverse path is found, the Edge is attached to the
+ * destination vertex as destination and reverse Edge is attached to the source
+ * vertex as source.
+ *
+ * @param ted  Link State Data Base
+ * @param edge Link State Edge to be attached
+ */
+static void ls_edge_connect_to(struct ls_ted *ted, struct ls_edge *edge)
+{
+       struct ls_vertex *vertex = NULL;
+       struct ls_node *node;
+       struct ls_edge *dst;
+       const struct in_addr inaddr_any = {.s_addr = INADDR_ANY};
+
+       /* First, search if there is a Vertex that correspond to the Node ID */
+       vertex = ls_find_vertex_by_id(ted, edge->attributes->adv);
+       if (vertex == NULL) {
+               /* Create a new temporary Node & Vertex if not found */
+               node = ls_node_new(edge->attributes->adv, inaddr_any,
+                                  in6addr_any);
+               vertex = ls_vertex_add(ted, node);
+       }
+       /* and attach the edge as source to the vertex */
+       listnode_add(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);
+               dst->destination = vertex;
+               /* and destination vertex to this edge */
+               vertex = dst->source;
+               listnode_add(vertex->incoming_edges, edge);
+               edge->destination = vertex;
+       }
+}
+
+struct ls_edge *ls_edge_add(struct ls_ted *ted,
+                           struct ls_attributes *attributes)
+{
+       struct ls_edge *new;
+
+       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;
+       /* 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);
+       /* of local identifier if no IP addresses are defined */
+       else if (attributes->standard.local_id != 0)
+               new->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);
+               return NULL;
+       }
+
+       edges_add(&ted->edges, new);
+
+       /* Finally, connect edge to vertices */
+       ls_edge_connect_to(ted, new);
+
+       return new;
+}
+
+struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key)
+{
+       struct ls_edge edge = {};
+
+       if (key == 0)
+               return NULL;
+
+       edge.key = key;
+       return edges_find(&ted->edges, &edge);
+}
+
+struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted,
+                                      struct ls_attributes *attributes)
+{
+       struct ls_edge edge = {};
+
+       if (attributes == NULL)
+               return NULL;
+
+       /* Key is the IPv4 local address */
+       if (!IPV4_NET0(attributes->standard.local.s_addr))
+               edge.key = ((uint64_t)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))
+               edge.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)
+               edge.key = (uint64_t)(
+                       (attributes->standard.local_id & 0xffffffff)
+                       | ((uint64_t)attributes->standard.remote_id << 32));
+
+       if (edge.key == 0)
+               return NULL;
+
+       return edges_find(&ted->edges, &edge);
+}
+
+struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted,
+                                           struct ls_attributes *attributes)
+{
+       struct ls_edge edge = {};
+
+       if (attributes == NULL)
+               return NULL;
+
+       /* Key is the IPv4 local address */
+       if (!IPV4_NET0(attributes->standard.remote.s_addr))
+               edge.key = ((uint64_t)attributes->standard.remote.s_addr)
+                          & 0xffffffff;
+       /* or the IPv6 local 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 */
+       else if (attributes->standard.remote_id != 0)
+               edge.key = (uint64_t)(
+                       (attributes->standard.remote_id & 0xffffffff)
+                       | ((uint64_t)attributes->standard.local_id << 32));
+
+       if (edge.key == 0)
+               return NULL;
+
+       return edges_find(&ted->edges, &edge);
+}
+
+struct ls_edge *ls_edge_update(struct ls_ted *ted,
+                              struct ls_attributes *attributes)
+{
+       struct ls_edge *old;
+
+       if (attributes == NULL)
+               return NULL;
+
+       /* First, search for an existing Edge */
+       old = ls_find_edge_by_source(ted, attributes);
+       if (old) {
+               /* Check if attributes are similar */
+               if (!ls_attributes_same(old->attributes, attributes)) {
+                       ls_attributes_del(old->attributes);
+                       old->attributes = attributes;
+               }
+               return old;
+       }
+
+       /* If not found, add new Edge from the attributes */
+       return ls_edge_add(ted, attributes);
+}
+
+void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge)
+{
+       /* Fist disconnect Edge */
+       ls_disconnect_edge(edge);
+       /* Then remove it from the Data Base */
+       edges_del(&ted->edges, edge);
+       XFREE(MTYPE_LS_DB, edge);
+}
+
+/**
+ * Link State Subnet Management functions.
+ */
+struct ls_subnet *ls_subnet_add(struct ls_ted *ted,
+                               struct ls_prefix *ls_pref)
+{
+       struct ls_subnet *new;
+       struct ls_vertex *vertex;
+       struct ls_node *node;
+       const struct in_addr inaddr_any = {.s_addr = INADDR_ANY};
+
+       if (ls_pref == NULL)
+               return NULL;
+
+       new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_subnet));
+       new->ls_pref = ls_pref;
+       new->key = ls_pref->pref;
+
+       /* Find Vertex */
+       vertex = ls_find_vertex_by_id(ted, ls_pref->adv);
+       if (vertex == NULL) {
+               /* Create a new temporary Node & Vertex if not found */
+               node = ls_node_new(ls_pref->adv, inaddr_any, in6addr_any);
+               vertex = ls_vertex_add(ted, node);
+       }
+       /* And attach the subnet to the corresponding Vertex */
+       new->vertex = vertex;
+       listnode_add(vertex->prefixes, new);
+
+       subnets_add(&ted->subnets, new);
+
+       return new;
+}
+
+void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet)
+{
+       subnets_del(&ted->subnets, subnet);
+       XFREE(MTYPE_LS_DB, subnet);
+}
+
+struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix)
+{
+       struct ls_subnet subnet = {};
+
+       subnet.key = prefix;
+       return subnets_find(&ted->subnets, &subnet);
+}
+
+/**
+ * Link State TED management functions
+ */
+struct ls_ted *ls_ted_new(const uint32_t key, const char *name,
+                         uint32_t as_number)
+{
+       struct ls_ted *new;
+
+       new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_ted));
+       if (new == NULL)
+               return new;
+
+       /* Set basic information for this ted */
+       new->key = key;
+       new->as_number = as_number;
+       strlcpy(new->name, name, MAX_NAME_LENGTH);
+
+       /* Initialize the various RB tree */
+       vertices_init(&new->vertices);
+       edges_init(&new->edges);
+       subnets_init(&new->subnets);
+
+       return new;
+}
+
+void ls_ted_del(struct ls_ted *ted)
+{
+       if (ted == NULL)
+               return;
+
+       /* Release RB Tree */
+       vertices_fini(&ted->vertices);
+       edges_fini(&ted->edges);
+       subnets_fini(&ted->subnets);
+
+       XFREE(MTYPE_LS_DB, ted);
+       ted = NULL;
+}
+
+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);
+               edge->source = vertex;
+       } else {
+               listnode_add(vertex->incoming_edges, edge);
+               edge->destination = vertex;
+       }
+}
+
+void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, bool source)
+{
+
+       if (vertex == NULL || edge == NULL)
+               return;
+
+       if (source) {
+               listnode_delete(vertex->outgoing_edges, edge);
+               edge->source = NULL;
+       } else {
+               listnode_delete(vertex->incoming_edges, edge);
+               edge->destination = NULL;
+       }
+}
+
+void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst,
+                        struct ls_edge *edge)
+{
+       if (edge == NULL)
+               return;
+
+       edge->source = src;
+       edge->destination = dst;
+
+       if (src != NULL)
+               listnode_add(src->outgoing_edges, edge);
+
+       if (dst != NULL)
+               listnode_add(dst->incoming_edges, edge);
+
+}
+
+void ls_disconnect_edge(struct ls_edge *edge)
+{
+       if (edge == NULL)
+               return;
+
+       ls_disconnect(edge->source, edge, true);
+       ls_disconnect(edge->destination, edge, false);
+}
+
+/**
+ * Link State Message management functions
+ */
+
+static struct ls_node *ls_parse_node(struct stream *s)
+{
+       struct ls_node *node;
+       size_t len;
+
+       node = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node));
+       if (node == NULL)
+               return NULL;
+
+       STREAM_GET(&node->adv, s, sizeof(struct ls_node_id));
+       STREAM_GETW(s, node->flags);
+       if (CHECK_FLAG(node->flags, LS_NODE_NAME)) {
+               STREAM_GETC(s, len);
+               STREAM_GET(node->name, s, len);
+       }
+       if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID))
+               node->router_id.s_addr = stream_get_ipv4(s);
+       if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID6))
+               STREAM_GET(&node->router6_id, s, IPV6_MAX_BYTELEN);
+       if (CHECK_FLAG(node->flags, LS_NODE_FLAG))
+               STREAM_GETC(s, node->node_flag);
+       if (CHECK_FLAG(node->flags, LS_NODE_TYPE))
+               STREAM_GETC(s, node->type);
+       if (CHECK_FLAG(node->flags, LS_NODE_AS_NUMBER))
+               STREAM_GETL(s, node->as_number);
+       if (CHECK_FLAG(node->flags, LS_NODE_SR)) {
+               STREAM_GETL(s, node->srgb.lower_bound);
+               STREAM_GETL(s, node->srgb.range_size);
+               STREAM_GETC(s, node->srgb.flag);
+               STREAM_GET(node->algo, s, 2);
+       }
+       if (CHECK_FLAG(node->flags, LS_NODE_SRLB)) {
+               STREAM_GETL(s, node->srlb.lower_bound);
+               STREAM_GETL(s, node->srlb.range_size);
+       }
+       if (CHECK_FLAG(node->flags, LS_NODE_MSD))
+               STREAM_GETC(s, node->msd);
+
+       return node;
+
+stream_failure:
+       zlog_err("LS(%s): Could not parse Link State Node. Abort!", __func__);
+       XFREE(MTYPE_LS_DB, node);
+       return NULL;
+}
+
+static struct ls_attributes *ls_parse_attributes(struct stream *s)
+{
+       struct ls_attributes *attr;
+       size_t len;
+
+       attr = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes));
+       if (attr == NULL)
+               return NULL;
+       attr->srlgs = NULL;
+
+       STREAM_GET(&attr->adv, s, sizeof(struct ls_node_id));
+       STREAM_GETL(s, attr->flags);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) {
+               STREAM_GETC(s, len);
+               STREAM_GET(attr->name, s, len);
+       }
+       if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC))
+               STREAM_GETL(s, attr->standard.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))
+               STREAM_GETL(s, attr->standard.admin_group);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
+               attr->standard.local.s_addr = stream_get_ipv4(s);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR))
+               attr->standard.remote.s_addr = stream_get_ipv4(s);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
+               STREAM_GET(&attr->standard.local6, s, IPV6_MAX_BYTELEN);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6))
+               STREAM_GET(&attr->standard.remote6, s, IPV6_MAX_BYTELEN);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
+               STREAM_GETL(s, attr->standard.local_id);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID))
+               STREAM_GETL(s, attr->standard.remote_id);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW))
+               STREAM_GETF(s, attr->standard.max_bw);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW))
+               STREAM_GETF(s, attr->standard.max_rsv_bw);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW))
+               for (len = 0; len < MAX_CLASS_TYPE; len++)
+                       STREAM_GETF(s, attr->standard.unrsv_bw[len]);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS))
+               STREAM_GETL(s, attr->standard.remote_as);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR))
+               attr->standard.remote_addr.s_addr = stream_get_ipv4(s);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6))
+               STREAM_GET(&attr->standard.remote_addr6, s, IPV6_MAX_BYTELEN);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY))
+               STREAM_GETL(s, attr->extended.delay);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) {
+               STREAM_GETL(s, attr->extended.min_delay);
+               STREAM_GETL(s, attr->extended.max_delay);
+       }
+       if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER))
+               STREAM_GETL(s, attr->extended.jitter);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS))
+               STREAM_GETL(s, attr->extended.pkt_loss);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW))
+               STREAM_GETF(s, attr->extended.ava_bw);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW))
+               STREAM_GETF(s, attr->extended.rsv_bw);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW))
+               STREAM_GETF(s, attr->extended.used_bw);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) {
+               STREAM_GETL(s, attr->adj_sid[0].sid);
+               STREAM_GETC(s, attr->adj_sid[0].flags);
+               STREAM_GETC(s, attr->adj_sid[0].weight);
+               if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+                       STREAM_GET(attr->adj_sid[0].neighbor.sysid, s,
+                                  ISO_SYS_ID_LEN);
+               else if (attr->adv.origin == OSPFv2)
+                       attr->adj_sid[0].neighbor.addr.s_addr =
+                               stream_get_ipv4(s);
+       }
+       if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) {
+               STREAM_GETL(s, attr->adj_sid[1].sid);
+               STREAM_GETC(s, attr->adj_sid[1].flags);
+               STREAM_GETC(s, attr->adj_sid[1].weight);
+               if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+                       STREAM_GET(attr->adj_sid[1].neighbor.sysid, s,
+                                  ISO_SYS_ID_LEN);
+               else if (attr->adv.origin == OSPFv2)
+                       attr->adj_sid[1].neighbor.addr.s_addr =
+                               stream_get_ipv4(s);
+       }
+       if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) {
+               STREAM_GETC(s, len);
+               attr->srlgs = XCALLOC(MTYPE_LS_DB, len*sizeof(uint32_t));
+               attr->srlg_len = len;
+               for (len = 0; len < attr->srlg_len; len++)
+                       STREAM_GETL(s, attr->srlgs[len]);
+       }
+
+       return attr;
+
+stream_failure:
+       zlog_err("LS(%s): Could not parse Link State Attributes. Abort!",
+                __func__);
+       /* Clean memeory allocation */
+       if (attr->srlgs != NULL)
+               XFREE(MTYPE_LS_DB, attr->srlgs);
+       XFREE(MTYPE_LS_DB, attr);
+       return NULL;
+
+}
+
+static struct ls_prefix *ls_parse_prefix(struct stream *s)
+{
+       struct ls_prefix *ls_pref;
+       size_t len;
+
+       ls_pref = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix));
+       if (ls_pref == NULL)
+               return NULL;
+
+       STREAM_GET(&ls_pref->adv, s, sizeof(struct ls_node_id));
+       STREAM_GETW(s, ls_pref->flags);
+       STREAM_GETC(s, ls_pref->pref.family);
+       STREAM_GETW(s, ls_pref->pref.prefixlen);
+       len = prefix_blen(&ls_pref->pref);
+       STREAM_GET(&ls_pref->pref.u.prefix, s, len);
+       if (CHECK_FLAG(ls_pref->flags, LS_PREF_IGP_FLAG))
+               STREAM_GETC(s, ls_pref->igp_flag);
+       if (CHECK_FLAG(ls_pref->flags, LS_PREF_ROUTE_TAG))
+               STREAM_GETL(s, ls_pref->route_tag);
+       if (CHECK_FLAG(ls_pref->flags, LS_PREF_EXTENDED_TAG))
+               STREAM_GETQ(s, ls_pref->extended_tag);
+       if (CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC))
+               STREAM_GETL(s, ls_pref->metric);
+       if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) {
+               STREAM_GETL(s, ls_pref->sr.sid);
+               STREAM_GETC(s, ls_pref->sr.sid_flag);
+               STREAM_GETC(s, ls_pref->sr.algo);
+       }
+
+       return ls_pref;
+
+stream_failure:
+       zlog_err("LS(%s): Could not parse Link State Prefix. Abort!", __func__);
+       XFREE(MTYPE_LS_DB, ls_pref);
+       return NULL;
+}
+
+struct ls_message *ls_parse_msg(struct stream *s)
+{
+       struct ls_message *msg;
+
+       msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+       if (msg == NULL)
+               return NULL;
+
+       /* Read LS Message header */
+       STREAM_GETC(s, msg->event);
+       STREAM_GETC(s, msg->type);
+       STREAM_GET(&msg->remote_id, s, sizeof(struct ls_node_id));
+
+       /* Read Message Payload */
+       switch (msg->type) {
+       case LS_MSG_TYPE_NODE:
+               msg->data.node = ls_parse_node(s);
+               break;
+       case LS_MSG_TYPE_ATTRIBUTES:
+               msg->data.attr = ls_parse_attributes(s);
+               break;
+       case LS_MSG_TYPE_PREFIX:
+               msg->data.prefix = ls_parse_prefix(s);
+               break;
+       default:
+               zlog_err("Unsupported Payload");
+               goto stream_failure;
+       }
+
+       if (msg->data.node == NULL || msg->data.attr == NULL
+           || msg->data.prefix == NULL)
+               goto stream_failure;
+
+       return msg;
+
+stream_failure:
+       zlog_err("LS(%s): Could not parse LS message. Abort!", __func__);
+       XFREE(MTYPE_LS_DB, msg);
+       return NULL;
+}
+
+static int ls_format_node(struct stream *s, struct ls_node *node)
+{
+       size_t len;
+
+       /* Push Advertise node information first */
+       stream_put(s, &node->adv, sizeof(struct ls_node_id));
+
+       /* Push Flags & Origin then Node information if there are present */
+       stream_putw(s, node->flags);
+       if (CHECK_FLAG(node->flags, LS_NODE_NAME)) {
+               len = strlen(node->name);
+               stream_putc(s, len + 1);
+               stream_put(s, node->name, len);
+               stream_putc(s, '\0');
+       }
+       if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID))
+               stream_put_ipv4(s, node->router_id.s_addr);
+       if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID6))
+               stream_put(s, &node->router6_id, IPV6_MAX_BYTELEN);
+       if (CHECK_FLAG(node->flags, LS_NODE_FLAG))
+               stream_putc(s, node->node_flag);
+       if (CHECK_FLAG(node->flags, LS_NODE_TYPE))
+               stream_putc(s, node->type);
+       if (CHECK_FLAG(node->flags, LS_NODE_AS_NUMBER))
+               stream_putl(s, node->as_number);
+       if (CHECK_FLAG(node->flags, LS_NODE_SR)) {
+               stream_putl(s, node->srgb.lower_bound);
+               stream_putl(s, node->srgb.range_size);
+               stream_putc(s, node->srgb.flag);
+               stream_put(s, node->algo, 2);
+       }
+       if (CHECK_FLAG(node->flags, LS_NODE_SRLB)) {
+               stream_putl(s, node->srlb.lower_bound);
+               stream_putl(s, node->srlb.range_size);
+       }
+       if (CHECK_FLAG(node->flags, LS_NODE_MSD))
+               stream_putc(s, node->msd);
+
+       return 0;
+}
+
+static int ls_format_attributes(struct stream *s, struct ls_attributes *attr)
+{
+       size_t len;
+
+       /* Push Advertise node information first */
+       stream_put(s, &attr->adv, sizeof(struct ls_node_id));
+
+       /* Push Flags & Origin then LS attributes if there are present */
+       stream_putl(s, attr->flags);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) {
+               len = strlen(attr->name);
+               stream_putc(s, len + 1);
+               stream_put(s, attr->name, len);
+               stream_putc(s, '\0');
+       }
+       if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC))
+               stream_putl(s, attr->standard.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))
+               stream_putl(s, attr->standard.admin_group);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR))
+               stream_put_ipv4(s, attr->standard.local.s_addr);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR))
+               stream_put_ipv4(s, attr->standard.remote.s_addr);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6))
+               stream_put(s, &attr->standard.local6, IPV6_MAX_BYTELEN);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6))
+               stream_put(s, &attr->standard.remote6, IPV6_MAX_BYTELEN);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID))
+               stream_putl(s, attr->standard.local_id);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID))
+               stream_putl(s, attr->standard.remote_id);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW))
+               stream_putf(s, attr->standard.max_bw);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW))
+               stream_putf(s, attr->standard.max_rsv_bw);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW))
+               for (len = 0; len < MAX_CLASS_TYPE; len++)
+                       stream_putf(s, attr->standard.unrsv_bw[len]);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS))
+               stream_putl(s, attr->standard.remote_as);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR))
+               stream_put_ipv4(s, attr->standard.remote_addr.s_addr);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6))
+               stream_put(s, &attr->standard.remote_addr6, IPV6_MAX_BYTELEN);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY))
+               stream_putl(s, attr->extended.delay);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) {
+               stream_putl(s, attr->extended.min_delay);
+               stream_putl(s, attr->extended.max_delay);
+       }
+       if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER))
+               stream_putl(s, attr->extended.jitter);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS))
+               stream_putl(s, attr->extended.pkt_loss);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW))
+               stream_putf(s, attr->extended.ava_bw);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW))
+               stream_putf(s, attr->extended.rsv_bw);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW))
+               stream_putf(s, attr->extended.used_bw);
+       if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) {
+               stream_putl(s, attr->adj_sid[0].sid);
+               stream_putc(s, attr->adj_sid[0].flags);
+               stream_putc(s, attr->adj_sid[0].weight);
+               if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+                       stream_put(s, attr->adj_sid[0].neighbor.sysid,
+                                  ISO_SYS_ID_LEN);
+               else if (attr->adv.origin == OSPFv2)
+                       stream_put_ipv4(s,
+                                       attr->adj_sid[0].neighbor.addr.s_addr);
+       }
+       if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) {
+               stream_putl(s, attr->adj_sid[1].sid);
+               stream_putc(s, attr->adj_sid[1].flags);
+               stream_putc(s, attr->adj_sid[1].weight);
+               if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2)
+                       stream_put(s, attr->adj_sid[1].neighbor.sysid,
+                                  ISO_SYS_ID_LEN);
+               else if (attr->adv.origin == OSPFv2)
+                       stream_put_ipv4(s,
+                                       attr->adj_sid[1].neighbor.addr.s_addr);
+       }
+       if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) {
+               stream_putc(s, attr->srlg_len);
+               for (len = 0; len < attr->srlg_len; len++)
+                       stream_putl(s, attr->srlgs[len]);
+       }
+
+       return 0;
+}
+
+static int ls_format_prefix(struct stream *s, struct ls_prefix *ls_pref)
+{
+       size_t len;
+
+       /* Push Advertise node information first */
+       stream_put(s, &ls_pref->adv, sizeof(struct ls_node_id));
+
+       /* Push Flags, Origin & Prefix then information if there are present */
+       stream_putw(s, ls_pref->flags);
+       stream_putc(s, ls_pref->pref.family);
+       stream_putw(s, ls_pref->pref.prefixlen);
+       len = prefix_blen(&ls_pref->pref);
+       stream_put(s, &ls_pref->pref.u.prefix, len);
+       if (CHECK_FLAG(ls_pref->flags, LS_PREF_IGP_FLAG))
+               stream_putc(s, ls_pref->igp_flag);
+       if (CHECK_FLAG(ls_pref->flags, LS_PREF_ROUTE_TAG))
+               stream_putl(s, ls_pref->route_tag);
+       if (CHECK_FLAG(ls_pref->flags, LS_PREF_EXTENDED_TAG))
+               stream_putq(s, ls_pref->extended_tag);
+       if (CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC))
+               stream_putl(s, ls_pref->metric);
+       if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) {
+               stream_putl(s, ls_pref->sr.sid);
+               stream_putc(s, ls_pref->sr.sid_flag);
+               stream_putc(s, ls_pref->sr.algo);
+       }
+
+       return 0;
+}
+
+static int ls_format_msg(struct stream *s, struct ls_message *msg)
+{
+
+       /* Prepare Link State header */
+       stream_putc(s, msg->event);
+       stream_putc(s, msg->type);
+       stream_put(s, &msg->remote_id, sizeof(struct ls_node_id));
+
+       /* Add Message Payload */
+       switch (msg->type) {
+       case LS_MSG_TYPE_NODE:
+               return ls_format_node(s, msg->data.node);
+       case LS_MSG_TYPE_ATTRIBUTES:
+               return ls_format_attributes(s, msg->data.attr);
+       case LS_MSG_TYPE_PREFIX:
+               return ls_format_prefix(s, msg->data.prefix);
+       default:
+               zlog_warn("Unsupported Payload");
+               break;
+       }
+
+       return -1;
+}
+
+int ls_send_msg(struct zclient *zclient, struct ls_message *msg,
+               struct zapi_opaque_reg_info *dst)
+{
+       struct stream *s;
+       uint16_t flags = 0;
+
+       /* Check buffer size */
+       if (STREAM_SIZE(zclient->obuf) <
+           (ZEBRA_HEADER_SIZE + sizeof(uint32_t) + sizeof(msg)))
+               return -1;
+
+       s = zclient->obuf;
+       stream_reset(s);
+
+       zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT);
+
+       /* Send sub-type, flags and destination for unicast message */
+       stream_putl(s, LINK_STATE_UPDATE);
+       if (dst != NULL) {
+               SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST);
+               stream_putw(s, flags);
+               /* Send destination client info */
+               stream_putc(s, dst->proto);
+               stream_putw(s, dst->instance);
+               stream_putl(s, dst->session_id);
+       } else
+               stream_putw(s, flags);
+
+       /* Format Link State message */
+       if (ls_format_msg(s, msg) < 0) {
+               stream_reset(s);
+               return -1;
+       }
+
+       /* 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);
+}
+
+struct ls_message *ls_vertex2msg(struct ls_message *msg,
+                                struct ls_vertex *vertex)
+{
+       /* Allocate space if needed */
+       if (msg == NULL)
+               msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+       else
+               memset(msg, 0, sizeof(*msg));
+
+       msg->type = LS_MSG_TYPE_NODE;
+       msg->data.node = vertex->node;
+       msg->remote_id.origin = NONE;
+
+       return msg;
+}
+
+struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge)
+{
+       /* Allocate space if needed */
+       if (msg == NULL)
+               msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+       else
+               memset(msg, 0, sizeof(*msg));
+
+       msg->type = LS_MSG_TYPE_ATTRIBUTES;
+       msg->data.attr = edge->attributes;
+       if (edge->destination != NULL)
+               msg->remote_id = edge->destination->node->adv;
+       else
+               msg->remote_id.origin = NONE;
+
+       return msg;
+}
+
+struct ls_message *ls_subnet2msg(struct ls_message *msg,
+                                struct ls_subnet *subnet)
+{
+       /* Allocate space if needed */
+       if (msg == NULL)
+               msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message));
+       else
+               memset(msg, 0, sizeof(*msg));
+
+       msg->type = LS_MSG_TYPE_PREFIX;
+       msg->data.prefix = subnet->ls_pref;
+       msg->remote_id.origin = NONE;
+
+       return msg;
+}
+
+void ls_delete_msg(struct ls_message *msg)
+{
+       if (msg == NULL)
+               return;
+
+       switch (msg->type) {
+       case LS_MSG_TYPE_NODE:
+               if (msg->data.node)
+                       XFREE(MTYPE_LS_DB, msg->data.node);
+               break;
+       case LS_MSG_TYPE_ATTRIBUTES:
+               if (msg->data.attr)
+                       XFREE(MTYPE_LS_DB, msg->data.attr);
+               break;
+       case LS_MSG_TYPE_PREFIX:
+               if (msg->data.prefix)
+                       XFREE(MTYPE_LS_DB, msg->data.prefix);
+               break;
+       default:
+               break;
+       }
+
+       XFREE(MTYPE_LS_DB, msg);
+}
+
+int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient,
+               struct zapi_opaque_reg_info *dst)
+{
+       struct ls_vertex *vertex;
+       struct ls_edge *edge;
+       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);
+               ls_send_msg(zclient, &msg, dst);
+       }
+       frr_each(edges, &ted->edges, edge) {
+               ls_edge2msg(&msg, edge);
+               ls_send_msg(zclient, &msg, dst);
+       }
+       frr_each(subnets, &ted->subnets, subnet) {
+               ls_subnet2msg(&msg, subnet);
+               ls_send_msg(zclient, &msg, dst);
+       }
+       return 0;
+}
+
+void ls_dump_ted(struct ls_ted *ted)
+{
+       struct ls_vertex *vertex;
+       struct ls_edge *edge;
+       struct ls_subnet *subnet;
+       struct ls_message msg;
+
+       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);
+               zlog_debug("\tTed 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");
+               struct listnode *lst_node;
+               struct ls_edge *vertex_edge;
+
+               for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node,
+                                         vertex_edge)) {
+                       zlog_debug(
+                               "\t\tinc edge key:%lldn 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);
+               }
+               for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node,
+                                         vertex_edge)) {
+                       zlog_debug(
+                               "\t\tout edge key:%lld  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("\tTed edge key:%lld src:%s dst:%s", edge->key,
+                          edge->source ? edge->source->node->name
+                                       : "no_source",
+                          edge->destination ? edge->destination->node->name
+                                            : "no_dest");
+       }
+       frr_each(subnets, &ted->subnets, subnet) {
+               ls_subnet2msg(&msg, subnet);
+               zlog_debug(
+                       "\tTed subnet key:%s vertex:%pI4 pfx:%pFX",
+                       subnet->key.family == AF_INET
+                               ? inet_ntoa(subnet->key.u.prefix4)
+                               : inet6_ntoa(subnet->key.u.prefix6),
+                       &subnet->vertex->node->adv.id.ip.addr,
+                       &subnet->ls_pref->pref);
+       }
+       zlog_debug("(%s) Ted end", __func__);
+}
diff --git a/lib/link_state.h b/lib/link_state.h
new file mode 100644 (file)
index 0000000..93669f5
--- /dev/null
@@ -0,0 +1,780 @@
+/*
+ * Link State Database definition - ted.h
+ *
+ * Author: Olivier Dugeon <olivier.dugeon@orange.com>
+ *
+ * Copyright (C) 2020 Orange http://www.orange.com
+ *
+ * This file is part of Free Range Routing (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_LINK_STATE_H_
+#define _FRR_LINK_STATE_H_
+
+#include "typesafe.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This file defines the model used to implement a Link State Database
+ * suitable to be used by various protocol like RSVP-TE, BGP-LS, PCEP ...
+ * This database is normally fulfill by the link state routing protocol,
+ * commonly OSPF or ISIS, carrying Traffic Engineering information within
+ * Link State Attributes. See, RFC3630.(OSPF-TE) and RFC5305 (ISIS-TE).
+ *
+ * At least, 3 types of Link State structure are defined:
+ *  - Link State Node that groups all information related to a node
+ *  - Link State Attributes that groups all information related to a link
+ *  - Link State Prefix that groups all information related to a prefix
+ *
+ * These 3 types of structures are those handled by BGP-LS (see RFC7752).
+ *
+ * Each structure, in addition to the specific parameters, embed the node
+ * identifier which advertises the Link State and a bit mask as flags to
+ * indicates which parameters are valid i.e. for which the value corresponds
+ * to a Link State information convey by the routing protocol.
+ * Node identifier is composed of the route id as IPv4 address plus the area
+ * id for OSPF and the ISO System id plus the IS-IS level for IS-IS.
+ */
+
+/* Link State Common definitions */
+#define MAX_NAME_LENGTH                256
+#define ISO_SYS_ID_LEN         6
+
+/* Type of Node */
+enum ls_node_type {
+       STANDARD,       /* a P or PE node */
+       ABR,            /* an Array Border Node */
+       ASBR,           /* an Autonomous System Border Node */
+       PSEUDO,         /* a Pseudo Node */
+};
+
+/* Origin of the Link State information */
+enum ls_origin {NONE = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC};
+
+/**
+ * Link State Node Identifier as:
+ *  - IPv4 address + Area ID for OSPF
+ *  - ISO System ID + ISIS Level for ISIS
+ */
+struct ls_node_id {
+       enum ls_origin origin;          /* Origin of the LS information */
+       union {
+               struct {
+                       struct in_addr addr;            /* OSPF Router IS */
+                       struct in_addr area_id;         /* OSPF Area ID */
+               } ip;
+               struct {
+                       uint8_t sys_id[ISO_SYS_ID_LEN]; /* ISIS System ID */
+                       uint8_t level;                  /* ISIS Level */
+                       uint8_t padding;
+               } iso;
+       } id __attribute__((aligned(8)));
+};
+
+/* Link State flags to indicate which Node parameters are valid */
+#define LS_NODE_UNSET          0x0000
+#define LS_NODE_NAME           0x0001
+#define LS_NODE_ROUTER_ID      0x0002
+#define LS_NODE_ROUTER_ID6     0x0004
+#define LS_NODE_FLAG           0x0008
+#define LS_NODE_TYPE           0x0010
+#define LS_NODE_AS_NUMBER      0x0020
+#define LS_NODE_SR             0x0040
+#define LS_NODE_SRLB           0x0080
+#define LS_NODE_MSD            0x0100
+
+/* Link State Node structure */
+struct ls_node {
+       uint16_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 Node (IS-IS only) */
+       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 */
+       uint32_t as_number;             /* Local or neighbor AS number */
+       struct {                        /* 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 */
+               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 */
+};
+
+/* Link State flags to indicate which Attribute parameters are valid */
+#define LS_ATTR_UNSET          0x00000000
+#define LS_ATTR_NAME           0x00000001
+#define LS_ATTR_METRIC         0x00000002
+#define LS_ATTR_TE_METRIC      0x00000004
+#define LS_ATTR_ADM_GRP                0x00000008
+#define LS_ATTR_LOCAL_ADDR     0x00000010
+#define LS_ATTR_NEIGH_ADDR     0x00000020
+#define LS_ATTR_LOCAL_ADDR6    0x00000040
+#define LS_ATTR_NEIGH_ADDR6    0x00000080
+#define LS_ATTR_LOCAL_ID       0x00000100
+#define LS_ATTR_NEIGH_ID       0x00000200
+#define LS_ATTR_MAX_BW         0x00000400
+#define LS_ATTR_MAX_RSV_BW     0x00000800
+#define LS_ATTR_UNRSV_BW       0x00001000
+#define LS_ATTR_REMOTE_AS      0x00002000
+#define LS_ATTR_REMOTE_ADDR    0x00004000
+#define LS_ATTR_REMOTE_ADDR6   0x00008000
+#define LS_ATTR_DELAY          0x00010000
+#define LS_ATTR_MIN_MAX_DELAY  0x00020000
+#define LS_ATTR_JITTER         0x00040000
+#define LS_ATTR_PACKET_LOSS    0x00080000
+#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
+
+/* 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 te_metric;             /* Traffic Engineering metric */
+               uint32_t admin_group;           /* Administrative Group */
+               struct in_addr local;           /* Local IPv4 address */
+               struct in_addr remote;          /* Remote IPv4 address */
+               struct in6_addr local6;         /* Local IPv6 address */
+               struct in6_addr remote6;        /* Remote IPv6 address */
+               uint32_t local_id;              /* Local Identifier */
+               uint32_t remote_id;             /* Remote Identifier */
+               float max_bw;                   /* Maximum Link Bandwidth */
+               float max_rsv_bw;               /* Maximum Reservable BW */
+               float unrsv_bw[8];              /* Unreserved BW per CT (8) */
+               uint32_t remote_as;             /* Remote AS number */
+               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 */
+               uint32_t delay;         /* Unidirectional average delay */
+               uint32_t min_delay;     /* Unidirectional minimum delay */
+               uint32_t max_delay;     /* Unidirectional maximum delay */
+               uint32_t jitter;        /* Unidirectional delay variation */
+               uint32_t pkt_loss;      /* Unidirectional packet loss */
+               float ava_bw;           /* Available Bandwidth */
+               float rsv_bw;           /* Reserved Bandwidth */
+               float used_bw;          /* Utilized Bandwidth */
+       } extended;
+#define LS_ATTR_EXTENDED_SIZE  32
+       struct {                /* (LAN)-Adjacency SID for OSPF */
+               uint32_t sid;           /* SID as MPLS label or index */
+               uint8_t flags;          /* Flags */
+               uint8_t weight;         /* Administrative weight */
+               union {
+                       struct in_addr addr;    /* Neighbor @IP for OSPF */
+                       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 */
+};
+
+/* Link State flags to indicate which Prefix parameters are valid */
+#define LS_PREF_UNSET          0x00
+#define LS_PREF_IGP_FLAG       0x01
+#define LS_PREF_ROUTE_TAG      0x02
+#define LS_PREF_EXTENDED_TAG   0x04
+#define LS_PREF_METRIC         0x08
+#define LS_PREF_SR             0x10
+
+/* Link State Prefix */
+struct ls_prefix {
+       uint8_t flags;                  /* Flag for parameters validity */
+       struct ls_node_id adv;          /* Adv. Router of this Link State */
+       struct prefix pref;             /* IPv4 or IPv6 prefix */
+       uint8_t igp_flag;               /* IGP Flags associated to the 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 {
+               uint32_t sid;           /* Segment Routing ID */
+               uint8_t sid_flag;       /* Segment Routing Flags */
+               uint8_t algo;           /* Algorithm for Segment Routing */
+       } sr;
+};
+
+/**
+ * Create a new Link State Node. Structure is dynamically allocated.
+ *
+ * @param adv  Mandatory Link State Node ID i.e. advertise router information
+ * @param rid  Router ID as IPv4 address
+ * @param rid6 Router ID as IPv6 address
+ *
+ * @return     New Link State Node
+ */
+extern struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid,
+                                  struct in6_addr rid6);
+
+/**
+ * Remove Link State Node. Data structure is freed.
+ *
+ * @param node       Pointer to a valid Link State Node structure
+ */
+extern void ls_node_del(struct ls_node *node);
+
+/**
+ * Check if two Link State Nodes are equal. Note that this routine has the same
+ * return value sense as '==' (which is different from a comparison).
+ *
+ * @param n1   First Link State Node to be compare
+ * @param n2   Second Link State Node to be compare
+ *
+ * @return     1 if equal, 0 otherwise
+ */
+extern int ls_node_same(struct ls_node *n1, struct ls_node *n2);
+
+/**
+ * Create a new Link State Attributes. Structure is dynamically allocated.
+ * At least one of parameters MUST be valid and not equal to 0.
+ *
+ * @param adv          Mandatory Link State Node ID i.e. advertise router ID
+ * @param local                Local IPv4 address
+ * @param local6       Local Ipv6 address
+ * @param local_id     Local Identifier
+ *
+ * @return             New Link State Attributes
+ */
+extern struct ls_attributes *ls_attributes_new(struct ls_node_id adv,
+                                              struct in_addr local,
+                                              struct in6_addr local6,
+                                              uint32_t local_id);
+
+/**
+ * Remove Link State Attributes. Data structure is freed.
+ *
+ * @param attr          Pointer to a valid Link State Attribute structure
+ */
+extern void ls_attributes_del(struct ls_attributes *attr);
+
+/**
+ * Check if two Link State Attributes are equal. Note that this routine has the
+ * same return value sense as '==' (which is different from a comparison).
+ *
+ * @param a1   First Link State Attributes to be compare
+ * @param a2   Second Link State Attributes to be compare
+ *
+ * @return     1 if equal, 0 otherwise
+ */
+extern int ls_attributes_same(struct ls_attributes *a1,
+                             struct ls_attributes *a2);
+
+/**
+ * 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
+ * represents the network Node and a list of Edges (E) which represents node
+ * Link. An additional list of prefixes (P) is also added.
+ * A prefix (P) is also attached to the Vertex (V) which advertise it.
+ *
+ * Vertex (V) contains the list of outgoing Edges (E) that connect this Vertex
+ * with its direct neighbors and the list of incoming Edges (E) that connect
+ * the direct neighbors to this Vertex. Indeed, the Edge (E) is unidirectional,
+ * thus, it is necessary to add 2 Edges to model a bidirectional relation
+ * between 2 Vertices.
+ *
+ * Edge (E) contains the source and destination Vertex that this Edge
+ * is connecting.
+ *
+ * A unique Key is used to identify both Vertices and Edges within the Graph.
+ * An easy way to build this key is to used the IP address: i.e. loopback
+ * address for Vertices and link IP address for Edges.
+ *
+ *      --------------     ---------------------------    --------------
+ *      | Connected  |---->| Connected Edge Va to Vb |--->| Connected  |
+ *  --->|  Vertex    |     ---------------------------    |  Vertex    |---->
+ *      |            |                                    |            |
+ *      | - Key (Va) |                                    | - Key (Vb) |
+ *  <---| - Vertex   |     ---------------------------    | - Vertex   |<----
+ *      |            |<----| Connected Edge Vb to Va |<---|            |
+ *      --------------     ---------------------------    --------------
+ *
+ */
+
+/* Link State Vertex structure */
+PREDECL_RBTREE_UNIQ(vertices)
+struct ls_vertex {
+       struct vertices_item entry;     /* Entry in RB Tree */
+       uint64_t key;                   /* Unique Key identifier */
+       struct ls_node *node;           /* Link State Node */
+       struct list *incoming_edges;    /* List of incoming Link State links */
+       struct list *outgoing_edges;    /* List of outgoing Link State links */
+       struct list *prefixes;          /* List of advertised prefix */
+};
+
+/* Link State Edge structure */
+PREDECL_RBTREE_UNIQ(edges)
+struct ls_edge {
+       struct edges_item entry;        /* Entry in RB tree */
+       uint64_t key;                   /* Unique Key identifier */
+       struct ls_attributes *attributes;       /* Link State attributes */
+       struct ls_vertex *source;       /* Pointer to the source Vertex */
+       struct ls_vertex *destination;  /* Pointer to the destination Vertex */
+};
+
+/* Link State Subnet structure */
+PREDECL_RBTREE_UNIQ(subnets)
+struct ls_subnet {
+       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 */
+};
+
+/* Declaration of Vertices, Edges and Prefixes RB Trees */
+macro_inline int vertex_cmp(const struct ls_vertex *node1,
+                           const struct ls_vertex *node2)
+{
+       return (node1->key - node2->key);
+}
+DECLARE_RBTREE_UNIQ(vertices, struct ls_vertex, entry, vertex_cmp)
+
+macro_inline int edge_cmp(const struct ls_edge *edge1,
+                         const struct ls_edge *edge2)
+{
+       return (edge1->key - edge2->key);
+}
+DECLARE_RBTREE_UNIQ(edges, struct ls_edge, entry, edge_cmp)
+
+macro_inline int subnet_cmp(const struct ls_subnet *a,
+                            const struct ls_subnet *b)
+{
+       return prefix_cmp(&a->key, &b->key);
+}
+DECLARE_RBTREE_UNIQ(subnets, struct ls_subnet, entry, subnet_cmp)
+
+/* Link State TED Structure */
+struct ls_ted {
+       uint32_t key;                   /* Unique identifier */
+       char name[MAX_NAME_LENGTH];     /* Name of this graph. Could be null */
+       uint32_t as_number;             /* AS number of the modeled network */
+       struct ls_vertex *self;         /* Vertex of the FRR instance */
+       struct vertices_head vertices;  /* List of Vertices */
+       struct edges_head edges;        /* List of Edges */
+       struct subnets_head subnets;    /* List of Subnets */
+};
+
+/**
+ * Create a new Link State Vertex structure and initialize is with the Link
+ * State Node parameter.
+ *
+ * @param node Link State Node
+ *
+ * @return     New Vertex
+ */
+extern struct ls_vertex *ls_vertex_new(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.
+ *
+ * @param vertex       Link State Vertex to be removed
+ */
+extern void ls_vertex_del(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.
+ *
+ * @param ted  Traffic Engineering Database structure
+ * @param node Link State Node
+ *
+ * @return     New Vertex or NULL in case of error
+ */
+extern struct ls_vertex *ls_vertex_add(struct ls_ted *ted,
+                                      struct ls_node *node);
+
+/**
+ * Update Vertex with the Link State Node. A new vertex is created if no one
+ * corresponds to the Link State Node.
+ *
+ * @param ted  Link State Data Base
+ * @param node Link State Node to be updated
+ *
+ * @return     Updated Link State Vertex or Null in case of error
+ */
+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.
+ *
+ * @param ted          Link State Data Base
+ * @param vertex       Vertex to be removed
+ */
+extern void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex);
+
+/**
+ * Find Vertex in the Link State DB by its unique key.
+ *
+ * @param ted  Link State Data Base
+ * @param key  Vertex Key different from 0
+ *
+ * @return     Vertex if found, NULL otherwise
+ */
+extern struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted,
+                                              const uint64_t key);
+
+/**
+ * Find Vertex in the Link State DB by its Link State Node.
+ *
+ * @param ted  Link State Data Base
+ * @param nid  Link State Node ID
+ *
+ * @return     Vertex if found, NULL otherwise
+ */
+extern struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted,
+                                             struct ls_node_id nid);
+
+/**
+ * Check if two Vertices are equal. Note that this routine has the same return
+ * value sense as '==' (which is different from a comparison).
+ *
+ * @param v1   First vertex to compare
+ * @param v2   Second vertex to compare
+ *
+ * @return     1 if equal, 0 otherwise
+ */
+extern int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2);
+
+/**
+ * Add new Edge to the Link State DB. Edge is created from the Link State
+ * Attributes. Edge data structure is dynamically allocated.
+ *
+ * @param ted          Link State Data Base
+ * @param attributes   Link State attributes
+ *
+ * @return             New Edge or NULL in case of error
+ */
+extern struct ls_edge *ls_edge_add(struct ls_ted *ted,
+                                  struct ls_attributes *attributes);
+
+/**
+ * Update the Link State Attributes information of an existing Edge. If there is
+ * no corresponding Edge in the Link State Data Base, a new Edge is created.
+ *
+ * @param ted          Link State Data Base
+ * @param attributes   Link State Attributes
+ *
+ * @return             Updated Link State Edge, or NULL in case of error
+ */
+extern struct ls_edge *ls_edge_update(struct ls_ted *ted,
+                                     struct ls_attributes *attributes);
+
+/**
+ * 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.
+ *
+ * @param ted  Link State Data Base
+ * @param edge Edge to be removed
+ */
+extern void ls_edge_del(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
+ * @param key  Edge key
+ *
+ * @return     Edge if found, NULL otherwise
+ */
+extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted,
+                                          const uint64_t key);
+
+/**
+ * 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
+ *
+ * @param ted          Link State Data Base
+ * @param attributes   Link State Attributes
+ *
+ * @return             Edge if found, NULL otherwise
+ */
+extern struct ls_edge *
+ls_find_edge_by_source(struct ls_ted *ted, struct ls_attributes *attributes);
+
+/**
+ * Find Edge in the Link State Data Base by the destination (remote IPv4 or IPv6
+ * address of remote ID) information of the Link State Attributes
+ *
+ * @param ted          Link State Data Base
+ * @param attributes   Link State Attributes
+ *
+ * @return             Edge if found, NULL otherwise
+ */
+extern struct ls_edge *
+ls_find_edge_by_destination(struct ls_ted *ted,
+                           struct ls_attributes *attributes);
+
+/**
+ * Add new Subnet to the Link State DB. Subnet is created from the Link State
+ * prefix. Subnet data structure is dynamically allocated.
+ *
+ * @param ted  Link State Data Base
+ * @param pref Link State Prefix
+ *
+ * @return     New Subnet
+ */
+extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted,
+                                      struct ls_prefix *pref);
+
+/**
+ * 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.
+ *
+ * @param ted          Link State Data Base
+ * @param subnet       Subnet to be removed
+ */
+extern void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet);
+
+/**
+ * Find Subnet in the Link State Data Base by prefix.
+ *
+ * @param ted          Link State Data Base
+ * @param prefix       Link State Prefix
+ *
+ * @return             Subnet if found, NULL otherwise
+ */
+extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted,
+                                       const struct prefix prefix);
+
+/**
+ * Create a new Link State Data Base.
+ *
+ * @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
+ *
+ * @return     New Link State Database or NULL in case of error
+ */
+extern struct ls_ted *ls_ted_new(const uint32_t key, const char *name,
+                                uint32_t asn);
+
+/**
+ * Delete existing Link State Data Base.
+ *
+ * @param ted  Link State Data Base
+ */
+extern void ls_ted_del(struct ls_ted *ted);
+
+/**
+ * Connect Source and Destination Vertices by given Edge. Only non NULL source
+ * and destination vertices are connected.
+ *
+ * @param src  Link State Source Vertex
+ * @param dst  Link State Destination Vertex
+ * @param edge Link State Edge. Must not be NULL
+ */
+extern void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst,
+                               struct ls_edge *edge);
+
+/**
+ * Connect Link State Edge to the Link State Vertex which could be a Source or
+ * a Destination Vertex.
+ *
+ * @param vertex       Link State Vertex to be connected. Must not be NULL
+ * @param edge         Link State Edge connection. Must not be NULL
+ * @param source       True for a Source, false for a Destination Vertex
+ */
+extern void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge,
+                      bool source);
+
+/**
+ * Disconnect Link State Edge from the Link State Vertex which could be a
+ * Source or a Destination Vertex.
+ *
+ * @param vertex       Link State Vertex to be connected. Must not be NULL
+ * @param edge         Link State Edge connection. Must not be NULL
+ * @param source       True for a Source, false for a Destination Vertex
+ */
+extern void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge,
+                         bool source);
+
+/**
+ * Disconnect Link State Edge from both Source and Destination Vertex.
+ *
+ * @param edge         Link State Edge to be disconnected
+ */
+extern void ls_disconnect_edge(struct ls_edge *edge);
+
+
+/**
+ * The Link State Message is defined to convey Link State parameters from
+ * the routing protocol (OSPF or IS-IS) to other daemons e.g. BGP.
+ *
+ * The structure is composed of:
+ *  - Event of the message:
+ *    - Sync: Send the whole LS DB following a request
+ *    - Add: Send the a new Link State element
+ *    -  Update: Send an update of an existing Link State element
+ *    - Delete: Indicate that the given Link State element is removed
+ *  - Type of Link State element: Node, Attribute or Prefix
+ *  - Remote node id when known
+ *  - Data: Node, Attributes or Prefix
+ *
+ * A Link State Message can carry only one Link State Element (Node, Attributes
+ * of Prefix) at once, and only one Link State Message is sent through ZAPI
+ * Opaque Link State type at once.
+ */
+
+/* ZAPI Opaque Link State Message Event */
+#define LS_MSG_EVENT_SYNC      1
+#define LS_MSG_EVENT_ADD       2
+#define LS_MSG_EVENT_UPDATE    3
+#define LS_MSG_EVENT_DELETE    4
+
+/* ZAPI Opaque Link State Message sub-Type */
+#define LS_MSG_TYPE_NODE       1
+#define LS_MSG_TYPE_ATTRIBUTES 2
+#define LS_MSG_TYPE_PREFIX     3
+
+/* Link State Message */
+struct ls_message {
+       uint8_t event;          /* Message Event: Sync, Add, Update, Delete */
+       uint8_t type;           /* Message Data Type: Node, Attribute, Prefix */
+       struct ls_node_id remote_id;    /* Remote Link State Node ID */
+       union {
+               struct ls_node *node;           /* Link State Node */
+               struct ls_attributes *attr;     /* Link State Attributes */
+               struct ls_prefix *prefix;       /* Link State Prefix */
+       } data;
+};
+
+/**
+ * Parse Link State Message from stream. Used this function once receiving a
+ * new ZAPI Opaque message of type Link State.
+ *
+ * @param s    Stream buffer. Must not be NULL.
+ *
+ * @return     New Link State Message or NULL in case of error
+ */
+extern struct ls_message *ls_parse_msg(struct stream *s);
+
+/**
+ * Delete existing message, freeing all substructure.
+ *
+ * @param msg  Link state message to be deleted
+ */
+extern void ls_delete_msg(struct ls_message *msg);
+
+/**
+ * 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.
+ *
+ * @param zclient      Zebra Client
+ * @param msg          Link State Message to be sent
+ * @param dst          Destination daemon for unicast message,
+ *                     NULL for broadcast message
+ *
+ * @return             0 on success, -1 otherwise
+ */
+extern int ls_send_msg(struct zclient *zclient, struct ls_message *msg,
+                      struct zapi_opaque_reg_info *dst);
+
+/**
+ * Create a new Link State Message from a Link State Vertex. If Link State
+ * Message is NULL, a new data structure is dynamically allocated.
+ *
+ * @param msg          Link State Message to be filled or NULL
+ * @param vertex       Link State Vertex. Must not be NULL
+ *
+ * @return             New Link State Message msg parameter is NULL or pointer
+ *                     to the provided Link State Message
+ */
+extern struct ls_message *ls_vertex2msg(struct ls_message *msg,
+                                       struct ls_vertex *vertex);
+
+/**
+ * Create a new Link State Message from a Link State Edge. If Link State
+ * Message is NULL, a new data structure is dynamically allocated.
+ *
+ * @param msg          Link State Message to be filled or NULL
+ * @param edge         Link State Edge. Must not be NULL
+ *
+ * @return             New Link State Message msg parameter is NULL or pointer
+ *                     to the provided Link State Message
+ */
+extern struct ls_message *ls_edge2msg(struct ls_message *msg,
+                                     struct ls_edge *edge);
+
+/**
+ * Create a new Link State Message from a Link State Subnet. If Link State
+ * Message is NULL, a new data structure is dynamically allocated.
+ *
+ * @param msg          Link State Message to be filled or NULL
+ * @param subnet       Link State Subnet. Must not be NULL
+ *
+ * @return             New Link State Message msg parameter is NULL or pointer
+ *                     to the provided Link State Message
+ */
+extern struct ls_message *ls_subnet2msg(struct ls_message *msg,
+                                       struct ls_subnet *subnet);
+
+/**
+ * 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
+ * Synchronization.
+ *
+ * @param ted          Link State Data Base. Must not be NULL
+ * @param zclient      Zebra Client. Must not be NULL
+ * @param dst          Destination FRR daemon. Must not be NULL
+ *
+ * @return             0 on success, -1 otherwise
+ */
+extern int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient,
+                      struct zapi_opaque_reg_info *dst);
+
+/**
+ * Dump all Link State Data Base elements for debugging purposes
+ *
+ * @param ted  Link State Data Base. Must not be NULL
+ *
+ */
+extern void ls_dump_ted(struct ls_ted *ted);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_LINK_STATE_H_ */
index 038282a99b918015ff1d5c61141eac7f30b36b24..ee9e827ee8eff18a4705a7f06d52288f2b80969a 100644 (file)
@@ -48,6 +48,7 @@ lib_libfrr_la_SOURCES = \
        lib/libfrr.c \
        lib/libfrr_trace.c \
        lib/linklist.c \
+       lib/link_state.c \
        lib/log.c \
        lib/log_filter.c \
        lib/log_vty.c \
@@ -208,6 +209,7 @@ pkginclude_HEADERS += \
        lib/libfrr_trace.h \
        lib/libospf.h \
        lib/linklist.h \
+       lib/link_state.h \
        lib/log.h \
        lib/log_vty.h \
        lib/md5.h \