From a3b70dc499b64f41aa776b5b4afee5c7bfb8dfa6 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Mon, 27 Oct 2008 00:03:30 +0100 Subject: Two new informative CLI commands for OSPF. Two new CLI commands for OSPF giving nice informative (and still machine parsable) representation of OSPF network graph (based on datas from the LSA database). The first command (show ospf topology) shows routers, networks and stub networks, The second command (show ospf state) shows also external routes and area-external networks and routers propagated by given area boundary router. --- proto/ospf/config.Y | 10 ++- proto/ospf/ospf.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+), 2 deletions(-) diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 0c6a408..dfcab4e 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -255,14 +255,20 @@ CF_ADDTO(dynamic_attr, OSPF_METRIC2 { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF CF_ADDTO(dynamic_attr, OSPF_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_OSPF_TAG); }) CF_CLI(SHOW OSPF, optsym, [], [[Show information about OSPF protocol]]) -{ ospf_sh(proto_get_named($3, &proto_ospf)); } ; +{ ospf_sh(proto_get_named($3, &proto_ospf)); }; CF_CLI(SHOW OSPF NEIGHBORS, optsym opttext, [] [\"\"], [[Show information about OSPF neighbors]]) -{ ospf_sh_neigh(proto_get_named($4, &proto_ospf), $5); } ; +{ ospf_sh_neigh(proto_get_named($4, &proto_ospf), $5); }; CF_CLI(SHOW OSPF INTERFACE, optsym opttext, [] [\"\"], [[Show information about interface]]) { ospf_sh_iface(proto_get_named($4, &proto_ospf), $5); }; +CF_CLI(SHOW OSPF TOPOLOGY, optsym opttext, [], [[Show information about OSPF network topology]]) +{ ospf_sh_state(proto_get_named($4, &proto_ospf), 0); }; + +CF_CLI(SHOW OSPF STATE, optsym opttext, [], [[Show information about OSPF network state]]) +{ ospf_sh_state(proto_get_named($4, &proto_ospf), 1); }; + CF_CODE CF_END diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index b382df0..1fce709 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -73,6 +73,7 @@ * and conserves memory. */ +#include #include "ospf.h" static int ospf_rte_better(struct rte *new, struct rte *old); @@ -982,6 +983,218 @@ ospf_sh_iface(struct proto *p, char *iff) cli_msg(0, ""); } +/* First we want to separate network-LSAs and other LSAs (because network-LSAs + * will be presented as network nodes and other LSAs together as router nodes) + * Network-LSAs are sorted according to network prefix, other LSAs are sorted + * according to originating router id (to get all LSA needed to represent one + * router node together). Then, according to LSA type, ID and age. + */ +static int +he_compare(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); + int nt1 = (lsa1->type == LSA_T_NET); + int nt2 = (lsa2->type == LSA_T_NET); + + if (he1->oa->areaid != he2->oa->areaid) + return he1->oa->areaid - he2->oa->areaid; + + if (nt1 != nt2) + return nt1 - nt2; + + if (nt1) + { + // we are cheating for now + if (lsa1->id != lsa2->id) + return lsa1->id - lsa2->id; + + return lsa1->age - lsa2->age; + } + else + { + if (lsa1->rt != lsa2->rt) + return lsa1->rt - lsa2->rt; + + if (lsa1->type != lsa2->type) + return lsa1->type - lsa2->type; + + if (lsa1->id != lsa2->id) + return lsa1->id - lsa2->id; + + return lsa1->age - lsa2->age; + } +} + +static inline void +show_lsa_router(struct top_hash_entry *he) +{ + struct ospf_lsa_header *lsa = &(he->lsa); + struct ospf_lsa_rt *rt = he->lsa_body; + struct ospf_lsa_rt_link *rr = (struct ospf_lsa_rt_link *) (rt + 1); + u32 i; + + for (i = 0; i < rt->links; i++) + if (rr[i].type == LSART_PTP) + cli_msg(-1016, "\t\trouter %I metric %u ", rr[i].id, rr[i].metric); + + for (i = 0; i < rt->links; i++) + if (rr[i].type == LSART_NET) + { + struct proto_ospf *po = he->oa->po; + struct top_hash_entry *net_he = ospf_hash_find(po->gr, he->oa->areaid, rr[i].id, rr[i].id, LSA_T_NET); + if (net_he) + { + struct ospf_lsa_header *net_lsa = &(net_he->lsa); + struct ospf_lsa_net *net_ln = net_he->lsa_body; + cli_msg(-1016, "\t\tnetwork %I/%d metric %u ", ipa_and(ipa_from_u32(net_lsa->id), net_ln->netmask), ipa_mklen(net_ln->netmask), rr[i].metric); + } + else + cli_msg(-1016, "\t\tnetwork ??? metric %u ", rr[i].id, ipa_mklen(rr[i].data), rr[i].metric); + } + + for (i = 0; i < rt->links; i++) + if (rr[i].type == LSART_STUB) + cli_msg(-1016, "\t\tstubnet %I/%d metric %u ", rr[i].id, ipa_mklen(rr[i].data), rr[i].metric); + + for (i = 0; i < rt->links; i++) + if (rr[i].type == LSART_VLNK) + cli_msg(-1016, "\t\tvlink %I metric %u ", rr[i].id, rr[i].metric); +} + +static inline void +show_lsa_network(struct top_hash_entry *he) +{ + struct ospf_lsa_header *lsa = &(he->lsa); + struct ospf_lsa_net *ln = he->lsa_body; + u32 *rts = (u32 *) (ln + 1); + u32 max = (lsa->length - sizeof(struct ospf_lsa_header) - sizeof(struct ospf_lsa_net)) / sizeof(u32); + u32 i; + + cli_msg(-1016, ""); + cli_msg(-1016, "\tnetwork %I/%d", ipa_and(ipa_from_u32(lsa->id), ln->netmask), ipa_mklen(ln->netmask)); + cli_msg(-1016, "\t\tdr %I", lsa->rt); + + for (i = 0; i < max; i++) + cli_msg(-1016, "\t\trouter %I", rts[i]); +} + +static inline void +show_lsa_sum_net(struct top_hash_entry *he) +{ + struct ospf_lsa_header *lsa = &(he->lsa); + struct ospf_lsa_sum *sm = he->lsa_body; + + cli_msg(-1016, "\t\txnetwork %I/%d", ipa_and(ipa_from_u32(lsa->id), sm->netmask), ipa_mklen(sm->netmask)); +} + +static inline void +show_lsa_sum_rt(struct top_hash_entry *he) +{ + cli_msg(-1016, "\t\txrouter %I", he->lsa.id); +} + + +static inline void +show_lsa_external(struct top_hash_entry *he) +{ + struct ospf_lsa_header *lsa = &(he->lsa); + struct ospf_lsa_ext *ext = he->lsa_body; + struct ospf_lsa_ext_tos *et = (struct ospf_lsa_ext_tos *) (ext + 1); + + char str_via[STD_ADDRESS_P_LENGTH + 8] = ""; + char str_tag[16] = ""; + + if (et->fwaddr) + bsprintf(str_via, " via %I", et->fwaddr); + + if (et->tag) + bsprintf(str_tag, " tag %08x", et->tag); + + cli_msg(-1016, "\t\texternal %I/%d metric%s %u%s%s", + ipa_and(ipa_from_u32(lsa->id), ext->netmask), + ipa_mklen(ext->netmask), et->etm.etos.ebit ? "2" : "", + et->etm.metric & METRIC_MASK, str_via, str_tag); +} + + +void +ospf_sh_state(struct proto *p, int verbose) +{ + struct proto_ospf *po = (struct proto_ospf *) p; + struct top_graph *f = po->gr; + unsigned int i, j; + u32 last_rt = 0xFFFFFFFF; + u32 last_area = 0xFFFFFFFF; + + if (p->proto_state != PS_UP) + { + cli_msg(-1016, "%s: is not up", p->name); + cli_msg(0, ""); + return; + } + + struct top_hash_entry *hea[f->hash_entries]; + struct top_hash_entry *he; + + j = 0; + for (i = 0; i < f->hash_size; i++) + for (he = f->hash_table[i]; he != NULL; he = he->next) + hea[j++] = he; + + if (j == f->hash_size) + die("Fatal mismatch"); + + qsort(hea, j, sizeof(struct top_hash_entry *), he_compare); + + for (i = 0; i < j; i++) + { + if ((verbose == 0) && (hea[i]->lsa.type > LSA_T_NET)) + continue; + + if (last_area != hea[i]->oa->areaid) + { + cli_msg(-1016, ""); + cli_msg(-1016, "area %I", hea[i]->oa->areaid); + last_area = hea[i]->oa->areaid; + last_rt = 0xFFFFFFFF; + } + + if ((hea[i]->lsa.rt != last_rt) && (hea[i]->lsa.type != LSA_T_NET)) + { + cli_msg(-1016, ""); + cli_msg(-1016, (hea[i]->lsa.type != LSA_T_EXT) ? "\trouter %I" : "\txrouter %I", hea[i]->lsa.rt); + last_rt = hea[i]->lsa.rt; + } + + switch (hea[i]->lsa.type) + { + case LSA_T_RT: + show_lsa_router(hea[i]); + break; + + case LSA_T_NET: + show_lsa_network(hea[i]); + break; + + case LSA_T_SUM_NET: + show_lsa_sum_net(hea[i]); + break; + + case LSA_T_SUM_RT: + show_lsa_sum_rt(hea[i]); + break; + case LSA_T_EXT: + show_lsa_external(hea[i]); + break; + } + } + + cli_msg(0, ""); +} + struct protocol proto_ospf = { name:"OSPF", template:"ospf%d", -- cgit v1.2.3