From b0c0b43348b12163f88c22e58f87763152942ca4 Mon Sep 17 00:00:00 2001 From: Olivier Dugeon Date: Wed, 6 Jan 2021 11:24:07 +0100 Subject: [PATCH] lib: Update Link State Database Add new status for Vertex, Edge and Subnet to manage their respective states in the data base. Add new functions: - to register/unregister server and client - to show content of the Database (VTY and Json output) - to update and compare subnets - to clean vertex and ted from ORPHAN elements - to convert message or stream into a Link State Element and update Link State Database accordingly to message event Change Edge and Vertex key computation by using the host order systematically. This impact mostly key based on IPv4 addresses where `ntohl()` function must be used when searching a Vertex or Edge by key. Update the documentation accordingly Signed-off-by: Olivier Dugeon --- doc/developer/link-state.rst | 244 +++++- lib/link_state.c | 1441 +++++++++++++++++++++++++++++++--- lib/link_state.h | 411 ++++++++-- lib/zclient.h | 2 +- 4 files changed, 1892 insertions(+), 206 deletions(-) diff --git a/doc/developer/link-state.rst b/doc/developer/link-state.rst index f1fc52966b..1cbaf27ffe 100644 --- a/doc/developer/link-state.rst +++ b/doc/developer/link-state.rst @@ -81,26 +81,47 @@ corresponds to a Link State information conveyed by the routing protocol. Functions ^^^^^^^^^ -A set of functions is provided to create, delete and compare Link State Node: +A set of functions is provided to create, delete and compare Link State +Node, Atribute and Prefix: .. c:function:: struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr router_id, struct in6_addr router6_id) -.. c:function:: voidls_node_del(struct ls_node *node) -.. c:function:: int ls_node_same(struct ls_node *n1, struct ls_node *n2) - -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) +.. c:function:: struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p) + + Create respectively a new Link State Node, Attribute or Prefix. + Structure is dynamically allocated. Link State Node ID (adv) is mandatory + and: + + - at least one of IPv4 or IPv6 must be provided for the router ID + (router_id or router6_id) for Node + - at least one of local, local6 or local_id must be provided for Attribute + - prefix is mandatory for Link State Prefix. + +.. c:function:: void ls_node_del(struct ls_node *node) +.. c:function:: void ls_attributes_del(struct ls_attributes *attr) +.. c:function:: void ls_prefix_del(struct ls_prefix *pref) + + Remove, respectively Link State Node, Attributes or Prefix. + Data structure is freed. + +.. c:function:: void ls_attributes_srlg_del(struct ls_attributes *attr) + + Remove SRLGs attribute if defined. Data structure is freed. + +.. c:function:: int ls_node_same(struct ls_node *n1, struct ls_node *n2) +.. c:function:: int ls_attributes_same(struct ls_attributes *a1, struct ls_attributes *a2) +.. c:function:: int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix*p2) + + Check, respectively if two Link State Nodes, Attributes or Prefix are equal. + Note that these routines have the same return value sense as '==' (which is + different from a comparison). -The low level API doesn't provide any particular functions for the Link State -Prefix structure as this latter is simpler to manipulate. Link State TED -------------- This is the high level API that provides functions to create, update, delete a -Link State Database to from a Traffic Engineering Database (TED). +Link State Database to build a Traffic Engineering Database (TED). Data Structures ^^^^^^^^^^^^^^^ @@ -143,35 +164,143 @@ A unique Key is used to identify both Vertices and Edges within the Graph. .. c:type:: struct ls_prefix .. c:type:: struct ls_ted +TED stores Vertex, Edge and Subnet elements with a RB Tree structure. +The Vertex key corresponds to the Router ID for OSPF and ISO System ID for +IS-IS. The Edge key corresponds to the IPv4 address, the lowest 64 bits of +the IPv6 address or the combination of the local & remote ID of the interface. +The Subnet key corresponds to the Prefix address (v4 or v6). -Functions -^^^^^^^^^ +An additional status for Vertex, Edge and Subnet allows to determine the state +of the element in the TED: UNSET, NEW, UPDATE, DELETE, SYNC, ORPHAN. Normal +state is SYNC. NEW, UPDATE and DELETE are temporary state when element is +processed. UNSET is normally never used and ORPHAN serves to identify elements +that must be remove when TED is cleaning. + +Vertex, Edges and Subnets management functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. c:function:: struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node) +.. c:function:: struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes) +.. c:function:: struct ls_subnet *ls_subnet_add(struct ls_ted *ted, struct ls_prefix *pref) + + Add, respectively new Vertex, Edge or Subnet to the Link State Datebase. + Vertex, Edge or Subnet are created from, respectively the Link State Node, + Attribute or Prefix structure. Data structure are dynamically allocated. + .. c:function:: struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node) +.. c:function:: struct ls_edge *ls_edge_update(struct ls_ted *ted, struct ls_attributes *attributes) +.. c:function:: struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref) + + Update, respectively Vertex, Edge or Subnet with, respectively the Link + State Node, Attribute or Prefix. A new data structure is created if no one + corresponds to the Link State Node, Attribute or Prefix. If element already + exists in the TED, its associated Link State information is replaced by the + new one if there are different and the old associated Link State information + is deleted and memory freed. + .. c:function:: void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex) +.. c:function:: void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex) +.. c:function:: void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge) +.. c:function:: void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge) +.. c:function:: void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet) +.. c:function:: void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet) + + Delete, respectively Link State Vertex, Edge or Subnet. Data structure are + freed but not the associated Link State information with the simple `_del()` + form of the function while the `_del_all()` version freed also associated + Link State information. TED is not modified if Vertex, Edge or Subnet is + NULL or not found in the Data Base. Note that references between Vertices, + Edges and Subnets are removed first. + .. c:function:: struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key) .. c:function:: struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted, struct ls_node_id id) -.. c:function:: int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2) -.. c:function:: struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes) -.. c:function:: struct ls_edge *ls_edge_update(struct ls_ted *ted, struct ls_attributes *attributes) -.. c:function:: void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge) + Find Vertex in the TED by its unique key or its Link State Node ID. + Return Vertex if found, NULL otherwise. + .. c:function:: struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key) .. c:function:: struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted, struct ls_attributes *attributes); .. c:function:: struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted, struct ls_attributes *attributes); -.. c:function:: struct ls_subnet *ls_subnet_add(struct ls_ted *ted, struct ls_prefix *pref) -.. c:function:: void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet) + Find Edge in the Link State Data Base by its key, source or distination + (local IPv4 or IPv6 address or local ID) informations of the Link State + Attributes. Return Edge if found, NULL otherwise. + .. c:function:: struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix) + Find Subnet in the Link State Data Base by its key, i.e. the associated + prefix. Return Subnet if found, NULL otherwise. + +.. c:function:: int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2) +.. c:function:: int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2) +.. c:function:: int ls_subnet_same(struct ls_subnet *s1, struct ls_subnet *s2) + + Check, respectively if two Vertices, Edges or Subnets are equal. + Note that these routines has the same return value sense as '==' + (which is different from a comparison). + + +TED management functions +^^^^^^^^^^^^^^^^^^^^^^^^ + +Some helpers functions have been also provided to ease TED management: + .. c:function:: struct ls_ted *ls_ted_new(const uint32_t key, char *name, uint32_t asn) + + Create a new Link State Data Base. Key must be different from 0. + Name could be NULL and AS number equal to 0 if unknown. + .. c:function:: void ls_ted_del(struct ls_ted *ted) +.. c:function:: void ls_ted_del_all(struct ls_ted *ted) + + Delete existing Link State Data Base. Vertices, Edges, and Subnets are not + removed with ls_ted_del() function while they are with ls_ted_del_all(). + .. c:function:: void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, struct ls_edge *edge) + + Connect Source and Destination Vertices by given Edge. Only non NULL source + and destination vertices are connected. + .. c:function:: void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source) .. c:function:: void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, bool source) + + Connect / Disconnect Link State Edge to the Link State Vertex which could be + a Source (source = true) or a Destination (source = false) Vertex. + .. c:function:: void ls_disconnect_edge(struct ls_edge *edge) + Disconnect Link State Edge from both Source and Destination Vertex. + Note that Edge is not removed but its status is marked as ORPHAN. + +.. c:function:: void ls_vertex_clean(struct ls_ted *ted, struct ls_vertex *vertex, struct zclient *zclient) + + Clean Vertex structure by removing all Edges and Subnets marked as ORPHAN + from this vertex. Corresponding Link State Update message is sent if zclient + parameter is not NULL. Note that associated Link State Attribute and Prefix + are also removed and memory freed. + +.. c:function:: void ls_ted_clean(struct ls_ted *ted) + + Clean Link State Data Base by removing all Vertices, Edges and SubNets + marked as ORPHAN. Note that associated Link State Node, Attributes and + Prefix are removed too. + +.. c:function:: void ls_show_vertex(struct ls_vertex *vertex, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_edge(struct ls_edeg *edge, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_vertices(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_edges(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_subnets(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose) +.. c:function:: void ls_show_ted(struct ls_ted *ted, struct vty *vty, struct json_object *json, bool verbose) + + Respectively, show Vertex, Edge, Subnet provided as parameter, all Vertices, + all Edges, all Subnets and the whole TED if not specified. Output could be + more detailed with verbose parameter for VTY output. If both JSON and VTY + output are specified, JSON takes precedence over VTY. + +.. c:function:: void ls_dump_ted(struct ls_ted *ted) + + Dump TED information to the current logging output. Link State Messages ------------------- @@ -198,8 +327,8 @@ Figure 1 below, illustrates the ZAPI Opaque message exchange between a message sequences are as follows: - First, both *Producer* and *Consumer* must register to their respective ZAPI - Opaque Message. **Link State Sync** for the *Producer* in order to receive - Database synchronisation request from a *Consumer*. **Link State Update** for + Opaque Message: **Link State Sync** for the *Producer* in order to receive + Database synchronisation request from a *Consumer*, **Link State Update** for the *Consumer* in order to received any Link State update from a *Producer*. These register messages are stored by Zebra to determine to which daemon it should redistribute the ZAPI messages it receives. @@ -245,22 +374,22 @@ message sequences are as follows: | | Request LS Sync | v \ | Request LS Sync |<----------------------------| | |<-----------------------------| | Synchronistation - | LS DB Sync | | Phase - |----------------------------->| LS DB Sync | | + | LS DB Update | | Phase + |----------------------------->| LS DB Update | | | |---------------------------->| | - | LS DB Sync (cont'd) | | | - |----------------------------->| LS DB Sync (cont'd) | | + | LS DB Update (cont'd) | | | + |----------------------------->| LS DB Update (cont'd) | | | . |---------------------------->| | | . | . | | | . | . | | - | LS DB Sync (end) | . | | - |----------------------------->| LS DB Sync (end) | | + | LS DB Update (end) | . | | + |----------------------------->| LS DB Update (end) | | | |---------------------------->| | | | | / : : : : : : - | LS Update | | \ - |----------------------------->| LS Update | | + | LS DB Update | | \ + |----------------------------->| LS DB Update | | | |---------------------------->| Update Phase | | | | : : : / @@ -269,7 +398,7 @@ message sequences are as follows: | | Unregister LS Update | | | |<----------------------------| Deregister Phase | | | | - | LS Update | | | + | LS DB Update | | | |----------------------------->| | | | | | / | | | @@ -305,10 +434,65 @@ Opaque Link State type at once. Functions ^^^^^^^^^ +.. c:function:: int ls_register(struct zclient *zclient, bool server) +.. c:function:: int ls_unregister(struct zclient *zclient, bool server) + + Register / Unregister daemon to received ZAPI Link State Opaque messages. + Server must be set to true for *Producer* and to false for *Consumer*. + +.. c:function:: int ls_request_sync(struct zclient *zclient) + + Request initial Synchronisation to collect the whole Link State Database. + .. c:function:: struct ls_message *ls_parse_msg(struct stream *s) + + Parse Link State Message from stream. Used this function once receiving a + new ZAPI Opaque message of type Link State. + +.. c:function:: void ls_delete_msg(struct ls_message *msg) + + Delete existing message. Data structure is freed. + .. c:function:: int ls_send_msg(struct zclient *zclient, struct ls_message *msg, struct zapi_opaque_reg_info *dst) + + Send Link State Message as new ZAPI Opaque message of type Link State. + If destination is not NULL, message is sent as Unicast otherwise it is + broadcast to all registered daemon. + .. c:function:: struct ls_message *ls_vertex2msg(struct ls_message *msg, struct ls_vertex *vertex) .. c:function:: struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge) .. c:function:: struct ls_message *ls_subnet2msg(struct ls_message *msg, struct ls_subnet *subnet) + + Create respectively a new Link State Message from a Link State Vertex, Edge + or Subnet. If Link State Message is NULL, a new data structure is + dynamically allocated. Note that the Vertex, Edge and Subnet status is used + to determine the corresponding Link State Message event: ADD, UPDATE, + DELETE, SYNC. + +.. c:function:: int ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg) +.. c:function:: int ls_msg2edge(struct ls_ted *ted, struct ls_message *msg) +.. c:function:: int ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg) + + Convert Link State Message respectively in Vertex, Edge or Subnet and + update the Link State Database accordingly to the message event: SYNC, ADD, + UPDATE or DELETE. + +.. c:function:: struct ls_element *ls_msg2ted(struct ls_ted *ted, struct ls_message *msg, bool delete) +.. c:function:: struct ls_element *ls_stream2ted(struct ls_ted *ted, struct ls_message *msg, bool delete) + + Convert Link State Message or Stream Buffer in a Link State element (Vertex, + Edge or Subnet) and update the Link State Database accordingly to the + message event: SYNC, ADD, UPDATE or DELETE. The function return the generic + structure ls_element that point to the Vertex, Edge or Subnet which has been + added, updated or synchronous in the database. Note that the delete boolean + parameter governs the action for the DELETE action: true, Link State Element + is removed from the database and NULL is return. If set to false, database + is not updated and the function sets the Link State Element status to + Delete and return the element for futur deletion by the calling function. + .. c:function:: int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, struct zapi_opaque_reg_info *dst) + Send all the content of the Link State Data Base to the given destination. + Link State content is sent is this order: Vertices, Edges then Subnet. + This function must be used when a daemon request a Link State Data Base + Synchronization. diff --git a/lib/link_state.c b/lib/link_state.c index 7f0d2a1245..8606f8eb09 100644 --- a/lib/link_state.c +++ b/lib/link_state.c @@ -33,6 +33,9 @@ #include "vty.h" #include "zclient.h" #include "stream.h" +#include "sbuf.h" +#include "printfrr.h" +#include #include "link_state.h" /* Link State Memory allocation */ @@ -46,7 +49,7 @@ struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid, { struct ls_node *new; - if (adv.origin == NONE) + if (adv.origin == UNKNOWN) return NULL; new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node)); @@ -70,6 +73,9 @@ struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid, void ls_node_del(struct ls_node *node) { + if (!node) + return; + XFREE(MTYPE_LS_DB, node); node = NULL; } @@ -111,7 +117,7 @@ struct ls_attributes *ls_attributes_new(struct ls_node_id adv, { struct ls_attributes *new; - if (adv.origin == NONE) + if (adv.origin == UNKNOWN) return NULL; new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes)); @@ -139,7 +145,7 @@ struct ls_attributes *ls_attributes_new(struct ls_node_id adv, return new; } -void ls_attributes_del(struct ls_attributes *attr) +void ls_attributes_srlg_del(struct ls_attributes *attr) { if (!attr) return; @@ -147,6 +153,18 @@ void ls_attributes_del(struct ls_attributes *attr) if (attr->srlgs) XFREE(MTYPE_LS_DB, attr->srlgs); + attr->srlgs = NULL; + attr->srlg_len = 0; + UNSET_FLAG(attr->flags, LS_ATTR_SRLG); +} + +void ls_attributes_del(struct ls_attributes *attr) +{ + if (!attr) + return; + + ls_attributes_srlg_del(attr); + XFREE(MTYPE_LS_DB, attr); attr = NULL; } @@ -179,75 +197,155 @@ int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2) } /** - * Link State Vertices management functions + * Link State prefix management functions */ -struct ls_vertex *ls_vertex_new(struct ls_node *node) +struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p) { - struct ls_vertex *new; + struct ls_prefix *new; - if (node == NULL) + if (adv.origin == UNKNOWN) return NULL; - new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_vertex)); - new->node = node; - new->incoming_edges = list_new(); - new->outgoing_edges = list_new(); - new->prefixes = list_new(); + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes)); + new->adv = adv; + new->pref = p; return new; } -void ls_vertex_del(struct ls_vertex *vertex) +void ls_prefix_del(struct ls_prefix *pref) { - if (vertex == NULL) + if (!pref) return; - list_delete_all_node(vertex->incoming_edges); - list_delete_all_node(vertex->outgoing_edges); - list_delete_all_node(vertex->prefixes); - XFREE(MTYPE_LS_DB, vertex); - vertex = NULL; + XFREE(MTYPE_LS_DB, pref); + pref = NULL; } +int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2) +{ + if ((p1 && !p2) || (!p1 && p2)) + return 0; + + if (p1 == p2) + return 1; + + if (p1->flags != p2->flags) + return 0; + + if (p1->adv.origin != p2->adv.origin) + return 0; + + if (!memcmp(&p1->adv.id, &p2->adv.id, sizeof(struct ls_node_id))) + return 0; + + /* Do we need to test individually each field, instead performing a + * global memcmp? There is a risk that an old value that is bit masked + * i.e. corresponding flag = 0, will result into a false negative + */ + if (!memcmp(p1, p2, sizeof(struct ls_prefix))) + return 0; + else + return 1; +} + +/** + * Link State Vertices management functions + */ struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node) { struct ls_vertex *new; + uint64_t key = 0; if ((ted == NULL) || (node == NULL)) return NULL; - new = ls_vertex_new(node); - if (!new) - return NULL; - /* set Key as the IPv4/Ipv6 Router ID or ISO System ID */ switch (node->adv.origin) { case OSPFv2: case STATIC: case DIRECT: - memcpy(&new->key, &node->adv.id.ip.addr, IPV4_MAX_BYTELEN); + key = ((uint64_t)ntohl(node->adv.id.ip.addr.s_addr)) + & 0xffffffff; break; case ISIS_L1: case ISIS_L2: - memcpy(&new->key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN); + memcpy(&key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN); break; default: - new->key = 0; + key = 0; break; } - /* Remove Vertex if key is not set */ - if (new->key == 0) { - ls_vertex_del(new); + /* Check that key is valid */ + if (key == 0) return NULL; - } - /* Add Vertex to TED */ + /* Create Vertex and add it to the TED */ + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_vertex)); + if (!new) + return NULL; + + new->key = key; + new->node = node; + new->status = NEW; + new->type = VERTEX; + new->incoming_edges = list_new(); + new->incoming_edges->cmp = (int (*)(void *, void *))edge_cmp; + new->outgoing_edges = list_new(); + new->outgoing_edges->cmp = (int (*)(void *, void *))edge_cmp; + new->prefixes = list_new(); + new->prefixes->cmp = (int (*)(void *, void *))subnet_cmp; vertices_add(&ted->vertices, new); return new; } +void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex) +{ + struct listnode *node, *nnode; + struct ls_edge *edge; + struct ls_subnet *subnet; + + if (!ted || !vertex) + return; + + /* Remove outgoing Edges and list */ + for (ALL_LIST_ELEMENTS(vertex->outgoing_edges, node, nnode, edge)) + ls_edge_del_all(ted, edge); + list_delete(&vertex->outgoing_edges); + + /* Disconnect incoming Edges and remove list */ + for (ALL_LIST_ELEMENTS(vertex->incoming_edges, node, nnode, edge)) { + ls_disconnect(vertex, edge, false); + if (edge->source == NULL) + ls_edge_del_all(ted, edge); + } + list_delete(&vertex->incoming_edges); + + /* Remove subnet and list */ + for (ALL_LIST_ELEMENTS(vertex->prefixes, node, nnode, subnet)) + ls_subnet_del_all(ted, subnet); + list_delete(&vertex->prefixes); + + /* Then remove Vertex from Link State Data Base and free memory */ + vertices_del(&ted->vertices, vertex); + XFREE(MTYPE_LS_DB, vertex); + vertex = NULL; +} + +void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex) +{ + if (!ted || !vertex) + return; + + /* First remove associated Link State Node */ + ls_node_del(vertex->node); + + /* Then, Vertex itself */ + ls_vertex_del(ted, vertex); +} + struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node) { struct ls_vertex *old; @@ -261,49 +359,46 @@ struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node) ls_node_del(old->node); old->node = node; } + old->status = UPDATE; return old; } return ls_vertex_add(ted, node); } -void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex) -{ - vertices_del(&ted->vertices, vertex); - ls_vertex_del(vertex); -} - struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key) { - struct ls_vertex node = {}; + struct ls_vertex vertex = {}; if (key == 0) return NULL; - node.key = key; - return vertices_find(&ted->vertices, &node); + vertex.key = key; + return vertices_find(&ted->vertices, &vertex); } struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted, struct ls_node_id nid) { - struct ls_vertex node = {}; + struct ls_vertex vertex = {}; + vertex.key = 0; switch (nid.origin) { case OSPFv2: case STATIC: case DIRECT: - memcpy(&node.key, &nid.id.ip.addr, IPV4_MAX_BYTELEN); + vertex.key = + ((uint64_t)ntohl(nid.id.ip.addr.s_addr)) & 0xffffffff; break; case ISIS_L1: case ISIS_L2: - memcpy(&node.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN); + memcpy(&vertex.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN); break; default: return NULL; } - return vertices_find(&ted->vertices, &node); + return vertices_find(&ted->vertices, &vertex); } int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2) @@ -323,6 +418,49 @@ int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2) return ls_node_same(v1->node, v2->node); } +void ls_vertex_clean(struct ls_ted *ted, struct ls_vertex *vertex, + struct zclient *zclient) +{ + struct listnode *node, *nnode; + struct ls_edge *edge; + struct ls_subnet *subnet; + struct ls_message msg; + + /* Remove Orphan Edge ... */ + for (ALL_LIST_ELEMENTS(vertex->outgoing_edges, node, nnode, edge)) { + if (edge->status == ORPHAN) { + if (zclient) { + edge->status = DELETE; + ls_edge2msg(&msg, edge); + ls_send_msg(zclient, &msg, NULL); + } + ls_edge_del_all(ted, edge); + } + } + for (ALL_LIST_ELEMENTS(vertex->incoming_edges, node, nnode, edge)) { + if (edge->status == ORPHAN) { + if (zclient) { + edge->status = DELETE; + ls_edge2msg(&msg, edge); + ls_send_msg(zclient, &msg, NULL); + } + ls_edge_del_all(ted, edge); + } + } + + /* ... and Subnet from the Vertex */ + for (ALL_LIST_ELEMENTS(vertex->prefixes, node, nnode, subnet)) { + if (subnet->status == ORPHAN) { + if (zclient) { + subnet->status = DELETE; + ls_subnet2msg(&msg, subnet); + ls_send_msg(zclient, &msg, NULL); + } + ls_subnet_del_all(ted, subnet); + } + } +} + /** * Link State Edges management functions */ @@ -354,18 +492,18 @@ static void ls_edge_connect_to(struct ls_ted *ted, struct ls_edge *edge) vertex = ls_vertex_add(ted, node); } /* and attach the edge as source to the vertex */ - listnode_add(vertex->outgoing_edges, edge); + listnode_add_sort_nodup(vertex->outgoing_edges, edge); edge->source = vertex; /* Then search if there is a reverse Edge */ dst = ls_find_edge_by_destination(ted, edge->attributes); /* attach the destination edge to the vertex */ if (dst) { - listnode_add(vertex->incoming_edges, dst); + listnode_add_sort_nodup(vertex->incoming_edges, dst); dst->destination = vertex; /* and destination vertex to this edge */ vertex = dst->source; - listnode_add(vertex->incoming_edges, edge); + listnode_add_sort_nodup(vertex->incoming_edges, edge); edge->destination = vertex; } } @@ -374,37 +512,43 @@ struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes) { struct ls_edge *new; + uint64_t key = 0; if (attributes == NULL) return NULL; - new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge)); - new->attributes = attributes; /* Key is the IPv4 local address */ if (!IPV4_NET0(attributes->standard.local.s_addr)) - new->key = ((uint64_t)attributes->standard.local.s_addr) - & 0xffffffff; + key = ((uint64_t)ntohl(attributes->standard.local.s_addr)) + & 0xffffffff; /* or the IPv6 local address if IPv4 is not defined */ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6)) - new->key = (uint64_t)(attributes->standard.local6.s6_addr32[0] - & 0xffffffff) - | ((uint64_t)attributes->standard.local6.s6_addr32[1] - << 32); + key = (uint64_t)(attributes->standard.local6.s6_addr32[0] + & 0xffffffff) + | ((uint64_t)attributes->standard.local6.s6_addr32[1] + << 32); /* of local identifier if no IP addresses are defined */ else if (attributes->standard.local_id != 0) - new->key = (uint64_t)( + key = (uint64_t)( (attributes->standard.local_id & 0xffffffff) | ((uint64_t)attributes->standard.remote_id << 32)); - /* Remove Edge if key is not known */ - if (new->key == 0) { - XFREE(MTYPE_LS_DB, new); + /* Check that key is valid */ + if (key == 0) return NULL; - } + /* Create Edge and add it to the TED */ + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge)); + if (!new) + return NULL; + + new->attributes = attributes; + new->key = key; + new->status = NEW; + new->type = EDGE; edges_add(&ted->edges, new); - /* Finally, connect edge to vertices */ + /* Finally, connect Edge to Vertices */ ls_edge_connect_to(ted, new); return new; @@ -429,9 +573,10 @@ struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted, if (attributes == NULL) return NULL; + edge.key = 0; /* Key is the IPv4 local address */ if (!IPV4_NET0(attributes->standard.local.s_addr)) - edge.key = ((uint64_t)attributes->standard.local.s_addr) + edge.key = ((uint64_t)ntohl(attributes->standard.local.s_addr)) & 0xffffffff; /* or the IPv6 local address if IPv4 is not defined */ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6)) @@ -459,18 +604,19 @@ struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted, if (attributes == NULL) return NULL; - /* Key is the IPv4 local address */ + edge.key = 0; + /* Key is the IPv4 remote address */ if (!IPV4_NET0(attributes->standard.remote.s_addr)) - edge.key = ((uint64_t)attributes->standard.remote.s_addr) + edge.key = ((uint64_t)ntohl(attributes->standard.remote.s_addr)) & 0xffffffff; - /* or the IPv6 local address if IPv4 is not defined */ + /* or the IPv6 remote address if IPv4 is not defined */ else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.remote6)) edge.key = (uint64_t)(attributes->standard.remote6.s6_addr32[0] & 0xffffffff) | ((uint64_t)attributes->standard.remote6.s6_addr32[1] << 32); - /* of local identifier if no IP addresses are defined */ + /* of remote identifier if no IP addresses are defined */ else if (attributes->standard.remote_id != 0) edge.key = (uint64_t)( (attributes->standard.remote_id & 0xffffffff) @@ -498,6 +644,7 @@ struct ls_edge *ls_edge_update(struct ls_ted *ted, ls_attributes_del(old->attributes); old->attributes = attributes; } + old->status = UPDATE; return old; } @@ -505,15 +652,46 @@ struct ls_edge *ls_edge_update(struct ls_ted *ted, return ls_edge_add(ted, attributes); } +int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2) +{ + if ((e1 && !e2) || (!e1 && e2)) + return 0; + + if (!e1 && !e2) + return 1; + + if (e1->key != e2->key) + return 0; + + if (e1->attributes == e2->attributes) + return 1; + + return ls_attributes_same(e1->attributes, e2->attributes); +} + void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge) { - /* Fist disconnect Edge */ + if (!ted || !edge) + return; + + /* Fist disconnect Edge from Vertices */ ls_disconnect_edge(edge); /* Then remove it from the Data Base */ edges_del(&ted->edges, edge); XFREE(MTYPE_LS_DB, edge); } +void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge) +{ + if (!ted || !edge) + return; + + /* Remove associated Link State Attributes */ + ls_attributes_del(edge->attributes); + /* Then Edge itself */ + ls_edge_del(ted, edge); +} + /** * Link State Subnet Management functions. */ @@ -531,6 +709,8 @@ struct ls_subnet *ls_subnet_add(struct ls_ted *ted, new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_subnet)); new->ls_pref = ls_pref; new->key = ls_pref->pref; + new->status = NEW; + new->type = SUBNET; /* Find Vertex */ vertex = ls_find_vertex_by_id(ted, ls_pref->adv); @@ -541,19 +721,73 @@ struct ls_subnet *ls_subnet_add(struct ls_ted *ted, } /* And attach the subnet to the corresponding Vertex */ new->vertex = vertex; - listnode_add(vertex->prefixes, new); + listnode_add_sort_nodup(vertex->prefixes, new); subnets_add(&ted->subnets, new); return new; } +struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref) +{ + struct ls_subnet *old; + + if (pref == NULL) + return NULL; + + old = ls_find_subnet(ted, pref->pref); + if (old) { + if (!ls_prefix_same(old->ls_pref, pref)) { + ls_prefix_del(old->ls_pref); + old->ls_pref = pref; + } + old->status = UPDATE; + return old; + } + + return ls_subnet_add(ted, pref); +} + +int ls_subnet_same(struct ls_subnet *s1, struct ls_subnet *s2) +{ + if ((s1 && !s2) || (!s1 && s2)) + return 0; + + if (!s1 && !s2) + return 1; + + if (!prefix_same(&s1->key, &s2->key)) + return 0; + + if (s1->ls_pref == s2->ls_pref) + return 1; + + return ls_prefix_same(s1->ls_pref, s2->ls_pref); +} + void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet) { + if (!ted || !subnet) + return; + + /* First, disconnect Subnet from associated Vertex */ + listnode_delete(subnet->vertex->prefixes, subnet); + /* Then delete Subnet */ subnets_del(&ted->subnets, subnet); XFREE(MTYPE_LS_DB, subnet); } +void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet) +{ + if (!ted || !subnet) + return; + + /* First, remove associated Link State Subnet */ + ls_prefix_del(subnet->ls_pref); + /* Then, delete Subnet itself */ + ls_subnet_del(ted, subnet); +} + struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix) { struct ls_subnet subnet = {}; @@ -592,6 +826,11 @@ void ls_ted_del(struct ls_ted *ted) if (ted == NULL) return; + /* Check that TED is empty */ + if (vertices_count(&ted->vertices) || edges_count(&ted->edges) + || subnets_count(&ted->subnets)) + return; + /* Release RB Tree */ vertices_fini(&ted->vertices); edges_fini(&ted->edges); @@ -601,16 +840,63 @@ void ls_ted_del(struct ls_ted *ted) ted = NULL; } +void ls_ted_del_all(struct ls_ted *ted) +{ + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + + if (ted == NULL) + return; + + /* First remove Vertices, Edges and Subnets and associated Link State */ + frr_each (vertices, &ted->vertices, vertex) + ls_vertex_del_all(ted, vertex); + frr_each (edges, &ted->edges, edge) + ls_edge_del_all(ted, edge); + frr_each (subnets, &ted->subnets, subnet) + ls_subnet_del_all(ted, subnet); + + /* then remove TED itself */ + ls_ted_del(ted); +} + +void ls_ted_clean(struct ls_ted *ted) +{ + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + + if (ted == NULL) + return; + + /* First, start with Vertices */ + frr_each (vertices, &ted->vertices, vertex) + if (vertex->status == ORPHAN) + ls_vertex_del_all(ted, vertex); + + /* Then Edges */ + frr_each (edges, &ted->edges, edge) + if (edge->status == ORPHAN) + ls_edge_del_all(ted, edge); + + /* and Subnets */ + frr_each (subnets, &ted->subnets, subnet) + if (subnet->status == ORPHAN) + ls_subnet_del_all(ted, subnet); + +} + void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source) { if (vertex == NULL || edge == NULL) return; if (source) { - listnode_add(vertex->outgoing_edges, edge); + listnode_add_sort_nodup(vertex->outgoing_edges, edge); edge->source = vertex; } else { - listnode_add(vertex->incoming_edges, edge); + listnode_add_sort_nodup(vertex->incoming_edges, edge); edge->destination = vertex; } } @@ -640,11 +926,10 @@ void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, edge->destination = dst; if (src != NULL) - listnode_add(src->outgoing_edges, edge); + listnode_add_sort_nodup(src->outgoing_edges, edge); if (dst != NULL) - listnode_add(dst->incoming_edges, edge); - + listnode_add_sort_nodup(dst->incoming_edges, edge); } void ls_disconnect_edge(struct ls_edge *edge) @@ -654,12 +939,68 @@ void ls_disconnect_edge(struct ls_edge *edge) ls_disconnect(edge->source, edge, true); ls_disconnect(edge->destination, edge, false); + + /* Mark this Edge as ORPHAN for future cleanup */ + edge->status = ORPHAN; } /** * Link State Message management functions */ +int ls_register(struct zclient *zclient, bool server) +{ + int rc; + + if (server) + rc = zclient_register_opaque(zclient, LINK_STATE_SYNC); + else + rc = zclient_register_opaque(zclient, LINK_STATE_UPDATE); + + return rc; +} + +int ls_unregister(struct zclient *zclient, bool server) +{ + int rc; + + if (server) + rc = zclient_unregister_opaque(zclient, LINK_STATE_SYNC); + else + rc = zclient_unregister_opaque(zclient, LINK_STATE_UPDATE); + + return rc; +} + +int ls_request_sync(struct zclient *zclient) +{ + struct stream *s; + uint16_t flags = 0; + + /* Check buffer size */ + if (STREAM_SIZE(zclient->obuf) + < (ZEBRA_HEADER_SIZE + 3 * sizeof(uint32_t))) + return -1; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); + + /* Set type and flags */ + stream_putl(s, LINK_STATE_SYNC); + stream_putw(s, flags); + /* Send destination client info */ + stream_putc(s, zclient->redist_default); + stream_putw(s, zclient->instance); + stream_putl(s, zclient->session_id); + + /* Put length into the header at the start of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + static struct ls_node *ls_parse_node(struct stream *s) { struct ls_node *node; @@ -723,7 +1064,7 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s) STREAM_GET(attr->name, s, len); } if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC)) - STREAM_GETL(s, attr->standard.metric); + STREAM_GETL(s, attr->metric); if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) STREAM_GETL(s, attr->standard.te_metric); if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) @@ -804,7 +1145,7 @@ static struct ls_attributes *ls_parse_attributes(struct stream *s) stream_failure: zlog_err("LS(%s): Could not parse Link State Attributes. Abort!", __func__); - /* Clean memeory allocation */ + /* Clean memory allocation */ if (attr->srlgs != NULL) XFREE(MTYPE_LS_DB, attr->srlgs); XFREE(MTYPE_LS_DB, attr); @@ -947,7 +1288,7 @@ static int ls_format_attributes(struct stream *s, struct ls_attributes *attr) stream_putc(s, '\0'); } if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC)) - stream_putl(s, attr->standard.metric); + stream_putl(s, attr->metric); if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) stream_putl(s, attr->standard.te_metric); if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) @@ -1084,6 +1425,10 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg, struct stream *s; uint16_t flags = 0; + /* Check if we have a valid message */ + if (msg->event == LS_MSG_EVENT_UNDEF) + return -1; + /* Check buffer size */ if (STREAM_SIZE(zclient->obuf) < (ZEBRA_HEADER_SIZE + sizeof(uint32_t) + sizeof(msg))) @@ -1094,7 +1439,7 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg, zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); - /* Send sub-type, flags and destination for unicast message */ + /* Set sub-type, flags and destination for unicast message */ stream_putl(s, LINK_STATE_UPDATE); if (dst != NULL) { SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST); @@ -1103,8 +1448,9 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg, stream_putc(s, dst->proto); stream_putw(s, dst->instance); stream_putl(s, dst->session_id); - } else + } else { stream_putw(s, flags); + } /* Format Link State message */ if (ls_format_msg(s, msg) < 0) { @@ -1128,8 +1474,25 @@ struct ls_message *ls_vertex2msg(struct ls_message *msg, memset(msg, 0, sizeof(*msg)); msg->type = LS_MSG_TYPE_NODE; + switch (vertex->status) { + case NEW: + msg->event = LS_MSG_EVENT_ADD; + break; + case UPDATE: + msg->event = LS_MSG_EVENT_UPDATE; + break; + case DELETE: + msg->event = LS_MSG_EVENT_DELETE; + break; + case SYNC: + msg->event = LS_MSG_EVENT_SYNC; + break; + default: + msg->event = LS_MSG_EVENT_UNDEF; + break; + } msg->data.node = vertex->node; - msg->remote_id.origin = NONE; + msg->remote_id.origin = UNKNOWN; return msg; } @@ -1143,11 +1506,28 @@ struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge) memset(msg, 0, sizeof(*msg)); msg->type = LS_MSG_TYPE_ATTRIBUTES; + switch (edge->status) { + case NEW: + msg->event = LS_MSG_EVENT_ADD; + break; + case UPDATE: + msg->event = LS_MSG_EVENT_UPDATE; + break; + case DELETE: + msg->event = LS_MSG_EVENT_DELETE; + break; + case SYNC: + msg->event = LS_MSG_EVENT_SYNC; + break; + default: + msg->event = LS_MSG_EVENT_UNDEF; + break; + } msg->data.attr = edge->attributes; if (edge->destination != NULL) msg->remote_id = edge->destination->node->adv; else - msg->remote_id.origin = NONE; + msg->remote_id.origin = UNKNOWN; return msg; } @@ -1162,34 +1542,189 @@ struct ls_message *ls_subnet2msg(struct ls_message *msg, memset(msg, 0, sizeof(*msg)); msg->type = LS_MSG_TYPE_PREFIX; + switch (subnet->status) { + case NEW: + msg->event = LS_MSG_EVENT_ADD; + break; + case UPDATE: + msg->event = LS_MSG_EVENT_UPDATE; + break; + case DELETE: + msg->event = LS_MSG_EVENT_DELETE; + break; + case SYNC: + msg->event = LS_MSG_EVENT_SYNC; + break; + default: + msg->event = LS_MSG_EVENT_UNDEF; + break; + } msg->data.prefix = subnet->ls_pref; - msg->remote_id.origin = NONE; + msg->remote_id.origin = UNKNOWN; return msg; } +struct ls_vertex *ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg, + bool delete) +{ + struct ls_node *node = (struct ls_node *)msg->data.node; + struct ls_vertex *vertex = NULL; + + switch (msg->event) { + case LS_MSG_EVENT_SYNC: + vertex = ls_vertex_add(ted, node); + if (vertex) + vertex->status = SYNC; + break; + case LS_MSG_EVENT_ADD: + vertex = ls_vertex_add(ted, node); + if (vertex) + vertex->status = NEW; + break; + case LS_MSG_EVENT_UPDATE: + vertex = ls_vertex_update(ted, node); + if (vertex) + vertex->status = UPDATE; + break; + case LS_MSG_EVENT_DELETE: + vertex = ls_find_vertex_by_id(ted, node->adv); + if (vertex) { + if (delete) + ls_vertex_del_all(ted, vertex); + else + vertex->status = DELETE; + } + break; + default: + vertex = NULL; + break; + } + + return vertex; +} + +struct ls_edge *ls_msg2edge(struct ls_ted *ted, struct ls_message *msg, + bool delete) +{ + struct ls_attributes *attr = (struct ls_attributes *)msg->data.attr; + struct ls_edge *edge = NULL; + + switch (msg->event) { + case LS_MSG_EVENT_SYNC: + edge = ls_edge_add(ted, attr); + if (edge) + edge->status = SYNC; + break; + case LS_MSG_EVENT_ADD: + edge = ls_edge_add(ted, attr); + if (edge) + edge->status = NEW; + break; + case LS_MSG_EVENT_UPDATE: + edge = ls_edge_update(ted, attr); + if (edge) + edge->status = UPDATE; + break; + case LS_MSG_EVENT_DELETE: + edge = ls_find_edge_by_source(ted, attr); + if (edge) { + if (delete) + ls_edge_del_all(ted, edge); + else + edge->status = DELETE; + } + break; + default: + edge = NULL; + break; + } + + return edge; +} + +struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg, + bool delete) +{ + struct ls_prefix *pref = (struct ls_prefix *)msg->data.prefix; + struct ls_subnet *subnet = NULL; + + switch (msg->event) { + case LS_MSG_EVENT_SYNC: + subnet = ls_subnet_add(ted, pref); + if (subnet) + subnet->status = SYNC; + break; + case LS_MSG_EVENT_ADD: + subnet = ls_subnet_add(ted, pref); + if (subnet) + subnet->status = NEW; + break; + case LS_MSG_EVENT_UPDATE: + subnet = ls_subnet_update(ted, pref); + if (subnet) + subnet->status = UPDATE; + break; + case LS_MSG_EVENT_DELETE: + subnet = ls_find_subnet(ted, pref->pref); + if (subnet) { + if (delete) + ls_subnet_del_all(ted, subnet); + else + subnet->status = DELETE; + } + break; + default: + subnet = NULL; + break; + } + + return subnet; +} + +struct ls_element *ls_msg2ted(struct ls_ted *ted, struct ls_message *msg, + bool delete) +{ + struct ls_element *lse = NULL; + + switch (msg->type) { + case LS_MSG_TYPE_NODE: + lse = (struct ls_element *)ls_msg2vertex(ted, msg, delete); + break; + case LS_MSG_TYPE_ATTRIBUTES: + lse = (struct ls_element *)ls_msg2edge(ted, msg, delete); + break; + case LS_MSG_TYPE_PREFIX: + lse = (struct ls_element *)ls_msg2subnet(ted, msg, delete); + break; + default: + lse = NULL; + break; + } + + return lse; +} + +struct ls_element *ls_stream2ted(struct ls_ted *ted, struct stream *s, + bool delete) +{ + struct ls_message *msg; + struct ls_element *lse = NULL; + + msg = ls_parse_msg(s); + if (msg) { + lse = ls_msg2ted(ted, msg, delete); + ls_delete_msg(msg); + } + + return lse; +} + void ls_delete_msg(struct ls_message *msg) { if (msg == NULL) return; - 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); } @@ -1201,9 +1736,6 @@ int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, struct ls_subnet *subnet; struct ls_message msg; - /* Prepare message */ - msg.event = LS_MSG_EVENT_SYNC; - /* Loop TED, start sending Node, then Attributes and finally Prefix */ frr_each(vertices, &ted->vertices, vertex) { ls_vertex2msg(&msg, vertex); @@ -1220,33 +1752,696 @@ int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, return 0; } +/** + * Link State Show functions + */ +static const char *const origin2txt[] = { + "Unknown", + "ISIS_L1", + "ISIS_L2", + "OSPFv2", + "Direct", + "Static" +}; + +static const char *const type2txt[] = { + "Unknown", + "Standard", + "ABR", + "ASBR", + "Remote ASBR", + "Pseudo" +}; + +static const char *const status2txt[] = { + "Unknown", + "New", + "Update", + "Delete", + "Sync", + "Orphan" +}; + +static const char *ls_node_id_to_text(struct ls_node_id lnid, char *str, + size_t size) +{ + if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2) { + uint8_t *id; + + id = lnid.id.iso.sys_id; + snprintfrr(str, size, "%02x%02x.%02x%02x.%02x%02x", id[0], + id[1], id[2], id[3], id[4], id[5]); + } else + snprintfrr(str, size, "%pI4", &lnid.id.ip.addr); + + return str; +} + +static void ls_show_vertex_vty(struct ls_vertex *vertex, struct vty *vty, + bool verbose) +{ + struct listnode *node; + struct ls_node *lsn; + struct ls_edge *edge; + struct ls_subnet *subnet; + struct sbuf sbuf; + uint32_t upper; + + /* Sanity Check */ + if (!vertex) + return; + + lsn = vertex->node; + + sbuf_init(&sbuf, NULL, 0); + + sbuf_push(&sbuf, 2, "Vertex (%" PRIu64 "): %s", vertex->key, lsn->name); + sbuf_push(&sbuf, 0, "\tRouter Id: %pI4", &lsn->router_id); + sbuf_push(&sbuf, 0, "\tOrigin: %s", origin2txt[lsn->adv.origin]); + sbuf_push(&sbuf, 0, "\tStatus: %s\n", status2txt[vertex->status]); + if (!verbose) { + sbuf_push( + &sbuf, 0, + "\t%d Outgoing Edges, %d Incoming Edges, %d Subnets\n", + listcount(vertex->outgoing_edges), + listcount(vertex->incoming_edges), + listcount(vertex->prefixes)); + goto end; + } + + if (CHECK_FLAG(lsn->flags, LS_NODE_TYPE)) + sbuf_push(&sbuf, 4, "Type: %s\n", type2txt[lsn->type]); + if (CHECK_FLAG(lsn->flags, LS_NODE_AS_NUMBER)) + sbuf_push(&sbuf, 4, "AS number: %u\n", lsn->as_number); + if (CHECK_FLAG(lsn->flags, LS_NODE_SR)) { + sbuf_push(&sbuf, 4, "Segment Routing Capabilities:\n"); + upper = lsn->srgb.lower_bound + lsn->srgb.range_size - 1; + sbuf_push(&sbuf, 8, "SRGB: [%d/%d]", lsn->srgb.lower_bound, + upper); + if (CHECK_FLAG(lsn->flags, LS_NODE_SRLB)) { + upper = lsn->srlb.lower_bound + lsn->srlb.range_size + - 1; + sbuf_push(&sbuf, 0, "\tSRLB: [%d/%d]", + lsn->srlb.lower_bound, upper); + } + sbuf_push(&sbuf, 0, "\tAlgo: "); + for (int i = 0; i < 2; i++) { + if (lsn->algo[i] == 255) + continue; + + sbuf_push(&sbuf, 0, + lsn->algo[i] == 0 ? "SPF " : "S-SPF "); + } + if (CHECK_FLAG(lsn->flags, LS_NODE_MSD)) + sbuf_push(&sbuf, 0, "\tMSD: %d", lsn->msd); + sbuf_push(&sbuf, 0, "\n"); + } + + sbuf_push(&sbuf, 4, "Outgoing Edges: %d\n", + listcount(vertex->outgoing_edges)); + for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) { + if (edge->destination) { + lsn = edge->destination->node; + sbuf_push(&sbuf, 6, "To:\t%s(%pI4)", lsn->name, + &lsn->router_id); + } else { + sbuf_push(&sbuf, 6, "To:\t- (0.0.0.0)"); + } + sbuf_push(&sbuf, 0, "\tLocal: %pI4\tRemote: %pI4\n", + &edge->attributes->standard.local, + &edge->attributes->standard.remote); + } + + sbuf_push(&sbuf, 4, "Incoming Edges: %d\n", + listcount(vertex->incoming_edges)); + for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, node, edge)) { + if (edge->source) { + lsn = edge->source->node; + sbuf_push(&sbuf, 6, "From:\t%s(%pI4)", lsn->name, + &lsn->router_id); + } else { + sbuf_push(&sbuf, 6, "From:\t- (0.0.0.0)"); + } + sbuf_push(&sbuf, 0, "\tRemote: %pI4\tLocal: %pI4\n", + &edge->attributes->standard.local, + &edge->attributes->standard.remote); + } + + sbuf_push(&sbuf, 4, "Subnets: %d\n", listcount(vertex->prefixes)); + for (ALL_LIST_ELEMENTS_RO(vertex->prefixes, node, subnet)) + sbuf_push(&sbuf, 6, "Prefix:\t%pFX\n", &subnet->key); + +end: + vty_out(vty, "%s\n", sbuf_buf(&sbuf)); + sbuf_free(&sbuf); +} + +static void ls_show_vertex_json(struct ls_vertex *vertex, + struct json_object *json) +{ + struct ls_node *lsn; + json_object *jsr, *jalgo, *jobj; + char buf[INET6_BUFSIZ]; + + /* Sanity Check */ + if (!vertex) + return; + + lsn = vertex->node; + + json_object_int_add(json, "vertex-id", vertex->key); + json_object_string_add(json, "status", status2txt[vertex->status]); + json_object_string_add(json, "origin", origin2txt[lsn->adv.origin]); + if (CHECK_FLAG(lsn->flags, LS_NODE_NAME)) + json_object_string_add(json, "name", lsn->name); + if (CHECK_FLAG(lsn->flags, LS_NODE_ROUTER_ID)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI4", &lsn->router_id); + json_object_string_add(json, "router-id", buf); + } + if (CHECK_FLAG(lsn->flags, LS_NODE_ROUTER_ID6)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI6", &lsn->router6_id); + json_object_string_add(json, "router-id-v6", buf); + } + if (CHECK_FLAG(lsn->flags, LS_NODE_TYPE)) + json_object_string_add(json, "vertex-type", + type2txt[lsn->type]); + if (CHECK_FLAG(lsn->flags, LS_NODE_AS_NUMBER)) + json_object_int_add(json, "asn", lsn->as_number); + if (CHECK_FLAG(lsn->flags, LS_NODE_SR)) { + jsr = json_object_new_object(); + json_object_object_add(json, "segment-routing", jsr); + json_object_int_add(jsr, "srgb-size", lsn->srgb.range_size); + json_object_int_add(jsr, "srgb-lower", lsn->srgb.lower_bound); + jalgo = json_object_new_array(); + json_object_object_add(jsr, "algorithms", jalgo); + for (int i = 0; i < 2; i++) { + if (lsn->algo[i] == 255) + continue; + jobj = json_object_new_object(); + + snprintfrr(buf, 2, "%u", i); + json_object_string_add( + jobj, buf, lsn->algo[i] == 0 ? "SPF" : "S-SPF"); + json_object_array_add(jalgo, jobj); + } + if (CHECK_FLAG(lsn->flags, LS_NODE_SRLB)) { + json_object_int_add(jsr, "srlb-size", + lsn->srlb.range_size); + json_object_int_add(jsr, "srlb-lower", + lsn->srlb.lower_bound); + } + if (CHECK_FLAG(lsn->flags, LS_NODE_MSD)) + json_object_int_add(jsr, "msd", lsn->msd); + } +} + +void ls_show_vertex(struct ls_vertex *vertex, struct vty *vty, + struct json_object *json, bool verbose) +{ + if (json) + ls_show_vertex_json(vertex, json); + else if (vty) + ls_show_vertex_vty(vertex, vty, verbose); +} + +void ls_show_vertices(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose) +{ + struct ls_vertex *vertex; + json_object *jnodes, *jnode; + + if (json) { + jnodes = json_object_new_array(); + json_object_object_add(json, "vertices", jnodes); + frr_each (vertices, &ted->vertices, vertex) { + jnode = json_object_new_object(); + ls_show_vertex(vertex, NULL, jnode, verbose); + json_object_array_add(jnodes, jnode); + } + } else if (vty) { + frr_each (vertices, &ted->vertices, vertex) + ls_show_vertex(vertex, vty, NULL, verbose); + } +} + +static void ls_show_edge_vty(struct ls_edge *edge, struct vty *vty, + bool verbose) +{ + struct ls_attributes *attr; + struct sbuf sbuf; + char buf[INET6_BUFSIZ]; + + attr = edge->attributes; + sbuf_init(&sbuf, NULL, 0); + + sbuf_push(&sbuf, 2, "Edge (%" PRIu64 "): ", edge->key); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) + sbuf_push(&sbuf, 0, "%pI4", &attr->standard.local); + else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) + sbuf_push(&sbuf, 0, "%pI6", &attr->standard.local6); + else + sbuf_push(&sbuf, 0, "%u/%u", attr->standard.local_id, + attr->standard.remote_id); + ls_node_id_to_text(attr->adv, buf, INET6_BUFSIZ); + sbuf_push(&sbuf, 0, "\tAdv. Vertex: %s", buf); + sbuf_push(&sbuf, 0, "\tMetric: %u", attr->metric); + sbuf_push(&sbuf, 0, "\tStatus: %s\n", status2txt[edge->status]); + + if (!verbose) + goto end; + + sbuf_push(&sbuf, 4, "Origin: %s\n", origin2txt[attr->adv.origin]); + if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) + sbuf_push(&sbuf, 4, "Name: %s\n", attr->name); + if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) + sbuf_push(&sbuf, 4, "TE Metric: %u\n", + attr->standard.te_metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) + sbuf_push(&sbuf, 4, "Admin Group: 0x%x\n", + attr->standard.admin_group); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) + sbuf_push(&sbuf, 4, "Local IPv4 address: %pI4\n", + &attr->standard.local); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) + sbuf_push(&sbuf, 4, "Remote IPv4 address: %pI4\n", + &attr->standard.remote); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) + sbuf_push(&sbuf, 4, "Local IPv6 address: %pI6\n", + &attr->standard.local6); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) + sbuf_push(&sbuf, 4, "Remote IPv6 address: %pI6\n", + &attr->standard.remote6); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) + sbuf_push(&sbuf, 4, "Local Identifier: %u\n", + attr->standard.local_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) + sbuf_push(&sbuf, 4, "Remote Identifier: %u\n", + attr->standard.remote_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW)) + sbuf_push(&sbuf, 4, "Maximum Bandwidth: %g (Bytes/s)\n", + attr->standard.max_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW)) + sbuf_push(&sbuf, 4, + "Maximum Reservable Bandwidth: %g (Bytes/s)\n", + attr->standard.max_rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW)) { + sbuf_push(&sbuf, 4, "Unreserved Bandwidth per Class Type\n"); + for (int i = 0; i < MAX_CLASS_TYPE; i += 2) + sbuf_push(&sbuf, 8, + "[%d]: %g (Bytes/sec)\t[%d]: %g (Bytes/s)\n", + i, attr->standard.unrsv_bw[i], i + 1, + attr->standard.unrsv_bw[i + 1]); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS)) + sbuf_push(&sbuf, 4, "Remote AS: %u\n", + attr->standard.remote_as); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR)) + sbuf_push(&sbuf, 4, "Remote ASBR IPv4 address: %pI4\n", + &attr->standard.remote_addr); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6)) + sbuf_push(&sbuf, 4, "Remote ASBR IPv6 address: %pI6\n", + &attr->standard.remote_addr6); + if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY)) + sbuf_push(&sbuf, 4, "Average Link Delay: %d (micro-sec)\n", + attr->extended.delay); + if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) + sbuf_push(&sbuf, 4, "Min/Max Link Delay: %d/%d (micro-sec)\n", + attr->extended.min_delay, attr->extended.max_delay); + if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER)) + sbuf_push(&sbuf, 4, "Delay Variation: %d (micro-sec)\n", + attr->extended.jitter); + if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS)) + sbuf_push(&sbuf, 4, "Link Loss: %g (%%)\n", + (float)(attr->extended.pkt_loss * LOSS_PRECISION)); + if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW)) + sbuf_push(&sbuf, 4, "Available Bandwidth: %g (Bytes/s)\n", + attr->extended.ava_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW)) + sbuf_push(&sbuf, 4, "Residual Bandwidth: %g (Bytes/s)\n", + attr->extended.rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW)) + sbuf_push(&sbuf, 4, "Utilized Bandwidth: %g (Bytes/s)\n", + attr->extended.used_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) { + sbuf_push(&sbuf, 4, "Adjacency-SID: %u", attr->adj_sid[0].sid); + sbuf_push(&sbuf, 0, "\tFlags: 0x%x\tWeight: 0x%x\n", + attr->adj_sid[0].flags, attr->adj_sid[0].weight); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) { + sbuf_push(&sbuf, 4, "Bck. Adjacency-SID: %u", + attr->adj_sid[1].sid); + sbuf_push(&sbuf, 0, "\tFlags: 0x%x\tWeight: 0x%x\n", + attr->adj_sid[1].flags, attr->adj_sid[1].weight); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { + sbuf_push(&sbuf, 4, "SRLGs: %d", attr->srlg_len); + for (int i = 1; i < attr->srlg_len; i++) { + if (i % 8) + sbuf_push(&sbuf, 8, "\n%u", attr->srlgs[i]); + else + sbuf_push(&sbuf, 8, ", %u", attr->srlgs[i]); + } + sbuf_push(&sbuf, 0, "\n"); + } + +end: + vty_out(vty, "%s\n", sbuf_buf(&sbuf)); + sbuf_free(&sbuf); +} + +static void ls_show_edge_json(struct ls_edge *edge, struct json_object *json) +{ + struct ls_attributes *attr; + struct json_object *jte, *jbw, *jobj, *jsr = NULL, *jsrlg; + char buf[INET6_BUFSIZ]; + + attr = edge->attributes; + + json_object_int_add(json, "edge-id", edge->key); + json_object_string_add(json, "status", status2txt[edge->status]); + json_object_string_add(json, "origin", origin2txt[attr->adv.origin]); + ls_node_id_to_text(attr->adv, buf, INET6_BUFSIZ); + json_object_string_add(json, "advertised-router", buf); + if (edge->source) + json_object_int_add(json, "local-vertex-id", edge->source->key); + if (edge->destination) + json_object_int_add(json, "remote-vertex-id", + edge->destination->key); + json_object_int_add(json, "metric", attr->metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) + json_object_string_add(json, "name", attr->name); + jte = json_object_new_object(); + json_object_object_add(json, "edge-attributes", jte); + if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) + json_object_int_add(jte, "te-metric", attr->standard.te_metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) + json_object_int_add(jte, "admin-group", + attr->standard.admin_group); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI4", &attr->standard.local); + json_object_string_add(jte, "local-address", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI4", &attr->standard.remote); + json_object_string_add(jte, "remote-address", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI6", &attr->standard.local6); + json_object_string_add(jte, "local-address-v6", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI6", &attr->standard.remote6); + json_object_string_add(jte, "remote-address-v6", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) + json_object_int_add(jte, "local-identifier", + attr->standard.local_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) + json_object_int_add(jte, "remote-identifier", + attr->standard.remote_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW)) + json_object_double_add(jte, "max-link-bandwidth", + attr->standard.max_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW)) + json_object_double_add(jte, "max-resv-link-bandwidth", + attr->standard.max_rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW)) { + jbw = json_object_new_array(); + json_object_object_add(jte, "unreserved-bandwidth", jbw); + for (int i = 0; i < MAX_CLASS_TYPE; i++) { + jobj = json_object_new_object(); + snprintfrr(buf, 13, "class-type-%u", i); + json_object_double_add(jobj, buf, + attr->standard.unrsv_bw[i]); + json_object_array_add(jbw, jobj); + } + } + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS)) + json_object_int_add(jte, "remote-asn", + attr->standard.remote_as); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI4", + &attr->standard.remote_addr); + json_object_string_add(jte, "remote-as-address", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6)) { + snprintfrr(buf, INET6_BUFSIZ, "%pI6", + &attr->standard.remote_addr6); + json_object_string_add(jte, "remote-as-address-v6", buf); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY)) + json_object_int_add(jte, "delay", attr->extended.delay); + if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) { + json_object_int_add(jte, "min-delay", attr->extended.min_delay); + json_object_int_add(jte, "max-delay", attr->extended.max_delay); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER)) + json_object_int_add(jte, "jitter", attr->extended.jitter); + if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS)) + json_object_double_add( + jte, "loss", attr->extended.pkt_loss * LOSS_PRECISION); + if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW)) + json_object_double_add(jte, "available-bandwidth", + attr->extended.ava_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW)) + json_object_double_add(jte, "residual-bandwidth", + attr->extended.rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW)) + json_object_double_add(jte, "utilized-bandwidth", + attr->extended.used_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { + jsrlg = json_object_new_array(); + json_object_object_add(jte, "srlgs", jsrlg); + for (int i = 1; i < attr->srlg_len; i++) { + jobj = json_object_new_object(); + json_object_int_add(jobj, "srlg", attr->srlgs[i]); + json_object_array_add(jsrlg, jobj); + } + } + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) { + jsr = json_object_new_array(); + json_object_object_add(json, "segment-routing", jsr); + jobj = json_object_new_object(); + json_object_int_add(jobj, "adj-sid", attr->adj_sid[0].sid); + snprintfrr(buf, 6, "0x%x", attr->adj_sid[0].flags); + json_object_string_add(jobj, "flags", buf); + json_object_int_add(jobj, "weight", attr->adj_sid[0].weight); + json_object_array_add(jsr, jobj); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) { + if (!jsr) { + jsr = json_object_new_array(); + json_object_object_add(json, "segment-routing", jsr); + } + jobj = json_object_new_object(); + json_object_int_add(jobj, "adj-sid", attr->adj_sid[1].sid); + snprintfrr(buf, 6, "0x%x", attr->adj_sid[1].flags); + json_object_string_add(jobj, "flags", buf); + json_object_int_add(jobj, "weight", attr->adj_sid[1].weight); + json_object_array_add(jsr, jobj); + } +} + +void ls_show_edge(struct ls_edge *edge, struct vty *vty, + struct json_object *json, bool verbose) +{ + /* Sanity Check */ + if (!edge) + return; + + if (json) + ls_show_edge_json(edge, json); + else if (vty) + ls_show_edge_vty(edge, vty, verbose); +} + +void ls_show_edges(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose) +{ + struct ls_edge *edge; + json_object *jedges, *jedge; + + if (json) { + jedges = json_object_new_array(); + json_object_object_add(json, "edges", jedges); + frr_each (edges, &ted->edges, edge) { + jedge = json_object_new_object(); + ls_show_edge(edge, NULL, jedge, verbose); + json_object_array_add(jedges, jedge); + } + } else if (vty) { + frr_each (edges, &ted->edges, edge) + ls_show_edge(edge, vty, NULL, verbose); + } +} + +static void ls_show_subnet_vty(struct ls_subnet *subnet, struct vty *vty, + bool verbose) +{ + struct ls_prefix *pref; + struct sbuf sbuf; + char buf[INET6_BUFSIZ]; + + pref = subnet->ls_pref; + sbuf_init(&sbuf, NULL, 0); + + sbuf_push(&sbuf, 2, "Subnet: %pFX", &subnet->key); + ls_node_id_to_text(pref->adv, buf, INET6_BUFSIZ); + sbuf_push(&sbuf, 0, "\tAdv. Vertex: %s", buf); + sbuf_push(&sbuf, 0, "\tMetric: %d", pref->metric); + sbuf_push(&sbuf, 0, "\tStatus: %s\n", status2txt[subnet->status]); + + if (!verbose) + goto end; + + sbuf_push(&sbuf, 4, "Origin: %s\n", origin2txt[pref->adv.origin]); + if (CHECK_FLAG(pref->flags, LS_PREF_IGP_FLAG)) + sbuf_push(&sbuf, 4, "Flags: %d\n", pref->igp_flag); + + if (CHECK_FLAG(pref->flags, LS_PREF_ROUTE_TAG)) + sbuf_push(&sbuf, 4, "Tag: %d\n", pref->route_tag); + + if (CHECK_FLAG(pref->flags, LS_PREF_EXTENDED_TAG)) + sbuf_push(&sbuf, 4, "Extended Tag: %" PRIu64 "\n", + pref->extended_tag); + + if (CHECK_FLAG(pref->flags, LS_PREF_SR)) + sbuf_push(&sbuf, 4, "SID: %d\tAlgorithm: %d\tFlags: 0x%x\n", + pref->sr.sid, pref->sr.algo, pref->sr.sid_flag); + +end: + vty_out(vty, "%s\n", sbuf_buf(&sbuf)); + sbuf_free(&sbuf); +} + +static void ls_show_subnet_json(struct ls_subnet *subnet, + struct json_object *json) +{ + struct ls_prefix *pref; + json_object *jsr; + char buf[INET6_BUFSIZ]; + + pref = subnet->ls_pref; + + snprintfrr(buf, INET6_BUFSIZ, "%pFX", &subnet->key); + json_object_string_add(json, "subnet-id", buf); + json_object_string_add(json, "status", status2txt[subnet->status]); + json_object_string_add(json, "origin", origin2txt[pref->adv.origin]); + ls_node_id_to_text(pref->adv, buf, INET6_BUFSIZ); + json_object_string_add(json, "advertised-router", buf); + if (subnet->vertex) + json_object_int_add(json, "vertex-id", subnet->vertex->key); + json_object_int_add(json, "metric", pref->metric); + if (CHECK_FLAG(pref->flags, LS_PREF_IGP_FLAG)) { + snprintfrr(buf, INET6_BUFSIZ, "0x%x", pref->igp_flag); + json_object_string_add(json, "flags", buf); + } + if (CHECK_FLAG(pref->flags, LS_PREF_ROUTE_TAG)) + json_object_int_add(json, "tag", pref->route_tag); + if (CHECK_FLAG(pref->flags, LS_PREF_EXTENDED_TAG)) + json_object_int_add(json, "extended-tag", pref->extended_tag); + if (CHECK_FLAG(pref->flags, LS_PREF_SR)) { + jsr = json_object_new_object(); + json_object_object_add(json, "segment-routing", jsr); + json_object_int_add(jsr, "pref-sid", pref->sr.sid); + json_object_int_add(jsr, "algo", pref->sr.algo); + snprintfrr(buf, INET6_BUFSIZ, "0x%x", pref->sr.sid_flag); + json_object_string_add(jsr, "flags", buf); + } +} + +void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty, + struct json_object *json, bool verbose) +{ + /* Sanity Check */ + if (!subnet) + return; + + if (json) + ls_show_subnet_json(subnet, json); + else if (vty) + ls_show_subnet_vty(subnet, vty, verbose); +} + +void ls_show_subnets(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose) +{ + struct ls_subnet *subnet; + json_object *jsubs, *jsub; + + if (json) { + jsubs = json_object_new_array(); + json_object_object_add(json, "subnets", jsubs); + frr_each (subnets, &ted->subnets, subnet) { + jsub = json_object_new_object(); + ls_show_subnet(subnet, NULL, jsub, verbose); + json_object_array_add(jsubs, jsub); + } + } else if (vty) { + frr_each (subnets, &ted->subnets, subnet) + ls_show_subnet(subnet, vty, NULL, verbose); + } +} + +void ls_show_ted(struct ls_ted *ted, struct vty *vty, struct json_object *json, + bool verbose) +{ + json_object *jted; + + if (json) { + jted = json_object_new_object(); + json_object_object_add(json, "ted", jted); + json_object_string_add(jted, "name", ted->name); + json_object_int_add(jted, "key", ted->key); + json_object_int_add(jted, "verticesCount", + vertices_count(&ted->vertices)); + json_object_int_add(jted, "edgesCount", + edges_count(&ted->edges)); + json_object_int_add(jted, "subnetsCount", + subnets_count(&ted->subnets)); + ls_show_vertices(ted, NULL, jted, verbose); + ls_show_edges(ted, NULL, jted, verbose); + ls_show_subnets(ted, NULL, jted, verbose); + return; + } + + if (vty) { + vty_out(vty, + "\n\tTraffic Engineering Database: %s (key: %d)\n\n", + ted->name, ted->key); + ls_show_vertices(ted, vty, NULL, verbose); + ls_show_edges(ted, vty, NULL, verbose); + ls_show_subnets(ted, vty, NULL, verbose); + vty_out(vty, + "\n\tTotal: %zu Vertices, %zu Edges, %zu Subnets\n\n", + vertices_count(&ted->vertices), + edges_count(&ted->edges), subnets_count(&ted->subnets)); + } +} + void ls_dump_ted(struct ls_ted *ted) { struct ls_vertex *vertex; struct ls_edge *edge; struct ls_subnet *subnet; - struct ls_message msg; + const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; zlog_debug("(%s) Ted init", __func__); - /* Prepare message */ - msg.event = LS_MSG_EVENT_SYNC; /* Loop TED, start printing Node, then Attributes and finally Prefix */ - frr_each(vertices, &ted->vertices, vertex) { - ls_vertex2msg(&msg, vertex); + frr_each (vertices, &ted->vertices, vertex) { zlog_debug(" Ted node (%s %pI4 %s)", vertex->node->name[0] ? vertex->node->name : "no name node", &vertex->node->router_id, - vertex->node->adv.origin == DIRECT ? "DIRECT" - : "NO DIRECT"); + origin2txt[vertex->node->adv.origin]); struct listnode *lst_node; struct ls_edge *vertex_edge; for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node, vertex_edge)) { zlog_debug( - " inc edge key:%"PRIu64"n attr key:%pI4 loc:(%pI4) rmt:(%pI4)", + " inc edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)", vertex_edge->key, &vertex_edge->attributes->adv.id.ip.addr, &vertex_edge->attributes->standard.local, @@ -1255,29 +2450,25 @@ void ls_dump_ted(struct ls_ted *ted) for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node, vertex_edge)) { zlog_debug( - " out edge key:%"PRIu64" attr key:%pI4 loc:(%pI4) rmt:(%pI4)", + " out edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)", vertex_edge->key, &vertex_edge->attributes->adv.id.ip.addr, &vertex_edge->attributes->standard.local, &vertex_edge->attributes->standard.remote); } } - frr_each(edges, &ted->edges, edge) { - ls_edge2msg(&msg, edge); - zlog_debug(" Ted edge key:%"PRIu64" src:%s dst:%s", - edge->key, - edge->source ? edge->source->node->name - : "no_source", - edge->destination ? edge->destination->node->name - : "no_dest"); + frr_each (edges, &ted->edges, edge) { + zlog_debug(" Ted edge key:%" PRIu64 "src:%pI4 dst:%pI4", edge->key, + edge->source ? &edge->source->node->router_id + : &inaddr_any, + edge->destination + ? &edge->destination->node->router_id + : &inaddr_any); } - frr_each(subnets, &ted->subnets, subnet) { - ls_subnet2msg(&msg, subnet); - zlog_debug( - " Ted subnet key:%pFX vertex:%pI4 pfx:%pFX", - &subnet->key, - &subnet->vertex->node->adv.id.ip.addr, - &subnet->ls_pref->pref); + frr_each (subnets, &ted->subnets, subnet) { + zlog_debug(" Ted subnet key:%pFX vertex:%pI4", + &subnet->ls_pref->pref, + &subnet->vertex->node->adv.id.ip.addr); } zlog_debug("(%s) Ted end", __func__); } diff --git a/lib/link_state.h b/lib/link_state.h index f9eb59b76a..de116df89e 100644 --- a/lib/link_state.h +++ b/lib/link_state.h @@ -53,20 +53,26 @@ extern "C" { * id for OSPF and the ISO System id plus the IS-IS level for IS-IS. */ +/* external reference */ +struct zapi_opaque_reg_info; +struct zclient; + /* Link State Common definitions */ #define MAX_NAME_LENGTH 256 #define ISO_SYS_ID_LEN 6 /* Type of Node */ enum ls_node_type { + NONE = 0, /* Unknown */ STANDARD, /* a P or PE node */ ABR, /* an Array Border Node */ ASBR, /* an Autonomous System Border Node */ - PSEUDO, /* a Pseudo Node */ + RMT_ASBR, /* Remote ASBR */ + PSEUDO /* a Pseudo Node */ }; /* Origin of the Link State information */ -enum ls_origin {NONE = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC}; +enum ls_origin { UNKNOWN = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC }; /** * Link State Node Identifier as: @@ -108,19 +114,17 @@ struct ls_node { struct in_addr router_id; /* IPv4 Router ID */ struct in6_addr router6_id; /* IPv6 Router ID */ uint8_t node_flag; /* IS-IS or OSPF Node flag */ - enum node_type type; /* Type of Node */ + enum ls_node_type type; /* Type of Node */ uint32_t as_number; /* Local or neighbor AS number */ - struct { /* Segment Routing Global Block */ + struct ls_srgb { /* Segment Routing Global Block */ uint32_t lower_bound; /* MPLS label lower bound */ uint32_t range_size; /* MPLS label range size */ uint8_t flag; /* IS-IS SRGB flags */ } srgb; -#define LS_NODE_SRGB_SIZE 9 - struct { /* Segment Routing Local Block */ + struct ls_srlb { /* Segment Routing Local Block */ uint32_t lower_bound; /* MPLS label lower bound */ uint32_t range_size; /* MPLS label range size */ } srlb; -#define LS_NODE_SRLB_SIZE 8 uint8_t algo[2]; /* Segment Routing Algorithms */ uint8_t msd; /* Maximum Stack Depth */ }; @@ -150,17 +154,17 @@ struct ls_node { #define LS_ATTR_AVA_BW 0x00100000 #define LS_ATTR_RSV_BW 0x00200000 #define LS_ATTR_USE_BW 0x00400000 -#define LS_ATTR_ADJ_SID 0x00800000 -#define LS_ATTR_BCK_ADJ_SID 0x01000000 -#define LS_ATTR_SRLG 0x02000000 +#define LS_ATTR_ADJ_SID 0x01000000 +#define LS_ATTR_BCK_ADJ_SID 0x02000000 +#define LS_ATTR_SRLG 0x10000000 /* Link State Attributes */ struct ls_attributes { uint32_t flags; /* Flag for parameters validity */ struct ls_node_id adv; /* Adv. Router of this Link State */ char name[MAX_NAME_LENGTH]; /* Name of the Edge. Could be null */ - struct { /* Standard TE metrics */ - uint32_t metric; /* IGP standard metric */ + uint32_t metric; /* IGP standard metric */ + struct ls_standard { /* Standard TE metrics */ uint32_t te_metric; /* Traffic Engineering metric */ uint32_t admin_group; /* Administrative Group */ struct in_addr local; /* Local IPv4 address */ @@ -176,8 +180,7 @@ struct ls_attributes { struct in_addr remote_addr; /* Remote IPv4 address */ struct in6_addr remote_addr6; /* Remote IPv6 address */ } standard; -#define LS_ATTR_STANDARD_SIZE 124 - struct { /* Extended TE Metrics */ + struct ls_extended { /* Extended TE Metrics */ uint32_t delay; /* Unidirectional average delay */ uint32_t min_delay; /* Unidirectional minimum delay */ uint32_t max_delay; /* Unidirectional maximum delay */ @@ -187,8 +190,7 @@ struct ls_attributes { float rsv_bw; /* Reserved Bandwidth */ float used_bw; /* Utilized Bandwidth */ } extended; -#define LS_ATTR_EXTENDED_SIZE 32 - struct { /* (LAN)-Adjacency SID for OSPF */ + struct ls_adjacency { /* (LAN)-Adjacency SID for OSPF */ uint32_t sid; /* SID as MPLS label or index */ uint8_t flags; /* Flags */ uint8_t weight; /* Administrative weight */ @@ -197,7 +199,6 @@ struct ls_attributes { uint8_t sysid[ISO_SYS_ID_LEN]; /* or Sys-ID for ISIS */ } neighbor; } adj_sid[2]; /* Primary & Backup (LAN)-Adj. SID */ -#define LS_ATTR_ADJ_SID_SIZE 120 uint32_t *srlgs; /* List of Shared Risk Link Group */ uint8_t srlg_len; /* number of SRLG in the list */ }; @@ -219,7 +220,7 @@ struct ls_prefix { uint32_t route_tag; /* IGP Route Tag */ uint64_t extended_tag; /* IGP Extended Route Tag */ uint32_t metric; /* Route metric for this prefix */ - struct { + struct ls_sid { uint32_t sid; /* Segment Routing ID */ uint8_t sid_flag; /* Segment Routing Flags */ uint8_t algo; /* Algorithm for Segment Routing */ @@ -272,10 +273,17 @@ extern struct ls_attributes *ls_attributes_new(struct ls_node_id adv, struct in6_addr local6, uint32_t local_id); +/** + * Remove SRLGs from Link State Attributes if defined. + * + * @param attr Pointer to a valid Link State Attribute structure + */ +extern void ls_attributes_srlg_del(struct ls_attributes *attr); + /** * Remove Link State Attributes. Data structure is freed. * - * @param attr Pointer to a valid Link State Attribute structure + * @param attr Pointer to a valid Link State Attribute structure */ extern void ls_attributes_del(struct ls_attributes *attr); @@ -291,6 +299,34 @@ extern void ls_attributes_del(struct ls_attributes *attr); extern int ls_attributes_same(struct ls_attributes *a1, struct ls_attributes *a2); +/** + * Create a new Link State Prefix. Structure is dynamically allocated. + * + * @param adv Mandatory Link State Node ID i.e. advertise router ID + * @param p Mandatory Prefix + * + * @return New Link State Prefix + */ +extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p); + +/** + * Remove Link State Prefix. Data Structure is freed. + * + * @param pref Pointer to a valid Link State Attribute Prefix. + */ +extern void ls_prefix_del(struct ls_prefix *pref); + +/** + * Check if two Link State Prefix are equal. Note that this routine has the + * same return value sense as '==' (which is different from a comparison). + * + * @param p1 First Link State Prefix to be compare + * @param p2 Second Link State Prefix to be compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_prefix_same(struct ls_prefix *p1, struct ls_prefix *p2); + /** * In addition a Graph model is defined as an overlay on top of link state * database in order to ease Path Computation algorithm implementation. @@ -323,9 +359,14 @@ extern int ls_attributes_same(struct ls_attributes *a1, * */ +enum ls_status { UNSET = 0, NEW, UPDATE, DELETE, SYNC, ORPHAN }; +enum ls_type { GENERIC = 0, VERTEX, EDGE, SUBNET }; + /* Link State Vertex structure */ PREDECL_RBTREE_UNIQ(vertices); struct ls_vertex { + enum ls_type type; /* Link State Type */ + enum ls_status status; /* Status of the Vertex in the TED */ struct vertices_item entry; /* Entry in RB Tree */ uint64_t key; /* Unique Key identifier */ struct ls_node *node; /* Link State Node */ @@ -337,6 +378,8 @@ struct ls_vertex { /* Link State Edge structure */ PREDECL_RBTREE_UNIQ(edges); struct ls_edge { + enum ls_type type; /* Link State Type */ + enum ls_status status; /* Status of the Edge in the TED */ struct edges_item entry; /* Entry in RB tree */ uint64_t key; /* Unique Key identifier */ struct ls_attributes *attributes; /* Link State attributes */ @@ -347,10 +390,12 @@ struct ls_edge { /* Link State Subnet structure */ PREDECL_RBTREE_UNIQ(subnets); struct ls_subnet { + enum ls_type type; /* Link State Type */ + enum ls_status status; /* Status of the Subnet in the TED */ struct subnets_item entry; /* Entry in RB tree */ struct prefix key; /* Unique Key identifier */ - struct ls_vertex *vertex; /* Back pointer to the Vertex owner */ struct ls_prefix *ls_pref; /* Link State Prefix */ + struct ls_vertex *vertex; /* Back pointer to the Vertex owner */ }; /* Declaration of Vertices, Edges and Prefixes RB Trees */ @@ -386,25 +431,12 @@ struct ls_ted { 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); +/* Generic Link State Element */ +struct ls_element { + enum ls_type type; /* Link State Element Type */ + enum ls_status status; /* Link State Status in the TED */ + void *data; /* Link State payload */ +}; /** * Add new vertex to the Link State DB. Vertex is created from the Link State @@ -418,6 +450,27 @@ extern void ls_vertex_del(struct ls_vertex *vertex); extern struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node); +/** + * Delete Link State Vertex. This function clean internal Vertex lists (incoming + * and outgoing Link State Edge and Link State Subnet). Vertex Data structure + * is freed but not the Link State Node. Link State DB is not modified if Vertex + * is NULL or not found in the Data Base. Note that referenced to Link State + * Edges & SubNets are not removed as they could be connected to other Vertices. + * + * @param ted Traffic Engineering Database structure + * @param vertex Link State Vertex to be removed + */ +extern void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex); + +/** + * Delete Link State Vertex as ls_vertex_del() but also removed associated + * Link State Node. + * + * @param ted Traffic Engineering Database structure + * @param vertex Link State Vertex to be removed + */ +extern void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex); + /** * Update Vertex with the Link State Node. A new vertex is created if no one * corresponds to the Link State Node. @@ -431,15 +484,15 @@ extern struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node); /** - * Remove Vertex from the Link State DB. Vertex Data structure is freed but - * not the Link State Node. Link State DB is not modified if Vertex is NULL or - * not found in the Data Base. + * Clean Vertex structure by removing all Edges and Subnets marked as ORPHAN + * from this vertex. Link State Update message is sent if zclient is not NULL. * * @param ted Link State Data Base - * @param vertex Vertex to be removed + * @param vertex Link State Vertex to be cleaned + * @param zclient Reference to Zebra Client */ -extern void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex); - +extern void ls_vertex_clean(struct ls_ted *ted, struct ls_vertex *vertex, + struct zclient *zclient); /** * Find Vertex in the Link State DB by its unique key. * @@ -497,6 +550,17 @@ extern struct ls_edge *ls_edge_add(struct ls_ted *ted, extern struct ls_edge *ls_edge_update(struct ls_ted *ted, struct ls_attributes *attributes); +/** + * Check if two Edges are equal. Note that this routine has the same return + * value sense as '==' (which is different from a comparison). + * + * @param e1 First edge to compare + * @param e2 Second edge to compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2); + /** * Remove Edge from the Link State DB. Edge data structure is freed but not the * Link State Attributes data structure. Link State DB is not modified if Edge @@ -507,6 +571,15 @@ extern struct ls_edge *ls_edge_update(struct ls_ted *ted, */ extern void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge); +/** + * Remove Edge and associated Link State Attributes from the Link State DB. + * Link State DB is not modified if Edge is NULL or not found. + * + * @param ted Link State Data Base + * @param edge Edge to be removed + */ +extern void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge); + /** * Find Edge in the Link State Data Base by Edge key. * @@ -520,8 +593,7 @@ extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, /** * Find Edge in the Link State Data Base by the source (local IPv4 or IPv6 - * address or local ID) informations of the Link - * State Attributes + * address or local ID) informations of the Link State Attributes * * @param ted Link State Data Base * @param attributes Link State Attributes @@ -556,6 +628,29 @@ ls_find_edge_by_destination(struct ls_ted *ted, extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted, struct ls_prefix *pref); +/** + * Update the Link State Prefix information of an existing Subnet. If there is + * no corresponding Subnet in the Link State Data Base, a new Subnet is created. + * + * @param ted Link State Data Base + * @param pref Link State Prefix + * + * @return Updated Link State Subnet, or NULL in case of error + */ +extern struct ls_subnet *ls_subnet_update(struct ls_ted *ted, + struct ls_prefix *pref); + +/** + * Check if two Subnets are equal. Note that this routine has the same return + * value sense as '==' (which is different from a comparison). + * + * @param s1 First subnet to compare + * @param s2 Second subnet to compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_subnet_same(struct ls_subnet *s1, struct ls_subnet *s2); + /** * Remove Subnet from the Link State DB. Subnet data structure is freed but * not the Link State prefix data structure. Link State DB is not modified @@ -566,6 +661,15 @@ extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted, */ extern void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet); +/** + * Remove Subnet and the associated Link State Prefix from the Link State DB. + * Link State DB is not modified if Subnet is NULL or not found. + * + * @param ted Link State Data Base + * @param subnet Subnet to be removed + */ +extern void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet); + /** * Find Subnet in the Link State Data Base by prefix. * @@ -582,7 +686,7 @@ extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted, * * @param key Unique key of the data base. Must be different from 0 * @param name Name of the data base (may be NULL) - * @param asn AS Number for this data base. Must be different from 0 + * @param asn AS Number for this data base. 0 if unknown * * @return New Link State Database or NULL in case of error */ @@ -590,12 +694,28 @@ extern struct ls_ted *ls_ted_new(const uint32_t key, const char *name, uint32_t asn); /** - * Delete existing Link State Data Base. + * Delete existing Link State Data Base. Vertices, Edges, and Subnets are not + * removed. * * @param ted Link State Data Base */ extern void ls_ted_del(struct ls_ted *ted); +/** + * Delete all Link State Vertices, Edges and SubNets and the Link State DB. + * + * @param ted Link State Data Base + */ +extern void ls_ted_del_all(struct ls_ted *ted); + +/** + * Clean Link State Data Base by removing all Vertices, Edges and SubNets marked + * as ORPHAN. + * + * @param ted Link State Data Base + */ +extern void ls_ted_clean(struct ls_ted *ted); + /** * Connect Source and Destination Vertices by given Edge. Only non NULL source * and destination vertices are connected. @@ -657,6 +777,7 @@ extern void ls_disconnect_edge(struct ls_edge *edge); */ /* ZAPI Opaque Link State Message Event */ +#define LS_MSG_EVENT_UNDEF 0 #define LS_MSG_EVENT_SYNC 1 #define LS_MSG_EVENT_ADD 2 #define LS_MSG_EVENT_UPDATE 3 @@ -679,6 +800,35 @@ struct ls_message { } data; }; +/** + * Register Link State daemon as a server or client for Zebra OPAQUE API. + * + * @param zclient Zebra client structure + * @param server Register daemon as a server (true) or as a client (false) + * + * @return 0 if success, -1 otherwise + */ +extern int ls_register(struct zclient *zclient, bool server); + +/** + * Unregister Link State daemon as a server or client for Zebra OPAQUE API. + * + * @param zclient Zebra client structure + * @param server Unregister daemon as a server (true) or as a client (false) + * + * @return 0 if success, -1 otherwise + */ +extern int ls_unregister(struct zclient *zclient, bool server); + +/** + * Send Link State SYNC message to request the complete Link State Database. + * + * @param zclient Zebra client + * + * @return 0 if success, -1 otherwise + */ +extern int ls_request_sync(struct zclient *zclient); + /** * Parse Link State Message from stream. Used this function once receiving a * new ZAPI Opaque message of type Link State. @@ -690,7 +840,7 @@ struct ls_message { extern struct ls_message *ls_parse_msg(struct stream *s); /** - * Delete existing message, freeing all substructure. + * Delete existing message. Data structure is freed. * * @param msg Link state message to be deleted */ @@ -750,6 +900,81 @@ extern struct ls_message *ls_edge2msg(struct ls_message *msg, extern struct ls_message *ls_subnet2msg(struct ls_message *msg, struct ls_subnet *subnet); +/** + * Convert Link State Message into Vertex and update TED accordingly to + * the message event: SYNC, ADD, UPDATE or DELETE. + * + * @param ted Link State Database + * @param msg Link State Message + * @param delete True to delete the Link State Vertex from the Database, + * False otherwise. If true, return value is NULL in case + * of deletion. + * + * @return Vertex if success, NULL otherwise or if Vertex is removed + */ +extern struct ls_vertex *ls_msg2vertex(struct ls_ted *ted, + struct ls_message *msg, bool delete); + +/** + * Convert Link State Message into Edge and update TED accordingly to + * the message event: SYNC, ADD, UPDATE or DELETE. + * + * @param ted Link State Database + * @param msg Link State Message + * @param delete True to delete the Link State Edge from the Database, + * False otherwise. If true, return value is NULL in case + * of deletion. + * + * @return Edge if success, NULL otherwise or if Edge is removed + */ +extern struct ls_edge *ls_msg2edge(struct ls_ted *ted, struct ls_message *msg, + bool delete); + +/** + * Convert Link State Message into Subnet and update TED accordingly to + * the message event: SYNC, ADD, UPDATE or DELETE. + * + * @param ted Link State Database + * @param msg Link State Message + * @param delete True to delete the Link State Subnet from the Database, + * False otherwise. If true, return value is NULL in case + * of deletion. + * + * @return Subnet if success, NULL otherwise or if Subnet is removed + */ +extern struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, + struct ls_message *msg, bool delete); + +/** + * Convert Link State Message into Link State element (Vertex, Edge or Subnet) + * and update TED accordingly to the message event: SYNC, ADD, UPDATE or DELETE. + * + * @param ted Link State Database + * @param msg Link State Message + * @param delete True to delete the Link State Element from the Database, + * False otherwise. If true, return value is NULL in case + * of deletion. + * + * @return Element if success, NULL otherwise or if Element is removed + */ +extern struct ls_element *ls_msg2ted(struct ls_ted *ted, struct ls_message *msg, + bool delete); + +/** + * Convert stream buffer into Link State element (Vertex, Edge or Subnet) and + * update TED accordingly to the message event: SYNC, ADD, UPDATE or DELETE. + * + * @param ted Link State Database + * @param s Stream buffer + * @param delete True to delete the Link State Element from the Database, + * False otherwise. If true, return value is NULL in case + * of deletion. + * + * @return Element if success, NULL otherwise or if Element is removed + */ +extern struct ls_element *ls_stream2ted(struct ls_ted *ted, struct stream *s, + bool delete); + /** * Send all the content of the Link State Data Base to the given destination. * Link State content is sent is this order: Vertices, Edges, Subnet. @@ -765,6 +990,92 @@ extern struct ls_message *ls_subnet2msg(struct ls_message *msg, extern int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, struct zapi_opaque_reg_info *dst); +struct json_object; +struct vty; +/** + * Show Link State Vertex information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param vertex Link State Vertex to show. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_vertex(struct ls_vertex *vertex, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show all Link State Vertices information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param ted Link State Data Base. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_vertices(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show Link State Edge information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param edge Link State Edge to show. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_edge(struct ls_edge *edge, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show all Link State Edges information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param ted Link State Data Base. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_edges(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show Link State Subnets information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param subnet Link State Subnet to show. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_subnet(struct ls_subnet *subnet, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show all Link State Subnet information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param ted Link State Data Base. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_subnets(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose); + +/** + * Show Link State Data Base information. If both vty and json are specified, + * Json format output supersedes standard vty output. + * + * @param ted Link State Data Base to show. Must not be NULL + * @param vty Pointer to vty output, could be NULL + * @param json Pointer to json output, could be NULL + * @param verbose Set to true for more detail + */ +extern void ls_show_ted(struct ls_ted *ted, struct vty *vty, + struct json_object *json, bool verbose); + /** * Dump all Link State Data Base elements for debugging purposes * diff --git a/lib/zclient.h b/lib/zclient.h index 43197534a8..0870e5e025 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -1132,7 +1132,7 @@ int zapi_opaque_reg_decode(struct stream *msg, */ enum zapi_opaque_registry { /* Request link-state database dump, at restart for example */ - LINK_STATE_REQUEST = 1, + LINK_STATE_SYNC = 1, /* Update containing link-state db info */ LINK_STATE_UPDATE = 2, /* Request LDP-SYNC state from LDP */