forked from Mirror/frr

Back when I put this together in 2015, ISO C11 was still reasonably new and we couldn't require it just yet. Without ISO C11, there is no "good" way (only bad hacks) to require a semicolon after a macro that ends with a function definition. And if you added one anyway, you'd get "spurious semicolon" warnings on some compilers... With C11, `_Static_assert()` at the end of a macro will make it so that the semicolon is properly required, consumed, and not warned about. Consistently requiring semicolons after "file-level" macros matches Linux kernel coding style and helps some editors against mis-syntax'ing these macros. Signed-off-by: David Lamparter <equinox@diac24.net>
1284 lines
34 KiB
C
1284 lines
34 KiB
C
/*
|
|
* 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:%"PRIu64"n 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:%"PRIu64" attr key:%pI4 loc:(%pI4) rmt:(%pI4)",
|
|
vertex_edge->key,
|
|
&vertex_edge->attributes->adv.id.ip.addr,
|
|
&vertex_edge->attributes->standard.local,
|
|
&vertex_edge->attributes->standard.remote);
|
|
}
|
|
}
|
|
frr_each(edges, &ted->edges, edge) {
|
|
ls_edge2msg(&msg, edge);
|
|
zlog_debug(" Ted edge key:%"PRIu64" src:%s dst:%s",
|
|
edge->key,
|
|
edge->source ? edge->source->node->name
|
|
: "no_source",
|
|
edge->destination ? edge->destination->node->name
|
|
: "no_dest");
|
|
}
|
|
frr_each(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__);
|
|
}
|