diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/link_state.c | 1284 | ||||
| -rw-r--r-- | lib/link_state.h | 780 | ||||
| -rw-r--r-- | lib/subdir.am | 2 |
3 files changed, 2066 insertions, 0 deletions
diff --git a/lib/link_state.c b/lib/link_state.c new file mode 100644 index 0000000000..f8fdda64f0 --- /dev/null +++ b/lib/link_state.c @@ -0,0 +1,1284 @@ +/* + * Link State Database - link_state.c + * + * Author: Olivier Dugeon <olivier.dugeon@orange.com> + * + * Copyright (C) 2020 Orange http://www.orange.com + * + * This file is part of Free Range Routing (FRR). + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "if.h" +#include "linklist.h" +#include "log.h" +#include "command.h" +#include "termtable.h" +#include "memory.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "zclient.h" +#include "stream.h" +#include "link_state.h" + +/* Link State Memory allocation */ +DEFINE_MTYPE_STATIC(LIB, LS_DB, "Link State Database") + +/** + * Link State Node management functions + */ +struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid, + struct in6_addr rid6) +{ + struct ls_node *new; + + if (adv.origin == NONE) + return NULL; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node)); + new->adv = adv; + if (!IPV4_NET0(rid.s_addr)) { + new->router_id = rid; + SET_FLAG(new->flags, LS_NODE_ROUTER_ID); + } else { + if (adv.origin == OSPFv2 || adv.origin == STATIC + || adv.origin == DIRECT) { + new->router_id = adv.id.ip.addr; + SET_FLAG(new->flags, LS_NODE_ROUTER_ID); + } + } + if (!IN6_IS_ADDR_UNSPECIFIED(&rid6)) { + new->router6_id = rid6; + SET_FLAG(new->flags, LS_NODE_ROUTER_ID6); + } + return new; +} + +void ls_node_del(struct ls_node *node) +{ + XFREE(MTYPE_LS_DB, node); + node = NULL; +} + +int ls_node_same(struct ls_node *n1, struct ls_node *n2) +{ + if ((n1 && !n2) || (!n1 && n2)) + return 0; + + if (n1 == n2) + return 1; + + if (n1->flags != n2->flags) + return 0; + + if (n1->adv.origin != n2->adv.origin) + return 0; + + if (!memcmp(&n1->adv.id, &n2->adv.id, sizeof(struct ls_node_id))) + return 0; + + /* Do we need to test individually each field, instead performing a + * global memcmp? There is a risk that an old value that is bit masked + * i.e. corresponding flag = 0, will result into a false negative + */ + if (!memcmp(n1, n2, sizeof(struct ls_node))) + return 0; + else + return 1; +} + +/** + * Link State Attributes management functions + */ +struct ls_attributes *ls_attributes_new(struct ls_node_id adv, + struct in_addr local, + struct in6_addr local6, + uint32_t local_id) +{ + struct ls_attributes *new; + + if (adv.origin == NONE) + return NULL; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes)); + new->adv = adv; + if (!IPV4_NET0(local.s_addr)) { + new->standard.local = local; + SET_FLAG(new->flags, LS_ATTR_LOCAL_ADDR); + } + if (!IN6_IS_ADDR_UNSPECIFIED(&local6)) { + new->standard.local6 = local6; + SET_FLAG(new->flags, LS_ATTR_LOCAL_ADDR6); + } + if (local_id != 0) { + new->standard.local_id = local_id; + SET_FLAG(new->flags, LS_ATTR_LOCAL_ID); + } + + /* Check that almost one identifier is set */ + if (!CHECK_FLAG(new->flags, LS_ATTR_LOCAL_ADDR | LS_ATTR_LOCAL_ADDR6 + | LS_ATTR_LOCAL_ID)) { + XFREE(MTYPE_LS_DB, new); + return NULL; + } + + return new; +} + +void ls_attributes_del(struct ls_attributes *attr) +{ + if (!attr) + return; + + if (attr->srlgs) + XFREE(MTYPE_LS_DB, attr->srlgs); + + XFREE(MTYPE_LS_DB, attr); + attr = NULL; +} + +int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2) +{ + if ((l1 && !l2) || (!l1 && l2)) + return 0; + + if (l1 == l2) + return 1; + + if (l1->flags != l2->flags) + return 0; + + if (l1->adv.origin != l2->adv.origin) + return 0; + + if (!memcmp(&l1->adv.id, &l2->adv.id, sizeof(struct ls_node_id))) + return 0; + + /* Do we need to test individually each field, instead performing a + * global memcmp? There is a risk that an old value that is bit masked + * i.e. corresponding flag = 0, will result into a false negative + */ + if (!memcmp(l1, l2, sizeof(struct ls_attributes))) + return 0; + else + return 1; +} + +/** + * Link State Vertices management functions + */ +struct ls_vertex *ls_vertex_new(struct ls_node *node) +{ + struct ls_vertex *new; + + if (node == NULL) + return NULL; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_vertex)); + new->node = node; + new->incoming_edges = list_new(); + new->outgoing_edges = list_new(); + new->prefixes = list_new(); + + return new; +} + +void ls_vertex_del(struct ls_vertex *vertex) +{ + if (vertex == NULL) + return; + + list_delete_all_node(vertex->incoming_edges); + list_delete_all_node(vertex->outgoing_edges); + list_delete_all_node(vertex->prefixes); + XFREE(MTYPE_LS_DB, vertex); + vertex = NULL; +} + +struct ls_vertex *ls_vertex_add(struct ls_ted *ted, struct ls_node *node) +{ + struct ls_vertex *new; + + if ((ted == NULL) || (node == NULL)) + return NULL; + + new = ls_vertex_new(node); + if (!new) + return NULL; + + /* set Key as the IPv4/Ipv6 Router ID or ISO System ID */ + switch (node->adv.origin) { + case OSPFv2: + case STATIC: + case DIRECT: + memcpy(&new->key, &node->adv.id.ip.addr, IPV4_MAX_BYTELEN); + break; + case ISIS_L1: + case ISIS_L2: + memcpy(&new->key, &node->adv.id.iso.sys_id, ISO_SYS_ID_LEN); + break; + default: + new->key = 0; + break; + } + + /* Remove Vertex if key is not set */ + if (new->key == 0) { + ls_vertex_del(new); + return NULL; + } + + /* Add Vertex to TED */ + vertices_add(&ted->vertices, new); + + return new; +} + +struct ls_vertex *ls_vertex_update(struct ls_ted *ted, struct ls_node *node) +{ + struct ls_vertex *old; + + if (node == NULL) + return NULL; + + old = ls_find_vertex_by_id(ted, node->adv); + if (old) { + if (!ls_node_same(old->node, node)) { + ls_node_del(old->node); + old->node = node; + } + return old; + } + + return ls_vertex_add(ted, node); +} + +void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex) +{ + vertices_del(&ted->vertices, vertex); + ls_vertex_del(vertex); +} + +struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, const uint64_t key) +{ + struct ls_vertex node = {}; + + if (key == 0) + return NULL; + + node.key = key; + return vertices_find(&ted->vertices, &node); +} + +struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted, + struct ls_node_id nid) +{ + struct ls_vertex node = {}; + + switch (nid.origin) { + case OSPFv2: + case STATIC: + case DIRECT: + memcpy(&node.key, &nid.id.ip.addr, IPV4_MAX_BYTELEN); + break; + case ISIS_L1: + case ISIS_L2: + memcpy(&node.key, &nid.id.iso.sys_id, ISO_SYS_ID_LEN); + break; + default: + return NULL; + } + + return vertices_find(&ted->vertices, &node); +} + +int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2) +{ + if ((v1 && !v2) || (!v1 && v2)) + return 0; + + if (!v1 && !v2) + return 1; + + if (v1->key != v2->key) + return 0; + + if (v1->node == v2->node) + return 1; + + return ls_node_same(v1->node, v2->node); +} + +/** + * Link State Edges management functions + */ + +/** + * This function allows to connect the Edge to the vertices present in the TED. + * A temporary vertex that corresponds to the source of this Edge i.e. the + * advertised router, is created if not found in the Data Base. If a Edge that + * corresponds to the reverse path is found, the Edge is attached to the + * destination vertex as destination and reverse Edge is attached to the source + * vertex as source. + * + * @param ted Link State Data Base + * @param edge Link State Edge to be attached + */ +static void ls_edge_connect_to(struct ls_ted *ted, struct ls_edge *edge) +{ + struct ls_vertex *vertex = NULL; + struct ls_node *node; + struct ls_edge *dst; + const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; + + /* First, search if there is a Vertex that correspond to the Node ID */ + vertex = ls_find_vertex_by_id(ted, edge->attributes->adv); + if (vertex == NULL) { + /* Create a new temporary Node & Vertex if not found */ + node = ls_node_new(edge->attributes->adv, inaddr_any, + in6addr_any); + vertex = ls_vertex_add(ted, node); + } + /* and attach the edge as source to the vertex */ + listnode_add(vertex->outgoing_edges, edge); + edge->source = vertex; + + /* Then search if there is a reverse Edge */ + dst = ls_find_edge_by_destination(ted, edge->attributes); + /* attach the destination edge to the vertex */ + if (dst) { + listnode_add(vertex->incoming_edges, dst); + dst->destination = vertex; + /* and destination vertex to this edge */ + vertex = dst->source; + listnode_add(vertex->incoming_edges, edge); + edge->destination = vertex; + } +} + +struct ls_edge *ls_edge_add(struct ls_ted *ted, + struct ls_attributes *attributes) +{ + struct ls_edge *new; + + if (attributes == NULL) + return NULL; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_edge)); + new->attributes = attributes; + /* Key is the IPv4 local address */ + if (!IPV4_NET0(attributes->standard.local.s_addr)) + new->key = ((uint64_t)attributes->standard.local.s_addr) + & 0xffffffff; + /* or the IPv6 local address if IPv4 is not defined */ + else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6)) + new->key = (uint64_t)(attributes->standard.local6.s6_addr32[0] + & 0xffffffff) + | ((uint64_t)attributes->standard.local6.s6_addr32[1] + << 32); + /* of local identifier if no IP addresses are defined */ + else if (attributes->standard.local_id != 0) + new->key = (uint64_t)( + (attributes->standard.local_id & 0xffffffff) + | ((uint64_t)attributes->standard.remote_id << 32)); + + /* Remove Edge if key is not known */ + if (new->key == 0) { + XFREE(MTYPE_LS_DB, new); + return NULL; + } + + edges_add(&ted->edges, new); + + /* Finally, connect edge to vertices */ + ls_edge_connect_to(ted, new); + + return new; +} + +struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key) +{ + struct ls_edge edge = {}; + + if (key == 0) + return NULL; + + edge.key = key; + return edges_find(&ted->edges, &edge); +} + +struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted, + struct ls_attributes *attributes) +{ + struct ls_edge edge = {}; + + if (attributes == NULL) + return NULL; + + /* Key is the IPv4 local address */ + if (!IPV4_NET0(attributes->standard.local.s_addr)) + edge.key = ((uint64_t)attributes->standard.local.s_addr) + & 0xffffffff; + /* or the IPv6 local address if IPv4 is not defined */ + else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.local6)) + edge.key = (uint64_t)(attributes->standard.local6.s6_addr32[0] + & 0xffffffff) + | ((uint64_t)attributes->standard.local6.s6_addr32[1] + << 32); + /* of local identifier if no IP addresses are defined */ + else if (attributes->standard.local_id != 0) + edge.key = (uint64_t)( + (attributes->standard.local_id & 0xffffffff) + | ((uint64_t)attributes->standard.remote_id << 32)); + + if (edge.key == 0) + return NULL; + + return edges_find(&ted->edges, &edge); +} + +struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted, + struct ls_attributes *attributes) +{ + struct ls_edge edge = {}; + + if (attributes == NULL) + return NULL; + + /* Key is the IPv4 local address */ + if (!IPV4_NET0(attributes->standard.remote.s_addr)) + edge.key = ((uint64_t)attributes->standard.remote.s_addr) + & 0xffffffff; + /* or the IPv6 local address if IPv4 is not defined */ + else if (!IN6_IS_ADDR_UNSPECIFIED(&attributes->standard.remote6)) + edge.key = + (uint64_t)(attributes->standard.remote6.s6_addr32[0] + & 0xffffffff) + | ((uint64_t)attributes->standard.remote6.s6_addr32[1] + << 32); + /* of local identifier if no IP addresses are defined */ + else if (attributes->standard.remote_id != 0) + edge.key = (uint64_t)( + (attributes->standard.remote_id & 0xffffffff) + | ((uint64_t)attributes->standard.local_id << 32)); + + if (edge.key == 0) + return NULL; + + return edges_find(&ted->edges, &edge); +} + +struct ls_edge *ls_edge_update(struct ls_ted *ted, + struct ls_attributes *attributes) +{ + struct ls_edge *old; + + if (attributes == NULL) + return NULL; + + /* First, search for an existing Edge */ + old = ls_find_edge_by_source(ted, attributes); + if (old) { + /* Check if attributes are similar */ + if (!ls_attributes_same(old->attributes, attributes)) { + ls_attributes_del(old->attributes); + old->attributes = attributes; + } + return old; + } + + /* If not found, add new Edge from the attributes */ + return ls_edge_add(ted, attributes); +} + +void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge) +{ + /* Fist disconnect Edge */ + ls_disconnect_edge(edge); + /* Then remove it from the Data Base */ + edges_del(&ted->edges, edge); + XFREE(MTYPE_LS_DB, edge); +} + +/** + * Link State Subnet Management functions. + */ +struct ls_subnet *ls_subnet_add(struct ls_ted *ted, + struct ls_prefix *ls_pref) +{ + struct ls_subnet *new; + struct ls_vertex *vertex; + struct ls_node *node; + const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; + + if (ls_pref == NULL) + return NULL; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_subnet)); + new->ls_pref = ls_pref; + new->key = ls_pref->pref; + + /* Find Vertex */ + vertex = ls_find_vertex_by_id(ted, ls_pref->adv); + if (vertex == NULL) { + /* Create a new temporary Node & Vertex if not found */ + node = ls_node_new(ls_pref->adv, inaddr_any, in6addr_any); + vertex = ls_vertex_add(ted, node); + } + /* And attach the subnet to the corresponding Vertex */ + new->vertex = vertex; + listnode_add(vertex->prefixes, new); + + subnets_add(&ted->subnets, new); + + return new; +} + +void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet) +{ + subnets_del(&ted->subnets, subnet); + XFREE(MTYPE_LS_DB, subnet); +} + +struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix) +{ + struct ls_subnet subnet = {}; + + subnet.key = prefix; + return subnets_find(&ted->subnets, &subnet); +} + +/** + * Link State TED management functions + */ +struct ls_ted *ls_ted_new(const uint32_t key, const char *name, + uint32_t as_number) +{ + struct ls_ted *new; + + new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_ted)); + if (new == NULL) + return new; + + /* Set basic information for this ted */ + new->key = key; + new->as_number = as_number; + strlcpy(new->name, name, MAX_NAME_LENGTH); + + /* Initialize the various RB tree */ + vertices_init(&new->vertices); + edges_init(&new->edges); + subnets_init(&new->subnets); + + return new; +} + +void ls_ted_del(struct ls_ted *ted) +{ + if (ted == NULL) + return; + + /* Release RB Tree */ + vertices_fini(&ted->vertices); + edges_fini(&ted->edges); + subnets_fini(&ted->subnets); + + XFREE(MTYPE_LS_DB, ted); + ted = NULL; +} + +void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, bool source) +{ + if (vertex == NULL || edge == NULL) + return; + + if (source) { + listnode_add(vertex->outgoing_edges, edge); + edge->source = vertex; + } else { + listnode_add(vertex->incoming_edges, edge); + edge->destination = vertex; + } +} + +void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, bool source) +{ + + if (vertex == NULL || edge == NULL) + return; + + if (source) { + listnode_delete(vertex->outgoing_edges, edge); + edge->source = NULL; + } else { + listnode_delete(vertex->incoming_edges, edge); + edge->destination = NULL; + } +} + +void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, + struct ls_edge *edge) +{ + if (edge == NULL) + return; + + edge->source = src; + edge->destination = dst; + + if (src != NULL) + listnode_add(src->outgoing_edges, edge); + + if (dst != NULL) + listnode_add(dst->incoming_edges, edge); + +} + +void ls_disconnect_edge(struct ls_edge *edge) +{ + if (edge == NULL) + return; + + ls_disconnect(edge->source, edge, true); + ls_disconnect(edge->destination, edge, false); +} + +/** + * Link State Message management functions + */ + +static struct ls_node *ls_parse_node(struct stream *s) +{ + struct ls_node *node; + size_t len; + + node = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_node)); + if (node == NULL) + return NULL; + + STREAM_GET(&node->adv, s, sizeof(struct ls_node_id)); + STREAM_GETW(s, node->flags); + if (CHECK_FLAG(node->flags, LS_NODE_NAME)) { + STREAM_GETC(s, len); + STREAM_GET(node->name, s, len); + } + if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID)) + node->router_id.s_addr = stream_get_ipv4(s); + if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID6)) + STREAM_GET(&node->router6_id, s, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(node->flags, LS_NODE_FLAG)) + STREAM_GETC(s, node->node_flag); + if (CHECK_FLAG(node->flags, LS_NODE_TYPE)) + STREAM_GETC(s, node->type); + if (CHECK_FLAG(node->flags, LS_NODE_AS_NUMBER)) + STREAM_GETL(s, node->as_number); + if (CHECK_FLAG(node->flags, LS_NODE_SR)) { + STREAM_GETL(s, node->srgb.lower_bound); + STREAM_GETL(s, node->srgb.range_size); + STREAM_GETC(s, node->srgb.flag); + STREAM_GET(node->algo, s, 2); + } + if (CHECK_FLAG(node->flags, LS_NODE_SRLB)) { + STREAM_GETL(s, node->srlb.lower_bound); + STREAM_GETL(s, node->srlb.range_size); + } + if (CHECK_FLAG(node->flags, LS_NODE_MSD)) + STREAM_GETC(s, node->msd); + + return node; + +stream_failure: + zlog_err("LS(%s): Could not parse Link State Node. Abort!", __func__); + XFREE(MTYPE_LS_DB, node); + return NULL; +} + +static struct ls_attributes *ls_parse_attributes(struct stream *s) +{ + struct ls_attributes *attr; + size_t len; + + attr = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_attributes)); + if (attr == NULL) + return NULL; + attr->srlgs = NULL; + + STREAM_GET(&attr->adv, s, sizeof(struct ls_node_id)); + STREAM_GETL(s, attr->flags); + if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) { + STREAM_GETC(s, len); + STREAM_GET(attr->name, s, len); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC)) + STREAM_GETL(s, attr->standard.metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) + STREAM_GETL(s, attr->standard.te_metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) + STREAM_GETL(s, attr->standard.admin_group); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) + attr->standard.local.s_addr = stream_get_ipv4(s); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) + attr->standard.remote.s_addr = stream_get_ipv4(s); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) + STREAM_GET(&attr->standard.local6, s, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) + STREAM_GET(&attr->standard.remote6, s, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) + STREAM_GETL(s, attr->standard.local_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) + STREAM_GETL(s, attr->standard.remote_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW)) + STREAM_GETF(s, attr->standard.max_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW)) + STREAM_GETF(s, attr->standard.max_rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW)) + for (len = 0; len < MAX_CLASS_TYPE; len++) + STREAM_GETF(s, attr->standard.unrsv_bw[len]); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS)) + STREAM_GETL(s, attr->standard.remote_as); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR)) + attr->standard.remote_addr.s_addr = stream_get_ipv4(s); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6)) + STREAM_GET(&attr->standard.remote_addr6, s, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY)) + STREAM_GETL(s, attr->extended.delay); + if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) { + STREAM_GETL(s, attr->extended.min_delay); + STREAM_GETL(s, attr->extended.max_delay); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER)) + STREAM_GETL(s, attr->extended.jitter); + if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS)) + STREAM_GETL(s, attr->extended.pkt_loss); + if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW)) + STREAM_GETF(s, attr->extended.ava_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW)) + STREAM_GETF(s, attr->extended.rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW)) + STREAM_GETF(s, attr->extended.used_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) { + STREAM_GETL(s, attr->adj_sid[0].sid); + STREAM_GETC(s, attr->adj_sid[0].flags); + STREAM_GETC(s, attr->adj_sid[0].weight); + if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2) + STREAM_GET(attr->adj_sid[0].neighbor.sysid, s, + ISO_SYS_ID_LEN); + else if (attr->adv.origin == OSPFv2) + attr->adj_sid[0].neighbor.addr.s_addr = + stream_get_ipv4(s); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) { + STREAM_GETL(s, attr->adj_sid[1].sid); + STREAM_GETC(s, attr->adj_sid[1].flags); + STREAM_GETC(s, attr->adj_sid[1].weight); + if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2) + STREAM_GET(attr->adj_sid[1].neighbor.sysid, s, + ISO_SYS_ID_LEN); + else if (attr->adv.origin == OSPFv2) + attr->adj_sid[1].neighbor.addr.s_addr = + stream_get_ipv4(s); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { + STREAM_GETC(s, len); + attr->srlgs = XCALLOC(MTYPE_LS_DB, len*sizeof(uint32_t)); + attr->srlg_len = len; + for (len = 0; len < attr->srlg_len; len++) + STREAM_GETL(s, attr->srlgs[len]); + } + + return attr; + +stream_failure: + zlog_err("LS(%s): Could not parse Link State Attributes. Abort!", + __func__); + /* Clean memeory allocation */ + if (attr->srlgs != NULL) + XFREE(MTYPE_LS_DB, attr->srlgs); + XFREE(MTYPE_LS_DB, attr); + return NULL; + +} + +static struct ls_prefix *ls_parse_prefix(struct stream *s) +{ + struct ls_prefix *ls_pref; + size_t len; + + ls_pref = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix)); + if (ls_pref == NULL) + return NULL; + + STREAM_GET(&ls_pref->adv, s, sizeof(struct ls_node_id)); + STREAM_GETW(s, ls_pref->flags); + STREAM_GETC(s, ls_pref->pref.family); + STREAM_GETW(s, ls_pref->pref.prefixlen); + len = prefix_blen(&ls_pref->pref); + STREAM_GET(&ls_pref->pref.u.prefix, s, len); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_IGP_FLAG)) + STREAM_GETC(s, ls_pref->igp_flag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_ROUTE_TAG)) + STREAM_GETL(s, ls_pref->route_tag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_EXTENDED_TAG)) + STREAM_GETQ(s, ls_pref->extended_tag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC)) + STREAM_GETL(s, ls_pref->metric); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) { + STREAM_GETL(s, ls_pref->sr.sid); + STREAM_GETC(s, ls_pref->sr.sid_flag); + STREAM_GETC(s, ls_pref->sr.algo); + } + + return ls_pref; + +stream_failure: + zlog_err("LS(%s): Could not parse Link State Prefix. Abort!", __func__); + XFREE(MTYPE_LS_DB, ls_pref); + return NULL; +} + +struct ls_message *ls_parse_msg(struct stream *s) +{ + struct ls_message *msg; + + msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message)); + if (msg == NULL) + return NULL; + + /* Read LS Message header */ + STREAM_GETC(s, msg->event); + STREAM_GETC(s, msg->type); + STREAM_GET(&msg->remote_id, s, sizeof(struct ls_node_id)); + + /* Read Message Payload */ + switch (msg->type) { + case LS_MSG_TYPE_NODE: + msg->data.node = ls_parse_node(s); + break; + case LS_MSG_TYPE_ATTRIBUTES: + msg->data.attr = ls_parse_attributes(s); + break; + case LS_MSG_TYPE_PREFIX: + msg->data.prefix = ls_parse_prefix(s); + break; + default: + zlog_err("Unsupported Payload"); + goto stream_failure; + } + + if (msg->data.node == NULL || msg->data.attr == NULL + || msg->data.prefix == NULL) + goto stream_failure; + + return msg; + +stream_failure: + zlog_err("LS(%s): Could not parse LS message. Abort!", __func__); + XFREE(MTYPE_LS_DB, msg); + return NULL; +} + +static int ls_format_node(struct stream *s, struct ls_node *node) +{ + size_t len; + + /* Push Advertise node information first */ + stream_put(s, &node->adv, sizeof(struct ls_node_id)); + + /* Push Flags & Origin then Node information if there are present */ + stream_putw(s, node->flags); + if (CHECK_FLAG(node->flags, LS_NODE_NAME)) { + len = strlen(node->name); + stream_putc(s, len + 1); + stream_put(s, node->name, len); + stream_putc(s, '\0'); + } + if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID)) + stream_put_ipv4(s, node->router_id.s_addr); + if (CHECK_FLAG(node->flags, LS_NODE_ROUTER_ID6)) + stream_put(s, &node->router6_id, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(node->flags, LS_NODE_FLAG)) + stream_putc(s, node->node_flag); + if (CHECK_FLAG(node->flags, LS_NODE_TYPE)) + stream_putc(s, node->type); + if (CHECK_FLAG(node->flags, LS_NODE_AS_NUMBER)) + stream_putl(s, node->as_number); + if (CHECK_FLAG(node->flags, LS_NODE_SR)) { + stream_putl(s, node->srgb.lower_bound); + stream_putl(s, node->srgb.range_size); + stream_putc(s, node->srgb.flag); + stream_put(s, node->algo, 2); + } + if (CHECK_FLAG(node->flags, LS_NODE_SRLB)) { + stream_putl(s, node->srlb.lower_bound); + stream_putl(s, node->srlb.range_size); + } + if (CHECK_FLAG(node->flags, LS_NODE_MSD)) + stream_putc(s, node->msd); + + return 0; +} + +static int ls_format_attributes(struct stream *s, struct ls_attributes *attr) +{ + size_t len; + + /* Push Advertise node information first */ + stream_put(s, &attr->adv, sizeof(struct ls_node_id)); + + /* Push Flags & Origin then LS attributes if there are present */ + stream_putl(s, attr->flags); + if (CHECK_FLAG(attr->flags, LS_ATTR_NAME)) { + len = strlen(attr->name); + stream_putc(s, len + 1); + stream_put(s, attr->name, len); + stream_putc(s, '\0'); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_METRIC)) + stream_putl(s, attr->standard.metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_TE_METRIC)) + stream_putl(s, attr->standard.te_metric); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADM_GRP)) + stream_putl(s, attr->standard.admin_group); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) + stream_put_ipv4(s, attr->standard.local.s_addr); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) + stream_put_ipv4(s, attr->standard.remote.s_addr); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) + stream_put(s, &attr->standard.local6, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) + stream_put(s, &attr->standard.remote6, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) + stream_putl(s, attr->standard.local_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) + stream_putl(s, attr->standard.remote_id); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_BW)) + stream_putf(s, attr->standard.max_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_MAX_RSV_BW)) + stream_putf(s, attr->standard.max_rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_UNRSV_BW)) + for (len = 0; len < MAX_CLASS_TYPE; len++) + stream_putf(s, attr->standard.unrsv_bw[len]); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_AS)) + stream_putl(s, attr->standard.remote_as); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR)) + stream_put_ipv4(s, attr->standard.remote_addr.s_addr); + if (CHECK_FLAG(attr->flags, LS_ATTR_REMOTE_ADDR6)) + stream_put(s, &attr->standard.remote_addr6, IPV6_MAX_BYTELEN); + if (CHECK_FLAG(attr->flags, LS_ATTR_DELAY)) + stream_putl(s, attr->extended.delay); + if (CHECK_FLAG(attr->flags, LS_ATTR_MIN_MAX_DELAY)) { + stream_putl(s, attr->extended.min_delay); + stream_putl(s, attr->extended.max_delay); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_JITTER)) + stream_putl(s, attr->extended.jitter); + if (CHECK_FLAG(attr->flags, LS_ATTR_PACKET_LOSS)) + stream_putl(s, attr->extended.pkt_loss); + if (CHECK_FLAG(attr->flags, LS_ATTR_AVA_BW)) + stream_putf(s, attr->extended.ava_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_RSV_BW)) + stream_putf(s, attr->extended.rsv_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_USE_BW)) + stream_putf(s, attr->extended.used_bw); + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID)) { + stream_putl(s, attr->adj_sid[0].sid); + stream_putc(s, attr->adj_sid[0].flags); + stream_putc(s, attr->adj_sid[0].weight); + if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2) + stream_put(s, attr->adj_sid[0].neighbor.sysid, + ISO_SYS_ID_LEN); + else if (attr->adv.origin == OSPFv2) + stream_put_ipv4(s, + attr->adj_sid[0].neighbor.addr.s_addr); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) { + stream_putl(s, attr->adj_sid[1].sid); + stream_putc(s, attr->adj_sid[1].flags); + stream_putc(s, attr->adj_sid[1].weight); + if (attr->adv.origin == ISIS_L1 || attr->adv.origin == ISIS_L2) + stream_put(s, attr->adj_sid[1].neighbor.sysid, + ISO_SYS_ID_LEN); + else if (attr->adv.origin == OSPFv2) + stream_put_ipv4(s, + attr->adj_sid[1].neighbor.addr.s_addr); + } + if (CHECK_FLAG(attr->flags, LS_ATTR_SRLG)) { + stream_putc(s, attr->srlg_len); + for (len = 0; len < attr->srlg_len; len++) + stream_putl(s, attr->srlgs[len]); + } + + return 0; +} + +static int ls_format_prefix(struct stream *s, struct ls_prefix *ls_pref) +{ + size_t len; + + /* Push Advertise node information first */ + stream_put(s, &ls_pref->adv, sizeof(struct ls_node_id)); + + /* Push Flags, Origin & Prefix then information if there are present */ + stream_putw(s, ls_pref->flags); + stream_putc(s, ls_pref->pref.family); + stream_putw(s, ls_pref->pref.prefixlen); + len = prefix_blen(&ls_pref->pref); + stream_put(s, &ls_pref->pref.u.prefix, len); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_IGP_FLAG)) + stream_putc(s, ls_pref->igp_flag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_ROUTE_TAG)) + stream_putl(s, ls_pref->route_tag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_EXTENDED_TAG)) + stream_putq(s, ls_pref->extended_tag); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC)) + stream_putl(s, ls_pref->metric); + if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR)) { + stream_putl(s, ls_pref->sr.sid); + stream_putc(s, ls_pref->sr.sid_flag); + stream_putc(s, ls_pref->sr.algo); + } + + return 0; +} + +static int ls_format_msg(struct stream *s, struct ls_message *msg) +{ + + /* Prepare Link State header */ + stream_putc(s, msg->event); + stream_putc(s, msg->type); + stream_put(s, &msg->remote_id, sizeof(struct ls_node_id)); + + /* Add Message Payload */ + switch (msg->type) { + case LS_MSG_TYPE_NODE: + return ls_format_node(s, msg->data.node); + case LS_MSG_TYPE_ATTRIBUTES: + return ls_format_attributes(s, msg->data.attr); + case LS_MSG_TYPE_PREFIX: + return ls_format_prefix(s, msg->data.prefix); + default: + zlog_warn("Unsupported Payload"); + break; + } + + return -1; +} + +int ls_send_msg(struct zclient *zclient, struct ls_message *msg, + struct zapi_opaque_reg_info *dst) +{ + struct stream *s; + uint16_t flags = 0; + + /* Check buffer size */ + if (STREAM_SIZE(zclient->obuf) < + (ZEBRA_HEADER_SIZE + sizeof(uint32_t) + sizeof(msg))) + return -1; + + s = zclient->obuf; + stream_reset(s); + + zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); + + /* Send sub-type, flags and destination for unicast message */ + stream_putl(s, LINK_STATE_UPDATE); + if (dst != NULL) { + SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST); + stream_putw(s, flags); + /* Send destination client info */ + stream_putc(s, dst->proto); + stream_putw(s, dst->instance); + stream_putl(s, dst->session_id); + } else + stream_putw(s, flags); + + /* Format Link State message */ + if (ls_format_msg(s, msg) < 0) { + stream_reset(s); + return -1; + } + + /* Put length into the header at the start of the stream. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + +struct ls_message *ls_vertex2msg(struct ls_message *msg, + struct ls_vertex *vertex) +{ + /* Allocate space if needed */ + if (msg == NULL) + msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message)); + else + memset(msg, 0, sizeof(*msg)); + + msg->type = LS_MSG_TYPE_NODE; + msg->data.node = vertex->node; + msg->remote_id.origin = NONE; + + return msg; +} + +struct ls_message *ls_edge2msg(struct ls_message *msg, struct ls_edge *edge) +{ + /* Allocate space if needed */ + if (msg == NULL) + msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message)); + else + memset(msg, 0, sizeof(*msg)); + + msg->type = LS_MSG_TYPE_ATTRIBUTES; + msg->data.attr = edge->attributes; + if (edge->destination != NULL) + msg->remote_id = edge->destination->node->adv; + else + msg->remote_id.origin = NONE; + + return msg; +} + +struct ls_message *ls_subnet2msg(struct ls_message *msg, + struct ls_subnet *subnet) +{ + /* Allocate space if needed */ + if (msg == NULL) + msg = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_message)); + else + memset(msg, 0, sizeof(*msg)); + + msg->type = LS_MSG_TYPE_PREFIX; + msg->data.prefix = subnet->ls_pref; + msg->remote_id.origin = NONE; + + return msg; +} + +void ls_delete_msg(struct ls_message *msg) +{ + if (msg == NULL) + return; + + switch (msg->type) { + case LS_MSG_TYPE_NODE: + if (msg->data.node) + XFREE(MTYPE_LS_DB, msg->data.node); + break; + case LS_MSG_TYPE_ATTRIBUTES: + if (msg->data.attr) + XFREE(MTYPE_LS_DB, msg->data.attr); + break; + case LS_MSG_TYPE_PREFIX: + if (msg->data.prefix) + XFREE(MTYPE_LS_DB, msg->data.prefix); + break; + default: + break; + } + + XFREE(MTYPE_LS_DB, msg); +} + +int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, + struct zapi_opaque_reg_info *dst) +{ + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + struct ls_message msg; + + /* Prepare message */ + msg.event = LS_MSG_EVENT_SYNC; + + /* Loop TED, start sending Node, then Attributes and finally Prefix */ + frr_each(vertices, &ted->vertices, vertex) { + ls_vertex2msg(&msg, vertex); + ls_send_msg(zclient, &msg, dst); + } + frr_each(edges, &ted->edges, edge) { + ls_edge2msg(&msg, edge); + ls_send_msg(zclient, &msg, dst); + } + frr_each(subnets, &ted->subnets, subnet) { + ls_subnet2msg(&msg, subnet); + ls_send_msg(zclient, &msg, dst); + } + return 0; +} + +void ls_dump_ted(struct ls_ted *ted) +{ + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + struct ls_message msg; + + zlog_debug("(%s) Ted init", __func__); + /* Prepare message */ + msg.event = LS_MSG_EVENT_SYNC; + + /* Loop TED, start printing Node, then Attributes and finally Prefix */ + frr_each(vertices, &ted->vertices, vertex) { + ls_vertex2msg(&msg, vertex); + zlog_debug("\tTed node (%s %pI4 %s)", + vertex->node->name[0] ? vertex->node->name + : "no name node", + &vertex->node->router_id, + vertex->node->adv.origin == DIRECT ? "DIRECT" + : "NO DIRECT"); + struct listnode *lst_node; + struct ls_edge *vertex_edge; + + for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node, + vertex_edge)) { + zlog_debug( + "\t\tinc edge key:%lldn attr key:%pI4 loc:(%pI4) rmt:(%pI4)", + vertex_edge->key, + &vertex_edge->attributes->adv.id.ip.addr, + &vertex_edge->attributes->standard.local, + &vertex_edge->attributes->standard.remote); + } + for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node, + vertex_edge)) { + zlog_debug( + "\t\tout edge key:%lld attr key:%pI4 loc:(%pI4) rmt:(%pI4)", + vertex_edge->key, + &vertex_edge->attributes->adv.id.ip.addr, + &vertex_edge->attributes->standard.local, + &vertex_edge->attributes->standard.remote); + } + } + frr_each(edges, &ted->edges, edge) { + ls_edge2msg(&msg, edge); + zlog_debug("\tTed edge key:%lld src:%s dst:%s", edge->key, + edge->source ? edge->source->node->name + : "no_source", + edge->destination ? edge->destination->node->name + : "no_dest"); + } + frr_each(subnets, &ted->subnets, subnet) { + ls_subnet2msg(&msg, subnet); + zlog_debug( + "\tTed subnet key:%s vertex:%pI4 pfx:%pFX", + subnet->key.family == AF_INET + ? inet_ntoa(subnet->key.u.prefix4) + : inet6_ntoa(subnet->key.u.prefix6), + &subnet->vertex->node->adv.id.ip.addr, + &subnet->ls_pref->pref); + } + zlog_debug("(%s) Ted end", __func__); +} diff --git a/lib/link_state.h b/lib/link_state.h new file mode 100644 index 0000000000..93669f5b23 --- /dev/null +++ b/lib/link_state.h @@ -0,0 +1,780 @@ +/* + * Link State Database definition - ted.h + * + * Author: Olivier Dugeon <olivier.dugeon@orange.com> + * + * Copyright (C) 2020 Orange http://www.orange.com + * + * This file is part of Free Range Routing (FRR). + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_LINK_STATE_H_ +#define _FRR_LINK_STATE_H_ + +#include "typesafe.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This file defines the model used to implement a Link State Database + * suitable to be used by various protocol like RSVP-TE, BGP-LS, PCEP ... + * This database is normally fulfill by the link state routing protocol, + * commonly OSPF or ISIS, carrying Traffic Engineering information within + * Link State Attributes. See, RFC3630.(OSPF-TE) and RFC5305 (ISIS-TE). + * + * At least, 3 types of Link State structure are defined: + * - Link State Node that groups all information related to a node + * - Link State Attributes that groups all information related to a link + * - Link State Prefix that groups all information related to a prefix + * + * These 3 types of structures are those handled by BGP-LS (see RFC7752). + * + * Each structure, in addition to the specific parameters, embed the node + * identifier which advertises the Link State and a bit mask as flags to + * indicates which parameters are valid i.e. for which the value corresponds + * to a Link State information convey by the routing protocol. + * Node identifier is composed of the route id as IPv4 address plus the area + * id for OSPF and the ISO System id plus the IS-IS level for IS-IS. + */ + +/* Link State Common definitions */ +#define MAX_NAME_LENGTH 256 +#define ISO_SYS_ID_LEN 6 + +/* Type of Node */ +enum ls_node_type { + STANDARD, /* a P or PE node */ + ABR, /* an Array Border Node */ + ASBR, /* an Autonomous System Border Node */ + PSEUDO, /* a Pseudo Node */ +}; + +/* Origin of the Link State information */ +enum ls_origin {NONE = 0, ISIS_L1, ISIS_L2, OSPFv2, DIRECT, STATIC}; + +/** + * Link State Node Identifier as: + * - IPv4 address + Area ID for OSPF + * - ISO System ID + ISIS Level for ISIS + */ +struct ls_node_id { + enum ls_origin origin; /* Origin of the LS information */ + union { + struct { + struct in_addr addr; /* OSPF Router IS */ + struct in_addr area_id; /* OSPF Area ID */ + } ip; + struct { + uint8_t sys_id[ISO_SYS_ID_LEN]; /* ISIS System ID */ + uint8_t level; /* ISIS Level */ + uint8_t padding; + } iso; + } id __attribute__((aligned(8))); +}; + +/* Link State flags to indicate which Node parameters are valid */ +#define LS_NODE_UNSET 0x0000 +#define LS_NODE_NAME 0x0001 +#define LS_NODE_ROUTER_ID 0x0002 +#define LS_NODE_ROUTER_ID6 0x0004 +#define LS_NODE_FLAG 0x0008 +#define LS_NODE_TYPE 0x0010 +#define LS_NODE_AS_NUMBER 0x0020 +#define LS_NODE_SR 0x0040 +#define LS_NODE_SRLB 0x0080 +#define LS_NODE_MSD 0x0100 + +/* Link State Node structure */ +struct ls_node { + uint16_t flags; /* Flag for parameters validity */ + struct ls_node_id adv; /* Adv. Router of this Link State */ + char name[MAX_NAME_LENGTH]; /* Name of the Node (IS-IS only) */ + struct in_addr router_id; /* IPv4 Router ID */ + struct in6_addr router6_id; /* IPv6 Router ID */ + uint8_t node_flag; /* IS-IS or OSPF Node flag */ + enum node_type type; /* Type of Node */ + uint32_t as_number; /* Local or neighbor AS number */ + struct { /* Segment Routing Global Block */ + uint32_t lower_bound; /* MPLS label lower bound */ + uint32_t range_size; /* MPLS label range size */ + uint8_t flag; /* IS-IS SRGB flags */ + } srgb; +#define LS_NODE_SRGB_SIZE 9 + struct { /* Segment Routing Local Block */ + uint32_t lower_bound; /* MPLS label lower bound */ + uint32_t range_size; /* MPLS label range size */ + } srlb; +#define LS_NODE_SRLB_SIZE 8 + uint8_t algo[2]; /* Segment Routing Algorithms */ + uint8_t msd; /* Maximum Stack Depth */ +}; + +/* Link State flags to indicate which Attribute parameters are valid */ +#define LS_ATTR_UNSET 0x00000000 +#define LS_ATTR_NAME 0x00000001 +#define LS_ATTR_METRIC 0x00000002 +#define LS_ATTR_TE_METRIC 0x00000004 +#define LS_ATTR_ADM_GRP 0x00000008 +#define LS_ATTR_LOCAL_ADDR 0x00000010 +#define LS_ATTR_NEIGH_ADDR 0x00000020 +#define LS_ATTR_LOCAL_ADDR6 0x00000040 +#define LS_ATTR_NEIGH_ADDR6 0x00000080 +#define LS_ATTR_LOCAL_ID 0x00000100 +#define LS_ATTR_NEIGH_ID 0x00000200 +#define LS_ATTR_MAX_BW 0x00000400 +#define LS_ATTR_MAX_RSV_BW 0x00000800 +#define LS_ATTR_UNRSV_BW 0x00001000 +#define LS_ATTR_REMOTE_AS 0x00002000 +#define LS_ATTR_REMOTE_ADDR 0x00004000 +#define LS_ATTR_REMOTE_ADDR6 0x00008000 +#define LS_ATTR_DELAY 0x00010000 +#define LS_ATTR_MIN_MAX_DELAY 0x00020000 +#define LS_ATTR_JITTER 0x00040000 +#define LS_ATTR_PACKET_LOSS 0x00080000 +#define LS_ATTR_AVA_BW 0x00100000 +#define LS_ATTR_RSV_BW 0x00200000 +#define LS_ATTR_USE_BW 0x00400000 +#define LS_ATTR_ADJ_SID 0x00800000 +#define LS_ATTR_BCK_ADJ_SID 0x01000000 +#define LS_ATTR_SRLG 0x02000000 + +/* Link State Attributes */ +struct ls_attributes { + uint32_t flags; /* Flag for parameters validity */ + struct ls_node_id adv; /* Adv. Router of this Link State */ + char name[MAX_NAME_LENGTH]; /* Name of the Edge. Could be null */ + struct { /* Standard TE metrics */ + uint32_t metric; /* IGP standard metric */ + uint32_t te_metric; /* Traffic Engineering metric */ + uint32_t admin_group; /* Administrative Group */ + struct in_addr local; /* Local IPv4 address */ + struct in_addr remote; /* Remote IPv4 address */ + struct in6_addr local6; /* Local IPv6 address */ + struct in6_addr remote6; /* Remote IPv6 address */ + uint32_t local_id; /* Local Identifier */ + uint32_t remote_id; /* Remote Identifier */ + float max_bw; /* Maximum Link Bandwidth */ + float max_rsv_bw; /* Maximum Reservable BW */ + float unrsv_bw[8]; /* Unreserved BW per CT (8) */ + uint32_t remote_as; /* Remote AS number */ + struct in_addr remote_addr; /* Remote IPv4 address */ + struct in6_addr remote_addr6; /* Remote IPv6 address */ + } standard; +#define LS_ATTR_STANDARD_SIZE 124 + struct { /* Extended TE Metrics */ + uint32_t delay; /* Unidirectional average delay */ + uint32_t min_delay; /* Unidirectional minimum delay */ + uint32_t max_delay; /* Unidirectional maximum delay */ + uint32_t jitter; /* Unidirectional delay variation */ + uint32_t pkt_loss; /* Unidirectional packet loss */ + float ava_bw; /* Available Bandwidth */ + float rsv_bw; /* Reserved Bandwidth */ + float used_bw; /* Utilized Bandwidth */ + } extended; +#define LS_ATTR_EXTENDED_SIZE 32 + struct { /* (LAN)-Adjacency SID for OSPF */ + uint32_t sid; /* SID as MPLS label or index */ + uint8_t flags; /* Flags */ + uint8_t weight; /* Administrative weight */ + union { + struct in_addr addr; /* Neighbor @IP for OSPF */ + uint8_t sysid[ISO_SYS_ID_LEN]; /* or Sys-ID for ISIS */ + } neighbor; + } adj_sid[2]; /* Primary & Backup (LAN)-Adj. SID */ +#define LS_ATTR_ADJ_SID_SIZE 120 + uint32_t *srlgs; /* List of Shared Risk Link Group */ + uint8_t srlg_len; /* number of SRLG in the list */ +}; + +/* Link State flags to indicate which Prefix parameters are valid */ +#define LS_PREF_UNSET 0x00 +#define LS_PREF_IGP_FLAG 0x01 +#define LS_PREF_ROUTE_TAG 0x02 +#define LS_PREF_EXTENDED_TAG 0x04 +#define LS_PREF_METRIC 0x08 +#define LS_PREF_SR 0x10 + +/* Link State Prefix */ +struct ls_prefix { + uint8_t flags; /* Flag for parameters validity */ + struct ls_node_id adv; /* Adv. Router of this Link State */ + struct prefix pref; /* IPv4 or IPv6 prefix */ + uint8_t igp_flag; /* IGP Flags associated to the prefix */ + uint32_t route_tag; /* IGP Route Tag */ + uint64_t extended_tag; /* IGP Extended Route Tag */ + uint32_t metric; /* Route metric for this prefix */ + struct { + uint32_t sid; /* Segment Routing ID */ + uint8_t sid_flag; /* Segment Routing Flags */ + uint8_t algo; /* Algorithm for Segment Routing */ + } sr; +}; + +/** + * Create a new Link State Node. Structure is dynamically allocated. + * + * @param adv Mandatory Link State Node ID i.e. advertise router information + * @param rid Router ID as IPv4 address + * @param rid6 Router ID as IPv6 address + * + * @return New Link State Node + */ +extern struct ls_node *ls_node_new(struct ls_node_id adv, struct in_addr rid, + struct in6_addr rid6); + +/** + * Remove Link State Node. Data structure is freed. + * + * @param node Pointer to a valid Link State Node structure + */ +extern void ls_node_del(struct ls_node *node); + +/** + * Check if two Link State Nodes are equal. Note that this routine has the same + * return value sense as '==' (which is different from a comparison). + * + * @param n1 First Link State Node to be compare + * @param n2 Second Link State Node to be compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_node_same(struct ls_node *n1, struct ls_node *n2); + +/** + * Create a new Link State Attributes. Structure is dynamically allocated. + * At least one of parameters MUST be valid and not equal to 0. + * + * @param adv Mandatory Link State Node ID i.e. advertise router ID + * @param local Local IPv4 address + * @param local6 Local Ipv6 address + * @param local_id Local Identifier + * + * @return New Link State Attributes + */ +extern struct ls_attributes *ls_attributes_new(struct ls_node_id adv, + struct in_addr local, + struct in6_addr local6, + uint32_t local_id); + +/** + * Remove Link State Attributes. Data structure is freed. + * + * @param attr Pointer to a valid Link State Attribute structure + */ +extern void ls_attributes_del(struct ls_attributes *attr); + +/** + * Check if two Link State Attributes are equal. Note that this routine has the + * same return value sense as '==' (which is different from a comparison). + * + * @param a1 First Link State Attributes to be compare + * @param a2 Second Link State Attributes to be compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_attributes_same(struct ls_attributes *a1, + struct ls_attributes *a2); + +/** + * In addition a Graph model is defined as an overlay on top of link state + * database in order to ease Path Computation algorithm implementation. + * Denoted G(V, E), a graph is composed by a list of Vertices (V) which + * represents the network Node and a list of Edges (E) which represents node + * Link. An additional list of prefixes (P) is also added. + * A prefix (P) is also attached to the Vertex (V) which advertise it. + * + * Vertex (V) contains the list of outgoing Edges (E) that connect this Vertex + * with its direct neighbors and the list of incoming Edges (E) that connect + * the direct neighbors to this Vertex. Indeed, the Edge (E) is unidirectional, + * thus, it is necessary to add 2 Edges to model a bidirectional relation + * between 2 Vertices. + * + * Edge (E) contains the source and destination Vertex that this Edge + * is connecting. + * + * A unique Key is used to identify both Vertices and Edges within the Graph. + * An easy way to build this key is to used the IP address: i.e. loopback + * address for Vertices and link IP address for Edges. + * + * -------------- --------------------------- -------------- + * | Connected |---->| Connected Edge Va to Vb |--->| Connected | + * --->| Vertex | --------------------------- | Vertex |----> + * | | | | + * | - Key (Va) | | - Key (Vb) | + * <---| - Vertex | --------------------------- | - Vertex |<---- + * | |<----| Connected Edge Vb to Va |<---| | + * -------------- --------------------------- -------------- + * + */ + +/* Link State Vertex structure */ +PREDECL_RBTREE_UNIQ(vertices) +struct ls_vertex { + struct vertices_item entry; /* Entry in RB Tree */ + uint64_t key; /* Unique Key identifier */ + struct ls_node *node; /* Link State Node */ + struct list *incoming_edges; /* List of incoming Link State links */ + struct list *outgoing_edges; /* List of outgoing Link State links */ + struct list *prefixes; /* List of advertised prefix */ +}; + +/* Link State Edge structure */ +PREDECL_RBTREE_UNIQ(edges) +struct ls_edge { + struct edges_item entry; /* Entry in RB tree */ + uint64_t key; /* Unique Key identifier */ + struct ls_attributes *attributes; /* Link State attributes */ + struct ls_vertex *source; /* Pointer to the source Vertex */ + struct ls_vertex *destination; /* Pointer to the destination Vertex */ +}; + +/* Link State Subnet structure */ +PREDECL_RBTREE_UNIQ(subnets) +struct ls_subnet { + struct subnets_item entry; /* Entry in RB tree */ + struct prefix key; /* Unique Key identifier */ + struct ls_vertex *vertex; /* Back pointer to the Vertex owner */ + struct ls_prefix *ls_pref; /* Link State Prefix */ +}; + +/* Declaration of Vertices, Edges and Prefixes RB Trees */ +macro_inline int vertex_cmp(const struct ls_vertex *node1, + const struct ls_vertex *node2) +{ + return (node1->key - node2->key); +} +DECLARE_RBTREE_UNIQ(vertices, struct ls_vertex, entry, vertex_cmp) + +macro_inline int edge_cmp(const struct ls_edge *edge1, + const struct ls_edge *edge2) +{ + return (edge1->key - edge2->key); +} +DECLARE_RBTREE_UNIQ(edges, struct ls_edge, entry, edge_cmp) + +macro_inline int subnet_cmp(const struct ls_subnet *a, + const struct ls_subnet *b) +{ + return prefix_cmp(&a->key, &b->key); +} +DECLARE_RBTREE_UNIQ(subnets, struct ls_subnet, entry, subnet_cmp) + +/* Link State TED Structure */ +struct ls_ted { + uint32_t key; /* Unique identifier */ + char name[MAX_NAME_LENGTH]; /* Name of this graph. Could be null */ + uint32_t as_number; /* AS number of the modeled network */ + struct ls_vertex *self; /* Vertex of the FRR instance */ + struct vertices_head vertices; /* List of Vertices */ + struct edges_head edges; /* List of Edges */ + struct subnets_head subnets; /* List of Subnets */ +}; + +/** + * Create a new Link State Vertex structure and initialize is with the Link + * State Node parameter. + * + * @param node Link State Node + * + * @return New Vertex + */ +extern struct ls_vertex *ls_vertex_new(struct ls_node *node); + +/** + * Delete Link State Vertex. This function clean internal Vertex lists (incoming + * and outgoing Link State Edge and Link State Subnet). Note that referenced + * objects of the different lists (Edges & SubNet) are not removed as they could + * be connected to other Vertices. + * + * @param vertex Link State Vertex to be removed + */ +extern void ls_vertex_del(struct ls_vertex *vertex); + +/** + * Add new vertex to the Link State DB. Vertex is created from the Link State + * Node. Vertex data structure is dynamically allocated. + * + * @param ted Traffic Engineering Database structure + * @param node Link State Node + * + * @return New Vertex or NULL in case of error + */ +extern struct ls_vertex *ls_vertex_add(struct ls_ted *ted, + struct ls_node *node); + +/** + * Update Vertex with the Link State Node. A new vertex is created if no one + * corresponds to the Link State Node. + * + * @param ted Link State Data Base + * @param node Link State Node to be updated + * + * @return Updated Link State Vertex or Null in case of error + */ +extern struct ls_vertex *ls_vertex_update(struct ls_ted *ted, + struct ls_node *node); + +/** + * Remove Vertex from the Link State DB. Vertex Data structure is freed but + * not the Link State Node. Link State DB is not modified if Vertex is NULL or + * not found in the Data Base. + * + * @param ted Link State Data Base + * @param vertex Vertex to be removed + */ +extern void ls_vertex_remove(struct ls_ted *ted, struct ls_vertex *vertex); + +/** + * Find Vertex in the Link State DB by its unique key. + * + * @param ted Link State Data Base + * @param key Vertex Key different from 0 + * + * @return Vertex if found, NULL otherwise + */ +extern struct ls_vertex *ls_find_vertex_by_key(struct ls_ted *ted, + const uint64_t key); + +/** + * Find Vertex in the Link State DB by its Link State Node. + * + * @param ted Link State Data Base + * @param nid Link State Node ID + * + * @return Vertex if found, NULL otherwise + */ +extern struct ls_vertex *ls_find_vertex_by_id(struct ls_ted *ted, + struct ls_node_id nid); + +/** + * Check if two Vertices are equal. Note that this routine has the same return + * value sense as '==' (which is different from a comparison). + * + * @param v1 First vertex to compare + * @param v2 Second vertex to compare + * + * @return 1 if equal, 0 otherwise + */ +extern int ls_vertex_same(struct ls_vertex *v1, struct ls_vertex *v2); + +/** + * Add new Edge to the Link State DB. Edge is created from the Link State + * Attributes. Edge data structure is dynamically allocated. + * + * @param ted Link State Data Base + * @param attributes Link State attributes + * + * @return New Edge or NULL in case of error + */ +extern struct ls_edge *ls_edge_add(struct ls_ted *ted, + struct ls_attributes *attributes); + +/** + * Update the Link State Attributes information of an existing Edge. If there is + * no corresponding Edge in the Link State Data Base, a new Edge is created. + * + * @param ted Link State Data Base + * @param attributes Link State Attributes + * + * @return Updated Link State Edge, or NULL in case of error + */ +extern struct ls_edge *ls_edge_update(struct ls_ted *ted, + struct ls_attributes *attributes); + +/** + * Remove Edge from the Link State DB. Edge data structure is freed but not the + * Link State Attributes data structure. Link State DB is not modified if Edge + * is NULL or not found in the Data Base. + * + * @param ted Link State Data Base + * @param edge Edge to be removed + */ +extern void ls_edge_del(struct ls_ted *ted, struct ls_edge *edge); + +/** + * Find Edge in the Link State Data Base by Edge key. + * + * @param ted Link State Data Base + * @param key Edge key + * + * @return Edge if found, NULL otherwise + */ +extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, + const uint64_t key); + +/** + * Find Edge in the Link State Data Base by the source (local IPv4 or IPv6 + * address or local ID) informations of the Link + * State Attributes + * + * @param ted Link State Data Base + * @param attributes Link State Attributes + * + * @return Edge if found, NULL otherwise + */ +extern struct ls_edge * +ls_find_edge_by_source(struct ls_ted *ted, struct ls_attributes *attributes); + +/** + * Find Edge in the Link State Data Base by the destination (remote IPv4 or IPv6 + * address of remote ID) information of the Link State Attributes + * + * @param ted Link State Data Base + * @param attributes Link State Attributes + * + * @return Edge if found, NULL otherwise + */ +extern struct ls_edge * +ls_find_edge_by_destination(struct ls_ted *ted, + struct ls_attributes *attributes); + +/** + * Add new Subnet to the Link State DB. Subnet is created from the Link State + * prefix. Subnet data structure is dynamically allocated. + * + * @param ted Link State Data Base + * @param pref Link State Prefix + * + * @return New Subnet + */ +extern struct ls_subnet *ls_subnet_add(struct ls_ted *ted, + struct ls_prefix *pref); + +/** + * Remove Subnet from the Link State DB. Subnet data structure is freed but + * not the Link State prefix data structure. Link State DB is not modified + * if Subnet is NULL or not found in the Data Base. + * + * @param ted Link State Data Base + * @param subnet Subnet to be removed + */ +extern void ls_subnet_del(struct ls_ted *ted, struct ls_subnet *subnet); + +/** + * Find Subnet in the Link State Data Base by prefix. + * + * @param ted Link State Data Base + * @param prefix Link State Prefix + * + * @return Subnet if found, NULL otherwise + */ +extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted, + const struct prefix prefix); + +/** + * Create a new Link State Data Base. + * + * @param key Unique key of the data base. Must be different from 0 + * @param name Name of the data base (may be NULL) + * @param asn AS Number for this data base. Must be different from 0 + * + * @return New Link State Database or NULL in case of error + */ +extern struct ls_ted *ls_ted_new(const uint32_t key, const char *name, + uint32_t asn); + +/** + * Delete existing Link State Data Base. + * + * @param ted Link State Data Base + */ +extern void ls_ted_del(struct ls_ted *ted); + +/** + * Connect Source and Destination Vertices by given Edge. Only non NULL source + * and destination vertices are connected. + * + * @param src Link State Source Vertex + * @param dst Link State Destination Vertex + * @param edge Link State Edge. Must not be NULL + */ +extern void ls_connect_vertices(struct ls_vertex *src, struct ls_vertex *dst, + struct ls_edge *edge); + +/** + * Connect Link State Edge to the Link State Vertex which could be a Source or + * a Destination Vertex. + * + * @param vertex Link State Vertex to be connected. Must not be NULL + * @param edge Link State Edge connection. Must not be NULL + * @param source True for a Source, false for a Destination Vertex + */ +extern void ls_connect(struct ls_vertex *vertex, struct ls_edge *edge, + bool source); + +/** + * Disconnect Link State Edge from the Link State Vertex which could be a + * Source or a Destination Vertex. + * + * @param vertex Link State Vertex to be connected. Must not be NULL + * @param edge Link State Edge connection. Must not be NULL + * @param source True for a Source, false for a Destination Vertex + */ +extern void ls_disconnect(struct ls_vertex *vertex, struct ls_edge *edge, + bool source); + +/** + * Disconnect Link State Edge from both Source and Destination Vertex. + * + * @param edge Link State Edge to be disconnected + */ +extern void ls_disconnect_edge(struct ls_edge *edge); + + +/** + * The Link State Message is defined to convey Link State parameters from + * the routing protocol (OSPF or IS-IS) to other daemons e.g. BGP. + * + * The structure is composed of: + * - Event of the message: + * - Sync: Send the whole LS DB following a request + * - Add: Send the a new Link State element + * - Update: Send an update of an existing Link State element + * - Delete: Indicate that the given Link State element is removed + * - Type of Link State element: Node, Attribute or Prefix + * - Remote node id when known + * - Data: Node, Attributes or Prefix + * + * A Link State Message can carry only one Link State Element (Node, Attributes + * of Prefix) at once, and only one Link State Message is sent through ZAPI + * Opaque Link State type at once. + */ + +/* ZAPI Opaque Link State Message Event */ +#define LS_MSG_EVENT_SYNC 1 +#define LS_MSG_EVENT_ADD 2 +#define LS_MSG_EVENT_UPDATE 3 +#define LS_MSG_EVENT_DELETE 4 + +/* ZAPI Opaque Link State Message sub-Type */ +#define LS_MSG_TYPE_NODE 1 +#define LS_MSG_TYPE_ATTRIBUTES 2 +#define LS_MSG_TYPE_PREFIX 3 + +/* Link State Message */ +struct ls_message { + uint8_t event; /* Message Event: Sync, Add, Update, Delete */ + uint8_t type; /* Message Data Type: Node, Attribute, Prefix */ + struct ls_node_id remote_id; /* Remote Link State Node ID */ + union { + struct ls_node *node; /* Link State Node */ + struct ls_attributes *attr; /* Link State Attributes */ + struct ls_prefix *prefix; /* Link State Prefix */ + } data; +}; + +/** + * Parse Link State Message from stream. Used this function once receiving a + * new ZAPI Opaque message of type Link State. + * + * @param s Stream buffer. Must not be NULL. + * + * @return New Link State Message or NULL in case of error + */ +extern struct ls_message *ls_parse_msg(struct stream *s); + +/** + * Delete existing message, freeing all substructure. + * + * @param msg Link state message to be deleted + */ +extern void ls_delete_msg(struct ls_message *msg); + +/** + * Send Link State Message as new ZAPI Opaque message of type Link State. + * If destination is not NULL, message is sent as Unicast otherwise it is + * broadcast to all registered daemon. + * + * @param zclient Zebra Client + * @param msg Link State Message to be sent + * @param dst Destination daemon for unicast message, + * NULL for broadcast message + * + * @return 0 on success, -1 otherwise + */ +extern int ls_send_msg(struct zclient *zclient, struct ls_message *msg, + struct zapi_opaque_reg_info *dst); + +/** + * Create a new Link State Message from a Link State Vertex. If Link State + * Message is NULL, a new data structure is dynamically allocated. + * + * @param msg Link State Message to be filled or NULL + * @param vertex Link State Vertex. Must not be NULL + * + * @return New Link State Message msg parameter is NULL or pointer + * to the provided Link State Message + */ +extern struct ls_message *ls_vertex2msg(struct ls_message *msg, + struct ls_vertex *vertex); + +/** + * Create a new Link State Message from a Link State Edge. If Link State + * Message is NULL, a new data structure is dynamically allocated. + * + * @param msg Link State Message to be filled or NULL + * @param edge Link State Edge. Must not be NULL + * + * @return New Link State Message msg parameter is NULL or pointer + * to the provided Link State Message + */ +extern struct ls_message *ls_edge2msg(struct ls_message *msg, + struct ls_edge *edge); + +/** + * Create a new Link State Message from a Link State Subnet. If Link State + * Message is NULL, a new data structure is dynamically allocated. + * + * @param msg Link State Message to be filled or NULL + * @param subnet Link State Subnet. Must not be NULL + * + * @return New Link State Message msg parameter is NULL or pointer + * to the provided Link State Message + */ +extern struct ls_message *ls_subnet2msg(struct ls_message *msg, + struct ls_subnet *subnet); + +/** + * Send all the content of the Link State Data Base to the given destination. + * Link State content is sent is this order: Vertices, Edges, Subnet. + * This function must be used when a daemon request a Link State Data Base + * Synchronization. + * + * @param ted Link State Data Base. Must not be NULL + * @param zclient Zebra Client. Must not be NULL + * @param dst Destination FRR daemon. Must not be NULL + * + * @return 0 on success, -1 otherwise + */ +extern int ls_sync_ted(struct ls_ted *ted, struct zclient *zclient, + struct zapi_opaque_reg_info *dst); + +/** + * Dump all Link State Data Base elements for debugging purposes + * + * @param ted Link State Data Base. Must not be NULL + * + */ +extern void ls_dump_ted(struct ls_ted *ted); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_LINK_STATE_H_ */ diff --git a/lib/subdir.am b/lib/subdir.am index 038282a99b..ee9e827ee8 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -48,6 +48,7 @@ lib_libfrr_la_SOURCES = \ lib/libfrr.c \ lib/libfrr_trace.c \ lib/linklist.c \ + lib/link_state.c \ lib/log.c \ lib/log_filter.c \ lib/log_vty.c \ @@ -208,6 +209,7 @@ pkginclude_HEADERS += \ lib/libfrr_trace.h \ lib/libospf.h \ lib/linklist.h \ + lib/link_state.h \ lib/log.h \ lib/log_vty.h \ lib/md5.h \ |
