]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
memory and show_ospf meta yang
authorKaterina Kubecova <katerina.kubecova@nic.cz>
Thu, 25 Apr 2024 10:41:46 +0000 (12:41 +0200)
committerKaterina Kubecova <katerina.kubecova@nic.cz>
Thu, 25 Apr 2024 10:41:46 +0000 (12:41 +0200)
proto/ospf/ospf_for_cbor.c
yang/show_memory_meta.c
yang/show_ospf.yang
yang/show_ospf_meta.c

index feed56066b235294930680bf3e1b760d7a362344..462af013cd5c8dc38e7772085be53b90d418323b 100644 (file)
@@ -27,42 +27,37 @@ show_lsa_router_cbor(struct cbor_writer *w, struct ospf_proto *p, struct top_has
   cbor_string_ipv4(w, "router", he->lsa.rt);
   show_lsa_distance_cbor(w, he);
 
-  cbor_add_string(w, "vlink");
+  cbor_add_string(w, "rt");
   cbor_open_list(w);
   lsa_walk_rt_init(p, he, &rtl);
+  
+  int dummy_id = 0;
   while (lsa_walk_rt(&rtl))
   {
-    if (rtl.type == LSART_VLNK)
+    cbor_open_block(w);
+    cbor_string_int(spitw, "dummy_id", i);
+    dummy_id++;
+    cbor_string_int(w, "metric", rtl.metric);
+    switch (rtl.type)
+    case (LSART_VLNK)
     {
-      cbor_open_block_with_length(w, 2);
+      cbor_add_string(w, "vlink");
+      cbor_open_block_with_length(w, 1);
       cbor_string_ipv4(w, "vlink", rtl.id);
-      cbor_string_int(w, "metric", rtl.metric);
+      break;
     }
-  }
-  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)
+  
+    case (LSART_PTP)
     {
-      cbor_open_block_with_length(w, 2);
+      cbor_add_string(w, "router_metric");
+      cbor_open_block_with_length(w, 1);
       cbor_string_ipv4(w, "router", rtl.id);
-      cbor_string_int(w, "metric", rtl.metric);
+      break;
     }
-  }
-  cbor_close_block_or_list(w);
 
-  cbor_add_string(w, "network");
-  cbor_open_list(w);
-  lsa_walk_rt_init(p, he, &rtl);
-  int dummy_id = 0;
-  while (lsa_walk_rt(&rtl))
-  {
-    if (rtl.type == LSART_NET)
+    case (LSART_NET)
     {
+      cbor_add_string(w, "network");
       if (ospf_is_v2(p))
       {
        /* In OSPFv2, we try to find network-LSA to get prefix/pxlen */
@@ -73,51 +68,38 @@ show_lsa_router_cbor(struct cbor_writer *w, struct ospf_proto *p, struct top_has
          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, 4);
-          cbor_string_int(w, "dummy_yang_id", dummy_id);
+          cbor_open_block_with_length(w, 2);
           cbor_string_ipv4(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, 3);
-         cbor_string_int(w, "dummy_yang_id", dummy_id);
+         cbor_open_block_with_length(w, 1);
           cbor_string_ipv4(w, "network", rtl.id);
-          cbor_string_int(w, "metric", rtl.metric);
         }
       }
       else
       {
-        cbor_open_block_with_length(w, 4);
-        cbor_string_int(w, "dummy_yang_id", dummy_id);
+        cbor_open_block_with_length(w, 2);
         cbor_string_ipv4(w, "network", rtl.id);
         cbor_string_int(w, "nif", rtl.nif);
-        cbor_string_int(w, "metric", rtl.metric);
       }
+      break;
     }
