[PATCH 3/4] OSPFv3 supports SADR

Dean Luga dluga93 at gmail.com
Sun May 21 23:01:36 CEST 2017


From: dean <dluga93 at gmail.com>

A new OSPF variant can be used to support SADR.

protocol ospf v3_sadr {
        ...
}

The channel type should be the same as for the static protocol,
sadr_ip6.

Static routes are distributed in OSPF using external LSAs. A new
LSA type was created (LSA_T_EXT_SADR 0x4025) to distribute SADR
routes. The format of the LSA is similar to external LSAs, but it
will contain a pair of (dst, src) prefixes instead of just dst.
Since the U-bit of the LSA is not set, routers that don't support
SADR will not flood the new type of LSA.

Parsing, validation, prep and origination functions were added
for the new type of LSA. The external path processing function
handles the new type of LSA.
The prefix and summary LSA processing functions in rt.c use SADR
networks with ::/0 source prefix to satisfy the type assertion of
fib_get. The same thing stands for the net_fib and enet_fib
data members of the ospf_area structure, they are not both of
type NET_SADR_IP6.
---
 proto/ospf/config.Y   |  12 ++++--
 proto/ospf/lsalib.c   |  68 ++++++++++++++++++++++++++++--
 proto/ospf/lsalib.h   |   3 +-
 proto/ospf/lsupd.c    |   2 +-
 proto/ospf/ospf.c     |  69 +++++++++++++++++++++++--------
 proto/ospf/ospf.h     |  13 ++++++
 proto/ospf/rt.c       | 112 +++++++++++++++++++++++++++++++++++++++++++++-----
 proto/ospf/topology.c |  62 +++++++++++++++++++++++++++-
 8 files changed, 304 insertions(+), 37 deletions(-)

diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
index 8a1e005..f732ead 100644
--- a/proto/ospf/config.Y
+++ b/proto/ospf/config.Y
@@ -139,7 +139,7 @@ ospf_check_auth(void)
 
 CF_DECLS
 
-CF_KEYWORDS(OSPF, V2, V3, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID)
+CF_KEYWORDS(OSPF, V2, V3, V3_SADR, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID)
 CF_KEYWORDS(AREA, NEIGHBORS, RFC1583COMPAT, STUB, TICK, COST, COST2, RETRANSMIT)
 CF_KEYWORDS(HELLO, TRANSMIT, PRIORITY, DEAD, TYPE, BROADCAST, BCAST, DEFAULT)
 CF_KEYWORDS(NONBROADCAST, NBMA, POINTOPOINT, PTP, POINTOMULTIPOINT, PTMP)
@@ -161,17 +161,23 @@ ospf_variant:
    OSPF    { $$ = 1; }
  | OSPF V2 { $$ = 1; }
  | OSPF V3 { $$ = 0; }
+ | OSPF V3_SADR { $$ = 2; }
  ;
 
 ospf_proto_start: proto_start ospf_variant
 {
   this_proto = proto_config_new(&proto_ospf, $1);
-  this_proto->net_type = $2 ? NET_IP4 : NET_IP6;
+
+  switch ($2) {
+    case 0:   this_proto->net_type = NET_IP6; break;
+    case 1:   this_proto->net_type = NET_IP4; break;
+    case 2:   this_proto->net_type = NET_SADR_IP6; break;
+  }
 
   init_list(&OSPF_CFG->area_list);
   init_list(&OSPF_CFG->vlink_list);
   OSPF_CFG->tick = OSPF_DEFAULT_TICK;
-  OSPF_CFG->ospf2 = $2;
+  OSPF_CFG->ospf2 = ($2 == 1);
 };
 
 ospf_proto:
diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c
index b88a114..c8d1064 100644
--- a/proto/ospf/lsalib.c
+++ b/proto/ospf/lsalib.c
@@ -11,6 +11,7 @@
 #include "ospf.h"
 
 #include "lib/fletcher16.h"
