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 */