-    dummy_id++;
-  }
-  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))
+    case (LSART_STUB)
     {
-      if (rtl.type == LSART_STUB)
+      if (ospf_is_v2(p) && verbose)
       {
-        cbor_open_block_with_length(w, 3);
+        cbor_add_string(w, "stubnet");
+        cbor_open_block_with_length(w, 2);
         cbor_string_ipv4(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);
+  cbor_close_block_or_list(w);
 }
 
 static inline void
@@ -407,7 +389,6 @@ ext_compare_for_state_cbor(const void *p1, const void *p2)
   return lsa1->sn - lsa2->sn;
 }
 
-
 void
 ospf_sh_state_cbor(struct cbor_writer *w, struct proto *P, int verbose, int reachable)
 {
@@ -433,99 +414,16 @@ ospf_sh_state_cbor(struct cbor_writer *w, struct proto *P, int verbose, int reac
   struct top_hash_entry *he;
   struct top_hash_entry *cnode = NULL;
 
-  j1 = jx = 0;
+  int i = 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_add_string(w, "areas");
+    cbor_open_list_with_length(w, j1);
+    
     cbor_open_block(w);
     cbor_string_int(w, "dummy_yang_id", i);
-    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_ipv4(w, "area", he->domain);
-         last_area = he->domain;
-         ix = 0;
-       }
-      }
-      else
-       continue;
-    }
-
-    ASSERT(cnode && (he->domain == last_area) && (he->lsa.rt == cnode->lsa.rt));
+    i++;
+    cbor_string_ipv4(w, "area", he->domain);
 
     switch (he->lsa_type)
     {
@@ -558,45 +456,16 @@ ospf_sh_state_cbor(struct cbor_writer *w, struct proto *P, int verbose, int reac
       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. */
+    u32 last_rt = 0xFFFFFFFF;
     if (he->domain)
     {
-      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;
-      }
+      cbor_add_string(w, "other_ASBR");
+      cbor_open_block(w);
 
       if (he->lsa.rt != last_rt)
       {
index 8d2b6a3b0a9ffa415f8fc20571d285a336ac337c..c07f628cc40381f099929ea6ba98bfb28349afad 100644 (file)
@@ -7,14 +7,15 @@ UYTC_MODULE(show_memory) {
 
   UYTC_CONTAINER(message, msg) {
     UYTC_LEAF(header, "BIRD memory usage");
-    UYTC_CONTAINER(body, body) {
-      UYTC_LEAF(routing_tables, rmemsize(rt_table_pool));
-      UYTC_LEAF(route_attributes, rmemsize(rta_pool));
-      ...;
+    UYTC_CONTAINER(body) {
+      UYTC_USE(memory, routing_tables, rmemsize(rt_table_pool));
+      UYTC_USE(memory, route_attributes, rmemsize(rta_pool));
+      UYTC_USE(memory, protocols, rmemsize(proto_pool));
+      UYTC_USE(memory, current_config, rmemsize(config_pool));
 #ifdef HAVE_MMAP
-      UYTC_LEAF(standby_memory, (struct resmem) { .overhead = page_size * *pages_kept });
+      UYTC_USE(memory, standby_memory, (struct resmem) { .overhead = page_size * *pages_kept });
 #endif
-      UYTC_LEAF(total, rmemsize(&root_pool));
+      UYTC_LEAF(total, (struct resmem) { .overhead = &root_pool.overhead + page_size * *pages_kept, .effective = &root_pool.effective });
     }
   }
 }
index b8b328896a4124e9427b6aa07d3f4ab7d6648d23..985bbdfd4b0371de1cf161a3e1f10379e722bb5d 100644 (file)
@@ -37,26 +37,24 @@ module show_ospf {
        leaf dummy_yang_id {
          type int32;
        }
-       leaf metric {
-         type int32;
-       }
+       uses metric;
        choice rt_type {
          case vlink {
-           grouping vlink {
-             leaf vlink {
-               type int32;
-             }
+            container vlink {
+              leaf vlink {
+                type int32;
+              }
            }
          }
 
          case router {
-           grouping router_metric {
+           container router {
              uses router;
            }
          }
 
          case network {
-           grouping network {
+           container network {
              leaf network {
                type ipv4;
              }
@@ -72,7 +70,7 @@ module show_ospf {
          }
 
          case stubnet {
-           grouping stubnet {
+           container stubnet {
              leaf stubnet {
                type ipv4;
              }
@@ -204,12 +202,7 @@ module show_ospf {
         }
       }
     }
-    list asbrs {
-      key "router";
-
-      leaf other_ABSRs {
-        type empty;
-      }
+    container other_ABSR {
       uses router;
       uses lsa_external;
       
index acf3c5b0da1f9c460cda25b5e5223c671f3d9305..abde6724ed663024c8d56b146183fa0fa9b19774 100644 (file)
+static inline void
+show_lsa_distance_cbor(UYTC_CONTEXT_TYPE UYTC_CONTEXT, struct top_hash_entry *he)
+{
+  if (he->color == INSPF)
+    UYTC_LEAF(distance, he->dist);
+  else
+    UYTC_LEAF(distance, unreachable);
+}
+
 static inline void
 show_lsa_router_cbor(UYTC_CONTEXT_TYPE UYTC_CONTEXT, struct top_hash_entry *he, int verbose)
 {
-  UYTC_GROUPING(lsa_router) {
-    UYTC_ITEM(router, he->lsa.rt);
-    
-    ...;
+  //FIXME: This is not accurate - in yang this corresponds to DEFINITION, not USAGE of the grouping. That means, from yang point of view, we are calling definition of grouping. From cbor perspective this is correct. 
+  UYTC_DEF_GROUPING(lsa_router) {
+    UYTC_LEAF(router, he->lsa.rt);
+    show_lsa_distance_cbor(UYTC_CONTEXT, he);
 
     lsa_walk_rt_init(p, he, &rtl);
+    int dummy_id_ = 0;
+    UYTC_LIST_WHILE (rt, lsa_walk_rt(&rtl))  // The while corresponds to list and they need to be tight together.
+    {
+      UITC_LEAF(dummy_id, dummy_id_);
+      dummy_id_++;
+      UYTC_LEAF(metric, rtl.metric);
+      switch (rtl.type)
+      {
+        case (LSART_VLNK)
+        {
+          UYTC_CONTAINER(vlink)
+          {
+            UYTC_LEAF(vlink, rtl.id);
+          }
+          break;
+        }
+  
+        case (LSART_PTP)
+        {
+          UYTC_CONTAINER(router_metric)
+          {
+            UYTC_LEAF(router, rtl.id);
+          }
+          break;
+        }
+
+        case (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;
+
+              UYTC_CONTAINER(network)
+              {
+                UYTC_LEAF(network, net_lsa->id & net_ln->optx);
+                UYTC_LEAF(len, u32_masklen(net_ln->optx));
+              }
+            }
+           else
+           {
+             UYTC_CONTAINER(network)
+              {
+                UYTC_LEAF(network, rtl.id);
+              }
+            }
+          }
+          else
+          {
+            UYTC_CONTAINER(network)
+            {
+              UYTC_LEAF(network, rtl.id);
+              UITC_LEAF(nif, rtl.nif);
+            }
+            break;
+          }
+          case (LSART_STUB)
+          {
+            if (ospf_is_v2(p) && verbose)
+            {
+              UYTC_CONTAINER(stubnet)
+              {
+                UYTC_LEAF(stubnet, rtl.id);
+                UYTC_LEAF(len, u32_masklen(rtl.data));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+static inline void
+show_lsa_network_cbor(UYTC_CONTEXT_TYPE UYTC_CONTEXT, struct top_hash_entry *he, int ospf2)
+{
+  UYTC_DEF_GROUPING(lsa_network)
+  {
+    struct ospf_lsa_header *lsa = &(he->lsa);
+    struct ospf_lsa_net *ln = he->lsa_body;
+    u32 i;
+
+    if (ospf2)
+    {
+      UYTC_CONTAINER(ospf2)
+      {
+        UYTC_LEAF(network, lsa->id & ln->optx);
+        UYTC_LEAF(optx, u32_masklen(ln->optx));
+        UYTC_LEAF(dr, lsa->rt);
+      }
+    }
+    else
+    {
+      UYTC_CONTAINER(ospf)
+      {
+        UYTC_LEAF(network, lsa->rt);
+        UYTC_LEAF(lsa_id, lsa->id);
+      }
+    }
+
+    show_lsa_distance_cbor(UYTC_CONTEXT, he);
+
+    int i = 0;
+    UITC_LIST_FOR (routers, i; i < lsa_net_count(lsa); i++) // Not sure if this syntax is ok, but, again, we need to squash cbor list and for()
+    {
+      UYTC_LEAF(router, ln->routers[i]);
+    }
+  }
+}
+
+static inline void
+show_lsa_sum_net_cbor(UYTC_CONTEXT_TYPE UYTC_CONTEXT, struct top_hash_entry *he, int ospf2, int af)
+{
+  net_addr net;
+  u8 pxopts;
+  u32 metric_;
+
+  lsa_parse_sum_net(he, ospf2, af, &net, &pxopts, &metric_);
+  UYTC_DEF_GROUPING(lsa_sum_net)
+  {
+    UYTC_LEAF(net, &net);
+    UYTC_LEAF(metric_, metric);
+  }
+}
+
+static inline void
+show_lsa_sum_rt_cbor(UYTC_CONTEXT_TYPE UYTC_CONTEXT, struct top_hash_entry *he, int ospf2)
+{
+  u32 metric;
+  u32 dst_rid;
+  u32 options;
+
+  lsa_parse_sum_rt(he, ospf2, &dst_rid, &metric, &options);
+
+  UYTC_DEF_GROUPING(lsa_sum_rt);
+  UYTC_LEAF(router, dst_rid);
+  UYTC_LEAF(metric, metric);
+}
+
+static inline void
+show_lsa_external_cbor(UYTC_CONTEXT_TYPE UYTC_CONTEXT, struct top_hash_entry *he, int ospf2, int af)
+{
+  struct ospf_lsa_ext_local rt;
+
+  UYTC_DEF_GROUPING(lsa_external)
+  {
+    if (he->lsa_type == LSA_T_EXT)
+      he->domain = 0; /* Unmark the LSA */
+
+    lsa_parse_ext(he, ospf2, af, &rt);
+
+    if (rt.fbit)
+    {
+      UYTC_LEAF(via, rt.fwaddr.addr[0]);
+    }
+
+    if (rt.tag)
+      UYTC_LEAF(tag, rt.tag);
+
+    if (he->lsa_type == LSA_T_NSSA)
+    {
+      UYTC_LEAF(lsa_type, "nssa-ext");
+    } else {
+      UYTC_LEAF(lsa_type, "external");
+    }
+    UYTC_LEAF(rt_net, &rt.net);
+
+    if(rt.ebit)
+    {
+      UYTC_LEAF(lsa_type_num, 2);
+    }
+    UYTC_LEAF(metric, rt.metric);
+  }
+}
+
+static inline void
+show_lsa_prefix_cbor(UYTC_CONTEXT_TYPE UYTC_CONTEXT, struct top_hash_entry *he, struct top_hash_entry *cnode, int af)
+{
+  struct ospf_lsa_prefix *px = he->lsa_body;
+  u32 *buf;
+  int i;
+
+  /* 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))
+    return;
+
+  if ((px->ref_type == LSA_T_RT) && (px->ref_id != 0))
+    return;
+
+  if ((px->ref_type == LSA_T_NET) && (px->ref_id != cnode->lsa.id))
+    return;
+
+  UYTC_DEF_GROUPING(lsa_prefix)
+  {
+    buf = px->rest;
+
     int i = 0;
-    while (lsa_walk_rt(&rtl))
-    {
-      i++;
-      UYTC_LIST_ITEM(rt) {
-       UYTC_LEAF(dummy_yang_id, i);
-       UYTC_LEAF(metric, rtl.metric);
-       switch (rtl.type) {
-         case LSART_VLNK:
-           UYTC_CHOICE_ITEM(rt_type, vlink) {
-             UYTC_GROUPING(vlink) {
-               UYTC_LEAF(vlink, rtl.id);
-               UYTC_LEAF(name, rtl.name);
-             }
-           }
-           break;
-
-         case LSART_PTP:
-           ...;
-       }
+    UYTC_LIST_FOR (prefixes, i; i < px->pxcount; i++)
+    {
+      net_addr net;
+      u8 pxopts;
+      u16 metric_;
+
+      buf = ospf3_get_prefix(buf, af, &net, &pxopts, &metric_);
+
+      if (px->ref_type == LSA_T_RT)
+      {
+        UYTC_LEAF(stubnet, &net);
+        UYTC_LEAF(metric_, metric);
+      }
+      else
+      {
+        UYTC_LEAF(address, &net);
       }
     }
   }
 }
 
+void
+ospf_sh_state_cbor(UYTC_CONTEXT_TYPE UYTC_CONTEXT, struct proto *P, int verbose, int reachable)
+{
+  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;
 
-for (i = 0; i < j1; i++) {
-  UYTC_LIST_ITEM(areas) {
-    struct top_hash_entry *he = hea[i];
+  if (p->p.proto_state != PS_UP)
+  {
+    UYTC_LEAF(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;
+
+  int i = 0;
+  UYTC_LIST_WALK_SLIST(areas, he, p->lsal)
+  {
     UYTC_LEAF(dummy_yang_id, i);
+    i++;
     UYTC_LEAF(area, he->domain);
-
     switch (he->lsa_type)
     {
-      case LSA_T_RT:
-       UYTC_CHOICE_ITEM(lsa_type, rt, show_lsa_router_cbor(UYTC_CONTEXT, he));
-       break;
+    case LSA_T_RT:
+      if (he->lsa.id == cnode->lsa.id)
+       show_lsa_router_cbor(UYTC_CONTEXT, p, he, verbose);
+      break;
+
+    case LSA_T_NET:
+      show_lsa_network_cbor(UYTC_CONTEXT, he, ospf2);
+      break;
+
+    case LSA_T_SUM_NET:
+      if (cnode->lsa_type == LSA_T_RT)
+       show_lsa_sum_net_cbor(UYTC_CONTEXT, he, ospf2, af);
+      break;
+
+    case LSA_T_SUM_RT:
+      if (cnode->lsa_type == LSA_T_RT)
+       show_lsa_sum_rt_cbor(UYTC_CONTEXT, he, ospf2);
+      break;
+
+    case LSA_T_EXT:
+    case LSA_T_NSSA:
+      show_lsa_external_cbor(UYTC_CONTEXT, he, ospf2, af);
+      break;
+
+    case LSA_T_PREFIX:
+      show_lsa_prefix_cbor(UYTC_CONTEXT, he, cnode, af);
+      break;
+    }
+
+    u32 last_rt = 0xFFFFFFFF;
+    if (he->domain)
+    {
+      he->domain = 0;
+
+      if ((he->color != INSPF) && reachable)
+       continue;
+
+      UITC_CONTAINER(other_ASBR)
+      {
+        if (he->lsa.rt != last_rt)
+        {
+         UYTC_LEAF(router, he->lsa.rt);
+         last_rt = he->lsa.rt;
+        }
 
-      case LSA_T_NET:
-       UYTC_CHOICE_ITEM(lsa_type, net, show_lsa_network_cbor(UYTC_CONTEXT, he, ospf2));
-       break;
+        show_lsa_external_cbor(UYTC_CONTEXT, he, ospf2, af);
+      }
     }
   }
 }
+