+#include "ospf.h"
 
 #ifndef CPU_BIG_ENDIAN
 void
@@ -93,7 +94,7 @@ lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa)
 
 
 static int
-unknown_lsa_type(u32 type)
+unknown_lsa_type(u32 type, struct ospf_proto *p)
 {
   switch (type)
   {
@@ -107,6 +108,9 @@ unknown_lsa_type(u32 type)
   case LSA_T_PREFIX:
     return 0;
 
+  case LSA_T_EXT_SADR:
+    return !ospf_is_v3_sadr(p);
+
   default:
     return 1;
   }
@@ -127,7 +131,7 @@ lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain)
   else
   {
     /* For unkown LSAs without U-bit change scope to LSA_SCOPE_LINK */
-    if (unknown_lsa_type(itype) && !(itype & LSA_UBIT))
+    if (unknown_lsa_type(itype, ifa->oa->po) && !(itype & LSA_UBIT))
       itype = itype & ~LSA_SCOPE_MASK;
   }
 
@@ -353,6 +357,30 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *r
   }
 }
 
+void
+lsa_parse_ext_sadr(struct top_hash_entry *en, struct ospf_lsa_ext_sadr_local *rt)
+{
+  struct ospf_lsa_ext3 *ext = en->lsa_body;
+
+  u8 dummy_opts;
+  net_addr_ip6 dst, src;
+  u32 *buf = ospf_get_ipv6_prefix(ext->rest, (net_addr *)&dst, &rt->pxopts, NULL);
+  buf = ospf_get_ipv6_prefix(buf, (net_addr *)&src, &dummy_opts, NULL);
+  rt->net = NET_ADDR_SADR_IP6(dst.prefix, dst.pxlen, src.prefix, src.pxlen);
+
+  rt->metric = ext->metric & LSA_METRIC_MASK;
+  rt->ebit = ext->metric & LSA_EXT3_EBIT;
+
+  rt->fbit = ext->metric & LSA_EXT3_FBIT;
+  if (rt->fbit)
+    buf = ospf_get_ipv6_addr(buf, &rt->fwaddr);
+  else
+    rt->fwaddr = IPA_NONE;
+
+  rt->tag = (ext->metric & LSA_EXT3_TBIT) ? *buf++ : 0;
+  rt->propagate = rt->pxopts & OPT_PX_P;
+}
+
 #define HDRLEN sizeof(struct ospf_lsa_header)
 
 static int
@@ -507,6 +535,35 @@ lsa_validate_ext3(struct ospf_lsa_header *lsa, struct ospf_lsa_ext3 *body)
 }
 
 static int
+lsa_validate_ext3_sadr(struct ospf_lsa_header *lsa, struct ospf_lsa_ext3 *body)
+{
+  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext3) + 8))
+    return 0;
+
+  u8 pxl = pxlen(body->rest);
+  if (pxl > IP6_MAX_PREFIX_LENGTH)
+    return 0;
+
+  int len = IPV6_PREFIX_SPACE(pxl);
+
+  // source prefix
+  int spxl = pxlen(body->rest + len/4);
+  len += IPV6_PREFIX_SPACE(spxl);
+
+  if (body->metric & LSA_EXT3_FBIT) // forwarding address
+    len += 16;
+  if (body->metric & LSA_EXT3_TBIT) // route tag
+    len += 4;
+  if (*body->rest & 0xFFFF) // referenced LS type field
+    len += 4;
+
+  if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_ext3) + len))
+    return 0;
+
+  return 1;
+}
+
+static int
 lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, uint offset, u8 *pbuf)
 {
   uint bound = lsa->length - HDRLEN - 4;
@@ -560,9 +617,9 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body)
  * consistency). Returns true if valid.
  */
 int
-lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
+lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, struct ospf_proto *p, void *body)
 {
-  if (ospf2)
+  if (p->ospf2)
   {
     switch (lsa_type)
     {
@@ -600,6 +657,9 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
       return lsa_validate_link(lsa, body);
     case LSA_T_PREFIX:
       return lsa_validate_prefix(lsa, body);
+    case LSA_T_EXT_SADR:
+      if (ospf_is_v3_sadr(p))
+        return lsa_validate_ext3_sadr(lsa, body);
     default:
       return 1;	/* Unknown LSAs are OK in OSPFv3 */
     }
diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h
index c93f029..fe25cb9 100644
--- a/proto/ospf/lsalib.h
+++ b/proto/ospf/lsalib.h
@@ -58,6 +58,7 @@ int lsa_walk_rt(struct ospf_lsa_rt_walk *rt);
 void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopts, u32 *metric);
 void lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u32 *options);
 void lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt);
-int lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body);
+void lsa_parse_ext_sadr(struct top_hash_entry *en, struct ospf_lsa_ext_sadr_local *rt);
+int lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, struct ospf_proto *p, void *body);
 
 #endif /* _BIRD_OSPF_LSALIB_H_ */
diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c
index 157d962..82a955d 100644
--- a/proto/ospf/lsupd.c
+++ b/proto/ospf/lsupd.c
@@ -583,7 +583,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
       void *body = mb_alloc(p->p.pool, blen);
       lsa_ntoh_body(lsa_n + 1, body, blen);
 
-      if (lsa_validate(&lsa, lsa_type, ospf_is_v2(p), body) == 0)
+      if (lsa_validate(&lsa, lsa_type, p, body) == 0)
       {
 	mb_free(body);
 	SKIP("invalid body");
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index daf76ff..fc96d48 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -114,21 +114,37 @@ add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac)
   struct ospf_proto *p = oa->po;
   struct area_net_config *anc;
   struct area_net *an;
+  struct net_addr_sadr_ip6 addr;
 
-  fib_init(&oa->net_fib,  p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6,
+  fib_init(&oa->net_fib,  p->p.pool, p->p.net_type,
 	   sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL);
-  fib_init(&oa->enet_fib, p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6,
+  fib_init(&oa->enet_fib, p->p.pool, p->p.net_type,
 	   sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL);
 
   WALK_LIST(anc, ac->net_list)
   {
-    an = fib_get(&oa->net_fib, &anc->prefix);
+    if (ospf_is_v3_sadr(p))
+    {
+      addr = NET_ADDR_SADR_IP6(net6_prefix(&anc->prefix), 
+          anc->prefix.pxlen, IP6_NONE, 0);
+      an = fib_get(&oa->net_fib, (net_addr *)&addr);
+    }
+    else
+      an = fib_get(&oa->net_fib, &anc->prefix);
+
     an->hidden = anc->hidden;
   }
 
   WALK_LIST(anc, ac->enet_list)
   {
-    an = fib_get(&oa->enet_fib, &anc->prefix);
+    if (ospf_is_v3_sadr(p))
+    {
+      an = fib_get(&oa->enet_fib, (net_addr *)&addr);
+      addr = NET_ADDR_SADR_IP6(net6_prefix(&anc->prefix), 
+          anc->prefix.pxlen, IP6_NONE, 0);
+    }
+    else
+      an = fib_get(&oa->enet_fib, &anc->prefix);
     an->hidden = anc->hidden;
     an->tag = anc->tag;
   }
@@ -238,7 +254,7 @@ ospf_start(struct proto *P)
   p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop));
   init_list(&(p->iface_list));
   init_list(&(p->area_list));
-  fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6,
+  fib_init(&p->rtf, P->pool, p->p.net_type,
 	   sizeof(ort), OFFSETOF(ort, fn), 0, NULL);
   if (ospf_is_v3(p))
     idm_init(&p->idm, P->pool, 16);
@@ -1094,28 +1110,48 @@ show_lsa_sum_rt(struct top_hash_entry *he, int ospf2)
   cli_msg(-1016, "\t\txrouter %R metric %u", dst_rid, metric);
 }
 
