--- /dev/null
+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)
+
--- /dev/null
+/*
+ * 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__);
+}
--- /dev/null
+/*
+ * 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_ */