ospf6d: bring ospf6d closer to ospfd

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

ospf6d: bring ospf6d closer to ospfd

Denis Fondras
Biggest chunk is rework of rde_asext_get()/rde_asext_put().
Also change get_net_link() and get_rtr_link() to work like ospfd couterpart.

Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/ospf6d/rde.c,v
retrieving revision 1.84
diff -u -p -r1.84 rde.c
--- rde.c 17 Feb 2020 08:12:22 -0000 1.84
+++ rde.c 21 Mar 2020 16:04:47 -0000
@@ -59,8 +59,9 @@ int rde_req_list_exists(struct rde_nbr
 void rde_req_list_del(struct rde_nbr *, struct lsa_hdr *);
 void rde_req_list_free(struct rde_nbr *);
 
-struct lsa *rde_asext_get(struct kroute *);
-struct lsa *rde_asext_put(struct kroute *);
+struct iface *rde_asext_lookup(struct in6_addr, int);
+void rde_asext_get(struct kroute *);
+void rde_asext_put(struct kroute *);
 
 int comp_asext(struct lsa *, struct lsa *);
 struct lsa *orig_asext_lsa(struct kroute *, u_int16_t);
@@ -217,6 +218,7 @@ __dead void
 rde_shutdown(void)
 {
  struct area *a;
+ struct vertex *v, *nv;
 
  /* close pipes */
  msgbuf_clear(&iev_ospfe->ibuf.w);
@@ -232,6 +234,10 @@ rde_shutdown(void)
  LIST_REMOVE(a, entry);
  area_del(a);
  }
+ for (v = RB_MIN(lsa_tree, &asext_tree); v != NULL; v = nv) {
+ nv = RB_NEXT(lsa_tree, &asext_tree, v);
+ vertex_free(v);
+ }
  rde_nbr_free();
 
  free(iev_ospfe);
@@ -643,8 +649,6 @@ rde_dispatch_parent(int fd, short event,
  struct kroute kr;
  struct imsgev *iev = bula;
  struct imsgbuf *ibuf = &iev->ibuf;
- struct lsa *lsa;
- struct vertex *v;
  ssize_t n;
  int shut = 0, link_ok, prev_link_ok, orig_lsa;
  unsigned int ifindex;
@@ -676,13 +680,7 @@ rde_dispatch_parent(int fd, short event,
  break;
  }
  memcpy(&kr, imsg.data, sizeof(kr));
-
- if ((lsa = rde_asext_get(&kr)) != NULL) {
- v = lsa_find(NULL, lsa->hdr.type,
-    lsa->hdr.ls_id, lsa->hdr.adv_rtr);
-
- lsa_merge(nbrself, lsa, v);
- }
+ rde_asext_get(&kr);
  break;
  case IMSG_NETWORK_DEL:
  if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) {
@@ -691,20 +689,7 @@ rde_dispatch_parent(int fd, short event,
  break;
  }
  memcpy(&kr, imsg.data, sizeof(kr));
-
- if ((lsa = rde_asext_put(&kr)) != NULL) {
- v = lsa_find(NULL, lsa->hdr.type,
-    lsa->hdr.ls_id, lsa->hdr.adv_rtr);
-
- /*
- * if v == NULL no LSA is in the table and
- * nothing has to be done.
- */
- if (v)
- lsa_merge(nbrself, lsa, v);
- else
- free(lsa);
- }
+ rde_asext_put(&kr);
  break;
  case IMSG_IFINFO:
  if (imsg.hdr.len != IMSG_HEADER_SIZE +
@@ -1202,48 +1187,77 @@ rde_req_list_free(struct rde_nbr *nbr)
 /*
  * as-external LSA handling
  */
-struct lsa *
-rde_asext_get(struct kroute *kr)
+struct iface *
+rde_asext_lookup(struct in6_addr prefix, int plen)
 {
+
  struct area *area;
  struct iface *iface;
  struct iface_addr *ia;
- struct in6_addr addr;
-
- LIST_FOREACH(area, &rdeconf->area_list, entry)
- LIST_FOREACH(iface, &area->iface_list, entry)
+ struct in6_addr ina, inb;
+
+ LIST_FOREACH(area, &rdeconf->area_list, entry) {
+ LIST_FOREACH(iface, &area->iface_list, entry) {
  TAILQ_FOREACH(ia, &iface->ifa_list, entry) {
  if (IN6_IS_ADDR_LINKLOCAL(&ia->addr))
  continue;
 
- inet6applymask(&addr, &ia->addr,
-    kr->prefixlen);
- if (!memcmp(&addr, &kr->prefix,
-    sizeof(addr)) && kr->prefixlen ==
-    ia->prefixlen) {
- /* already announced as Prefix LSA */
- log_debug("rde_asext_get: %s/%d is "
-    "part of prefix LSA",
-    log_in6addr(&kr->prefix),
-    kr->prefixlen);
- return (NULL);
- }
+ inet6applymask(&ina, &ia->addr, ia->prefixlen);
+ inet6applymask(&inb, &prefix, ia->prefixlen);
+ if (IN6_ARE_ADDR_EQUAL(&ina, &inb) &&
+    (plen == -1 || plen == ia->prefixlen))
+ return (iface);
  }
+ }
+ }
+ return (NULL);
+}
+
+void
+rde_asext_get(struct kroute *kr)
+{
+ struct vertex *v;
+ struct lsa *lsa;
+
+ if (rde_asext_lookup(kr->prefix, kr->prefixlen)) {
+ /* already announced as (stub) net LSA */
+ log_debug("rde_asext_get: %s/%d is net LSA",
+    log_in6addr(&kr->prefix), kr->prefixlen);
+ return;
+ }
 
  /* update of seqnum is done by lsa_merge */
- return (orig_asext_lsa(kr, DEFAULT_AGE));
+ if ((lsa = orig_asext_lsa(kr, DEFAULT_AGE))) {
+ v = lsa_find(NULL, lsa->hdr.type, lsa->hdr.ls_id,
+    lsa->hdr.adv_rtr);
+ lsa_merge(nbrself, lsa, v);
+ }
 }
 
-struct lsa *
+void
 rde_asext_put(struct kroute *kr)
 {
+ struct vertex *v;
+ struct lsa *lsa;
  /*
  * just try to remove the LSA. If the prefix is announced as
  * stub net LSA lsa_find() will fail later and nothing will happen.
  */
 
  /* remove by reflooding with MAX_AGE */
- return (orig_asext_lsa(kr, MAX_AGE));
+ if ((lsa = orig_asext_lsa(kr, MAX_AGE))) {
+ v = lsa_find(NULL, lsa->hdr.type, lsa->hdr.ls_id,
+    lsa->hdr.adv_rtr);
+
+ /*
+ * if v == NULL no LSA is in the table and
+ * nothing has to be done.
+ */
+ if (v)
+ lsa_merge(nbrself, lsa, v);
+ else
+ free(lsa);
+ }
 }
 
 /*
@@ -1706,8 +1720,7 @@ orig_asext_lsa(struct kroute *kr, u_int1
  }
 
  lsa->hdr.ls_chksum = 0;
- lsa->hdr.ls_chksum =
-    htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
+ lsa->hdr.ls_chksum = htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
 
  return (lsa);
 }
Index: rde_spf.c
===================================================================
RCS file: /cvs/src/usr.sbin/ospf6d/rde_spf.c,v
retrieving revision 1.26
diff -u -p -r1.26 rde_spf.c
--- rde_spf.c 22 Dec 2019 11:19:07 -0000 1.26
+++ rde_spf.c 21 Mar 2020 16:04:47 -0000
@@ -44,10 +44,11 @@ void calc_nexthop(struct vertex *, str
      struct area *, struct lsa_rtr_link *);
 void rt_nexthop_clear(struct rt_node *);
 void rt_nexthop_add(struct rt_node *, struct v_nexthead *,
-     struct in_addr);
+     u_int16_t, struct in_addr);
 void rt_update(struct in6_addr *, u_int8_t, struct v_nexthead *,
-     u_int32_t, u_int32_t, struct in_addr, struct in_addr,
-     enum path_type, enum dst_type, u_int8_t, u_int32_t);
+     u_int16_t, u_int32_t, u_int32_t, struct in_addr,
+     struct in_addr, enum path_type, enum dst_type, u_int8_t,
+     u_int32_t);
 struct rt_node *rt_lookup(enum dst_type, struct in6_addr *);
 void rt_invalidate(struct area *);
 int linked(struct vertex *, struct vertex *);
@@ -167,6 +168,7 @@ spf_calc(struct area *area)
  /* spf_dump(area); */
  log_debug("spf_calc: area %s calculated", inet_ntoa(area->id));
 
+#if 0
  /* Dump SPF tree to log */
  RB_FOREACH(v, lsa_tree, &area->lsa_tree) {
  struct v_nexthop *vn;
@@ -191,6 +193,7 @@ spf_calc(struct area *area)
     v == spf_root ? "*" : " ", log_rtr_id(htonl(v->adv_rtr)),
     v->type, log_rtr_id(htonl(v->ls_id)), v->cost, hops);
  }
+#endif
 
  area->num_spf_calc++;
  start_spf_timer();
@@ -225,7 +228,7 @@ rt_calc(struct vertex *v, struct area *a
  adv_rtr.s_addr = htonl(v->adv_rtr);
  bcopy(&adv_rtr, &ia6.s6_addr[12], sizeof(adv_rtr));
 
- rt_update(&ia6, 128, &v->nexthop, v->cost, 0, area->id,
+ rt_update(&ia6, 128, &v->nexthop, v->type, v->cost, 0, area->id,
     adv_rtr, PT_INTER_AREA, DT_RTR, flags, 0);
  break;
  case LSA_TYPE_INTRA_A_PREFIX:
@@ -279,7 +282,7 @@ rt_calc(struct vertex *v, struct area *a
  adv_rtr.s_addr = htonl(w->adv_rtr);
 
  rt_update(&ia6, prefix->prefixlen, &w->nexthop,
-    w->cost + ntohs(prefix->metric), 0,
+    v->type, w->cost + ntohs(prefix->metric), 0,
     area->id, adv_rtr, PT_INTRA_AREA, DT_NET,
     flags, 0);
  }
@@ -315,9 +318,10 @@ rt_calc(struct vertex *v, struct area *a
  bzero(&ia6, sizeof(ia6));
  bcopy(prefix + 1, &ia6, LSA_PREFIXSIZE(prefix->prefixlen));
 
- rt_update(&ia6, prefix->prefixlen, &w->nexthop, w->cost +
-    (ntohs(v->lsa->data.rtr_sum.metric) & LSA_METRIC_MASK), 0,
-    area->id, adv_rtr, PT_INTER_AREA, DT_NET, 0, 0);
+ rt_update(&ia6, prefix->prefixlen, &w->nexthop, v->type,
+    w->cost + (ntohs(v->lsa->data.rtr_sum.metric) &
+    LSA_METRIC_MASK), 0, area->id, adv_rtr, PT_INTER_AREA,
+    DT_NET, 0, 0);
  break;
  case LSA_TYPE_INTER_A_ROUTER:
  /* XXX if ABR only look at area 0.0.0.0 LSA */
@@ -343,7 +347,7 @@ rt_calc(struct vertex *v, struct area *a
  bcopy(&v->lsa->data.rtr_sum.dest_rtr_id, &ia6.s6_addr[12],
     4);
 
- rt_update(&ia6, 128, &w->nexthop, w->cost +
+ rt_update(&ia6, 128, &w->nexthop, v->type, w->cost +
     (ntohs(v->lsa->data.rtr_sum.metric) & LSA_METRIC_MASK), 0,
     area->id, adv_rtr, PT_INTER_AREA, DT_RTR, 0, 0);
  break;
@@ -434,8 +438,8 @@ asext_calc(struct vertex *v)
     rn->ifindex);
  }
 
- rt_update(&addr, prefix->prefixlen, &v->nexthop, v->cost, cost2,
-    area, adv_rtr, type, DT_NET, 0, ext_tag);
+ rt_update(&addr, prefix->prefixlen, &v->nexthop, v->type,
+    v->cost, cost2, area, adv_rtr, type, DT_NET, 0, ext_tag);
  break;
  default:
  fatalx("asext_calc: invalid LSA type");
@@ -863,7 +867,7 @@ rt_nexthop_clear(struct rt_node *r)
 }
 
 void
-rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh,
+rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh, u_int16_t type,
     struct in_addr adv_rtr)
 {
  struct v_nexthop *vn;
@@ -876,7 +880,9 @@ rt_nexthop_add(struct rt_node *r, struct
  continue;
 
  rn->adv_rtr.s_addr = adv_rtr.s_addr;
- rn->connected = vn->prev == spf_root;
+ rn->connected = (type == LSA_TYPE_NETWORK &&
+    vn->prev == spf_root) ||
+    (IN6_IS_ADDR_UNSPECIFIED(&vn->nexthop));
  rn->invalid = 0;
 
  r->invalid = 0;
@@ -973,7 +979,7 @@ rt_dump(struct in_addr area, pid_t pid,
 
 void
 rt_update(struct in6_addr *prefix, u_int8_t prefixlen, struct v_nexthead *vnh,
-     u_int32_t cost, u_int32_t cost2, struct in_addr area,
+     u_int16_t v_type, u_int32_t cost, u_int32_t cost2, struct in_addr area,
      struct in_addr adv_rtr, enum path_type p_type, enum dst_type d_type,
      u_int8_t flags, u_int32_t tag)
 {
@@ -999,7 +1005,7 @@ rt_update(struct in6_addr *prefix, u_int
  rte->flags = flags;
  rte->ext_tag = tag;
 
- rt_nexthop_add(rte, vnh, adv_rtr);
+ rt_nexthop_add(rte, vnh, v_type, adv_rtr);
 
  rt_insert(rte);
  } else {
@@ -1054,7 +1060,7 @@ rt_update(struct in6_addr *prefix, u_int
  }
 
  if (equal || better)
- rt_nexthop_add(rte, vnh, adv_rtr);
+ rt_nexthop_add(rte, vnh, v_type, adv_rtr);
  }
 }
 
@@ -1115,7 +1121,7 @@ get_rtr_link(struct vertex *v, unsigned
  v = lsa_find_rtr_frag(v->area, htonl(v->adv_rtr), frag++);
  } while (v);
 
- return (NULL);
+ fatalx("get_rtr_link: index not found");
 }
 
 /* network LSA links */
@@ -1124,21 +1130,23 @@ get_net_link(struct vertex *v, unsigned
 {
  struct lsa_net_link *net_link = NULL;
  char *buf = (char *)v->lsa;
- unsigned int i;
+ unsigned int i, nlinks;
 
  if (v->type != LSA_TYPE_NETWORK)
  fatalx("get_net_link: invalid LSA type");
 
- /* number of links validated earlier by lsa_check() */
  net_link = (struct lsa_net_link *)(buf + sizeof(v->lsa->hdr) +
     sizeof(struct lsa_net));
- for (i = 0; i < lsa_num_links(v); i++) {
+
+ /* number of links validated earlier by lsa_check() */
+ nlinks = lsa_num_links(v);
+ for (i = 0; i < nlinks; i++) {
  if (i == idx)
  return (net_link);
  net_link++;
  }
 
- return (NULL);
+ fatalx("get_net_link: index not found");
 }
 
 /* misc */

Reply | Threaded
Open this post in threaded view
|

Re: ospf6d: bring ospf6d closer to ospfd

Remi Locherer
On Sat, Mar 21, 2020 at 05:25:45PM +0100, Denis Fondras wrote:
> Biggest chunk is rework of rde_asext_get()/rde_asext_put().
> Also change get_net_link() and get_rtr_link() to work like ospfd couterpart.

Reads good to me and I didn't spot any issues running tests with it.

One question: why "if 0" the "Dump SPF tree to log"?

>
> Index: rde.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/ospf6d/rde.c,v
> retrieving revision 1.84
> diff -u -p -r1.84 rde.c
> --- rde.c 17 Feb 2020 08:12:22 -0000 1.84
> +++ rde.c 21 Mar 2020 16:04:47 -0000
> @@ -59,8 +59,9 @@ int rde_req_list_exists(struct rde_nbr
>  void rde_req_list_del(struct rde_nbr *, struct lsa_hdr *);
>  void rde_req_list_free(struct rde_nbr *);
>  
> -struct lsa *rde_asext_get(struct kroute *);
> -struct lsa *rde_asext_put(struct kroute *);
> +struct iface *rde_asext_lookup(struct in6_addr, int);
> +void rde_asext_get(struct kroute *);
> +void rde_asext_put(struct kroute *);
>  
>  int comp_asext(struct lsa *, struct lsa *);
>  struct lsa *orig_asext_lsa(struct kroute *, u_int16_t);
> @@ -217,6 +218,7 @@ __dead void
>  rde_shutdown(void)
>  {
>   struct area *a;
> + struct vertex *v, *nv;
>  
>   /* close pipes */
>   msgbuf_clear(&iev_ospfe->ibuf.w);
> @@ -232,6 +234,10 @@ rde_shutdown(void)
>   LIST_REMOVE(a, entry);
>   area_del(a);
>   }
> + for (v = RB_MIN(lsa_tree, &asext_tree); v != NULL; v = nv) {
> + nv = RB_NEXT(lsa_tree, &asext_tree, v);
> + vertex_free(v);
> + }
>   rde_nbr_free();
>  
>   free(iev_ospfe);
> @@ -643,8 +649,6 @@ rde_dispatch_parent(int fd, short event,
>   struct kroute kr;
>   struct imsgev *iev = bula;
>   struct imsgbuf *ibuf = &iev->ibuf;
> - struct lsa *lsa;
> - struct vertex *v;
>   ssize_t n;
>   int shut = 0, link_ok, prev_link_ok, orig_lsa;
>   unsigned int ifindex;
> @@ -676,13 +680,7 @@ rde_dispatch_parent(int fd, short event,
>   break;
>   }
>   memcpy(&kr, imsg.data, sizeof(kr));
> -
> - if ((lsa = rde_asext_get(&kr)) != NULL) {
> - v = lsa_find(NULL, lsa->hdr.type,
> -    lsa->hdr.ls_id, lsa->hdr.adv_rtr);
> -
> - lsa_merge(nbrself, lsa, v);
> - }
> + rde_asext_get(&kr);
>   break;
>   case IMSG_NETWORK_DEL:
>   if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) {
> @@ -691,20 +689,7 @@ rde_dispatch_parent(int fd, short event,
>   break;
>   }
>   memcpy(&kr, imsg.data, sizeof(kr));
> -
> - if ((lsa = rde_asext_put(&kr)) != NULL) {
> - v = lsa_find(NULL, lsa->hdr.type,
> -    lsa->hdr.ls_id, lsa->hdr.adv_rtr);
> -
> - /*
> - * if v == NULL no LSA is in the table and
> - * nothing has to be done.
> - */
> - if (v)
> - lsa_merge(nbrself, lsa, v);
> - else
> - free(lsa);
> - }
> + rde_asext_put(&kr);
>   break;
>   case IMSG_IFINFO:
>   if (imsg.hdr.len != IMSG_HEADER_SIZE +
> @@ -1202,48 +1187,77 @@ rde_req_list_free(struct rde_nbr *nbr)
>  /*
>   * as-external LSA handling
>   */
> -struct lsa *
> -rde_asext_get(struct kroute *kr)
> +struct iface *
> +rde_asext_lookup(struct in6_addr prefix, int plen)
>  {
> +
>   struct area *area;
>   struct iface *iface;
>   struct iface_addr *ia;
> - struct in6_addr addr;
> -
> - LIST_FOREACH(area, &rdeconf->area_list, entry)
> - LIST_FOREACH(iface, &area->iface_list, entry)
> + struct in6_addr ina, inb;
> +
> + LIST_FOREACH(area, &rdeconf->area_list, entry) {
> + LIST_FOREACH(iface, &area->iface_list, entry) {
>   TAILQ_FOREACH(ia, &iface->ifa_list, entry) {
>   if (IN6_IS_ADDR_LINKLOCAL(&ia->addr))
>   continue;
>  
> - inet6applymask(&addr, &ia->addr,
> -    kr->prefixlen);
> - if (!memcmp(&addr, &kr->prefix,
> -    sizeof(addr)) && kr->prefixlen ==
> -    ia->prefixlen) {
> - /* already announced as Prefix LSA */
> - log_debug("rde_asext_get: %s/%d is "
> -    "part of prefix LSA",
> -    log_in6addr(&kr->prefix),
> -    kr->prefixlen);
> - return (NULL);
> - }
> + inet6applymask(&ina, &ia->addr, ia->prefixlen);
> + inet6applymask(&inb, &prefix, ia->prefixlen);
> + if (IN6_ARE_ADDR_EQUAL(&ina, &inb) &&
> +    (plen == -1 || plen == ia->prefixlen))
> + return (iface);
>   }
> + }
> + }
> + return (NULL);
> +}
> +
> +void
> +rde_asext_get(struct kroute *kr)
> +{
> + struct vertex *v;
> + struct lsa *lsa;
> +
> + if (rde_asext_lookup(kr->prefix, kr->prefixlen)) {
> + /* already announced as (stub) net LSA */
> + log_debug("rde_asext_get: %s/%d is net LSA",
> +    log_in6addr(&kr->prefix), kr->prefixlen);
> + return;
> + }
>  
>   /* update of seqnum is done by lsa_merge */
> - return (orig_asext_lsa(kr, DEFAULT_AGE));
> + if ((lsa = orig_asext_lsa(kr, DEFAULT_AGE))) {
> + v = lsa_find(NULL, lsa->hdr.type, lsa->hdr.ls_id,
> +    lsa->hdr.adv_rtr);
> + lsa_merge(nbrself, lsa, v);
> + }
>  }
>  
> -struct lsa *
> +void
>  rde_asext_put(struct kroute *kr)
>  {
> + struct vertex *v;
> + struct lsa *lsa;
>   /*
>   * just try to remove the LSA. If the prefix is announced as
>   * stub net LSA lsa_find() will fail later and nothing will happen.
>   */
>  
>   /* remove by reflooding with MAX_AGE */
> - return (orig_asext_lsa(kr, MAX_AGE));
> + if ((lsa = orig_asext_lsa(kr, MAX_AGE))) {
> + v = lsa_find(NULL, lsa->hdr.type, lsa->hdr.ls_id,
> +    lsa->hdr.adv_rtr);
> +
> + /*
> + * if v == NULL no LSA is in the table and
> + * nothing has to be done.
> + */
> + if (v)
> + lsa_merge(nbrself, lsa, v);
> + else
> + free(lsa);
> + }
>  }
>  
>  /*
> @@ -1706,8 +1720,7 @@ orig_asext_lsa(struct kroute *kr, u_int1
>   }
>  
>   lsa->hdr.ls_chksum = 0;
> - lsa->hdr.ls_chksum =
> -    htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
> + lsa->hdr.ls_chksum = htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
>  
>   return (lsa);
>  }
> Index: rde_spf.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/ospf6d/rde_spf.c,v
> retrieving revision 1.26
> diff -u -p -r1.26 rde_spf.c
> --- rde_spf.c 22 Dec 2019 11:19:07 -0000 1.26
> +++ rde_spf.c 21 Mar 2020 16:04:47 -0000
> @@ -44,10 +44,11 @@ void calc_nexthop(struct vertex *, str
>       struct area *, struct lsa_rtr_link *);
>  void rt_nexthop_clear(struct rt_node *);
>  void rt_nexthop_add(struct rt_node *, struct v_nexthead *,
> -     struct in_addr);
> +     u_int16_t, struct in_addr);
>  void rt_update(struct in6_addr *, u_int8_t, struct v_nexthead *,
> -     u_int32_t, u_int32_t, struct in_addr, struct in_addr,
> -     enum path_type, enum dst_type, u_int8_t, u_int32_t);
> +     u_int16_t, u_int32_t, u_int32_t, struct in_addr,
> +     struct in_addr, enum path_type, enum dst_type, u_int8_t,
> +     u_int32_t);
>  struct rt_node *rt_lookup(enum dst_type, struct in6_addr *);
>  void rt_invalidate(struct area *);
>  int linked(struct vertex *, struct vertex *);
> @@ -167,6 +168,7 @@ spf_calc(struct area *area)
>   /* spf_dump(area); */
>   log_debug("spf_calc: area %s calculated", inet_ntoa(area->id));
>  
> +#if 0
>   /* Dump SPF tree to log */
>   RB_FOREACH(v, lsa_tree, &area->lsa_tree) {
>   struct v_nexthop *vn;
> @@ -191,6 +193,7 @@ spf_calc(struct area *area)
>      v == spf_root ? "*" : " ", log_rtr_id(htonl(v->adv_rtr)),
>      v->type, log_rtr_id(htonl(v->ls_id)), v->cost, hops);
>   }
> +#endif
>  
>   area->num_spf_calc++;
>   start_spf_timer();
> @@ -225,7 +228,7 @@ rt_calc(struct vertex *v, struct area *a
>   adv_rtr.s_addr = htonl(v->adv_rtr);
>   bcopy(&adv_rtr, &ia6.s6_addr[12], sizeof(adv_rtr));
>  
> - rt_update(&ia6, 128, &v->nexthop, v->cost, 0, area->id,
> + rt_update(&ia6, 128, &v->nexthop, v->type, v->cost, 0, area->id,
>      adv_rtr, PT_INTER_AREA, DT_RTR, flags, 0);
>   break;
>   case LSA_TYPE_INTRA_A_PREFIX:
> @@ -279,7 +282,7 @@ rt_calc(struct vertex *v, struct area *a
>   adv_rtr.s_addr = htonl(w->adv_rtr);
>  
>   rt_update(&ia6, prefix->prefixlen, &w->nexthop,
> -    w->cost + ntohs(prefix->metric), 0,
> +    v->type, w->cost + ntohs(prefix->metric), 0,
>      area->id, adv_rtr, PT_INTRA_AREA, DT_NET,
>      flags, 0);
>   }
> @@ -315,9 +318,10 @@ rt_calc(struct vertex *v, struct area *a
>   bzero(&ia6, sizeof(ia6));
>   bcopy(prefix + 1, &ia6, LSA_PREFIXSIZE(prefix->prefixlen));
>  
> - rt_update(&ia6, prefix->prefixlen, &w->nexthop, w->cost +
> -    (ntohs(v->lsa->data.rtr_sum.metric) & LSA_METRIC_MASK), 0,
> -    area->id, adv_rtr, PT_INTER_AREA, DT_NET, 0, 0);
> + rt_update(&ia6, prefix->prefixlen, &w->nexthop, v->type,
> +    w->cost + (ntohs(v->lsa->data.rtr_sum.metric) &
> +    LSA_METRIC_MASK), 0, area->id, adv_rtr, PT_INTER_AREA,
> +    DT_NET, 0, 0);
>   break;
>   case LSA_TYPE_INTER_A_ROUTER:
>   /* XXX if ABR only look at area 0.0.0.0 LSA */
> @@ -343,7 +347,7 @@ rt_calc(struct vertex *v, struct area *a
>   bcopy(&v->lsa->data.rtr_sum.dest_rtr_id, &ia6.s6_addr[12],
>      4);
>  
> - rt_update(&ia6, 128, &w->nexthop, w->cost +
> + rt_update(&ia6, 128, &w->nexthop, v->type, w->cost +
>      (ntohs(v->lsa->data.rtr_sum.metric) & LSA_METRIC_MASK), 0,
>      area->id, adv_rtr, PT_INTER_AREA, DT_RTR, 0, 0);
>   break;
> @@ -434,8 +438,8 @@ asext_calc(struct vertex *v)
>      rn->ifindex);
>   }
>  
> - rt_update(&addr, prefix->prefixlen, &v->nexthop, v->cost, cost2,
> -    area, adv_rtr, type, DT_NET, 0, ext_tag);
> + rt_update(&addr, prefix->prefixlen, &v->nexthop, v->type,
> +    v->cost, cost2, area, adv_rtr, type, DT_NET, 0, ext_tag);
>   break;
>   default:
>   fatalx("asext_calc: invalid LSA type");
> @@ -863,7 +867,7 @@ rt_nexthop_clear(struct rt_node *r)
>  }
>  
>  void
> -rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh,
> +rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh, u_int16_t type,
>      struct in_addr adv_rtr)
>  {
>   struct v_nexthop *vn;
> @@ -876,7 +880,9 @@ rt_nexthop_add(struct rt_node *r, struct
>   continue;
>  
>   rn->adv_rtr.s_addr = adv_rtr.s_addr;
> - rn->connected = vn->prev == spf_root;
> + rn->connected = (type == LSA_TYPE_NETWORK &&
> +    vn->prev == spf_root) ||
> +    (IN6_IS_ADDR_UNSPECIFIED(&vn->nexthop));
>   rn->invalid = 0;
>  
>   r->invalid = 0;
> @@ -973,7 +979,7 @@ rt_dump(struct in_addr area, pid_t pid,
>  
>  void
>  rt_update(struct in6_addr *prefix, u_int8_t prefixlen, struct v_nexthead *vnh,
> -     u_int32_t cost, u_int32_t cost2, struct in_addr area,
> +     u_int16_t v_type, u_int32_t cost, u_int32_t cost2, struct in_addr area,
>       struct in_addr adv_rtr, enum path_type p_type, enum dst_type d_type,
>       u_int8_t flags, u_int32_t tag)
>  {
> @@ -999,7 +1005,7 @@ rt_update(struct in6_addr *prefix, u_int
>   rte->flags = flags;
>   rte->ext_tag = tag;
>  
> - rt_nexthop_add(rte, vnh, adv_rtr);
> + rt_nexthop_add(rte, vnh, v_type, adv_rtr);
>  
>   rt_insert(rte);
>   } else {
> @@ -1054,7 +1060,7 @@ rt_update(struct in6_addr *prefix, u_int
>   }
>  
>   if (equal || better)
> - rt_nexthop_add(rte, vnh, adv_rtr);
> + rt_nexthop_add(rte, vnh, v_type, adv_rtr);
>   }
>  }
>  
> @@ -1115,7 +1121,7 @@ get_rtr_link(struct vertex *v, unsigned
>   v = lsa_find_rtr_frag(v->area, htonl(v->adv_rtr), frag++);
>   } while (v);
>  
> - return (NULL);
> + fatalx("get_rtr_link: index not found");
>  }
>  
>  /* network LSA links */
> @@ -1124,21 +1130,23 @@ get_net_link(struct vertex *v, unsigned
>  {
>   struct lsa_net_link *net_link = NULL;
>   char *buf = (char *)v->lsa;
> - unsigned int i;
> + unsigned int i, nlinks;
>  
>   if (v->type != LSA_TYPE_NETWORK)
>   fatalx("get_net_link: invalid LSA type");
>  
> - /* number of links validated earlier by lsa_check() */
>   net_link = (struct lsa_net_link *)(buf + sizeof(v->lsa->hdr) +
>      sizeof(struct lsa_net));
> - for (i = 0; i < lsa_num_links(v); i++) {
> +
> + /* number of links validated earlier by lsa_check() */
> + nlinks = lsa_num_links(v);
> + for (i = 0; i < nlinks; i++) {
>   if (i == idx)
>   return (net_link);
>   net_link++;
>   }
>  
> - return (NULL);
> + fatalx("get_net_link: index not found");
>  }
>  
>  /* misc */
>

Reply | Threaded
Open this post in threaded view
|

Re: ospf6d: bring ospf6d closer to ospfd

Denis Fondras
On Sat, Mar 28, 2020 at 05:00:11PM +0100, Remi Locherer wrote:
> On Sat, Mar 21, 2020 at 05:25:45PM +0100, Denis Fondras wrote:
> > Biggest chunk is rework of rde_asext_get()/rde_asext_put().
> > Also change get_net_link() and get_rtr_link() to work like ospfd couterpart.
>
> Reads good to me and I didn't spot any issues running tests with it.
>

Thank you Remi.

> One question: why "if 0" the "Dump SPF tree to log"?
>

Doh! It is not (yet) time to '#if 0' this part. This is from an unpublished diff
that changes how if_find() works. Thus printing the SPF tree needs to be
rewritten.


> >
> > Index: rde.c
> > ===================================================================
> > RCS file: /cvs/src/usr.sbin/ospf6d/rde.c,v
> > retrieving revision 1.84
> > diff -u -p -r1.84 rde.c
> > --- rde.c 17 Feb 2020 08:12:22 -0000 1.84
> > +++ rde.c 21 Mar 2020 16:04:47 -0000
> > @@ -59,8 +59,9 @@ int rde_req_list_exists(struct rde_nbr
> >  void rde_req_list_del(struct rde_nbr *, struct lsa_hdr *);
> >  void rde_req_list_free(struct rde_nbr *);
> >  
> > -struct lsa *rde_asext_get(struct kroute *);
> > -struct lsa *rde_asext_put(struct kroute *);
> > +struct iface *rde_asext_lookup(struct in6_addr, int);
> > +void rde_asext_get(struct kroute *);
> > +void rde_asext_put(struct kroute *);
> >  
> >  int comp_asext(struct lsa *, struct lsa *);
> >  struct lsa *orig_asext_lsa(struct kroute *, u_int16_t);
> > @@ -217,6 +218,7 @@ __dead void
> >  rde_shutdown(void)
> >  {
> >   struct area *a;
> > + struct vertex *v, *nv;
> >  
> >   /* close pipes */
> >   msgbuf_clear(&iev_ospfe->ibuf.w);
> > @@ -232,6 +234,10 @@ rde_shutdown(void)
> >   LIST_REMOVE(a, entry);
> >   area_del(a);
> >   }
> > + for (v = RB_MIN(lsa_tree, &asext_tree); v != NULL; v = nv) {
> > + nv = RB_NEXT(lsa_tree, &asext_tree, v);
> > + vertex_free(v);
> > + }
> >   rde_nbr_free();
> >  
> >   free(iev_ospfe);
> > @@ -643,8 +649,6 @@ rde_dispatch_parent(int fd, short event,
> >   struct kroute kr;
> >   struct imsgev *iev = bula;
> >   struct imsgbuf *ibuf = &iev->ibuf;
> > - struct lsa *lsa;
> > - struct vertex *v;
> >   ssize_t n;
> >   int shut = 0, link_ok, prev_link_ok, orig_lsa;
> >   unsigned int ifindex;
> > @@ -676,13 +680,7 @@ rde_dispatch_parent(int fd, short event,
> >   break;
> >   }
> >   memcpy(&kr, imsg.data, sizeof(kr));
> > -
> > - if ((lsa = rde_asext_get(&kr)) != NULL) {
> > - v = lsa_find(NULL, lsa->hdr.type,
> > -    lsa->hdr.ls_id, lsa->hdr.adv_rtr);
> > -
> > - lsa_merge(nbrself, lsa, v);
> > - }
> > + rde_asext_get(&kr);
> >   break;
> >   case IMSG_NETWORK_DEL:
> >   if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(kr)) {
> > @@ -691,20 +689,7 @@ rde_dispatch_parent(int fd, short event,
> >   break;
> >   }
> >   memcpy(&kr, imsg.data, sizeof(kr));
> > -
> > - if ((lsa = rde_asext_put(&kr)) != NULL) {
> > - v = lsa_find(NULL, lsa->hdr.type,
> > -    lsa->hdr.ls_id, lsa->hdr.adv_rtr);
> > -
> > - /*
> > - * if v == NULL no LSA is in the table and
> > - * nothing has to be done.
> > - */
> > - if (v)
> > - lsa_merge(nbrself, lsa, v);
> > - else
> > - free(lsa);
> > - }
> > + rde_asext_put(&kr);
> >   break;
> >   case IMSG_IFINFO:
> >   if (imsg.hdr.len != IMSG_HEADER_SIZE +
> > @@ -1202,48 +1187,77 @@ rde_req_list_free(struct rde_nbr *nbr)
> >  /*
> >   * as-external LSA handling
> >   */
> > -struct lsa *
> > -rde_asext_get(struct kroute *kr)
> > +struct iface *
> > +rde_asext_lookup(struct in6_addr prefix, int plen)
> >  {
> > +
> >   struct area *area;
> >   struct iface *iface;
> >   struct iface_addr *ia;
> > - struct in6_addr addr;
> > -
> > - LIST_FOREACH(area, &rdeconf->area_list, entry)
> > - LIST_FOREACH(iface, &area->iface_list, entry)
> > + struct in6_addr ina, inb;
> > +
> > + LIST_FOREACH(area, &rdeconf->area_list, entry) {
> > + LIST_FOREACH(iface, &area->iface_list, entry) {
> >   TAILQ_FOREACH(ia, &iface->ifa_list, entry) {
> >   if (IN6_IS_ADDR_LINKLOCAL(&ia->addr))
> >   continue;
> >  
> > - inet6applymask(&addr, &ia->addr,
> > -    kr->prefixlen);
> > - if (!memcmp(&addr, &kr->prefix,
> > -    sizeof(addr)) && kr->prefixlen ==
> > -    ia->prefixlen) {
> > - /* already announced as Prefix LSA */
> > - log_debug("rde_asext_get: %s/%d is "
> > -    "part of prefix LSA",
> > -    log_in6addr(&kr->prefix),
> > -    kr->prefixlen);
> > - return (NULL);
> > - }
> > + inet6applymask(&ina, &ia->addr, ia->prefixlen);
> > + inet6applymask(&inb, &prefix, ia->prefixlen);
> > + if (IN6_ARE_ADDR_EQUAL(&ina, &inb) &&
> > +    (plen == -1 || plen == ia->prefixlen))
> > + return (iface);
> >   }
> > + }
> > + }
> > + return (NULL);
> > +}
> > +
> > +void
> > +rde_asext_get(struct kroute *kr)
> > +{
> > + struct vertex *v;
> > + struct lsa *lsa;
> > +
> > + if (rde_asext_lookup(kr->prefix, kr->prefixlen)) {
> > + /* already announced as (stub) net LSA */
> > + log_debug("rde_asext_get: %s/%d is net LSA",
> > +    log_in6addr(&kr->prefix), kr->prefixlen);
> > + return;
> > + }
> >  
> >   /* update of seqnum is done by lsa_merge */
> > - return (orig_asext_lsa(kr, DEFAULT_AGE));
> > + if ((lsa = orig_asext_lsa(kr, DEFAULT_AGE))) {
> > + v = lsa_find(NULL, lsa->hdr.type, lsa->hdr.ls_id,
> > +    lsa->hdr.adv_rtr);
> > + lsa_merge(nbrself, lsa, v);
> > + }
> >  }
> >  
> > -struct lsa *
> > +void
> >  rde_asext_put(struct kroute *kr)
> >  {
> > + struct vertex *v;
> > + struct lsa *lsa;
> >   /*
> >   * just try to remove the LSA. If the prefix is announced as
> >   * stub net LSA lsa_find() will fail later and nothing will happen.
> >   */
> >  
> >   /* remove by reflooding with MAX_AGE */
> > - return (orig_asext_lsa(kr, MAX_AGE));
> > + if ((lsa = orig_asext_lsa(kr, MAX_AGE))) {
> > + v = lsa_find(NULL, lsa->hdr.type, lsa->hdr.ls_id,
> > +    lsa->hdr.adv_rtr);
> > +
> > + /*
> > + * if v == NULL no LSA is in the table and
> > + * nothing has to be done.
> > + */
> > + if (v)
> > + lsa_merge(nbrself, lsa, v);
> > + else
> > + free(lsa);
> > + }
> >  }
> >  
> >  /*
> > @@ -1706,8 +1720,7 @@ orig_asext_lsa(struct kroute *kr, u_int1
> >   }
> >  
> >   lsa->hdr.ls_chksum = 0;
> > - lsa->hdr.ls_chksum =
> > -    htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
> > + lsa->hdr.ls_chksum = htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
> >  
> >   return (lsa);
> >  }
> > Index: rde_spf.c
> > ===================================================================
> > RCS file: /cvs/src/usr.sbin/ospf6d/rde_spf.c,v
> > retrieving revision 1.26
> > diff -u -p -r1.26 rde_spf.c
> > --- rde_spf.c 22 Dec 2019 11:19:07 -0000 1.26
> > +++ rde_spf.c 21 Mar 2020 16:04:47 -0000
> > @@ -44,10 +44,11 @@ void calc_nexthop(struct vertex *, str
> >       struct area *, struct lsa_rtr_link *);
> >  void rt_nexthop_clear(struct rt_node *);
> >  void rt_nexthop_add(struct rt_node *, struct v_nexthead *,
> > -     struct in_addr);
> > +     u_int16_t, struct in_addr);
> >  void rt_update(struct in6_addr *, u_int8_t, struct v_nexthead *,
> > -     u_int32_t, u_int32_t, struct in_addr, struct in_addr,
> > -     enum path_type, enum dst_type, u_int8_t, u_int32_t);
> > +     u_int16_t, u_int32_t, u_int32_t, struct in_addr,
> > +     struct in_addr, enum path_type, enum dst_type, u_int8_t,
> > +     u_int32_t);
> >  struct rt_node *rt_lookup(enum dst_type, struct in6_addr *);
> >  void rt_invalidate(struct area *);
> >  int linked(struct vertex *, struct vertex *);
> > @@ -167,6 +168,7 @@ spf_calc(struct area *area)
> >   /* spf_dump(area); */
> >   log_debug("spf_calc: area %s calculated", inet_ntoa(area->id));
> >  
> > +#if 0
> >   /* Dump SPF tree to log */
> >   RB_FOREACH(v, lsa_tree, &area->lsa_tree) {
> >   struct v_nexthop *vn;
> > @@ -191,6 +193,7 @@ spf_calc(struct area *area)
> >      v == spf_root ? "*" : " ", log_rtr_id(htonl(v->adv_rtr)),
> >      v->type, log_rtr_id(htonl(v->ls_id)), v->cost, hops);
> >   }
> > +#endif
> >  
> >   area->num_spf_calc++;
> >   start_spf_timer();
> > @@ -225,7 +228,7 @@ rt_calc(struct vertex *v, struct area *a
> >   adv_rtr.s_addr = htonl(v->adv_rtr);
> >   bcopy(&adv_rtr, &ia6.s6_addr[12], sizeof(adv_rtr));
> >  
> > - rt_update(&ia6, 128, &v->nexthop, v->cost, 0, area->id,
> > + rt_update(&ia6, 128, &v->nexthop, v->type, v->cost, 0, area->id,
> >      adv_rtr, PT_INTER_AREA, DT_RTR, flags, 0);
> >   break;
> >   case LSA_TYPE_INTRA_A_PREFIX:
> > @@ -279,7 +282,7 @@ rt_calc(struct vertex *v, struct area *a
> >   adv_rtr.s_addr = htonl(w->adv_rtr);
> >  
> >   rt_update(&ia6, prefix->prefixlen, &w->nexthop,
> > -    w->cost + ntohs(prefix->metric), 0,
> > +    v->type, w->cost + ntohs(prefix->metric), 0,
> >      area->id, adv_rtr, PT_INTRA_AREA, DT_NET,
> >      flags, 0);
> >   }
> > @@ -315,9 +318,10 @@ rt_calc(struct vertex *v, struct area *a
> >   bzero(&ia6, sizeof(ia6));
> >   bcopy(prefix + 1, &ia6, LSA_PREFIXSIZE(prefix->prefixlen));
> >  
> > - rt_update(&ia6, prefix->prefixlen, &w->nexthop, w->cost +
> > -    (ntohs(v->lsa->data.rtr_sum.metric) & LSA_METRIC_MASK), 0,
> > -    area->id, adv_rtr, PT_INTER_AREA, DT_NET, 0, 0);
> > + rt_update(&ia6, prefix->prefixlen, &w->nexthop, v->type,
> > +    w->cost + (ntohs(v->lsa->data.rtr_sum.metric) &
> > +    LSA_METRIC_MASK), 0, area->id, adv_rtr, PT_INTER_AREA,
> > +    DT_NET, 0, 0);
> >   break;
> >   case LSA_TYPE_INTER_A_ROUTER:
> >   /* XXX if ABR only look at area 0.0.0.0 LSA */
> > @@ -343,7 +347,7 @@ rt_calc(struct vertex *v, struct area *a
> >   bcopy(&v->lsa->data.rtr_sum.dest_rtr_id, &ia6.s6_addr[12],
> >      4);
> >  
> > - rt_update(&ia6, 128, &w->nexthop, w->cost +
> > + rt_update(&ia6, 128, &w->nexthop, v->type, w->cost +
> >      (ntohs(v->lsa->data.rtr_sum.metric) & LSA_METRIC_MASK), 0,
> >      area->id, adv_rtr, PT_INTER_AREA, DT_RTR, 0, 0);
> >   break;
> > @@ -434,8 +438,8 @@ asext_calc(struct vertex *v)
> >      rn->ifindex);
> >   }
> >  
> > - rt_update(&addr, prefix->prefixlen, &v->nexthop, v->cost, cost2,
> > -    area, adv_rtr, type, DT_NET, 0, ext_tag);
> > + rt_update(&addr, prefix->prefixlen, &v->nexthop, v->type,
> > +    v->cost, cost2, area, adv_rtr, type, DT_NET, 0, ext_tag);
> >   break;
> >   default:
> >   fatalx("asext_calc: invalid LSA type");
> > @@ -863,7 +867,7 @@ rt_nexthop_clear(struct rt_node *r)
> >  }
> >  
> >  void
> > -rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh,
> > +rt_nexthop_add(struct rt_node *r, struct v_nexthead *vnh, u_int16_t type,
> >      struct in_addr adv_rtr)
> >  {
> >   struct v_nexthop *vn;
> > @@ -876,7 +880,9 @@ rt_nexthop_add(struct rt_node *r, struct
> >   continue;
> >  
> >   rn->adv_rtr.s_addr = adv_rtr.s_addr;
> > - rn->connected = vn->prev == spf_root;
> > + rn->connected = (type == LSA_TYPE_NETWORK &&
> > +    vn->prev == spf_root) ||
> > +    (IN6_IS_ADDR_UNSPECIFIED(&vn->nexthop));
> >   rn->invalid = 0;
> >  
> >   r->invalid = 0;
> > @@ -973,7 +979,7 @@ rt_dump(struct in_addr area, pid_t pid,
> >  
> >  void
> >  rt_update(struct in6_addr *prefix, u_int8_t prefixlen, struct v_nexthead *vnh,
> > -     u_int32_t cost, u_int32_t cost2, struct in_addr area,
> > +     u_int16_t v_type, u_int32_t cost, u_int32_t cost2, struct in_addr area,
> >       struct in_addr adv_rtr, enum path_type p_type, enum dst_type d_type,
> >       u_int8_t flags, u_int32_t tag)
> >  {
> > @@ -999,7 +1005,7 @@ rt_update(struct in6_addr *prefix, u_int
> >   rte->flags = flags;
> >   rte->ext_tag = tag;
> >  
> > - rt_nexthop_add(rte, vnh, adv_rtr);
> > + rt_nexthop_add(rte, vnh, v_type, adv_rtr);
> >  
> >   rt_insert(rte);
> >   } else {
> > @@ -1054,7 +1060,7 @@ rt_update(struct in6_addr *prefix, u_int
> >   }
> >  
> >   if (equal || better)
> > - rt_nexthop_add(rte, vnh, adv_rtr);
> > + rt_nexthop_add(rte, vnh, v_type, adv_rtr);
> >   }
> >  }
> >  
> > @@ -1115,7 +1121,7 @@ get_rtr_link(struct vertex *v, unsigned
> >   v = lsa_find_rtr_frag(v->area, htonl(v->adv_rtr), frag++);
> >   } while (v);
> >  
> > - return (NULL);
> > + fatalx("get_rtr_link: index not found");
> >  }
> >  
> >  /* network LSA links */
> > @@ -1124,21 +1130,23 @@ get_net_link(struct vertex *v, unsigned
> >  {
> >   struct lsa_net_link *net_link = NULL;
> >   char *buf = (char *)v->lsa;
> > - unsigned int i;
> > + unsigned int i, nlinks;
> >  
> >   if (v->type != LSA_TYPE_NETWORK)
> >   fatalx("get_net_link: invalid LSA type");
> >  
> > - /* number of links validated earlier by lsa_check() */
> >   net_link = (struct lsa_net_link *)(buf + sizeof(v->lsa->hdr) +
> >      sizeof(struct lsa_net));
> > - for (i = 0; i < lsa_num_links(v); i++) {
> > +
> > + /* number of links validated earlier by lsa_check() */
> > + nlinks = lsa_num_links(v);
> > + for (i = 0; i < nlinks; i++) {
> >   if (i == idx)
> >   return (net_link);
> >   net_link++;
> >   }
> >  
> > - return (NULL);
> > + fatalx("get_net_link: index not found");
> >  }
> >  
> >  /* misc */
> >