-
 static inline void
 show_lsa_external(struct top_hash_entry *he, int ospf2)
 {
-  struct ospf_lsa_ext_local rt;
   char str_via[IPA_MAX_TEXT_LENGTH + 8] = "";
   char str_tag[16] = "";
 
-  if (he->lsa_type == LSA_T_EXT)
+  if (he->lsa_type == LSA_T_EXT || he->lsa_type == LSA_T_EXT_SADR)
     he->domain = 0; /* Unmark the LSA */
 
-  lsa_parse_ext(he, ospf2, &rt);
+  if (he->lsa_type == LSA_T_EXT)
+  {
+    struct ospf_lsa_ext_local rt;
+    lsa_parse_ext(he, ospf2, &rt);
+
+    if (rt.fbit)
+      bsprintf(str_via, " via %I", rt.fwaddr);
 
-  if (rt.fbit)
-    bsprintf(str_via, " via %I", rt.fwaddr);
+    if (rt.tag)
+      bsprintf(str_tag, " tag %08x", rt.tag);
 
-  if (rt.tag)
-    bsprintf(str_tag, " tag %08x", rt.tag);
+    cli_msg(-1016, "\t\t%s %N metric%s %u%s%s",
+	    (he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external",
+	    &rt.net, rt.ebit ? "2" : "", rt.metric, str_via, str_tag);
+  }
+  else
+  {
+    struct ospf_lsa_ext_sadr_local rt;
+    lsa_parse_ext_sadr(he, &rt);
 
-  cli_msg(-1016, "\t\t%s %N metric%s %u%s%s",
-	  (he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external",
-	  &rt.net, rt.ebit ? "2" : "", rt.metric, str_via, str_tag);
+    if (rt.fbit)
+      bsprintf(str_via, " via %I", rt.fwaddr);
+
+    if (rt.tag)
+      bsprintf(str_tag, " tag %08x", rt.tag);
+
+    net_addr_ip6 dst = NET_ADDR_IP6(rt.net.dst_prefix, rt.net.dst_pxlen);
+    net_addr_ip6 src = NET_ADDR_IP6(rt.net.src_prefix, rt.net.src_pxlen);
+
+    cli_msg(-1016, "\t\t%s %N from %N metric%s %u%s%s",
+	    (he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external",
+	    &dst, &src, rt.ebit ? "2" : "", rt.metric, str_via, str_tag);
+  }
 }
 
 static inline void
@@ -1286,6 +1322,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
 
     case LSA_T_EXT:
     case LSA_T_NSSA:
+    case LSA_T_EXT_SADR:
       show_lsa_external(he, ospf2);
       break;
 
diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h
index e3eae2b..9e69bb3 100644
--- a/proto/ospf/ospf.h
+++ b/proto/ospf/ospf.h
@@ -520,6 +520,8 @@ union ospf_auth
 #define LSA_T_LINK	0x0008
 #define LSA_T_PREFIX	0x2009
 
+#define LSA_T_EXT_SADR 0x4025
+
 #define LSA_T_V2_MASK	0x00ff
 
 #define LSA_UBIT	0x8000
@@ -679,6 +681,14 @@ struct ospf_lsa_ext_local
   u8 pxopts;
 };
 
+struct ospf_lsa_ext_sadr_local
+{
+  net_addr_sadr_ip6 net;
+  ip_addr fwaddr;
+  u32 metric, ebit, fbit, tag, propagate;
+  u8 pxopts;
+};
+
 struct ospf_lsa_link
 {
   u32 options;
@@ -835,6 +845,9 @@ static inline int ospf_is_v2(struct ospf_proto *p)
 static inline int ospf_is_v3(struct ospf_proto *p)
 { return ! p->ospf2; }
 
+static inline int ospf_is_v3_sadr(struct ospf_proto *p)
+{ return p->p.net_type == NET_SADR_IP6; }
+
 static inline int ospf_get_version(struct ospf_proto *p)
 { return ospf_is_v2(p) ? 2 : 3; }
 
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index df9eb75..c0559b2 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -589,7 +589,12 @@ spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa)
       if ((pxopts & OPT_PX_LA) && ipa_zero(src->lb))
 	src->lb = ipa_from_ip6(net.prefix);
 
-      add_network(oa, (net_addr *) &net, src->dist + metric, src, i);
+      if (ospf_is_v3_sadr(p)) {
+        net_addr_sadr_ip6 sadr_net = NET_ADDR_SADR_IP6(net.prefix, net.pxlen, IP6_NONE, 0);
+        add_network(oa, (net_addr *) &sadr_net, src->dist + metric, src, i);
+      } else {
+        add_network(oa, (net_addr *) &net, src->dist + metric, src, i);
+      }
     }
   }
 }
