0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-08 20:28:43 +00:00
bird/proto/ospf/ospf_for_cbor.c

611 lines
14 KiB
C

/**
* There are cli functions from ospf.c adapted for cbor.
*/
#include <stdlib.h>
#include "ospf.h"
#include "nest/cbor_shortcuts.c"
static inline void
show_lsa_distance_cbor(struct cbor_writer *w, struct top_hash_entry *he)
{
if (he->color == INSPF)
cbor_string_int(w, "distance", he->dist);
else
cbor_string_string(w, "distance", "unreachable");
}
static inline void
show_lsa_router_cbor(struct cbor_writer *w, struct ospf_proto *p, struct top_hash_entry *he, int verbose)
{
struct ospf_lsa_rt_walk rtl;
cbor_add_string(w, "lsa_router");
cbor_open_block(w);
cbor_string_int(w, "router", he->lsa.rt);
show_lsa_distance_cbor(w, he);
cbor_add_string(w, "vlink");
cbor_open_list(w);
lsa_walk_rt_init(p, he, &rtl);
while (lsa_walk_rt(&rtl))
{
if (rtl.type == LSART_VLNK)
{
cbor_open_block_with_length(w, 2);
cbor_string_int(w, "vlink", rtl.id);
cbor_string_int(w, "metric", rtl.metric);
}
}
cbor_close_block_or_list(w);
cbor_add_string(w, "router_metric");
cbor_open_list(w);
lsa_walk_rt_init(p, he, &rtl);
while (lsa_walk_rt(&rtl))
{
if (rtl.type == LSART_PTP)
{
cbor_open_block_with_length(w, 2);
cbor_string_int(w, "router", rtl.id);
cbor_string_int(w, "metric", rtl.metric);
}
}
cbor_close_block_or_list(w);
cbor_add_string(w, "network");
cbor_open_list(w);
lsa_walk_rt_init(p, he, &rtl);
while (lsa_walk_rt(&rtl))
{
if (rtl.type == LSART_NET)
{
if (ospf_is_v2(p))
{
/* In OSPFv2, we try to find network-LSA to get prefix/pxlen */
struct top_hash_entry *net_he = ospf_hash_find_net2(p->gr, he->domain, rtl.id);
if (net_he && (net_he->lsa.age < LSA_MAXAGE))
{
struct ospf_lsa_header *net_lsa = &(net_he->lsa);
struct ospf_lsa_net *net_ln = net_he->lsa_body;
cbor_open_block_with_length(w, 3);
cbor_string_int(w, "network", net_lsa->id & net_ln->optx);
cbor_string_int(w, "len", u32_masklen(net_ln->optx));
cbor_string_int(w, "metric", rtl.metric);
}
else
{
cbor_open_block_with_length(w, 2);
cbor_string_int(w, "network", rtl.id);
cbor_string_int(w, "metric", rtl.metric);
}
}
else
{
cbor_open_block_with_length(w, 3);
cbor_string_int(w, "network", rtl.id);
cbor_string_int(w, "nif", rtl.nif);
cbor_string_int(w, "metric", rtl.metric);
}
}
}
cbor_close_block_or_list(w);
if (ospf_is_v2(p) && verbose)
{
cbor_add_string(w, "stubnet");
cbor_open_list(w);
lsa_walk_rt_init(p, he, &rtl);
while (lsa_walk_rt(&rtl))
{
if (rtl.type == LSART_STUB)
{
cbor_open_block_with_length(w, 3);
cbor_string_int(w, "stubnet", rtl.id);
cbor_string_int(w, "len", u32_masklen(rtl.data));
cbor_string_int(w, "metric", rtl.metric);
}
}
cbor_close_block_or_list(w);
}
cbor_close_block_or_list(w);
}
static inline void
show_lsa_network_cbor(struct cbor_writer *w, struct top_hash_entry *he, int ospf2)
{
cbor_add_string(w, "lsa_network");
cbor_open_block_with_length(w, 3);
struct ospf_lsa_header *lsa = &(he->lsa);
struct ospf_lsa_net *ln = he->lsa_body;
u32 i;
if (ospf2)
{
cbor_add_string(w, "ospf2");
cbor_open_block_with_length(w, 3);
cbor_string_int(w, "network", lsa->id & ln->optx);
cbor_string_int(w, "optx", u32_masklen(ln->optx));
cbor_string_int(w, "dr", lsa->rt);
}
else
{
cbor_add_string(w, "ospf");
cbor_open_block_with_length(w, 2);
cbor_string_int(w, "network", lsa->rt);
cbor_string_int(w, "lsa_id", lsa->id);
}
show_lsa_distance_cbor(w, he);
cbor_add_string(w, "routers");
cbor_open_list(w);
for (i = 0; i < lsa_net_count(lsa); i++)
cbor_string_int(w, "router", ln->routers[i]);
cbor_close_block_or_list(w);
}
static inline void
show_lsa_sum_net_cbor(struct cbor_writer *w, struct top_hash_entry *he, int ospf2, int af)
{
char str[IPA_MAX_TEXT_LENGTH + 8] = "";
net_addr net;
u8 pxopts;
u32 metric;
lsa_parse_sum_net(he, ospf2, af, &net, &pxopts, &metric);
cbor_add_string(w, "lsa_sum_net");
cbor_open_block_with_length(w, 2);
bsprintf(str, "%N", &net);
cbor_string_string(w, "net", str);
cbor_string_int(w, "metric", metric);
}
static inline void
show_lsa_sum_rt_cbor(struct cbor_writer *w, struct top_hash_entry *he, int ospf2)
{
u32 metric;
u32 dst_rid;
u32 options;
lsa_parse_sum_rt(he, ospf2, &dst_rid, &metric, &options);
cbor_add_string(w, "lsa_sum_rt");
cbor_open_block_with_length(w, 2);
cbor_string_int(w, "router", dst_rid);
cbor_string_int(w, "metric", metric);
}
static inline void
show_lsa_external_cbor(struct cbor_writer *w, struct top_hash_entry *he, int ospf2, int af)
{
struct ospf_lsa_ext_local rt;
char str[IPA_MAX_TEXT_LENGTH + 8] = "";
cbor_add_string(w, "lsa_external");
cbor_open_block(w);
if (he->lsa_type == LSA_T_EXT)
he->domain = 0; /* Unmark the LSA */
lsa_parse_ext(he, ospf2, af, &rt);
if (rt.fbit)
{
bsprintf(str, "%N", rt.fwaddr);
cbor_string_string(w, "via", str);
}
if (rt.tag)
cbor_string_int(w, "tag", rt.tag);
if (he->lsa_type == LSA_T_NSSA)
{
cbor_string_string(w, "lsa_type", "nssa-ext");
} else {
cbor_string_string(w, "lsa_type", "external");
}
bsprintf(str, "%N", rt.net);
cbor_string_string(w, "rt_net", str);
if(rt.ebit)
{
cbor_string_int(w, "lsa_type", 2);
}
cbor_string_int(w, "metric", rt.metric);
cbor_close_block_or_list(w);
}
static inline void
show_lsa_prefix_cbor(struct cbor_writer *w, struct top_hash_entry *he, struct top_hash_entry *cnode, int af)
{
struct ospf_lsa_prefix *px = he->lsa_body;
u32 *buf;
int i;
cbor_add_string(w, "lsa_prefix");
cbor_open_block(w);
/* We check whether given prefix-LSA is related to the current node */
if ((px->ref_type != cnode->lsa.type_raw) || (px->ref_rt != cnode->lsa.rt))
{
cbor_close_block_or_list(w);
return;
}
if ((px->ref_type == LSA_T_RT) && (px->ref_id != 0))
{
cbor_close_block_or_list(w);
return;
}
if ((px->ref_type == LSA_T_NET) && (px->ref_id != cnode->lsa.id))
{
cbor_close_block_or_list(w);
return;
}
buf = px->rest;
cbor_add_string(w, "prexixes");
cbor_open_list(w);
char str[IPA_MAX_TEXT_LENGTH + 8] = "";
for (i = 0; i < px->pxcount; i++)
{
net_addr net;
u8 pxopts;
u16 metric;
cbor_open_block(w);
buf = ospf3_get_prefix(buf, af, &net, &pxopts, &metric);
if (px->ref_type == LSA_T_RT)
{
bsprintf(str, "%N", &net);
cbor_string_string(w, "stubnet", str);
cbor_string_int(w, "metric", metric);
}
else{
bsprintf(str, "%N", &net);
cbor_string_string(w, "stubnet", str);
}
cbor_close_block_or_list(w);
}
cbor_close_block_or_list(w);
cbor_close_block_or_list(w);
}
static struct ospf_lsa_header *
fake_lsa_from_prefix_lsa_cbor(struct ospf_lsa_header *dst, struct ospf_lsa_header *src,
struct ospf_lsa_prefix *px)
{
dst->age = src->age;
dst->type_raw = px->ref_type;
dst->id = px->ref_id;
dst->rt = px->ref_rt;
dst->sn = src->sn;
return dst;
}
static int lsa_compare_ospf3_cbor;
static int
lsa_compare_for_state_cbor(const void *p1, const void *p2)
{
struct top_hash_entry *he1 = * (struct top_hash_entry **) p1;
struct top_hash_entry *he2 = * (struct top_hash_entry **) p2;
struct ospf_lsa_header *lsa1 = &(he1->lsa);
struct ospf_lsa_header *lsa2 = &(he2->lsa);
struct ospf_lsa_header lsatmp1, lsatmp2;
u16 lsa1_type = he1->lsa_type;
u16 lsa2_type = he2->lsa_type;
if (he1->domain < he2->domain)
return -1;
if (he1->domain > he2->domain)
return 1;
/* px1 or px2 assumes OSPFv3 */
int px1 = (lsa1_type == LSA_T_PREFIX);
int px2 = (lsa2_type == LSA_T_PREFIX);
if (px1)
{
lsa1 = fake_lsa_from_prefix_lsa_cbor(&lsatmp1, lsa1, he1->lsa_body);
lsa1_type = lsa1->type_raw; /* FIXME: handle unknown ref_type */
}
if (px2)
{
lsa2 = fake_lsa_from_prefix_lsa_cbor(&lsatmp2, lsa2, he2->lsa_body);
lsa2_type = lsa2->type_raw;
}
int nt1 = (lsa1_type == LSA_T_NET);
int nt2 = (lsa2_type == LSA_T_NET);
if (nt1 != nt2)
return nt1 - nt2;
if (nt1)
{
/* In OSPFv3, networks are named based on ID of DR */
if (lsa_compare_ospf3_cbor)
{
if (lsa1->rt < lsa2->rt)
return -1;
if (lsa1->rt > lsa2->rt)
return 1;
}
/* For OSPFv2, this is IP of the network,
for OSPFv3, this is interface ID */
if (lsa1->id < lsa2->id)
return -1;
if (lsa1->id > lsa2->id)
return 1;
if (px1 != px2)
return px1 - px2;
return lsa1->sn - lsa2->sn;
}
else
{
if (lsa1->rt < lsa2->rt)
return -1;
if (lsa1->rt > lsa2->rt)
return 1;
if (lsa1_type < lsa2_type)
return -1;
if (lsa1_type > lsa2_type)
return 1;
if (lsa1->id < lsa2->id)
return -1;
if (lsa1->id > lsa2->id)
return 1;
if (px1 != px2)
return px1 - px2;
return lsa1->sn - lsa2->sn;
}
}
static int
ext_compare_for_state_cbor(const void *p1, const void *p2)
{
struct top_hash_entry * he1 = * (struct top_hash_entry **) p1;
struct top_hash_entry * he2 = * (struct top_hash_entry **) p2;
struct ospf_lsa_header *lsa1 = &(he1->lsa);
struct ospf_lsa_header *lsa2 = &(he2->lsa);
if (lsa1->rt < lsa2->rt)
return -1;
if (lsa1->rt > lsa2->rt)
return 1;
if (lsa1->id < lsa2->id)
return -1;
if (lsa1->id > lsa2->id)
return 1;
return lsa1->sn - lsa2->sn;
}
void
ospf_sh_state_cbor(struct cbor_writer *w, struct proto *P, int verbose, int reachable)
{
log("in ospf_state");
struct ospf_proto *p = (struct ospf_proto *) P;
int ospf2 = ospf_is_v2(p);
int af = ospf_get_af(p);
uint i, ix, j1, jx;
u32 last_area = 0xFFFFFFFF;
if (p->p.proto_state != PS_UP)
{
cbor_string_string(w, "error", "protocol is not up");
return;
}
/* We store interesting area-scoped LSAs in array hea and
global-scoped (LSA_T_EXT) LSAs in array hex */
uint num = p->gr->hash_entries;
struct top_hash_entry *hea[num];
struct top_hash_entry **hex = verbose ? alloca(num * sizeof(struct top_hash_entry *)) : NULL;
struct top_hash_entry *he;
struct top_hash_entry *cnode = NULL;
j1 = jx = 0;
WALK_SLIST(he, p->lsal)
{
int accept;
if (he->lsa.age == LSA_MAXAGE)
continue;
switch (he->lsa_type)
{
case LSA_T_RT:
case LSA_T_NET:
accept = 1;
break;
case LSA_T_SUM_NET:
case LSA_T_SUM_RT:
case LSA_T_NSSA:
case LSA_T_PREFIX:
accept = verbose;
break;
case LSA_T_EXT:
if (verbose)
{
he->domain = 1; /* Abuse domain field to mark the LSA */
hex[jx++] = he;
}
/* fallthrough */
default:
accept = 0;
}
if (accept)
hea[j1++] = he;
}
ASSERT(j1 <= num && jx <= num);
lsa_compare_ospf3_cbor = !ospf2;
qsort(hea, j1, sizeof(struct top_hash_entry *), lsa_compare_for_state_cbor);
if (verbose)
qsort(hex, jx, sizeof(struct top_hash_entry *), ext_compare_for_state_cbor);
/*
* This code is a bit tricky, we have a primary LSAs (router and
* network) that are presented as a node, and secondary LSAs that
* are presented as a part of a primary node. cnode represents an
* currently opened node (whose header was presented). The LSAs are
* sorted to get secondary LSAs just after related primary LSA (if
* available). We present secondary LSAs only when related primary
* LSA is opened.
*
* AS-external LSAs are stored separately as they might be presented
* several times (for each area when related ASBR is opened). When
* the node is closed, related external routes are presented. We
* also have to take into account that in OSPFv3, there might be
* more router-LSAs and only the first should be considered as a
* primary. This is handled by not closing old router-LSA when next
* one is processed (which is not opened because there is already
* one opened).
*/
cbor_add_string(w, "areas");
cbor_open_list_with_length(w, j1);
ix = 0;
for (i = 0; i < j1; i++)
{
cbor_open_block(w);
he = hea[i];
/* If there is no opened node, we open the LSA (if appropriate) or skip to the next one */
if (!cnode)
{
if (((he->lsa_type == LSA_T_RT) || (he->lsa_type == LSA_T_NET))
&& ((he->color == INSPF) || !reachable))
{
cnode = he;
if (he->domain != last_area)
{
cbor_string_int(w, "area", he->domain);
last_area = he->domain;
ix = 0;
}
}
else
continue;
}
ASSERT(cnode && (he->domain == last_area) && (he->lsa.rt == cnode->lsa.rt));
switch (he->lsa_type)
{
case LSA_T_RT:
if (he->lsa.id == cnode->lsa.id)
show_lsa_router_cbor(w, p, he, verbose);
break;
case LSA_T_NET:
show_lsa_network_cbor(w, he, ospf2);
break;
case LSA_T_SUM_NET:
if (cnode->lsa_type == LSA_T_RT)
show_lsa_sum_net_cbor(w, he, ospf2, af);
break;
case LSA_T_SUM_RT:
if (cnode->lsa_type == LSA_T_RT)
show_lsa_sum_rt_cbor(w, he, ospf2);
break;
case LSA_T_EXT:
case LSA_T_NSSA:
show_lsa_external_cbor(w, he, ospf2, af);
break;
case LSA_T_PREFIX:
show_lsa_prefix_cbor(w, he, cnode, af);
break;
}
/* In these cases, we close the current node */
if ((i+1 == j1)
|| (hea[i+1]->domain != last_area)
|| (hea[i+1]->lsa.rt != cnode->lsa.rt)
|| (hea[i+1]->lsa_type == LSA_T_NET))
{
while ((ix < jx) && (hex[ix]->lsa.rt < cnode->lsa.rt))
ix++;
while ((ix < jx) && (hex[ix]->lsa.rt == cnode->lsa.rt))
show_lsa_external_cbor(w, hex[ix++], ospf2, af);
cnode = NULL;
}
cbor_close_block_or_list(w);
}
int hdr = 0;
u32 last_rt = 0xFFFFFFFF;
cbor_add_string(w, "asbrs");
cbor_open_list(w);
for (ix = 0; ix < jx; ix++)
{
he = hex[ix];
/* If it is still marked, we show it now. */
if (he->domain)
{
cbor_add_string(w, "asbr");
cbor_open_block(w);
he->domain = 0;
if ((he->color != INSPF) && reachable)
continue;
if (!hdr)
{
cbor_add_string(w, "other_ASBRs");
cbor_open_list_with_length(w, 0);
hdr = 1;
}
if (he->lsa.rt != last_rt)
{
cbor_string_int(w, "router", he->lsa.rt);
last_rt = he->lsa.rt;
}
show_lsa_external_cbor(w, he, ospf2, af);
cbor_close_block_or_list(w);
}
}
cbor_close_block_or_list(w);
}