diff options
Diffstat (limited to 'lib/link_state.c')
| -rw-r--r-- | lib/link_state.c | 1282 |
1 files changed, 1282 insertions, 0 deletions
diff --git a/lib/link_state.c b/lib/link_state.c new file mode 100644 index 0000000000..6bd7ef703c --- /dev/null +++ b/lib/link_state.c @@ -0,0 +1,1282 @@ +/* + * 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(" 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"); + 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:%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( + " out 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(" Ted 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( + " Ted subnet key:%pFX vertex:%pI4 pfx:%pFX", + &subnet->key, + &subnet->vertex->node->adv.id.ip.addr, + &subnet->ls_pref->pref); + } + zlog_debug("(%s) Ted end", __func__); +} |