@@ -817,8 +822,14 @@ ospf_rt_sum(struct ospf_area *oa)
       .nhs = abr->n.nhs
     };
 
-    if (type == ORT_NET)
-      ri_install_net(p, &net, &nf);
+    if (type == ORT_NET) {
+      if (ospf_is_v3_sadr(p)) {
+        net_addr_sadr_ip6 sadr_net = NET_ADDR_SADR_IP6(net_prefix(&net), net_pxlen(&net), IP6_NONE, 0);
+        ri_install_net(p, (net_addr *)&sadr_net, &nf);
+      } else {
+        ri_install_net(p, &net, &nf);
+      }
+    }
     else
       ri_install_rt(oa, dst_rid, &nf);
   }
@@ -1207,7 +1218,14 @@ ospf_rt_abr1(struct ospf_proto *p)
   else
     net_fill_ip6(&default_net, IP6_NONE, 0);
 
-  default_nf = fib_get(&p->rtf, &default_net);
+  if (ospf_is_v3_sadr(p))
+  {
+    net_addr_sadr_ip6 sadr_default =
+	NET_ADDR_SADR_IP6(IP6_NONE, 0, IP6_NONE, 0);
+    default_nf = fib_get(&p->rtf, &sadr_default);
+  }
+  else
+    default_nf = fib_get(&p->rtf, &default_net);
   default_nf->area_net = 1;
 
   struct ospf_area *oa;
@@ -1411,10 +1429,52 @@ loop:
 }
 
 static void *
-ospf_fib_route(struct fib *f, ip_addr a)
+ospf_fib_route_sadr_ip6(struct fib *f, ip6_addr a, int len, ip6_addr sa, int slen)
+{
+  net_addr_ip6 dst = NET_ADDR_IP6(a, len);
+  dst.type = NET_SADR_IP6;
+
+  // dst search
+  ort *tnf;
+  while (tnf = fib_find(f, (net_addr *) &dst), (!tnf) && dst.pxlen)
+  {
+    if (tnf)
+    {
+      // src addr search
+      ort *nf;
+
+      net_addr_sadr_ip6 full_addr = 
+        NET_ADDR_SADR_IP6(dst.prefix, dst.pxlen, sa, slen);
+
+      while (nf = fib_find(f, (net_addr *)&full_addr),
+      	(!nf || !nf->n.type) && (full_addr.src_pxlen > 0))
+      {
+        full_addr.src_pxlen--;
+        ip6_clrbit(&full_addr.src_prefix, full_addr.src_pxlen);
+      }
+
+      if (nf)
+      	return nf;
+    }
+
+    if (dst.pxlen == 0)
+      break;
+
+    dst.pxlen--;
+    ip6_clrbit(&dst.prefix, dst.pxlen);
+  }
+
+  return NULL;
+}
+
+static void *
+ospf_fib_route(struct fib *f, ip_addr a, ip_addr sa, int slen)
 {
   if (f->addr_type == NET_IP4)
     return ospf_fib_route_ip4(f, ipa_to_ip4(a), IP4_MAX_PREFIX_LENGTH);
+  else if (f->addr_type == NET_SADR_IP6)
+    return ospf_fib_route_sadr_ip6(f, ipa_to_ip6(a), IP6_MAX_PREFIX_LENGTH,
+    					ipa_to_ip6(sa), slen);
   else
     return ospf_fib_route_ip6(f, ipa_to_ip6(a), IP6_MAX_PREFIX_LENGTH);
 }
