mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
cbor_cmds.c, ospf_for_cbor.c: ospf show topology looks working
This commit is contained in:
parent
c27d43bf4c
commit
5a079e8eae
@ -81,6 +81,15 @@ void cbor_add_string(struct cbor_writer *writer, const char *string)
|
||||
writer->pt+=length;
|
||||
}
|
||||
|
||||
void cbor_nonterminated_string(struct cbor_writer *writer, const char *string, uint length)
|
||||
{
|
||||
int length = strlen(string);
|
||||
write_item(writer, 3, length); // 3 is major, then goes length of string and string
|
||||
check_memory(writer, length);
|
||||
memcpy(writer->cbor+writer->pt, string, length);
|
||||
writer->pt+=length;
|
||||
}
|
||||
|
||||
void write_item(struct cbor_writer *writer, int8_t major, int num)
|
||||
{
|
||||
major = major<<5;
|
||||
|
142
nest/cbor_cmds.c
142
nest/cbor_cmds.c
@ -1,4 +1,4 @@
|
||||
#include "nest/cbor_shortcuts.c"
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/route.h"
|
||||
@ -6,15 +6,11 @@
|
||||
#include "conf/conf.h"
|
||||
#include "lib/string.h"
|
||||
#include "filter/filter.h"
|
||||
#include "nest/cbor_cmds.h"
|
||||
#include "proto/ospf/ospf_for_cbor.c"
|
||||
|
||||
|
||||
struct cbor_show_data {
|
||||
int type; /* Symbols type to show */
|
||||
int name_length;
|
||||
char *name;
|
||||
};
|
||||
|
||||
uint compare_str(byte *str1, uint length, char *str2) {
|
||||
uint compare_str(byte *str1, uint length, const char *str2) {
|
||||
if (length != strlen(str2)) {
|
||||
return 0;
|
||||
}
|
||||
@ -32,10 +28,10 @@ extern pool *rta_pool;
|
||||
extern uint *pages_kept;
|
||||
|
||||
uint
|
||||
cmd_show_memory_cbor(byte *tbuf, uint capacity)
|
||||
cmd_show_memory_cbor(byte *tbuf, uint capacity, struct linpool *lp)
|
||||
{
|
||||
log("in cmd_show_memory_cbor");
|
||||
struct cbor_writer *w = cbor_init(tbuf, capacity, lp_new(proto_pool));
|
||||
struct cbor_writer *w = cbor_init(tbuf, capacity, lp);
|
||||
cbor_open_block_with_length(w, 1);
|
||||
|
||||
cbor_add_string(w, "show_memory:message");
|
||||
@ -75,9 +71,9 @@ extern int shutting_down;
|
||||
extern int configuring;
|
||||
|
||||
uint
|
||||
cmd_show_status_cbor(byte *tbuf, uint capacity)
|
||||
cmd_show_status_cbor(byte *tbuf, uint capacity, struct linpool *lp)
|
||||
{
|
||||
struct cbor_writer *w = cbor_init(tbuf, capacity, lp_new(proto_pool));
|
||||
struct cbor_writer *w = cbor_init(tbuf, capacity, lp);
|
||||
cbor_open_block_with_length(w, 1);
|
||||
cbor_add_string(w, "show_status:message");
|
||||
|
||||
@ -112,15 +108,35 @@ cmd_show_status_cbor(byte *tbuf, uint capacity)
|
||||
return w->pt;
|
||||
}
|
||||
|
||||
int
|
||||
cmd_show_symbols_cbor(byte *tbuf, uint capacity, struct cbor_show_data show)
|
||||
int parse_show_symbols_arg(struct argument *argument)
|
||||
{
|
||||
struct cbor_writer *w = cbor_init(tbuf, capacity, lp_new(proto_pool));
|
||||
char *params[] = {"table", "filter", "function", "protocol", "template"};
|
||||
int param_vals[] = {SYM_TABLE, SYM_FILTER, SYM_FUNCTION, SYM_PROTO, SYM_TEMPLATE}; // defined in conf.h
|
||||
for (size_t j = 0; j < sizeof(params)/sizeof(char*); j++)
|
||||
{
|
||||
if (compare_str(argument->arg, argument->len, params[j]))
|
||||
{
|
||||
return param_vals[j];
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint
|
||||
cmd_show_symbols_cbor(byte *tbuf, uint capacity, struct arg_list *args, struct linpool *lp)
|
||||
{
|
||||
struct cbor_writer *w = cbor_init(tbuf, capacity, lp);
|
||||
cbor_open_block_with_length(w, 1);
|
||||
cbor_add_string(w, "show_symbols:message");
|
||||
cbor_open_block_with_length(w, 1);
|
||||
|
||||
if (show.type == -1)
|
||||
int show_type = SYM_VOID;
|
||||
if (args->pt > 0)
|
||||
{
|
||||
show_type = parse_show_symbols_arg(&args->args[args->pt - 1]); // Takes just the last one argument. Current bird cli answers only last argument too, but can fail on previous.
|
||||
}
|
||||
|
||||
if (show_type == -1)
|
||||
{
|
||||
cbor_add_string(w, "table");
|
||||
cbor_open_list_with_length(w, 1);
|
||||
@ -130,16 +146,16 @@ cmd_show_symbols_cbor(byte *tbuf, uint capacity, struct cbor_show_data show)
|
||||
{
|
||||
HASH_WALK(scope->hash, next, sym)
|
||||
{
|
||||
if (compare_str(show.name, show.name_length, sym->name))
|
||||
if (compare_str(args->args[args->pt - 1].arg, args->args[args->pt - 1].len, sym->name))
|
||||
{
|
||||
cbor_string_string(w, "name", show.name);
|
||||
cbor_string_string(w, "name", args->args[args->pt - 1].arg);
|
||||
cbor_string_string(w, "type", cf_symbol_class_name(sym));
|
||||
return w->pt;
|
||||
}
|
||||
}
|
||||
HASH_WALK_END;
|
||||
}
|
||||
cbor_string_string(w, "name", show.name);
|
||||
cbor_string_string(w, "name", args->args[args->pt - 1].arg);
|
||||
cbor_string_string(w, "type", "symbol not known");
|
||||
return w->pt;
|
||||
}
|
||||
@ -154,7 +170,7 @@ cmd_show_symbols_cbor(byte *tbuf, uint capacity, struct cbor_show_data show)
|
||||
if (!sym->scope->active)
|
||||
continue;
|
||||
|
||||
if (show.type != SYM_VOID && (sym->class != show.type))
|
||||
if (show_type != SYM_VOID && (sym->class != show_type))
|
||||
continue;
|
||||
|
||||
cbor_open_block_with_length(w, 2);
|
||||
@ -169,5 +185,91 @@ cmd_show_symbols_cbor(byte *tbuf, uint capacity, struct cbor_show_data show)
|
||||
}
|
||||
|
||||
|
||||
struct proto *
|
||||
cbor_get_proto_type(enum protocol_class proto_type, struct cbor_writer *w)
|
||||
{
|
||||
log("in type");
|
||||
struct proto *p, *q;
|
||||
p = NULL;
|
||||
WALK_LIST(q, proto_list)
|
||||
if ((q->proto->class == proto_type) && (q->proto_state != PS_DOWN))
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
cbor_string_string(w, "error", "multiple protocols running");
|
||||
return NULL;
|
||||
}
|
||||
p = q;
|
||||
}
|
||||
if (!p)
|
||||
{
|
||||
cbor_string_string(w, "error", "no such protocols running");
|
||||
return NULL;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
struct proto *
|
||||
cbor_get_proto_name(struct argument *arg, enum protocol_class proto_type, struct cbor_writer *w)
|
||||
{
|
||||
log("in name");
|
||||
struct proto *q;
|
||||
WALK_LIST(q, proto_list)
|
||||
{
|
||||
log("%s %s %i %i %i", arg->arg, q->name, compare_str(arg->arg, arg->len, q->name) , (q->proto_state != PS_DOWN) , (q->proto->class == proto_type));
|
||||
if (compare_str(arg->arg, arg->len, q->name) && (q->proto_state != PS_DOWN) && (q->proto->class == proto_type))
|
||||
{
|
||||
return q;
|
||||
}
|
||||
}
|
||||
cbor_add_string(w, "not found");
|
||||
cbor_nonterminated_string(w, arg->arg, arg->len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
uint
|
||||
cmd_show_ospf_cbor(byte *tbuf, uint capacity, struct arg_list *args, struct linpool *lp)
|
||||
{
|
||||
log("in ospf args %i, pt %i", args, args->pt);
|
||||
struct cbor_writer *w = cbor_init(tbuf, capacity, lp);
|
||||
cbor_open_block_with_length(w, 1);
|
||||
cbor_add_string(w, "show_ospf:message");
|
||||
|
||||
if (args->pt == 0)
|
||||
{
|
||||
cbor_open_block_with_length(w, 1);
|
||||
cbor_string_string(w, "not implemented", "show everything about ospf");
|
||||
return w->pt;
|
||||
}
|
||||
|
||||
if (compare_str(args->args[0].arg, args->args[0].len, "topology"))
|
||||
{
|
||||
cbor_open_block(w);
|
||||
struct proto *proto;
|
||||
int all_ospf = (args->pt > 1) && compare_str(args->args[1].arg, args->args[1].len, "all");
|
||||
if (args->pt - all_ospf > 1) // if there is protocol name
|
||||
{
|
||||
proto = cbor_get_proto_name(&args->args[args->pt -1], PROTOCOL_OSPF, w);
|
||||
}
|
||||
else {
|
||||
proto = cbor_get_proto_type(PROTOCOL_OSPF, w);
|
||||
}
|
||||
|
||||
if (proto == NULL)
|
||||
{
|
||||
cbor_close_block_or_list(w);
|
||||
return w->pt;
|
||||
}
|
||||
|
||||
ospf_sh_state_cbor(w, proto, 0, all_ospf);
|
||||
cbor_close_block_or_list(w);
|
||||
return w->pt;
|
||||
} else {
|
||||
cbor_open_block_with_length(w, 1);
|
||||
cbor_add_string(w, "not implemented");
|
||||
cbor_nonterminated_string(w, args->args[0].arg, args->args[0].len);
|
||||
return w->pt;
|
||||
}
|
||||
}
|
||||
|
||||
|
18
nest/cbor_cmds.h
Normal file
18
nest/cbor_cmds.h
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
struct argument {
|
||||
char *arg;
|
||||
uint len;
|
||||
};
|
||||
|
||||
struct arg_list {
|
||||
struct argument *args;
|
||||
int capacity;
|
||||
int pt;
|
||||
struct linpool *lp;
|
||||
};
|
||||
|
||||
uint cmd_show_memory_cbor(byte *tbuf, uint capacity, struct linpool *lp);
|
||||
uint cmd_show_status_cbor(byte *tbuf, uint capacity, struct linpool *lp);
|
||||
uint cmd_show_symbols_cbor(byte *tbuf, uint capacity, struct arg_list *args, struct linpool *lp);
|
||||
|
@ -4,6 +4,7 @@ enum functions {
|
||||
SHOW_STATUS = 0,
|
||||
SHOW_MEMORY = 1,
|
||||
SHOW_SYMBOLS = 2,
|
||||
SHOW_OSPF = 3,
|
||||
};
|
||||
|
||||
enum cbor_majors {
|
||||
@ -29,6 +30,7 @@ struct buff_reader {
|
||||
uint size;
|
||||
};
|
||||
|
||||
|
||||
uint compare_buff_str(struct buff_reader *buf_read, uint length, char *string) {
|
||||
if (length != strlen(string)) {
|
||||
return 0;
|
||||
@ -39,7 +41,7 @@ uint compare_buff_str(struct buff_reader *buf_read, uint length, char *string) {
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
struct value
|
||||
get_value(struct buff_reader *reader)
|
||||
@ -105,16 +107,15 @@ void skip_optional_args(struct buff_reader *rbuf_read, int items_in_block)
|
||||
}
|
||||
}
|
||||
|
||||
void parse_show_symbols_args(struct buff_reader *rbuf_read, int items_in_block, struct cbor_show_data *arg)
|
||||
struct arg_list *parse_args(struct buff_reader *rbuf_read, int items_in_block, struct linpool *lp)
|
||||
{
|
||||
log("parse symbols args");
|
||||
char *params[] = {"table", "filter", "function", "protocol", "template"};
|
||||
int param_vals[] = {SYM_TABLE, SYM_FILTER, SYM_FUNCTION, SYM_PROTO, SYM_TEMPLATE}; // defined in conf.h
|
||||
arg->type = SYM_VOID; // default option
|
||||
arg->name = NULL;
|
||||
// We are in opened block, which could be empty or contain arguments <"args":[{"arg":"string"}]>
|
||||
struct arg_list *arguments = (struct arg_list*)lp_alloc(lp, sizeof(struct arg_list));
|
||||
arguments->capacity = 0;
|
||||
arguments->pt = 0;
|
||||
if (items_in_block == 0)
|
||||
{ // there should not be arg array
|
||||
return;
|
||||
return arguments;
|
||||
}
|
||||
struct value val = get_value(rbuf_read);
|
||||
if (val.major == TEXT)
|
||||
@ -127,13 +128,24 @@ void parse_show_symbols_args(struct buff_reader *rbuf_read, int items_in_block,
|
||||
ASSERT(val.major == ARRAY);
|
||||
int num_array_items = val.val;
|
||||
log("num arr items %i", num_array_items);
|
||||
for (int i = 0; i<num_array_items || (num_array_items == -1 && !val_is_break(val)); i++)
|
||||
if (num_array_items > 0)
|
||||
{
|
||||
arguments->args = (struct argument*)lp_alloc(lp, sizeof(struct argument) * num_array_items);
|
||||
arguments->capacity = num_array_items;
|
||||
}
|
||||
else if (num_array_items == -1)
|
||||
{
|
||||
arguments->args = (struct argument*)lp_alloc(lp, sizeof(struct argument) * 4);
|
||||
arguments->capacity = 4;
|
||||
}
|
||||
for (int i = 0; i < num_array_items || num_array_items == -1; i++)
|
||||
{
|
||||
// There will be only one argument in struct cbor_show_data arg after parsing the array of args. Current bird cli is behaving this way too.
|
||||
val = get_value(rbuf_read);
|
||||
if (val_is_break(val))
|
||||
{
|
||||
rbuf_read->pt--;
|
||||
return arguments;
|
||||
}
|
||||
else if (val.major == BLOCK)
|
||||
{
|
||||
@ -145,24 +157,20 @@ void parse_show_symbols_args(struct buff_reader *rbuf_read, int items_in_block,
|
||||
val = get_value(rbuf_read);
|
||||
ASSERT(compare_buff_str(rbuf_read, val.val, "arg"));
|
||||
rbuf_read->pt+=val.val;
|
||||
|
||||
val = get_value(rbuf_read);
|
||||
ASSERT(val.major == TEXT);
|
||||
int found = 0;
|
||||
for (size_t j = 0; j < sizeof(params)/sizeof(char*) && found == 0; j++)
|
||||
ASSERT(val.major == TEXT); // Now we have an argument in val
|
||||
if (num_array_items == -1 && arguments->capacity == arguments->pt)
|
||||
{
|
||||
if (compare_buff_str(rbuf_read, val.val, params[j]))
|
||||
{
|
||||
arg->type = param_vals[j];
|
||||
found = 1;
|
||||
log("found %s, on %i val %i", params[j], j, param_vals[j]);
|
||||
}
|
||||
}
|
||||
if (found == 0)
|
||||
{
|
||||
arg->type = -1;
|
||||
arg->name = rbuf_read->buff + rbuf_read->pt;
|
||||
arg->name_length = val.val;
|
||||
struct argument *a = arguments->args;
|
||||
arguments->args = (struct argument*)lp_alloc(lp, sizeof(struct argument) * 2 * arguments->capacity);
|
||||
arguments->capacity = 2 * arguments->capacity;
|
||||
memcpy(arguments->args, a, sizeof(struct argument) * arguments->pt);
|
||||
}
|
||||
arguments->args[arguments->pt].arg = rbuf_read->buff + rbuf_read->pt; // pointer to actual position in rbuf_read buffer
|
||||
arguments->args[arguments->pt].len = val.val;
|
||||
arguments->pt++;
|
||||
|
||||
rbuf_read->pt+=val.val;
|
||||
if (wait_close)
|
||||
{
|
||||
@ -177,28 +185,34 @@ void parse_show_symbols_args(struct buff_reader *rbuf_read, int items_in_block,
|
||||
}
|
||||
} else
|
||||
{
|
||||
ASSERT(items_in_block == -1); // assert the block was not open to exact num of items, because it cant be just for command (we would returned) and we did not find more items.
|
||||
ASSERT(items_in_block == -1); // assert the block was not open to exact num of items, because it cant be just for command (we would returned) and we did not found more items.
|
||||
rbuf_read->pt--; // we read one byte from future, we need to shift pointer back
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
uint
|
||||
do_command(struct buff_reader *rbuf_read, struct buff_reader *tbuf_read, int items_in_block)
|
||||
do_command(struct buff_reader *rbuf_read, struct buff_reader *tbuf_read, int items_in_block, struct linpool *lp)
|
||||
{
|
||||
struct value val = get_value(rbuf_read);
|
||||
ASSERT(val.major == UINT);
|
||||
struct arg_list * args;
|
||||
switch (val.val)
|
||||
{
|
||||
case SHOW_MEMORY:
|
||||
skip_optional_args(rbuf_read, items_in_block);
|
||||
return cmd_show_memory_cbor(tbuf_read->buff, tbuf_read->size);
|
||||
return cmd_show_memory_cbor(tbuf_read->buff, tbuf_read->size, lp);
|
||||
case SHOW_STATUS:
|
||||
skip_optional_args(rbuf_read, items_in_block);
|
||||
return cmd_show_status_cbor(tbuf_read->buff, tbuf_read->size);
|
||||
return cmd_show_status_cbor(tbuf_read->buff, tbuf_read->size, lp);
|
||||
case SHOW_SYMBOLS:
|
||||
struct cbor_show_data arg;
|
||||
parse_show_symbols_args(rbuf_read, items_in_block, &arg);
|
||||
return cmd_show_symbols_cbor(tbuf_read->buff, tbuf_read->size, arg);
|
||||
args = parse_args(rbuf_read, items_in_block, lp);
|
||||
return cmd_show_symbols_cbor(tbuf_read->buff, tbuf_read->size, args, lp);
|
||||
case SHOW_OSPF:
|
||||
args = parse_args(rbuf_read, items_in_block, lp);
|
||||
log("args %i, pt %i", args, args->pt);
|
||||
return cmd_show_ospf_cbor(tbuf_read->buff, tbuf_read->size, args, lp);
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -206,7 +220,7 @@ do_command(struct buff_reader *rbuf_read, struct buff_reader *tbuf_read, int ite
|
||||
|
||||
|
||||
uint
|
||||
parse_cbor(uint size, byte *rbuf, byte *tbuf, uint tbsize)
|
||||
parse_cbor(uint size, byte *rbuf, byte *tbuf, uint tbsize, struct linpool* lp)
|
||||
{
|
||||
log("cbor parse");
|
||||
struct buff_reader rbuf_read;
|
||||
@ -246,7 +260,7 @@ parse_cbor(uint size, byte *rbuf, byte *tbuf, uint tbsize)
|
||||
ASSERT(compare_buff_str(&rbuf_read, val.val, "command"));
|
||||
rbuf_read.pt+=val.val;
|
||||
|
||||
tbuf_read.pt = do_command(&rbuf_read, &tbuf_read, items_in_block);
|
||||
tbuf_read.pt = do_command(&rbuf_read, &tbuf_read, items_in_block, lp);
|
||||
if (items_in_block == -1)
|
||||
{
|
||||
val = get_value(&rbuf_read);
|
||||
|
@ -338,7 +338,7 @@ cli_kick(cli *c)
|
||||
|
||||
uint
|
||||
yi_process(uint size, byte *rbuf, byte *tbuf, uint tbsize) {
|
||||
return parse_cbor(size, rbuf, tbuf, tbsize);
|
||||
return parse_cbor(size, rbuf, tbuf, tbsize, lp_new(yi_pool));
|
||||
}
|
||||
|
||||
static list cli_log_hooks;
|
||||
|
@ -2382,6 +2382,12 @@ proto_get_named(struct symbol *sym, struct protocol *pr)
|
||||
return p;
|
||||
}
|
||||
|
||||
list
|
||||
get_protolist_for_cbor(void)
|
||||
{
|
||||
return proto_list;
|
||||
}
|
||||
|
||||
struct proto *
|
||||
proto_iterate_named(struct symbol *sym, struct protocol *proto, struct proto *old)
|
||||
{
|
||||
|
@ -300,6 +300,7 @@ void proto_cmd_mrtdump(struct proto *, uintptr_t, int);
|
||||
|
||||
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int), int restricted, uintptr_t arg);
|
||||
struct proto *proto_get_named(struct symbol *, struct protocol *);
|
||||
list get_protolist_for_cbor(void);
|
||||
struct proto *proto_iterate_named(struct symbol *sym, struct protocol *proto, struct proto *old);
|
||||
|
||||
#define PROTO_WALK_CMD(sym,pr,p) for(struct proto *p = NULL; p = proto_iterate_named(sym, pr, p); )
|
||||
|
610
proto/ospf/ospf_for_cbor.c
Normal file
610
proto/ospf/ospf_for_cbor.c
Normal file
@ -0,0 +1,610 @@
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
¡jcommand:do¢gcommanddargs<67>¡carghprotocol
|
||||
¡jcommand:do¢gcommanddargs‚¡carghtopology¡cargeospf6
|
@ -1,2 +1,2 @@
|
||||
{"command:do":{"command": 2, "args":[{"arg":"protocol"}]}}
|
||||
{"command:do":{"command": 3, "args":[{"arg":"topology"}, {"arg":"ospf6"}]}}
|
||||
|
||||
|
@ -1 +1 @@
|
||||
ˇtshow_symbols:messageˇetableź˘dnamegebgp4_1dtypehprotocol˘dnamegebgp6_1dtypehprotocol˘dnamegebgp4_2dtypehprotocol˘dnamegebgp6_2dtypehprotocol˘dnamegstatic6dtypehprotocol˘dnamegstatic4dtypehprotocol˘dnamegkernel6dtypehprotocol˘dnamegkernel4dtypehprotocol˘dnamegdevice1dtypehprotocol˙
|
||||
¡tshow_symbols:message¡etableŸ¢dnamehips_sitedtypehconstant¢dnamejipt_unspecdtypehconstant¢dnamehipp_xorpdtypehconstant¢dnamehipp_dhcpdtypehconstant¢dnamehipp_birddtypehconstant¢dnamegmaster5dtypemrouting table¢dnamegmaster4dtypemrouting table¢dnamegmaster6dtypemrouting table¢dnamehipp_bootdtypehconstant¢dnamehipt_maindtypehconstant¢dnamehips_linkdtypehconstant¢dnamehipp_isisdtypehconstant¢dnameeospf6dtypehprotocol¢dnameeospf5dtypehprotocol¢dnameeospf4dtypehprotocol¢dnamejips_globaldtypehconstant¢dnamegstatic6dtypehprotocol¢dnamegstatic5dtypehprotocol¢dnamegstatic4dtypehprotocol¢dnamejipp_kerneldtypehconstant¢dnamehips_hostdtypehconstant¢dnamekips_nowheredtypehconstant¢dnamelipp_dnrouteddtypehconstant¢dnamegkernel6dtypehprotocol¢dnamegkernel5dtypehprotocol¢dnamegkernel4dtypehprotocol¢dnamegipp_ntkdtypehconstant¢dnamegipp_mrtdtypehconstant¢dnamejipr_cosmosdtypehconstant¢dnameiipp_babeldtypehconstant¢dnamelipp_redirectdtypehconstant¢dnamenipp_keepaliveddtypehconstant¢dnameiipt_localdtypehconstant¢dnamegipp_bgpdtypehconstant¢dnameiipp_zebradtypehconstant¢dnameiipp_gateddtypehconstant¢dnameiipp_openrdtypehconstant¢dnameiipp_eigrpdtypehconstant¢dnamegipp_ripdtypehconstant¢dnamegdevice1dtypehprotocol¢dnamekipt_defaultdtypehconstant¢dnamejipp_staticdtypehconstant¢dnamefipp_radtypehconstant¢dnamehipp_ospfdtypehconstant¢dnamejipp_unspecdtypehconstantÿ
|
Loading…
Reference in New Issue
Block a user