@@ -1426,6 +1486,7 @@ ospf_ext_spf(struct ospf_proto *p)
 {
   struct top_hash_entry *en;
   struct ospf_lsa_ext_local rt;
+  net_addr_ip6 src;
   ort *nf1, *nf2;
   u32 br_metric;
   struct ospf_area *atmp;
@@ -1437,7 +1498,8 @@ ospf_ext_spf(struct ospf_proto *p)
     orta nfa = {};
 
     /* 16.4. (1) */
-    if ((en->lsa_type != LSA_T_EXT) && (en->lsa_type != LSA_T_NSSA))
+    if ((en->lsa_type != LSA_T_EXT) && (en->lsa_type != LSA_T_EXT_SADR)
+      && (en->lsa_type != LSA_T_NSSA))
       continue;
 
     if (en->lsa.age == LSA_MAXAGE)
@@ -1450,7 +1512,26 @@ ospf_ext_spf(struct ospf_proto *p)
     DBG("%s: Working on LSA. ID: %R, RT: %R, Type: %u\n",
 	p->p.name, en->lsa.id, en->lsa.rt, en->lsa_type);
 
-    lsa_parse_ext(en, ospf_is_v2(p), &rt);
+    if (en->lsa_type == LSA_T_EXT_SADR)
+    {
+      struct ospf_lsa_ext_sadr_local rt_sadr;
+      lsa_parse_ext_sadr(en, &rt_sadr);
+
+      src = NET_ADDR_IP6(rt_sadr.net.src_prefix, rt_sadr.net.src_pxlen);
+      net_fill_ip6(&rt.net, rt_sadr.net.dst_prefix, rt_sadr.net.dst_pxlen);
+      rt.fwaddr = rt_sadr.fwaddr;
+      rt.metric = rt_sadr.metric;
+      rt.ebit = rt_sadr.ebit;
+      rt.fbit = rt_sadr.fbit;
+      rt.tag = rt_sadr.tag;
+      rt.propagate = rt_sadr.propagate;
+      rt.pxopts = rt_sadr.pxopts;
+    }
+    else
+    {
+      lsa_parse_ext(en, ospf_is_v2(p), &rt);
+      src = NET_ADDR_IP6(IP6_NONE, 0);
+    }
 
     if (!ospf_valid_prefix(&rt.net))
     {
@@ -1469,7 +1550,7 @@ ospf_ext_spf(struct ospf_proto *p)
     /* If there are more areas, we already precomputed preferred ASBR
        entries in ospf_rt_abr1() and stored them in the backbone
        table. For NSSA, we examine the area to which the LSA is assigned */
-    if (en->lsa_type == LSA_T_EXT)
+    if (en->lsa_type == LSA_T_EXT || en->lsa_type == LSA_T_EXT_SADR)
       atmp = ospf_main_area(p);
     else /* NSSA */
       atmp = ospf_find_area(p, en->domain);
@@ -1500,11 +1581,14 @@ ospf_ext_spf(struct ospf_proto *p)
     }
     else
     {
-      nf2 = ospf_fib_route(&p->rtf, rt.fwaddr);
+      if (en->lsa_type == LSA_T_EXT_SADR)
+      	nf2 = ospf_fib_route(&p->rtf, rt.fwaddr, src.prefix, src.pxlen);
+      else
+      	nf2 = ospf_fib_route(&p->rtf, rt.fwaddr, IP6_NONE, 0);
       if (!nf2)
 	continue;
 
-      if (en->lsa_type == LSA_T_EXT)
+      if (en->lsa_type == LSA_T_EXT || en->lsa_type == LSA_T_EXT_SADR)
       {
 	/* For ext routes, we accept intra-area or inter-area routes */
 	if ((nf2->n.type != RTS_OSPF) && (nf2->n.type != RTS_OSPF_IA))
@@ -1562,7 +1646,13 @@ ospf_ext_spf(struct ospf_proto *p)
     nfa.oa = atmp; /* undefined in RFC 2328 */
     nfa.en = en; /* store LSA for later (NSSA processing) */
 
-    ri_install_ext(p, &rt.net, &nfa);
+    if (ospf_is_v3_sadr(p)) {
+      net_addr_sadr_ip6 sadr_net = NET_ADDR_SADR_IP6(net_prefix(&rt.net), net_pxlen(&rt.net),
+      			src.prefix, src.pxlen);
+      ri_install_ext(p, (net_addr *)&sadr_net, &nfa);
+    }
+    else
+      ri_install_ext(p, &rt.net, &nfa);
   }
 }
 
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index ce77f57..3891b40 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -1154,6 +1154,63 @@ ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 m
   ospf_originate_lsa(p, &lsa);
 }
 
+static inline void
+prepare_ext3_sadr_lsa_body(struct ospf_proto *p, ort *nf,
+          u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit)
+{
+  struct ospf_lsa_ext3 *ext;
+
+  net_addr_sadr_ip6 *sadr_net = (net_addr_sadr_ip6 *)(nf->fn.addr);
+  net_addr_ip6 dst = NET_ADDR_IP6(sadr_net->dst_prefix, sadr_net->dst_pxlen);
+  net_addr_ip6 src = NET_ADDR_IP6(sadr_net->src_prefix, sadr_net->src_pxlen);
+
+  int bsize = sizeof(struct ospf_lsa_ext3)
+    + IPV6_PREFIX_SPACE(dst.pxlen)
+    + IPV6_PREFIX_SPACE(src.pxlen)
+    + (ipa_nonzero(fwaddr) ? 16 : 0)
+    + (tag ? 4 : 0);
+
+  ext = lsab_allocz(p, bsize);
+  ext->metric = metric & LSA_METRIC_MASK;
+  u32 *buf = ext->rest;
+
+  buf = ospf_put_ipv6_prefix(buf, (net_addr *)&dst, pbit ? OPT_PX_P : 0, 0);
+  buf = ospf_put_ipv6_prefix(buf, (net_addr *)&src, 0, 0);
+
+  if (ebit)
+    ext->metric |= LSA_EXT3_EBIT;
+
+  if (ipa_nonzero(fwaddr))
+  {
+    ext->metric |= LSA_EXT3_FBIT;
+    buf = ospf_put_ipv6_addr(buf, fwaddr);
+  }
+
+  if (tag)
+  {
+    ext->metric |= LSA_EXT3_TBIT;
+    *buf++ = tag;
+  }
+}
+
+void
+ospf_originate_ext_sadr_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode,
+           u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit)
+{
+  struct ospf_new_lsa lsa = {
+    .type = LSA_T_EXT_SADR,
+    .mode = mode,
+    .dom  = oa ? oa->areaid : 0,
+    .id   = ort_to_lsaid(p, nf),
+    .opts = oa ? (pbit ? OPT_P : 0) : OPT_E,
+    .nf   = nf
+  };
+
+  prepare_ext3_sadr_lsa_body(p, nf, metric, ebit, fwaddr, tag, oa && pbit);
+
+  ospf_originate_lsa(p, &lsa);
+}
+
 static struct top_hash_entry *
 ospf_hash_find_(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type);
 
@@ -1305,7 +1362,10 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
   }
 
   nf = fib_get(&p->rtf, n->n.addr);
-  ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1);
+  if (ospf_is_v3_sadr(p) && !ip6_zero(net6_sadr_src_prefix(nf->fn.addr)))
+    ospf_originate_ext_sadr_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1);
+  else
+    ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1);
   nf->external_rte = 1;
 }
 
-- 
2.7.4



More information about the Bird-users mailing list