]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Temporary integrated OSPF commit.
authorOndrej Zajicek <santiago@crfreenet.org>
Thu, 26 Jun 2014 09:58:57 +0000 (11:58 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Thu, 26 Jun 2014 09:58:57 +0000 (11:58 +0200)
28 files changed:
doc/bird.sgml
lib/birdlib.h
lib/slists.h
proto/ospf/config.Y
proto/ospf/dbdes.c
proto/ospf/dbdes.h [deleted file]
proto/ospf/hello.c
proto/ospf/hello.h [deleted file]
proto/ospf/iface.c
proto/ospf/iface.h [deleted file]
proto/ospf/lsack.c
proto/ospf/lsack.h [deleted file]
proto/ospf/lsalib.c
proto/ospf/lsalib.h
proto/ospf/lsreq.c
proto/ospf/lsreq.h [deleted file]
proto/ospf/lsupd.c
proto/ospf/lsupd.h [deleted file]
proto/ospf/neighbor.c
proto/ospf/neighbor.h [deleted file]
proto/ospf/ospf.c
proto/ospf/ospf.h
proto/ospf/packet.c
proto/ospf/packet.h [deleted file]
proto/ospf/rt.c
proto/ospf/rt.h
proto/ospf/topology.c
proto/ospf/topology.h

index fa4c777fcde211b28d14c9bb0d49a7f88dec1cf1..04b6a845d4a2d6f3949b8a23f8a515a72ea892c9 100644 (file)
@@ -2332,6 +2332,7 @@ protocol ospf &lt;name&gt; {
                        tx length &lt;num&gt;;
                        type [broadcast|bcast|pointopoint|ptp|
                                nonbroadcast|nbma|pointomultipoint|ptmp];
+                       link lsa suppression &lt;switch&gt;;
                        strict nonbroadcast &lt;switch&gt;;
                        real broadcast &lt;switch&gt;;
                        ptp netmask &lt;switch&gt;;
@@ -2596,9 +2597,16 @@ protocol ospf &lt;name&gt; {
        communication, or if the NBMA network is used as an (possibly
        unnumbered) PtP link.
 
-       <tag>strict nonbroadcast <M>switch</M></tag>
+       <tag>link lsa suppression <m/switch/</tag>
+       In OSPFv3, link LSAs are generated for each link, announcing link-local
+       IPv6 address of the router to its local neighbors. These are useless on
+       PtP or PtMP networks and this option allows to suppress the link LSA
+       origination for such interfaces. The option is ignored on other than PtP
+       or PtMP interfaces. Default value is no.
+
+       <tag>strict nonbroadcast <m/switch/</tag>
        If set, don't send hello to any undefined neighbor. This switch is
-       ignored on other than NBMA or PtMP networks. Default value is no.
+       ignored on other than NBMA or PtMP interfaces. Default value is no.
 
        <tag>real broadcast <m/switch/</tag>
        In <cf/type broadcast/ or <cf/type ptp/ network configuration, OSPF
index 04fb7fedfe153e6b3fb2984932eb02b84c872244..c49c5b99c51dfec98d84bf384382a80db4abaf3a 100644 (file)
 #define ABS(a)   ((a)>=0 ? (a) : -(a))
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
 
+#define BIT32_VAL(p)           (((u32) 1) << ((p) % 32))
+#define BIT32_TEST(b,p)                ((b)[(p)/32] & BIT32_VAL(p))
+#define BIT32_SET(b,p)         ((b)[(p)/32] |= BIT32_VAL(p))
+#define BIT32_CLR(b,p)         ((b)[(p)/32] &= ~BIT32_VAL(p))
+#define BIT32_ZERO(b,l)                memset((b), 0, (l)/8)
+
 #ifndef NULL
 #define NULL ((void *) 0)
 #endif
index 2334e36a590adbbfb909368f5e8eeba9a057013e..d98d02d271ab9798c3f628f107a0c91c101c6e48 100644 (file)
@@ -68,10 +68,12 @@ typedef struct siterator {
 #define SNODE (snode *)
 #define SHEAD(list) ((void *)((list).head))
 #define STAIL(list) ((void *)((list).tail))
-#define WALK_SLIST(n,list) for(n=SHEAD(list);(SNODE (n))->next; \
-                               n=(void *)((SNODE (n))->next))
+#define SNODE_NEXT(n) ((void *)((SNODE (n))->next))
+#define SNODE_VALID(n) ((SNODE (n))->next)
+
+#define WALK_SLIST(n,list) for(n=SHEAD(list); SNODE_VALID(n); n=SNODE_NEXT(n))
 #define WALK_SLIST_DELSAFE(n,nxt,list) \
-     for(n=SHEAD(list); nxt=(void *)((SNODE (n))->next); n=(void *) nxt)
+     for(n=SHEAD(list); nxt=SNODE_NEXT(n); n=(void *) nxt)
 #define EMPTY_SLIST(list) (!(list).head->next)
 
 void s_add_tail(slist *, snode *);
index 478529bc3774ac333c8d79acf5c5006cd324ca4b..9cbfa9a31de8d96f920780b9a658500a4755827b 100644 (file)
@@ -21,7 +21,9 @@ static list *this_nets;
 static struct area_net_config *this_pref;
 static struct ospf_stubnet_config *this_stubnet; 
 
-#ifdef OSPFv2
+static inline int ospf_cfg_is_v2(void) { return OSPF_CFG->ospf2; }
+static inline int ospf_cfg_is_v3(void) { return ! OSPF_CFG->ospf2; }
+
 static void
 ospf_iface_finish(void)
 {
@@ -38,21 +40,6 @@ ospf_iface_finish(void)
   if ((ip->autype == OSPF_AUTH_NONE) && (ip->passwords != NULL))
     log(L_WARN "Password option without authentication option does not make sense");
 }
-#endif
-
-#ifdef OSPFv3
-static void
-ospf_iface_finish(void)
-{
-  struct ospf_iface_patt *ip = OSPF_PATT;
-
-  if (ip->deadint == 0)
-    ip->deadint = ip->deadc * ip->helloint;
-
-  if ((ip->autype != OSPF_AUTH_NONE) || (get_passwords() != NULL))
-    cf_error("Authentication not supported in OSPFv3");
-}
-#endif
 
 static void
 ospf_area_finish(void)
@@ -61,12 +48,12 @@ ospf_area_finish(void)
     cf_error("Backbone area cannot be stub/NSSA");
 
   if (this_area->summary && (this_area->type == OPT_E))
-    cf_error("Only Stub/NSSA areas can use summary propagation");
+    cf_error("Only stub/NSSA areas can use summary propagation");
 
   if (this_area->default_nssa && ((this_area->type != OPT_N) || ! this_area->summary))
     cf_error("Only NSSA areas with summary propagation can use NSSA default route");
 
-  if ((this_area->default_cost & LSA_EXT_EBIT) && ! this_area->default_nssa)
+  if ((this_area->default_cost & LSA_EXT3_EBIT) && ! this_area->default_nssa)
     cf_error("Only NSSA default route can use type 2 metric");
 }
 
@@ -80,15 +67,22 @@ ospf_proto_finish(void)
 
   int areano = 0;
   int backbone = 0;
+  int nssa = 0;
   struct ospf_area_config *ac;
   WALK_LIST(ac, cf->area_list)
   {
     areano++;
     if (ac->areaid == 0)
-     backbone = 1;
+      backbone = 1;
+    if (ac->type == OPT_N)
+      nssa = 1;
   }
+
   cf->abr = areano > 1;
 
+  /* Route export or NSSA translation (RFC 3101 3.1) */
+  cf->asbr = (this_proto->out_filter != FILTER_REJECT) || (nssa && cf->abr);
+
   if (cf->abr && !backbone)
   {
     struct ospf_area_config *ac = cfg_allocz(sizeof(struct ospf_area_config));
@@ -101,26 +95,27 @@ ospf_proto_finish(void)
   }
 
   if (!cf->abr && !EMPTY_LIST(cf->vlink_list))
-    cf_error( "Vlinks cannot be used on single area router");
+    cf_error("Vlinks cannot be used on single area router");
+
+  if (cf->asbr && (areano == 1) && (this_area->type == 0))
+    cf_error("ASBR must be in non-stub area");
 }
 
 static inline void
-check_defcost(int cost)
+ospf_check_defcost(int cost)
 {
   if ((cost <= 0) || (cost >= LSINFINITY))
    cf_error("Default cost must be in range 1-%d", LSINFINITY-1);
 }
 
 static inline void
-set_instance_id(unsigned id)
+ospf_check_auth(void)
 {
-#ifdef OSPFv3
-  OSPF_PATT->instance_id = id;
-#else
-  cf_error("Instance ID requires OSPFv3");
-#endif
+  if (ospf_cfg_is_v3())
+    cf_error("Authentication not supported in OSPFv3");
 }
 
+
 CF_DECLS
 
 CF_KEYWORDS(OSPF, AREA, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID)
@@ -132,7 +127,7 @@ CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK, ONLY, BFD)
 CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY, TAG, EXTERNAL)
 CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY)
 CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH)
-CF_KEYWORDS(SECONDARY, MERGE)
+CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION)
 
 %type <t> opttext
 %type <ld> lsadb_args
@@ -146,8 +141,8 @@ ospf_proto_start: proto_start OSPF {
      this_proto = proto_config_new(&proto_ospf, sizeof(struct ospf_config), $1);
      init_list(&OSPF_CFG->area_list);
      init_list(&OSPF_CFG->vlink_list);
-     OSPF_CFG->rfc1583 = DEFAULT_RFC1583;
-     OSPF_CFG->tick = DEFAULT_OSPFTICK;
+     OSPF_CFG->tick = OSPF_DEFAULT_TICK;
+     OSPF_CFG->ospf2 = OSPF_IS_V2;
   }
  ;
 
@@ -160,7 +155,7 @@ ospf_proto_item:
    proto_item
  | RFC1583COMPAT bool { OSPF_CFG->rfc1583 = $2; }
  | STUB ROUTER bool { OSPF_CFG->stub_router = $3; }
- | ECMP bool { OSPF_CFG->ecmp = $2 ? DEFAULT_ECMP_LIMIT : 0; }
+ | ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; }
  | ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; if ($4 < 0) cf_error("ECMP limit cannot be negative"); }
  | MERGE EXTERNAL bool { OSPF_CFG->merge_external = $3; }
  | TICK expr { OSPF_CFG->tick = $2; if($2<=0) cf_error("Tick must be greater than zero"); }
@@ -171,9 +166,9 @@ ospf_area_start: AREA idval {
   this_area = cfg_allocz(sizeof(struct ospf_area_config));
   add_tail(&OSPF_CFG->area_list, NODE this_area);
   this_area->areaid = $2;
-  this_area->default_cost = DEFAULT_STUB_COST;
+  this_area->default_cost = OSPF_DEFAULT_STUB_COST;
   this_area->type = OPT_E;
-  this_area->transint = DEFAULT_TRANSINT;
+  this_area->transint = OSPF_DEFAULT_TRANSINT;
 
   init_list(&this_area->patt_list);
   init_list(&this_area->net_list);
@@ -195,9 +190,9 @@ ospf_area_item:
  | NSSA { this_area->type = OPT_N; }
  | SUMMARY bool { this_area->summary = $2; }
  | DEFAULT NSSA bool { this_area->default_nssa = $3; }
- | DEFAULT COST expr { this_area->default_cost = $3; check_defcost($3); }
- | DEFAULT COST2 expr { this_area->default_cost = $3 | LSA_EXT_EBIT; check_defcost($3); }
- | STUB COST expr { this_area->default_cost = $3;  check_defcost($3); }
+ | DEFAULT COST expr { this_area->default_cost = $3; ospf_check_defcost($3); }
+ | DEFAULT COST2 expr { this_area->default_cost = $3 | LSA_EXT3_EBIT; ospf_check_defcost($3); }
+ | STUB COST expr { this_area->default_cost = $3; ospf_check_defcost($3); }
  | TRANSLATOR bool { this_area->translator = $2; }
  | TRANSLATOR STABILITY expr { this_area->transint = $3; }
  | NETWORKS { this_nets = &this_area->net_list; } '{' pref_list '}'
@@ -249,10 +244,10 @@ ospf_vlink_item:
  | WAIT expr { OSPF_PATT->waitint = $2 ; }
  | DEAD expr { OSPF_PATT->deadint = $2 ; if ($2<=1) cf_error("Dead interval must be greater than one"); }
  | DEAD COUNT expr { OSPF_PATT->deadc = $3 ; if ($3<=1) cf_error("Dead count must be greater than one"); }
- | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE ; }
- | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE ; }
- | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT ; }
- | password_list 
+ | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE }
+ | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE; ospf_check_auth(); }
+ | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT; ospf_check_auth(); }
+ | password_list { ospf_check_auth(); }
  ;
 
 ospf_vlink_start: VIRTUAL LINK idval
@@ -292,18 +287,19 @@ ospf_iface_item:
  | TYPE PTP { OSPF_PATT->type = OSPF_IT_PTP ; }
  | TYPE POINTOMULTIPOINT { OSPF_PATT->type = OSPF_IT_PTMP ; }
  | TYPE PTMP { OSPF_PATT->type = OSPF_IT_PTMP ; }
- | REAL BROADCAST bool { OSPF_PATT->real_bcast = $3; if (OSPF_VERSION != 2) cf_error("Real broadcast option requires OSPFv2"); }
- | PTP NETMASK bool { OSPF_PATT->ptp_netmask = $3; if (OSPF_VERSION != 2) cf_error("Real netmask option requires OSPFv2"); }
+ | REAL BROADCAST bool { OSPF_PATT->real_bcast = $3; if (!ospf_cfg_is_v2()) cf_error("Real broadcast option requires OSPFv2"); }
+ | PTP NETMASK bool { OSPF_PATT->ptp_netmask = $3; if (!ospf_cfg_is_v2()) cf_error("PtP netmask option requires OSPFv2"); }
  | TRANSMIT DELAY expr { OSPF_PATT->inftransdelay = $3 ; if (($3<=0) || ($3>65535)) cf_error("Transmit delay must be in range 1-65535"); }
  | PRIORITY expr { OSPF_PATT->priority = $2 ; if (($2<0) || ($2>255)) cf_error("Priority must be in range 0-255"); }
  | STRICT NONBROADCAST bool { OSPF_PATT->strictnbma = $3 ; }
  | STUB bool { OSPF_PATT->stub = $2 ; }
  | CHECK LINK bool { OSPF_PATT->check_link = $3; }
  | ECMP WEIGHT expr { OSPF_PATT->ecmp_weight = $3 - 1; if (($3<1) || ($3>256)) cf_error("ECMP weight must be in range 1-256"); }
+ | LINK LSA SUPPRESSION bool { OSPF_PATT->link_lsa_suppression = $4; if (!ospf_cfg_is_v3()) cf_error("Link LSA suppression option requires OSPFv3"); }
  | NEIGHBORS '{' nbma_list '}'
- | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE ; }
- | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE ; }
- | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT ; }
+ | AUTHENTICATION NONE { OSPF_PATT->autype = OSPF_AUTH_NONE; }
+ | AUTHENTICATION SIMPLE { OSPF_PATT->autype = OSPF_AUTH_SIMPLE; ospf_check_auth(); }
+ | AUTHENTICATION CRYPTOGRAPHIC { OSPF_PATT->autype = OSPF_AUTH_CRYPT; ospf_check_auth(); }
  | RX BUFFER NORMAL { OSPF_PATT->rx_buffer = 0; } 
  | RX BUFFER LARGE { OSPF_PATT->rx_buffer = OSPF_MAX_PKT_SIZE; } 
  | RX BUFFER expr { OSPF_PATT->rx_buffer = $3; if (($3 < OSPF_MIN_PKT_SIZE) || ($3 > OSPF_MAX_PKT_SIZE)) cf_error("Buffer size must be in range 256-65535"); }
@@ -314,7 +310,7 @@ ospf_iface_item:
  | TTL SECURITY TX ONLY { OSPF_PATT->ttl_security = 2; }
  | BFD bool { OSPF_PATT->bfd = $2; cf_check_bfd($2); }
  | SECONDARY bool { OSPF_PATT->bsd_secondary = $2; }
- | password_list
+ | password_list { ospf_check_auth(); }
  ;
 
 pref_list:
@@ -349,7 +345,7 @@ nbma_eligible:
  | ELIGIBLE { $$ = 1; }
  ;
  
-nbma_item: IPA nbma_eligible ';'
+nbma_item: ipa nbma_eligible ';'
  {
    this_nbma = cfg_allocz(sizeof(struct nbma_node));
    add_tail(&OSPF_PATT->nbma_list, NODE this_nbma);
@@ -384,11 +380,11 @@ ospf_iface_start:
 
 ospf_instance_id:
    /* empty */
- | INSTANCE expr { set_instance_id($2); }
+ | INSTANCE expr { OSPF_PATT->instance_id = $2; }
  ;
 
 ospf_iface_patt_list:
-   iface_patt_list { if (OSPF_VERSION == 3) iface_patt_check(); } ospf_instance_id
+   iface_patt_list { if (ospf_cfg_is_v3()) iface_patt_check(); } ospf_instance_id
  ; 
 
 ospf_iface_opts:
index 6b291344945405289bc8325ec3cf38a0460b60f0..1f37965c4272e3b4836be23c26ce877412914def 100644 (file)
@@ -2,6 +2,8 @@
  *     BIRD -- OSPF
  *
  *     (c) 1999--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 #include "ospf.h"
 
 
-#ifdef OSPFv2
-struct ospf_dbdes_packet
+struct ospf_dbdes2_packet
 {
-  struct ospf_packet ospf_packet;
+  struct ospf_packet hdr;
+  union ospf_auth auth;
+
   u16 iface_mtu;
   u8 options;
-  union imms imms;             /* I, M, MS bits */
+  u8 imms;                     /* I, M, MS bits */
   u32 ddseq;
-};
-
-#define hton_opt(X) X
-#define ntoh_opt(X) X
-#endif
 
+  struct ospf_lsa_header lsas[];
+};
 
-#ifdef OSPFv3
-struct ospf_dbdes_packet
+struct ospf_dbdes3_packet
 {
-  struct ospf_packet ospf_packet;
+  struct ospf_packet hdr;
+
   u32 options;
   u16 iface_mtu;
   u8 padding;
-  union imms imms;             /* I, M, MS bits */
+  u8 imms;                     /* I, M, MS bits */
   u32 ddseq;
+
+  struct ospf_lsa_header lsas[];
 };
 
-#define hton_opt(X) htonl(X)
-#define ntoh_opt(X) ntohl(X)
-#endif
 
+static inline uint
+ospf_dbdes_hdrlen(struct ospf_proto *p)
+{
+  return ospf_is_v2(p) ?
+    sizeof(struct ospf_dbdes2_packet) : sizeof(struct ospf_dbdes3_packet);
+}
+
+
+static void
+ospf_dbdes_body(struct ospf_proto *p, struct ospf_packet *pkt,
+               struct ospf_lsa_header **body, uint *count)
+{
+  uint plen = ntohs(pkt->length);
+  uint hlen = ospf_dbdes_hdrlen(p);
+
+  *body = ((void *) pkt) + hlen;
+  *count = (plen - hlen) / sizeof(struct ospf_lsa_header);
+}
   
-static void ospf_dump_dbdes(struct proto *p, struct ospf_dbdes_packet *pkt)
+static void
+ospf_dump_dbdes(struct ospf_proto *p, struct ospf_packet *pkt)
 {
-  struct ospf_packet *op = &pkt->ospf_packet;
+  struct ospf_lsa_header *lsas;
+  uint i, lsa_count;
+  u32 pkt_ddseq;
+  u16 pkt_iface_mtu;
+  u8 pkt_imms;
 
-  ASSERT(op->type == DBDES_P);
-  ospf_dump_common(p, op);
-  log(L_TRACE "%s:     imms     %s%s%s",
-      p->name, pkt->imms.bit.ms ? "MS " : "",
-      pkt->imms.bit.m ? "M " : "",
-      pkt->imms.bit.i ? "I " : "" );
-  log(L_TRACE "%s:     ddseq    %u", p->name, ntohl(pkt->ddseq));
+  ASSERT(pkt->type == DBDES_P);
+  ospf_dump_common(p, pkt);
 
-  struct ospf_lsa_header *plsa = (void *) (pkt + 1);
-  unsigned int i, j;
+  if (ospf_is_v2(p))
+  {
+    struct ospf_dbdes2_packet *ps = (void *) pkt;
+    pkt_iface_mtu = ntohs(ps->iface_mtu);
+    pkt_imms = ps->imms;
+    pkt_ddseq = ntohl(ps->ddseq);
+  }
+  else /* OSPFv3 */
+  {
+    struct ospf_dbdes3_packet *ps = (void *) pkt;
+    pkt_iface_mtu = ntohs(ps->iface_mtu);
+    pkt_imms = ps->imms;
+    pkt_ddseq = ntohl(ps->ddseq);
+  }
 
-  j = (ntohs(op->length) - sizeof(struct ospf_dbdes_packet)) /
-    sizeof(struct ospf_lsa_header);
+  log(L_TRACE "%s:     mtu      %u", p->p.name, pkt_iface_mtu);
+  log(L_TRACE "%s:     imms     %s%s%s", p->p.name,
+      (pkt_imms & DBDES_I) ? "I " : "",
+      (pkt_imms & DBDES_M) ? "M " : "",
+      (pkt_imms & DBDES_MS) ? "MS" : "");
+  log(L_TRACE "%s:     ddseq    %u", p->p.name, pkt_ddseq);
 
-  for (i = 0; i < j; i++)
-    ospf_dump_lsahdr(p, plsa + i);
+  ospf_dbdes_body(p, pkt, &lsas, &lsa_count);
+  for (i = 0; i < lsa_count; i++)
+    ospf_dump_lsahdr(p, lsas + i);
 }
 
 
+static void
+ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n, int body)
+{
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_packet *pkt;
+  uint length;
+
+  u16 iface_mtu = (ifa->type == OSPF_IT_VLINK) ? 0 : ifa->iface->mtu;
+
+  if (n->ldd_bsize != ifa->tx_length)
+  {
+    mb_free(n->ldd_buffer);
+    n->ldd_buffer = mb_allocz(n->pool, ifa->tx_length);
+    n->ldd_bsize = ifa->tx_length;
+  }
+
+  pkt = n->ldd_buffer;
+  ospf_pkt_fill_hdr(ifa, pkt, DBDES_P);
+
+  if (ospf_is_v2(p))
+  {
+    struct ospf_dbdes2_packet *ps = (void *) pkt;
+    ps->iface_mtu = htons(iface_mtu);
+    ps->options = ifa->oa->options;
+    ps->imms = 0;      /* Will be set later */
+    ps->ddseq = htonl(n->dds);
+    length = sizeof(struct ospf_dbdes2_packet);
+  }
+  else /* OSPFv3 */
+  {
+    struct ospf_dbdes3_packet *ps = (void *) pkt;
+    ps->options = htonl(ifa->oa->options);
+    ps->iface_mtu = htons(iface_mtu);
+    ps->padding = 0;
+    ps->imms = 0;      /* Will be set later */
+    ps->ddseq = htonl(n->dds);
+    length = sizeof(struct ospf_dbdes3_packet);
+  }
+
+  if (body && (n->myimms & DBDES_M))
+  {
+    struct ospf_lsa_header *lsas;
+    struct top_hash_entry *en;
+    uint i = 0, lsa_max;
+
+    ospf_dbdes_body(p, pkt, &lsas, &lsa_max);
+    en = (void *) s_get(&(n->dbsi));
+
+    while (i < lsa_max)
+    {
+      if (!SNODE_VALID(en))
+      {
+       n->myimms &= ~DBDES_M;  /* Unset More bit */
+       break;
+      }
+
+      if ((en->lsa.age < LSA_MAXAGE) &&
+         lsa_flooding_allowed(en->lsa_type, en->domain, ifa))
+      {
+       lsa_hton_hdr(&(en->lsa), lsas + i);
+       i++;
+      }
+
+      en = SNODE_NEXT(en);
+    }
+
+    s_put(&(n->dbsi), SNODE en);
+
+    length += i * sizeof(struct ospf_lsa_header);
+  }
+
+  if (ospf_is_v2(p))
+    ((struct ospf_dbdes2_packet *) pkt)->imms = n->myimms;
+  else
+    ((struct ospf_dbdes3_packet *) pkt)->imms = n->myimms;
+
+  pkt->length = htons(length);
+}
+
+static void
+ospf_do_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
+{
+  struct ospf_iface *ifa = n->ifa;
+
+  OSPF_PACKET(ospf_dump_dbdes, n->ldd_buffer,
+             "DBDES packet sent to %I via %s", n->ip, ifa->ifname);
+  sk_set_tbuf(ifa->sk, n->ldd_buffer);
+  ospf_send_to(ifa, n->ip);
+  sk_set_tbuf(ifa->sk, NULL);
+}
+
 /**
- * ospf_dbdes_send - transmit database description packet
+ * ospf_send_dbdes - transmit database description packet
  * @n: neighbor
  * @next: whether to send a next packet in a sequence (1) or to retransmit the old one (0)
  *
@@ -75,355 +200,322 @@ static void ospf_dump_dbdes(struct proto *p, struct ospf_dbdes_packet *pkt)
  * of the buffer.
  */
 void
-ospf_dbdes_send(struct ospf_neighbor *n, int next)
+ospf_send_dbdes(struct ospf_neighbor *n, int next)
 {
-  struct ospf_dbdes_packet *pkt;
-  struct ospf_packet *op;
   struct ospf_iface *ifa = n->ifa;
   struct ospf_area *oa = ifa->oa;
-  struct proto_ospf *po = oa->po;
-  struct proto *p = &po->proto;
-  u16 length, i, j;
+  struct ospf_proto *p = oa->po;
+
+  /* RFC 2328 10.8 */
 
-  /* FIXME ??? */
-  if ((oa->rt == NULL) || (EMPTY_LIST(po->lsal)))
-    update_rt_lsa(oa);
+  if (oa->rt == NULL)
+    return;
 
   switch (n->state)
   {
-  case NEIGHBOR_EXSTART:       /* Send empty packets */
-    n->myimms.bit.i = 1;
-    pkt = ospf_tx_buffer(ifa);
-    op = &pkt->ospf_packet;
-    ospf_pkt_fill_hdr(ifa, pkt, DBDES_P);
-    pkt->iface_mtu = (ifa->type == OSPF_IT_VLINK) ? 0 : htons(ifa->iface->mtu);
-    pkt->options = hton_opt(oa->options);
-    pkt->imms = n->myimms;
-    pkt->ddseq = htonl(n->dds);
-    length = sizeof(struct ospf_dbdes_packet);
-    op->length = htons(length);
-
-    OSPF_PACKET(ospf_dump_dbdes, pkt, "DBDES packet sent to %I via %s", n->ip, ifa->ifname);
-    ospf_send_to(ifa, n->ip);
+  case NEIGHBOR_EXSTART:
+    n->myimms |= DBDES_I;
+
+    /* Send empty packets */
+    ospf_prepare_dbdes(p, n, 0);
+    ospf_do_send_dbdes(p, n);
     break;
 
   case NEIGHBOR_EXCHANGE:
-    n->myimms.bit.i = 0;
+    n->myimms &= ~DBDES_I;
 
     if (next)
-    {
-      snode *sn;
-      struct ospf_lsa_header *lsa;
-
-      if (n->ldd_bsize != ifa->tx_length)
-      {
-       mb_free(n->ldd_buffer);
-       n->ldd_buffer = mb_allocz(n->pool, ifa->tx_length);
-       n->ldd_bsize = ifa->tx_length;
-      }
-
-      pkt = n->ldd_buffer;
-      op = (struct ospf_packet *) pkt;
+      ospf_prepare_dbdes(p, n, 1);
 
-      ospf_pkt_fill_hdr(ifa, pkt, DBDES_P);
-      pkt->iface_mtu = (ifa->type == OSPF_IT_VLINK) ? 0 : htons(ifa->iface->mtu);
-      pkt->ddseq = htonl(n->dds);
-      pkt->options = hton_opt(oa->options);
+    /* Send prepared packet */
+    ospf_do_send_dbdes(p, n);
 
-      j = i = (ospf_pkt_maxsize(ifa) - sizeof(struct ospf_dbdes_packet)) / sizeof(struct ospf_lsa_header);     /* Number of possible lsaheaders to send */
-      lsa = (n->ldd_buffer + sizeof(struct ospf_dbdes_packet));
+    /* Master should restart RXMT timer for each DBDES exchange */
+    if (n->myimms & DBDES_MS)
+      tm_start(n->rxmt_timer, n->ifa->rxmtint);
 
-      if (n->myimms.bit.m)
-      {
-       sn = s_get(&(n->dbsi));
-
-       DBG("Number of LSA: %d\n", j);
-       for (; i > 0; i--)
-       {
-         struct top_hash_entry *en= (struct top_hash_entry *) sn;
-
-          if (ospf_lsa_flooding_allowed(&en->lsa, en->domain, ifa))
-          {
-           htonlsah(&(en->lsa), lsa);
-           DBG("Working on: %d\n", i);
-           DBG("\tX%01x %-1R %-1R %p\n", en->lsa.type, en->lsa.id, en->lsa.rt, en->lsa_body);
-
-           lsa++;
-          }
-          else i++;    /* No lsa added */
-
-         if (sn == STAIL(po->lsal))
-          {
-            i--;
-           break;
-          }
-
-         sn = sn->next;
-       }
-
-       if (sn == STAIL(po->lsal))
-       {
-         DBG("Number of LSA NOT sent: %d\n", i);
-         DBG("M bit unset.\n");
-         n->myimms.bit.m = 0;  /* Unset more bit */
-       }
-
-       s_put(&(n->dbsi), sn);
-      }
-
-      pkt->imms.byte = n->myimms.byte;
-
-      length = (j - i) * sizeof(struct ospf_lsa_header) +
-       sizeof(struct ospf_dbdes_packet);
-      op->length = htons(length);
-
-      DBG("%s: DB_DES (M) prepared for %I.\n", p->name, n->ip);
-    }
+    if (!(n->myimms & DBDES_MS))
+      if (!(n->myimms & DBDES_M) && 
+         !(n->imms & DBDES_M))
+       ospf_neigh_sm(n, INM_EXDONE);
+    break;
 
   case NEIGHBOR_LOADING:
   case NEIGHBOR_FULL:
-    length = n->ldd_buffer ? ntohs(((struct ospf_packet *) n->ldd_buffer)->length) : 0;
 
-    if (!length)
+    if (!n->ldd_buffer)
     {
-      OSPF_TRACE(D_PACKETS, "No packet in my buffer for repeating");
+      OSPF_TRACE(D_PACKETS, "No DBDES packet for repeating");
       ospf_neigh_sm(n, INM_KILLNBR);
       return;
     }
 
-    /* Send last packet from ldd buffer */
+    /* Send last packet */
+    ospf_do_send_dbdes(p, n);
+    break;
+  }
+}
 
-    OSPF_PACKET(ospf_dump_dbdes, n->ldd_buffer, "DBDES packet sent to %I via %s", n->ip, ifa->ifname);
 
-    sk_set_tbuf(ifa->sk, n->ldd_buffer);
-    ospf_send_to(ifa, n->ip);
-    sk_set_tbuf(ifa->sk, NULL);
+static int
+ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_neighbor *n)
+{
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_lsa_header *lsas;
+  uint i, lsa_count;
+
+  ospf_dbdes_body(p, pkt, &lsas, &lsa_count);
+
+  for (i = 0; i < lsa_count; i++)
+  {
+    struct top_hash_entry *en, *req;
+    struct ospf_lsa_header lsa;
+    u32 lsa_type, lsa_domain;
+
+    lsa_ntoh_hdr(lsas + i, &lsa);
+    lsa_get_type_domain(&lsa, ifa, &lsa_type, &lsa_domain);
 
-    if(n->myimms.bit.ms) tm_start(n->rxmt_timer, n->ifa->rxmtint);             /* Restart timer */
+    /* RFC 2328 10.6 and RFC 5340 4.2.2 */
 
-    if (!n->myimms.bit.ms)
+    if (!lsa_type)
     {
-      if ((n->myimms.bit.m == 0) && (n->imms.bit.m == 0) &&
-         (n->state == NEIGHBOR_EXCHANGE))
-      {
-       ospf_neigh_sm(n, INM_EXDONE);
-      }
+      log(L_WARN "%s: Bad DBDES from %I - LSA of unknown type", p->p.name, n->ip);
+      goto err;
     }
-    break;
 
-  default:                     /* Ignore it */
-    break;
-  }
-}
+    if (!oa_is_ext(ifa->oa) && (LSA_SCOPE(lsa_type) == LSA_SCOPE_AS))
+    {
+      log(L_WARN "%s: Bad DBDES from %I - LSA with AS scope in stub area", p->p.name, n->ip);
+      goto err;
+    }
 
-static void
-ospf_dbdes_reqladd(struct ospf_dbdes_packet *ps, struct ospf_neighbor *n)
-{
-  struct ospf_lsa_header *plsa, lsa;
-  struct top_hash_entry *he, *sn;
-  struct ospf_area *oa = n->ifa->oa;
-  struct top_graph *gr = oa->po->gr;
-  struct ospf_packet *op;
-  int i, j;
+    /* Errata 3746 to RFC 2328 - rt-summary-LSAs forbidden in stub areas */
+    if (!oa_is_ext(ifa->oa) && (lsa_type == LSA_T_SUM_RT))
+    {
+      log(L_WARN "%s: Bad DBDES from %I - rt-summary-LSA in stub area", p->p.name, n->ip);
+      goto err;
+    }
 
-  op = (struct ospf_packet *) ps;
+    /* Not explicitly mentioned in RFC 5340 4.2.2 but makes sense */
+    if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES)
+    {
+      log(L_WARN "%s: Bad DBDES from %I - LSA with invalid scope", p->p.name, n->ip);
+      goto err;
+    }
 
-  plsa = (void *) (ps + 1);
+    en = ospf_hash_find(p->gr, lsa_domain, lsa.id, lsa.rt, lsa_type);
+    if (!en || (lsa_comp(&lsa, &(en->lsa)) == CMP_NEWER))
+    {
+      req = ospf_hash_get(n->lsrqh, lsa_domain, lsa.id, lsa.rt, lsa_type);
 
-  j = (ntohs(op->length) - sizeof(struct ospf_dbdes_packet)) /
-    sizeof(struct ospf_lsa_header);
+      if (!SNODE_VALID(req))
+       s_add_tail(&n->lsrql, SNODE req);
 
-  for (i = 0; i < j; i++)
-  {
-    ntohlsah(plsa + i, &lsa);
-    u32 dom = ospf_lsa_domain(lsa.type, n->ifa);
-    if (((he = ospf_hash_find_header(gr, dom, &lsa)) == NULL) ||
-       (lsa_comp(&lsa, &(he->lsa)) == 1))
-    {
-      /* Is this condition necessary? */
-      if (ospf_hash_find_header(n->lsrqh, dom, &lsa) == NULL)
-      {
-       sn = ospf_hash_get_header(n->lsrqh, dom, &lsa);
-       ntohlsah(plsa + i, &(sn->lsa));
-       s_add_tail(&(n->lsrql), SNODE sn);
-      }
+      req->lsa = lsa;
     }
   }
+
+  return 0;
+
+ err:
+  ospf_neigh_sm(n, INM_SEQMIS);
+  return -1;
 }
 
 void
-ospf_dbdes_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
+ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa,
                   struct ospf_neighbor *n)
 {
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
+  struct ospf_proto *p = ifa->oa->po;
+  u32 rcv_ddseq, rcv_options;
+  u16 rcv_iface_mtu;
+  u8 rcv_imms;
+  uint plen;
 
-  unsigned int size = ntohs(ps_i->length);
-  if (size < sizeof(struct ospf_dbdes_packet))
+  /* RFC 2328 10.6 */
+
+  plen = ntohs(pkt->length);
+  if (plen < ospf_dbdes_hdrlen(p))
   {
-    log(L_ERR "Bad OSPF DBDES packet from %I -  too short (%u B)", n->ip, size);
+    log(L_ERR "OSPF: Bad DBDES packet from %I - too short (%u B)", n->ip, plen);
     return;
   }
 
-  struct ospf_dbdes_packet *ps = (void *) ps_i;
-  u32 ps_ddseq = ntohl(ps->ddseq);
-  u32 ps_options = ntoh_opt(ps->options);
-  u16 ps_iface_mtu = ntohs(ps->iface_mtu);
-  
-  OSPF_PACKET(ospf_dump_dbdes, ps, "DBDES packet received from %I via %s", n->ip, ifa->ifname);
+  OSPF_PACKET(ospf_dump_dbdes, pkt, "DBDES packet received from %I via %s", n->ip, ifa->ifname);
 
   ospf_neigh_sm(n, INM_HELLOREC);
 
+  if (ospf_is_v2(p))
+  {
+    struct ospf_dbdes2_packet *ps = (void *) pkt;
+    rcv_iface_mtu = ntohs(ps->iface_mtu);
+    rcv_options = ps->options;
+    rcv_imms = ps->imms;
+    rcv_ddseq = ntohl(ps->ddseq);
+  }
+  else /* OSPFv3 */
+  {
+    struct ospf_dbdes3_packet *ps = (void *) pkt;
+    rcv_options = ntohl(ps->options);
+    rcv_iface_mtu = ntohs(ps->iface_mtu);
+    rcv_imms = ps->imms;
+    rcv_ddseq = ntohl(ps->ddseq);
+  }
+  
   switch (n->state)
   {
   case NEIGHBOR_DOWN:
   case NEIGHBOR_ATTEMPT:
   case NEIGHBOR_2WAY:
     return;
-    break;
+
   case NEIGHBOR_INIT:
     ospf_neigh_sm(n, INM_2WAYREC);
     if (n->state != NEIGHBOR_EXSTART)
       return;
-  case NEIGHBOR_EXSTART:
 
-    if ((ifa->type != OSPF_IT_VLINK) && (ps_iface_mtu != ifa->iface->mtu)
-       && (ps_iface_mtu != 0) && (ifa->iface->mtu != 0))
-      log(L_WARN "OSPF: MTU mismatch with neighbour %I on interface %s (remote %d, local %d)",
-         n->ip, ifa->ifname, ps_iface_mtu, ifa->iface->mtu);
-
-    if ((ps->imms.bit.m && ps->imms.bit.ms && ps->imms.bit.i)
-       && (n->rid > po->router_id) && (size == sizeof(struct ospf_dbdes_packet)))
+  case NEIGHBOR_EXSTART:
+    if ((ifa->type != OSPF_IT_VLINK) &&
+       (rcv_iface_mtu != ifa->iface->mtu) &&
+       (rcv_iface_mtu != 0) &&
+       (ifa->iface->mtu != 0))
+      log(L_WARN "OSPF: MTU mismatch with neighbor %I on interface %s (remote %d, local %d)",
+         n->ip, ifa->ifname, rcv_iface_mtu, ifa->iface->mtu);
+
+    if ((rcv_imms == DBDES_IMMS) &&
+       (n->rid > p->router_id) &&
+       (plen == ospf_dbdes_hdrlen(p)))
     {
       /* I'm slave! */
-      n->dds = ps_ddseq;
-      n->ddr = ps_ddseq;
-      n->options = ps_options;
-      n->myimms.bit.ms = 0;
-      n->imms.byte = ps->imms.byte;
-      OSPF_TRACE(D_PACKETS, "I'm slave to %I.", n->ip);
+      n->dds = rcv_ddseq;
+      n->ddr = rcv_ddseq;
+      n->options = rcv_options;
+      n->myimms &= ~DBDES_MS;
+      n->imms = rcv_imms;
+      OSPF_TRACE(D_PACKETS, "I'm slave to %I", n->ip);
       ospf_neigh_sm(n, INM_NEGDONE);
-      ospf_dbdes_send(n, 1);
+      ospf_send_dbdes(n, 1);
       break;
     }
 
-    if (((ps->imms.bit.i == 0) && (ps->imms.bit.ms == 0)) &&
-        (n->rid < po->router_id) && (n->dds == ps_ddseq))
+    if (!(rcv_imms & DBDES_I) &&
+       !(rcv_imms & DBDES_MS) &&
+        (n->rid < p->router_id) &&
+       (n->dds == rcv_ddseq))
     {
       /* I'm master! */
-      n->options = ps_options;
-      n->ddr = ps_ddseq - 1;   /* It will be set corectly a few lines down */
-      n->imms.byte = ps->imms.byte;
-      OSPF_TRACE(D_PACKETS, "I'm master to %I.", n->ip);
+      n->options = rcv_options;
+      n->ddr = rcv_ddseq - 1;  /* It will be set corectly a few lines down */
+      n->imms = rcv_imms;
+      OSPF_TRACE(D_PACKETS, "I'm master to %I", n->ip);
       ospf_neigh_sm(n, INM_NEGDONE);
     }
     else
     {
-      DBG("%s: Nothing happend to %I (imms=%u)\n", p->name, n->ip,
-          ps->imms.byte);
+      DBG("%s: Nothing happend to %I (imms=%d)\n", p->name, n->ip, rcv_imms);
       break;
     }
+
   case NEIGHBOR_EXCHANGE:
-    if ((ps->imms.byte == n->imms.byte) && (ps_options == n->options) &&
-       (ps_ddseq == n->ddr))
+    if ((rcv_imms == n->imms) &&
+       (rcv_options == n->options) &&
+       (rcv_ddseq == n->ddr))
     {
       /* Duplicate packet */
-      OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I.", n->ip);
-      if (n->myimms.bit.ms == 0)
+      OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I", n->ip);
+      if (!(n->myimms & DBDES_MS))
       {
        /* Slave should retransmit dbdes packet */
-       ospf_dbdes_send(n, 0);
+       ospf_send_dbdes(n, 0);
       }
       return;
     }
 
-    n->ddr = ps_ddseq;
-
-    if (ps->imms.bit.ms != n->imms.bit.ms)     /* M/S bit differs */
+    if ((rcv_imms & DBDES_MS) != (n->imms & DBDES_MS)) /* M/S bit differs */
     {
-      OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (bit MS)",
-                n->ip);
+      OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (bit MS)", n->ip);
       ospf_neigh_sm(n, INM_SEQMIS);
       break;
     }
 
-    if (ps->imms.bit.i)                /* I bit is set */
+    if (rcv_imms & DBDES_I)            /* I bit is set */
     {
-      OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (bit I)",
-                n->ip);
+      OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (bit I)", n->ip);
       ospf_neigh_sm(n, INM_SEQMIS);
       break;
     }
 
-    n->imms.byte = ps->imms.byte;
-
-    if (ps_options != n->options)      /* Options differs */
+    if (rcv_options != n->options)     /* Options differs */
     {
-      OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (options)",
-                n->ip);
+      OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (options)", n->ip);
       ospf_neigh_sm(n, INM_SEQMIS);
       break;
     }
 
-    if (n->myimms.bit.ms)
+    n->ddr = rcv_ddseq;
+    n->imms = rcv_imms;
+
+    if (n->myimms & DBDES_MS)
     {
-      if (ps_ddseq != n->dds)  /* MASTER */
+      if (rcv_ddseq != n->dds) /* MASTER */
       {
-       OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (master)",
-                  n->ip);
+       OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (master)", n->ip);
        ospf_neigh_sm(n, INM_SEQMIS);
        break;
       }
+
       n->dds++;
-      DBG("Incrementing dds\n");
-      ospf_dbdes_reqladd(ps, n);
-      if ((n->myimms.bit.m == 0) && (ps->imms.bit.m == 0))
-      {
+
+      if (ospf_process_dbdes(p, pkt, n) < 0)
+       return;
+
+      if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M))
        ospf_neigh_sm(n, INM_EXDONE);
-      }
       else
-      {
-       ospf_dbdes_send(n, 1);
-      }
-
+       ospf_send_dbdes(n, 1);
     }
     else
     {
-      if (ps_ddseq != (n->dds + 1))    /* SLAVE */
+      if (rcv_ddseq != (n->dds + 1))   /* SLAVE */
       {
        OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (slave)", n->ip);
        ospf_neigh_sm(n, INM_SEQMIS);
        break;
       }
-      n->ddr = ps_ddseq;
-      n->dds = ps_ddseq;
-      ospf_dbdes_reqladd(ps, n);
-      ospf_dbdes_send(n, 1);
-    }
 
+      n->ddr = rcv_ddseq;
+      n->dds = rcv_ddseq;
+
+      if (ospf_process_dbdes(p, pkt, n) < 0)
+       return;
+
+      ospf_send_dbdes(n, 1);
+    }
     break;
+
   case NEIGHBOR_LOADING:
   case NEIGHBOR_FULL:
-    if ((ps->imms.byte == n->imms.byte) && (ps_options == n->options)
-       && (ps_ddseq == n->ddr))
-      /* Only duplicate are accepted */
+    if ((rcv_imms == n->imms) &&
+       (rcv_options == n->options) &&
+       (rcv_ddseq == n->ddr))
     {
-      OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I.", n->ip);
-      if (n->myimms.bit.ms == 0)
+      /* Duplicate packet */
+      OSPF_TRACE(D_PACKETS, "Received duplicate dbdes from %I", n->ip);
+      if (!(n->myimms & DBDES_MS))
       {
        /* Slave should retransmit dbdes packet */
-       ospf_dbdes_send(n, 0);
+       ospf_send_dbdes(n, 0);
       }
       return;
     }
     else
     {
-      OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (full)",
-                n->ip);
-      DBG("PS=%u, DDR=%u, DDS=%u\n", ps_ddseq, n->ddr, n->dds);
+      OSPF_TRACE(D_PACKETS, "dbdes - sequence mismatch neighbor %I (full)", n->ip);
+      DBG("PS=%u, DDR=%u, DDS=%u\n", rcv_ddseq, n->ddr, n->dds);
       ospf_neigh_sm(n, INM_SEQMIS);
     }
     break;
+
   default:
     bug("Received dbdes from %I in undefined state.", n->ip);
   }
diff --git a/proto/ospf/dbdes.h b/proto/ospf/dbdes.h
deleted file mode 100644 (file)
index 63cca0a..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- *      BIRD -- OSPF
- *
- *      (c) 1999 - 2004 Ondrej Filip <feela@network.cz>
- *
- *      Can be freely distributed and used under the terms of the GNU GPL.
- *
- */
-
-#ifndef _BIRD_OSPF_DBDES_H_
-#define _BIRD_OSPF_DBDES_H_
-
-void ospf_dbdes_send(struct ospf_neighbor *n, int next);
-void ospf_dbdes_receive(struct ospf_packet *ps, struct ospf_iface *ifa,
-                       struct ospf_neighbor *n);
-
-#endif /* _BIRD_OSPF_DBDES_H_ */
index e8bce09fe4ee37cc4b86d7aa55611cc7af9815ac..376eac3c15a9794519c6f03d62405e0621aebbd9 100644 (file)
@@ -2,6 +2,8 @@
  *     BIRD -- OSPF
  *
  *     (c) 1999--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 #include "ospf.h"
 
 
-#ifdef OSPFv2
-struct ospf_hello_packet
+struct ospf_hello2_packet
 {
-  struct ospf_packet ospf_packet;
-  ip_addr netmask;
+  struct ospf_packet hdr;
+  union ospf_auth auth;
+
+  u32 netmask;
   u16 helloint;
   u8 options;
   u8 priority;
   u32 deadint;
   u32 dr;
   u32 bdr;
-};
-#endif
 
+  u32 neighbors[];
+};
 
-#ifdef OSPFv3
-struct ospf_hello_packet
+struct ospf_hello3_packet
 {
-  struct ospf_packet ospf_packet;
+  struct ospf_packet hdr;
+
   u32 iface_id;
   u8 priority;
   u8 options3;
@@ -37,286 +40,92 @@ struct ospf_hello_packet
   u16 deadint;
   u32 dr;
   u32 bdr;
-};
-#endif
-
-
-void
-ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
-                  struct ospf_neighbor *n, ip_addr faddr)
-{
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
-  char *beg = "OSPF: Bad HELLO packet from ";
-  unsigned int size, i, twoway, peers;
-  u32 tmp;
-  u32 *pnrid;
-
-  size = ntohs(ps_i->length);
-  if (size < sizeof(struct ospf_hello_packet))
-  {
-    log(L_ERR "%s%I - too short (%u B)", beg, faddr, size);
-    return;
-  }
-
-  struct ospf_hello_packet *ps = (void *) ps_i;
-
-  OSPF_TRACE(D_PACKETS, "HELLO packet received from %I via %s", faddr, ifa->ifname);
-
-#ifdef OSPFv2
-  ip_addr mask = ps->netmask;
-  ipa_ntoh(mask);
-  if ((ifa->type != OSPF_IT_VLINK) &&
-      (ifa->type != OSPF_IT_PTP) &&
-      !ipa_equal(mask, ipa_mkmask(ifa->addr->pxlen)))
-  {
-    log(L_ERR "%s%I - netmask mismatch (%I)", beg, faddr, mask);
-    return;
-  }
-#endif
-
-  tmp = ntohs(ps->helloint);
-  if (tmp != ifa->helloint)
-  {
-    log(L_ERR "%s%I - hello interval mismatch (%d)", beg, faddr, tmp);
-    return;
-  }
-
-#ifdef OSPFv2
-  tmp = ntohl(ps->deadint);
-#else /* OSPFv3 */
-  tmp = ntohs(ps->deadint);
-#endif
-  if (tmp != ifa->deadint)
-  {
-    log(L_ERR "%s%I - dead interval mismatch (%d)", beg, faddr, tmp);
-    return;
-  }
-
-  /* Check whether bits E, N match */
-  if ((ps->options ^ ifa->oa->options) & (OPT_E | OPT_N))
-  {
-    log(L_ERR "%s%I - area type mismatch (%x)", beg, faddr, ps->options);
-    return;
-  }
-
-#ifdef OSPFv2
-  if (n && (n->rid != ntohl(ps_i->routerid)))
-  {
-    OSPF_TRACE(D_EVENTS,
-       "Neighbor %I has changed router id from %R to %R.",
-            n->ip, n->rid, ntohl(ps_i->routerid));
-    ospf_neigh_remove(n);
-    n = NULL;
-  }
-#endif
-
-  if (!n)
-  {
-    if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP))
-    {
-      struct nbma_node *nn = find_nbma_node(ifa, faddr);
-
-      if (!nn && ifa->strictnbma)
-      {
-       log(L_WARN "Ignoring new neighbor: %I on %s", faddr, ifa->ifname);
-       return;
-      }
-
-      if (nn && (ifa->type == OSPF_IT_NBMA) &&
-         (((ps->priority == 0) && nn->eligible) ||
-          ((ps->priority > 0) && !nn->eligible)))
-      {
-       log(L_ERR "Eligibility mismatch for neighbor: %I on %s", faddr, ifa->ifname);
-       return;
-      }
-
-      if (nn)
-       nn->found = 1;
-    }
-
-    OSPF_TRACE(D_EVENTS, "New neighbor found: %I on %s", faddr, ifa->ifname);
-
-    n = ospf_neighbor_new(ifa);
-
-    n->rid = ntohl(ps_i->routerid);
-    n->ip = faddr;
-    n->dr = ntohl(ps->dr);
-    n->bdr = ntohl(ps->bdr);
-    n->priority = ps->priority;
-#ifdef OSPFv3
-    n->iface_id = ntohl(ps->iface_id);
-#endif
-
-    if (n->ifa->cf->bfd)
-      ospf_neigh_update_bfd(n, n->ifa->bfd);
-  }
-#ifdef OSPFv3  /* NOTE: this could also be relevant for OSPFv2 on PtP ifaces */
-  else if (!ipa_equal(faddr, n->ip))
-  {
-    OSPF_TRACE(D_EVENTS, "Neighbor address changed from %I to %I", n->ip, faddr);
-    n->ip = faddr;
-  }
-#endif
-
-  ospf_neigh_sm(n, INM_HELLOREC);
-
-  pnrid = (u32 *) ((struct ospf_hello_packet *) (ps + 1));
-
-  peers = (size - sizeof(struct ospf_hello_packet))/ sizeof(u32);
-
-  twoway = 0;
-  for (i = 0; i < peers; i++)
-  {
-    if (ntohl(pnrid[i]) == po->router_id)
-    {
-      DBG("%s: Twoway received from %I\n", p->name, faddr);
-      ospf_neigh_sm(n, INM_2WAYREC);
-      twoway = 1;
-      break;
-    }
-  }
-
-  if (!twoway)
-    ospf_neigh_sm(n, INM_1WAYREC);
-
-  u32 olddr = n->dr;
-  u32 oldbdr = n->bdr;
-  u32 oldpriority = n->priority;
-#ifdef OSPFv3
-  u32 oldiface_id = n->iface_id;
-#endif
-
-  n->dr = ntohl(ps->dr);
-  n->bdr = ntohl(ps->bdr);
-  n->priority = ps->priority;
-#ifdef OSPFv3
-  n->iface_id = ntohl(ps->iface_id);
-#endif
 
+  u32 neighbors[];
+};
 
-  /* Check priority change */
-  if (n->state >= NEIGHBOR_2WAY)
-  {
-#ifdef OSPFv2
-    u32 neigh = ipa_to_u32(n->ip);
-#else /* OSPFv3 */
-    u32 neigh = n->rid;
-#endif
-
-    if (n->priority != oldpriority)
-      ospf_iface_sm(ifa, ISM_NEICH);
-
-#ifdef OSPFv3
-    if (n->iface_id != oldiface_id)
-      ospf_iface_sm(ifa, ISM_NEICH);
-#endif
-
-    /* Neighbor is declaring itself ad DR and there is no BDR */
-    if ((n->dr == neigh) && (n->bdr == 0)
-       && (n->state != NEIGHBOR_FULL))
-      ospf_iface_sm(ifa, ISM_BACKS);
-
-    /* Neighbor is declaring itself as BDR */
-    if ((n->bdr == neigh) && (n->state != NEIGHBOR_FULL))
-      ospf_iface_sm(ifa, ISM_BACKS);
-
-    /* Neighbor is newly declaring itself as DR or BDR */
-    if (((n->dr == neigh) && (n->dr != olddr))
-       || ((n->bdr == neigh) && (n->bdr != oldbdr)))
-      ospf_iface_sm(ifa, ISM_NEICH);
-
-    /* Neighbor is no more declaring itself as DR or BDR */
-    if (((olddr == neigh) && (n->dr != olddr))
-       || ((oldbdr == neigh) && (n->bdr != oldbdr)))
-      ospf_iface_sm(ifa, ISM_NEICH);
-  }
-
-  if (ifa->type == OSPF_IT_NBMA)
-  {
-    if ((ifa->priority == 0) && (n->priority > 0))
-      ospf_hello_send(n->ifa, OHS_HELLO, n);
-  }
-  ospf_neigh_sm(n, INM_HELLOREC);
-}
 
 void
-ospf_hello_send(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn)
+ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn)
 {
-  struct ospf_hello_packet *pkt;
-  struct ospf_packet *op;
-  struct proto *p;
+  struct ospf_proto *p = ifa->oa->po;
+  struct ospf_packet *pkt;
   struct ospf_neighbor *neigh, *n1;
-  u16 length;
-  int i;
   struct nbma_node *nb;
+  u32 *neighbors;
+  uint length;
+  int i, max;
 
   if (ifa->state <= OSPF_IS_LOOP)
     return;
 
   if (ifa->stub)
-    return;                    /* Don't send any packet on stub iface */
+    return;
 
-  p = (struct proto *) (ifa->oa->po);
-  DBG("%s: Hello/Poll timer fired on interface %s with IP %I\n",
-      p->name, ifa->ifname, ifa->addr->ip);
 
-  /* Now we should send a hello packet */
   pkt = ospf_tx_buffer(ifa);
-  op = &pkt->ospf_packet;
-
-  /* Now fill ospf_hello header */
   ospf_pkt_fill_hdr(ifa, pkt, HELLO_P);
 
-#ifdef OSPFv2
-  pkt->netmask = ipa_mkmask(ifa->addr->pxlen);
-  ipa_hton(pkt->netmask);
-  if ((ifa->type == OSPF_IT_VLINK) ||
-      ((ifa->type == OSPF_IT_PTP) && !ifa->ptp_netmask))
-    pkt->netmask = IPA_NONE;
-#endif
-
-  pkt->helloint = ntohs(ifa->helloint);
-  pkt->priority = ifa->priority;
-
-#ifdef OSPFv3
-  pkt->iface_id = htonl(ifa->iface_id);
-
-  pkt->options3 = ifa->oa->options >> 16;
-  pkt->options2 = ifa->oa->options >> 8;
-#endif
-  pkt->options = ifa->oa->options;
-
-#ifdef OSPFv2
-  pkt->deadint = htonl(ifa->deadint);
-  pkt->dr = htonl(ipa_to_u32(ifa->drip));
-  pkt->bdr = htonl(ipa_to_u32(ifa->bdrip));
-#else /* OSPFv3 */
-  pkt->deadint = htons(ifa->deadint);
-  pkt->dr = htonl(ifa->drid);
-  pkt->bdr = htonl(ifa->bdrid);
-#endif
+  if (ospf_is_v2(p))
+  {
+    struct ospf_hello2_packet *ps = (void *) pkt;
+
+    if ((ifa->type == OSPF_IT_VLINK) ||
+       ((ifa->type == OSPF_IT_PTP) && !ifa->ptp_netmask))
+      ps->netmask = 0;
+    else
+      ps->netmask = htonl(u32_mkmask(ifa->addr->pxlen));
+
+    ps->helloint = ntohs(ifa->helloint);
+    ps->options = ifa->oa->options;
+    ps->priority = ifa->priority;
+    ps->deadint = htonl(ifa->deadint);
+    ps->dr = htonl(ipa_to_u32(ifa->drip));
+    ps->bdr = htonl(ipa_to_u32(ifa->bdrip));
+
+    length = sizeof(struct ospf_hello2_packet);
+    neighbors = ps->neighbors;
+  }
+  else
+  {
+    struct ospf_hello3_packet *ps = (void *) pkt;
+
+    ps->iface_id = htonl(ifa->iface_id);
+    ps->priority = ifa->priority;
+    ps->options3 = ifa->oa->options >> 16;
+    ps->options2 = ifa->oa->options >> 8;
+    ps->options = ifa->oa->options;
+    ps->helloint = ntohs(ifa->helloint);
+    ps->deadint = htons(ifa->deadint);
+    ps->dr = htonl(ifa->drid);
+    ps->bdr = htonl(ifa->bdrid);
+
+    length = sizeof(struct ospf_hello3_packet);
+    neighbors = ps->neighbors;
+  }
 
-  /* Fill all neighbors */
   i = 0;
+  max = (ospf_pkt_maxsize(ifa) - length) / sizeof(u32);
 
+  /* Fill all neighbors */
   if (kind != OHS_SHUTDOWN)
   {
-    u32 *pp = (u32 *) (((u8 *) pkt) + sizeof(struct ospf_hello_packet));
     WALK_LIST(neigh, ifa->neigh_list)
     {
-      if ((i+1) * sizeof(u32) + sizeof(struct ospf_hello_packet) > ospf_pkt_maxsize(ifa))
+      if (i == max)
       {
-       log(L_WARN "%s: Too many neighbors on interface %s", p->name, ifa->ifname);
+       log(L_WARN "%s: Too many neighbors on interface %s", p->p.name, ifa->ifname);
        break;
       }
-      *(pp + i) = htonl(neigh->rid);
+      neighbors[i] = htonl(neigh->rid);
       i++;
     }
   }
 
-  length = sizeof(struct ospf_hello_packet) + i * sizeof(u32);
-  op->length = htons(length);
+  length += i * sizeof(u32);
+  pkt->length = htons(length);
+
+  OSPF_TRACE(D_PACKETS, "HELLO packet sent via %s", ifa->ifname);
 
   switch(ifa->type)
   {
@@ -369,8 +178,226 @@ ospf_hello_send(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn)
     break;
 
   default:
-    bug("Bug in ospf_hello_send()");
+    bug("Bug in ospf_send_hello()");
   }
+}
 
-  OSPF_TRACE(D_PACKETS, "HELLO packet sent via %s", ifa->ifname);
+
+void
+ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa,
+                  struct ospf_neighbor *n, ip_addr faddr)
+{
+  struct ospf_proto *p = ifa->oa->po;
+  char *beg = "OSPF: Bad HELLO packet from ";
+  u32 rcv_iface_id, rcv_helloint, rcv_deadint, rcv_dr, rcv_bdr;
+  u8 rcv_options, rcv_priority;
+  u32 *neighbors;
+  u32 neigh_count;
+  uint plen, i;
+
+  /* RFC 2328 10.5 */
+
+  OSPF_TRACE(D_PACKETS, "HELLO packet received from %I via %s", faddr, ifa->ifname);
+
+  plen = ntohs(pkt->length);
+
+  if (ospf_is_v2(p))
+  {
+    struct ospf_hello2_packet *ps = (void *) pkt;
+
+    if (plen < sizeof(struct ospf_hello2_packet))
+    {
+      log(L_ERR "%s%I - too short (%u B)", beg, faddr, plen);
+      return;
+    }
+
+    rcv_iface_id = 0;
+    rcv_helloint = ntohs(ps->helloint);
+    rcv_deadint = ntohl(ps->deadint);
+    rcv_dr = ntohl(ps->dr);
+    rcv_bdr = ntohl(ps->bdr);
+    rcv_options = ps->options;
+    rcv_priority = ps->priority;
+
+    int pxlen = u32_masklen(ntohl(ps->netmask));
+    if ((ifa->type != OSPF_IT_VLINK) &&
+       (ifa->type != OSPF_IT_PTP) &&
+       (pxlen != ifa->addr->pxlen))
+    {
+      log(L_ERR "%s%I - prefix length mismatch (%d)", beg, faddr, pxlen);
+      return;
+    }
+
+    neighbors = ps->neighbors;
+    neigh_count = (plen - sizeof(struct ospf_hello2_packet)) / sizeof(u32);
+  }
+  else /* OSPFv3 */
+  {
+    struct ospf_hello3_packet *ps = (void *) pkt;
+
+    if (plen < sizeof(struct ospf_hello3_packet))
+    {
+      log(L_ERR "%s%I - too short (%u B)", beg, faddr, plen);
+      return;
+    }
+
+    rcv_iface_id = ntohl(ps->iface_id);
+    rcv_helloint = ntohs(ps->helloint);
+    rcv_deadint = ntohs(ps->deadint);
+    rcv_dr = ntohl(ps->dr);
+    rcv_bdr = ntohl(ps->bdr);
+    rcv_options = ps->options;
+    rcv_priority = ps->priority;
+
+    neighbors = ps->neighbors;
+    neigh_count = (plen - sizeof(struct ospf_hello3_packet)) / sizeof(u32);
+  }
+
+  if (rcv_helloint != ifa->helloint)
+  {
+    log(L_ERR "%s%I - hello interval mismatch (%d)", beg, faddr, rcv_helloint);
+    return;
+  }
+
+  if (rcv_deadint != ifa->deadint)
+  {
+    log(L_ERR "%s%I - dead interval mismatch (%d)", beg, faddr, rcv_deadint);
+    return;
+  }
+
+  /* Check whether bits E, N match */
+  if ((rcv_options ^ ifa->oa->options) & (OPT_E | OPT_N))
+  {
+    log(L_ERR "%s%I - area type mismatch (%x)", beg, faddr, rcv_options);
+    return;
+  }
+
+  /* Check consistency of existing neighbor entry */
+  if (n)
+  {
+    unsigned t = ifa->type;
+    if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP)))
+    {
+      /* Neighbor identified by IP address; Router ID may change */
+      if (n->rid != ntohl(pkt->routerid))
+      {
+       OSPF_TRACE(D_EVENTS, "Neighbor %I has changed Router ID from %R to %R",
+                  n->ip, n->rid, ntohl(pkt->routerid));
+       ospf_neigh_remove(n);
+       n = NULL;
+      }
+    }
+    else /* OSPFv3 or OSPFv2/PtP */
+    {
+      /* Neighbor identified by Router ID; IP address may change */
+      if (!ipa_equal(faddr, n->ip))
+      {
+       OSPF_TRACE(D_EVENTS, "Neighbor address changed from %I to %I", n->ip, faddr);
+       n->ip = faddr;
+      }
+    }
+  }
+
+  if (!n)
+  {
+    if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP))
+    {
+      struct nbma_node *nn = find_nbma_node(ifa, faddr);
+
+      if (!nn && ifa->strictnbma)
+      {
+       log(L_WARN "Ignoring new neighbor: %I on %s", faddr, ifa->ifname);
+       return;
+      }
+
+      if (nn && (ifa->type == OSPF_IT_NBMA) &&
+         (((rcv_priority == 0) && nn->eligible) ||
+          ((rcv_priority > 0) && !nn->eligible)))
+      {
+       log(L_ERR "Eligibility mismatch for neighbor: %I on %s", faddr, ifa->ifname);
+       return;
+      }
+
+      if (nn)
+       nn->found = 1;
+    }
+
+    OSPF_TRACE(D_EVENTS, "New neighbor found: %I on %s", faddr, ifa->ifname);
+
+    n = ospf_neighbor_new(ifa);
+
+    n->rid = ntohl(pkt->routerid);
+    n->ip = faddr;
+    n->dr = rcv_dr;
+    n->bdr = rcv_bdr;
+    n->priority = rcv_priority;
+    n->iface_id = rcv_iface_id;
+
+    if (n->ifa->cf->bfd)
+      ospf_neigh_update_bfd(n, n->ifa->bfd);
+  }
+
+  u32 n_id = ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid;
+
+  u32 old_dr = n->dr;
+  u32 old_bdr = n->bdr;
+  u32 old_priority = n->priority;
+  u32 old_iface_id = n->iface_id;
+
+  n->dr = rcv_dr;
+  n->bdr = rcv_bdr;
+  n->priority = rcv_priority;
+  n->iface_id = rcv_iface_id;
+
+
+  /* Update inactivity timer */
+  ospf_neigh_sm(n, INM_HELLOREC);
+
+  /* RFC 2328 9.5.1 - non-eligible routers reply to hello on NBMA nets */
+  if (ifa->type == OSPF_IT_NBMA)
+    if ((ifa->priority == 0) && (n->priority > 0))
+      ospf_send_hello(n->ifa, OHS_HELLO, n);
+
+  /* Examine list of neighbors */
+  for (i = 0; i < neigh_count; i++)
+    if (neighbors[i] == htonl(p->router_id))
+      goto found_self;
+
+  ospf_neigh_sm(n, INM_1WAYREC);
+  return;
+
+ found_self:
+  ospf_neigh_sm(n, INM_2WAYREC);
+
+
+  if (n->iface_id != old_iface_id)
+  {
+    /* If neighbor is DR, also update cached DR interface ID */
+    if (ifa->drid == n->rid)
+      ifa->dr_iface_id = n->iface_id;
+
+    /* RFC 5340 4.4.3 Event 4 - change of neighbor's interface ID */
+    ospf_notify_rt_lsa(ifa->oa);
+
+    /* Missed in RFC 5340 4.4.3 Event 4 - (Px-)Net-LSA uses iface_id to ref Link-LSAs */
+    ospf_notify_net_lsa(ifa);
+  }
+
+  if (ifa->state == OSPF_IS_WAITING)
+  {
+    /* Neighbor is declaring itself DR (and there is no BDR) or as BDR */
+    if (((n->dr == n_id) && (n->bdr == 0)) || (n->bdr == n_id))
+      ospf_iface_sm(ifa, ISM_BACKS);
+  }
+  else if (ifa->state >= OSPF_IS_DROTHER)
+  {
+    /* Neighbor changed priority or started/stopped declaring itself as DR/BDR */
+    if ((n->priority != old_priority) ||
+       ((n->dr == n_id) && (old_dr != n_id)) ||
+       ((n->dr != n_id) && (old_dr == n_id)) ||
+       ((n->bdr == n_id) && (old_bdr != n_id)) ||
+       ((n->bdr != n_id) && (old_bdr == n_id)))
+      ospf_iface_sm(ifa, ISM_NEICH);
+  }
 }
diff --git a/proto/ospf/hello.h b/proto/ospf/hello.h
deleted file mode 100644 (file)
index 0e47669..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- *      BIRD -- OSPF
- *
- *      (c) 1999 - 2004 Ondrej Filip <feela@network.cz>
- *
- *      Can be freely distributed and used under the terms of the GNU GPL.
- *
- */
-
-#ifndef _BIRD_OSPF_HELLO_H_
-#define _BIRD_OSPF_HELLO_H_
-
-void ospf_hello_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
-                       struct ospf_neighbor *n, ip_addr faddr);
-void ospf_hello_send(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn);
-
-#define OHS_HELLO    0
-#define OHS_POLL     1
-#define OHS_SHUTDOWN 2
-
-#endif /* _BIRD_OSPF_HELLO_H_ */
index 50cf15e2ed69240f0c3dc939c5c8d8d030c93394..5e17371d19749aab761ab2f757a30f6a969f10a2 100644 (file)
@@ -2,12 +2,15 @@
  *     BIRD -- OSPF
  *
  *     (c) 1999--2005 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
 #include "ospf.h"
 
+
 char *ospf_is[] = { "down", "loop", "waiting", "ptp", "drother",
   "backup", "dr"
 };
@@ -18,25 +21,26 @@ char *ospf_ism[] = { "interface up", "wait timer fired", "backup seen",
 
 char *ospf_it[] = { "broadcast", "nbma", "ptp", "ptmp", "virtual link" };
 
+
 static void
 poll_timer_hook(timer * timer)
 {
-  ospf_hello_send(timer->data, OHS_POLL, NULL);
+  ospf_send_hello(timer->data, OHS_POLL, NULL);
 }
 
 static void
 hello_timer_hook(timer * timer)
 {
-  ospf_hello_send(timer->data, OHS_HELLO, NULL);
+  ospf_send_hello(timer->data, OHS_HELLO, NULL);
 }
 
 static void
 wait_timer_hook(timer * timer)
 {
   struct ospf_iface *ifa = (struct ospf_iface *) timer->data;
-  struct proto *p = &ifa->oa->po->proto;
+  struct ospf_proto *p = ifa->oa->po;
 
-  OSPF_TRACE(D_EVENTS, "Wait timer fired on interface %s.", ifa->ifname);
+  OSPF_TRACE(D_EVENTS, "Wait timer fired on interface %s", ifa->ifname);
   ospf_iface_sm(ifa, ISM_WAITF);
 }
 
@@ -58,10 +62,9 @@ ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen)
 {
   plen += SIZE_OF_IP_HEADER;
 
-#ifdef OSPFv2
+  /* This is relevant just for OSPFv2 */
   if (ifa->autype == OSPF_AUTH_CRYPT)
     plen += OSPF_AUTH_CRYPT_SIZE;
-#endif
 
   if (plen <= ifa->sk->tbsize)
     return 0;
@@ -77,12 +80,14 @@ ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen)
 
 
 struct nbma_node *
-find_nbma_node_in(list *nnl, ip_addr ip)
+find_nbma_node_(list *nnl, ip_addr ip)
 {
   struct nbma_node *nn;
+
   WALK_LIST(nn, *nnl)
     if (ipa_equal(nn->ip, ip))
       return nn;
+
   return NULL;
 }
 
@@ -90,7 +95,7 @@ find_nbma_node_in(list *nnl, ip_addr ip)
 static int
 ospf_sk_open(struct ospf_iface *ifa)
 {
-  struct proto_ospf *po = ifa->oa->po;
+  struct ospf_proto *p = ifa->oa->po;
 
   sock *sk = sk_new(ifa->pool);
   sk->type = SK_IP;
@@ -111,24 +116,25 @@ ospf_sk_open(struct ospf_iface *ifa)
   if (sk_open(sk) < 0)
     goto err;
 
-#ifdef OSPFv3
-  /* 12 is an offset of the checksum in an OSPF packet */
-  if (sk_set_ipv6_checksum(sk, 12) < 0)
-    goto err;
-#endif
+  /* 12 is an offset of the checksum in an OSPFv3 packet */
+  if (ospf_is_v3(p))
+    if (sk_set_ipv6_checksum(sk, 12) < 0)
+      goto err;
 
   if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_PTP))
   {
     if (ifa->cf->real_bcast)
     {
       ifa->all_routers = ifa->addr->brd;
+      ifa->des_routers = IPA_NONE;
 
       if (sk_setup_broadcast(sk) < 0)
         goto err;
     }
     else
     {
-      ifa->all_routers = AllSPFRouters;
+      ifa->all_routers = ospf_is_v2(p) ? IP4_OSPF_ALL_ROUTERS : IP6_OSPF_ALL_ROUTERS;
+      ifa->des_routers = ospf_is_v2(p) ? IP4_OSPF_DES_ROUTERS : IP6_OSPF_DES_ROUTERS;
 
       if (sk_setup_multicast(sk) < 0)
         goto err;
@@ -143,7 +149,7 @@ ospf_sk_open(struct ospf_iface *ifa)
   return 1;
 
  err:
-  sk_log_error(sk, po->proto.name);
+  sk_log_error(sk, p->p.name);
   rfree(sk);
   return 0;
 }
@@ -154,8 +160,8 @@ ospf_sk_join_dr(struct ospf_iface *ifa)
   if (ifa->sk_dr)
     return;
 
-  if (sk_join_group(ifa->sk, AllDRouters) < 0)
-    sk_log_error(ifa->sk, ifa->oa->po->proto.name);
+  if (sk_join_group(ifa->sk, ifa->des_routers) < 0)
+    sk_log_error(ifa->sk, ifa->oa->po->p.name);
 
   ifa->sk_dr = 1;
 }
@@ -166,16 +172,16 @@ ospf_sk_leave_dr(struct ospf_iface *ifa)
   if (!ifa->sk_dr)
     return;
 
-  if (sk_leave_group(ifa->sk, AllDRouters) < 0)
-    sk_log_error(ifa->sk, ifa->oa->po->proto.name);
+  if (sk_leave_group(ifa->sk, ifa->des_routers) < 0)
+    sk_log_error(ifa->sk, ifa->oa->po->p.name);
 
   ifa->sk_dr = 0;
 }
 
 void
-ospf_open_vlink_sk(struct proto_ospf *po)
+ospf_open_vlink_sk(struct ospf_proto *p)
 {
-  sock *sk = sk_new(po->proto.pool);
+  sock *sk = sk_new(p->p.pool);
   sk->type = SK_IP;
   sk->dport = OSPF_PROTO;
 
@@ -185,48 +191,48 @@ ospf_open_vlink_sk(struct proto_ospf *po)
   sk->err_hook = ospf_verr_hook;
 
   sk->rbsize = 0;
-  sk->tbsize = OSPF_VLINK_MTU;
-  sk->data = (void *) po;
+  sk->tbsize = ospf_is_v2(p) ? IP4_MIN_MTU : IP6_MIN_MTU;
+  sk->data = (void *) p;
   sk->flags = 0;
 
   if (sk_open(sk) < 0)
     goto err;
 
-#ifdef OSPFv3
-  /* 12 is an offset of the checksum in an OSPF packet */
-  if (sk_set_ipv6_checksum(sk, 12) < 0)
-    goto err;
-#endif
+  /* 12 is an offset of the checksum in an OSPFv3 packet */
+  if (ospf_is_v3(p))
+    if (sk_set_ipv6_checksum(sk, 12) < 0)
+      goto err;
 
-  po->vlink_sk = sk;
+  p->vlink_sk = sk;
   return;
 
  err:
-  sk_log_error(sk, po->proto.name);
-  log(L_ERR "%s: Cannot open virtual link socket", po->proto.name);
+  sk_log_error(sk, p->p.name);
+  log(L_ERR "%s: Cannot open virtual link socket", p->p.name);
   rfree(sk);
 }
 
 static void
 ospf_iface_down(struct ospf_iface *ifa)
 {
+  struct ospf_proto *p = ifa->oa->po;
   struct ospf_neighbor *n, *nx;
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
   struct ospf_iface *iff;
 
   if (ifa->type != OSPF_IT_VLINK)
   {
-#ifdef OSPFv2
-    OSPF_TRACE(D_EVENTS, "Removing interface %s (%I/%d) from area %R",
-              ifa->ifname, ifa->addr->prefix, ifa->addr->pxlen, ifa->oa->areaid);
-#else
-    OSPF_TRACE(D_EVENTS, "Removing interface %s (IID %d) from area %R",
-              ifa->ifname, ifa->instance_id, ifa->oa->areaid);
-#endif
+    if (ospf_is_v3(ifa->oa->po))
+      OSPF_TRACE(D_EVENTS, "Removing interface %s (IID %d) from area %R",
+                ifa->ifname, ifa->instance_id, ifa->oa->areaid);
+    else if (ifa->addr->flags & IA_PEER)
+      OSPF_TRACE(D_EVENTS, "Removing interface %s (peer %I) from area %R",
+                ifa->ifname, ifa->addr->opposite, ifa->oa->areaid);
+    else
+      OSPF_TRACE(D_EVENTS, "Removing interface %s (%I/%d) from area %R",
+                ifa->ifname, ifa->addr->prefix, ifa->addr->pxlen, ifa->oa->areaid);
 
     /* First of all kill all the related vlinks */
-    WALK_LIST(iff, po->iface_list)
+    WALK_LIST(iff, p->iface_list)
     {
       if ((iff->type == OSPF_IT_VLINK) && (iff->vifa == ifa))
        ospf_iface_sm(iff, ISM_DOWN);
@@ -248,6 +254,10 @@ ospf_iface_down(struct ospf_iface *ifa)
   if (ifa->wait_timer)
     tm_stop(ifa->wait_timer);
 
+  ospf_flush2_lsa(p, &ifa->link_lsa);
+  ospf_flush2_lsa(p, &ifa->net_lsa);
+  ospf_flush2_lsa(p, &ifa->pxn_lsa);
+
   if (ifa->type == OSPF_IT_VLINK)
   {
     ifa->vifa = NULL;
@@ -258,17 +268,16 @@ ospf_iface_down(struct ospf_iface *ifa)
 
   ifa->rt_pos_beg = 0;
   ifa->rt_pos_end = 0;
-#ifdef OSPFv3
   ifa->px_pos_beg = 0;
   ifa->px_pos_end = 0;
-#endif
 }
 
 
 void
 ospf_iface_remove(struct ospf_iface *ifa)
 {
-  struct proto *p = &ifa->oa->po->proto;
+  struct ospf_proto *p = ifa->oa->po;
+
   if (ifa->type == OSPF_IT_VLINK)
     OSPF_TRACE(D_EVENTS, "Removing vlink to %R via area %R", ifa->vid, ifa->voa->areaid);
 
@@ -281,7 +290,7 @@ void
 ospf_iface_shutdown(struct ospf_iface *ifa)
 {
   if (ifa->state > OSPF_IS_DOWN)
-    ospf_hello_send(ifa, OHS_SHUTDOWN, NULL);
+    ospf_send_hello(ifa, OHS_SHUTDOWN, NULL);
 }
 
 /**
@@ -296,23 +305,18 @@ ospf_iface_shutdown(struct ospf_iface *ifa)
 void
 ospf_iface_chstate(struct ospf_iface *ifa, u8 state)
 {
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
+  struct ospf_proto *p = ifa->oa->po;
   u8 oldstate = ifa->state;
 
-  if (oldstate == state)
+  if (state == oldstate)
     return;
 
-  ifa->state = state;
+  OSPF_TRACE(D_EVENTS, "Changing state of iface %s from %s to %s",
+            ifa->ifname, ospf_is[oldstate], ospf_is[state]);
 
-  if (ifa->type == OSPF_IT_VLINK)
-    OSPF_TRACE(D_EVENTS, "Changing state of virtual link %R from %s to %s",
-              ifa->vid, ospf_is[oldstate], ospf_is[state]);
-  else
-    OSPF_TRACE(D_EVENTS, "Changing state of iface %s from %s to %s",
-              ifa->ifname, ospf_is[oldstate], ospf_is[state]);
+  ifa->state = state;
 
-  if ((ifa->type == OSPF_IT_BCAST) && !ifa->cf->real_bcast && ifa->sk)
+  if ((ifa->type == OSPF_IT_BCAST) && ipa_nonzero(ifa->des_routers) && ifa->sk)
   {
     if ((state == OSPF_IS_BACKUP) || (state == OSPF_IS_DR))
       ospf_sk_join_dr(ifa);
@@ -320,22 +324,17 @@ ospf_iface_chstate(struct ospf_iface *ifa, u8 state)
       ospf_sk_leave_dr(ifa);
   }
 
-  if ((oldstate == OSPF_IS_DR) && (ifa->net_lsa != NULL))
-  {
-    ifa->net_lsa->lsa.age = LSA_MAXAGE;
-    if (state >= OSPF_IS_WAITING)
-      ospf_lsupd_flush_nlsa(po, ifa->net_lsa);
-
-    if (can_flush_lsa(po))
-      flush_lsa(ifa->net_lsa, po);
-    ifa->net_lsa = NULL;
-  }
-
   if ((oldstate > OSPF_IS_LOOP) && (state <= OSPF_IS_LOOP))
     ospf_iface_down(ifa);
 
-  schedule_rt_lsa(ifa->oa);
-  // FIXME flushling of link LSA
+  /* RFC 2328 12.4 Event 2 - iface state change */
+  ospf_notify_rt_lsa(ifa->oa);
+
+  /* RFC 5340 4.4.3 Event 1 - iface state change */
+  ospf_notify_link_lsa(ifa);
+
+  /* RFC 2328 12.4 Event 3 - iface enters/leaves DR state */
+  ospf_notify_net_lsa(ifa);
 }
 
 /**
@@ -352,7 +351,7 @@ ospf_iface_chstate(struct ospf_iface *ifa, u8 state)
 void
 ospf_iface_sm(struct ospf_iface *ifa, int event)
 {
-  DBG("SM on iface %s. Event is '%s'\n", ifa->ifname, ospf_ism[event]);
+  DBG("SM on %s. Event is '%s'\n", ifa->ifname, ospf_ism[event]);
 
   switch (event)
   {
@@ -360,7 +359,9 @@ ospf_iface_sm(struct ospf_iface *ifa, int event)
     if (ifa->state <= OSPF_IS_LOOP)
     {
       /* Now, nothing should be adjacent */
-      if ((ifa->type == OSPF_IT_PTP) || (ifa->type == OSPF_IT_PTMP) || (ifa->type == OSPF_IT_VLINK))
+      if ((ifa->type == OSPF_IT_PTP) ||
+         (ifa->type == OSPF_IT_PTMP) ||
+         (ifa->type == OSPF_IT_VLINK))
       {
        ospf_iface_chstate(ifa, OSPF_IS_PTP);
       }
@@ -382,26 +383,19 @@ ospf_iface_sm(struct ospf_iface *ifa, int event)
       if (ifa->poll_timer)
        tm_start(ifa->poll_timer, ifa->pollint);
 
-      ospf_hello_send(ifa, OHS_HELLO, NULL);
-      schedule_link_lsa(ifa);
+      ospf_send_hello(ifa, OHS_HELLO, NULL);
     }
     break;
 
   case ISM_BACKS:
   case ISM_WAITF:
     if (ifa->state == OSPF_IS_WAITING)
-    {
-      bdr_election(ifa);
-    }
+      ospf_dr_election(ifa);
     break;
 
   case ISM_NEICH:
-    if ((ifa->state == OSPF_IS_DROTHER) || (ifa->state == OSPF_IS_DR) ||
-       (ifa->state == OSPF_IS_BACKUP))
-    {
-      bdr_election(ifa);
-      schedule_rt_lsa(ifa->oa);
-    }
+    if (ifa->state >= OSPF_IS_DROTHER)
+      ospf_dr_election(ifa);
     break;
 
   case ISM_LOOP:
@@ -427,7 +421,7 @@ ospf_iface_sm(struct ospf_iface *ifa, int event)
 }
 
 static u8
-ospf_iface_classify_int(struct iface *ifa, struct ifa *addr)
+ospf_iface_classify_(struct iface *ifa, struct ifa *addr)
 {
   if (ipa_nonzero(addr->opposite))
     return (ifa->flags & IF_MULTICAST) ? OSPF_IT_PTP :  OSPF_IT_PTMP;
@@ -445,17 +439,19 @@ ospf_iface_classify_int(struct iface *ifa, struct ifa *addr)
 static inline u8
 ospf_iface_classify(u8 type, struct ifa *addr)
 {
-  return (type != OSPF_IT_UNDEF) ? type : ospf_iface_classify_int(addr->iface, addr);
+  return (type != OSPF_IT_UNDEF) ? type : ospf_iface_classify_(addr->iface, addr);
 }
 
 
 struct ospf_iface *
-ospf_iface_find(struct proto_ospf *p, struct iface *what)
+ospf_iface_find(struct ospf_proto *p, struct iface *what)
 {
-  struct ospf_iface *i;
+  struct ospf_iface *ifa;
+
+  WALK_LIST(ifa, p->iface_list)
+    if ((ifa->iface == what) && (ifa->type != OSPF_IT_VLINK))
+      return ifa;
 
-  WALK_LIST(i, p->iface_list) if ((i->iface == what) && (i->type != OSPF_IT_VLINK))
-    return i;
   return NULL;
 }
 
@@ -463,13 +459,12 @@ static void
 ospf_iface_add(struct object_lock *lock)
 {
   struct ospf_iface *ifa = lock->data;
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
+  struct ospf_proto *p = ifa->oa->po;
 
   /* Open socket if interface is not stub */
   if (! ifa->stub && ! ospf_sk_open(ifa))
   {
-    log(L_ERR "%s: Cannot open socket for %s, declaring as stub", p->name, ifa->ifname);
+    log(L_ERR "%s: Cannot open socket for %s, declaring as stub", p->p.name, ifa->ifname);
     ifa->ioprob = OSPF_I_SK;
     ifa->stub = 1;
   }
@@ -525,20 +520,22 @@ ospf_iface_stubby(struct ospf_iface_patt *ip, struct ifa *addr)
 void
 ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *ip)
 {
-  struct proto *p = &oa->po->proto;
+  struct ospf_proto *p = oa->po;
   struct iface *iface = addr->iface;
   struct ospf_iface *ifa;
   struct pool *pool;
 
-#ifdef OSPFv2
-  OSPF_TRACE(D_EVENTS, "Adding interface %s (%I/%d) to area %R",
-            iface->name, addr->prefix, addr->pxlen, oa->areaid);
-#else
-  OSPF_TRACE(D_EVENTS, "Adding interface %s (IID %d) to area %R",
-            iface->name, ip->instance_id, oa->areaid);
-#endif
+  if (ospf_is_v3(p))
+    OSPF_TRACE(D_EVENTS, "Adding interface %s (IID %d) to area %R",
+              iface->name, ip->instance_id, oa->areaid);
+  else if (addr->flags & IA_PEER)
+    OSPF_TRACE(D_EVENTS, "Adding interface %s (peer %I) to area %R",
+              iface->name, addr->opposite, oa->areaid);
+  else
+    OSPF_TRACE(D_EVENTS, "Adding interface %s (%I/%d) to area %R",
+              iface->name, addr->prefix, addr->pxlen, oa->areaid);
 
-  pool = rp_new(p->pool, "OSPF Interface");
+  pool = rp_new(p->p.pool, "OSPF Interface");
   ifa = mb_allocz(pool, sizeof(struct ospf_iface));
   ifa->iface = iface;
   ifa->addr = addr;
@@ -560,24 +557,18 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
   ifa->deadint = ip->deadint;
   ifa->stub = ospf_iface_stubby(ip, addr);
   ifa->ioprob = OSPF_I_OK;
-
   ifa->tx_length = ifa_tx_length(ifa);
   ifa->check_link = ip->check_link;
   ifa->ecmp_weight = ip->ecmp_weight;
   ifa->check_ttl = (ip->ttl_security == 1);
   ifa->bfd = ip->bfd;
-
-#ifdef OSPFv2
   ifa->autype = ip->autype;
   ifa->passwords = ip->passwords;
+  ifa->instance_id = ip->instance_id;
+
   ifa->ptp_netmask = !(addr->flags & IA_PEER);
   if (ip->ptp_netmask < 2)
     ifa->ptp_netmask = ip->ptp_netmask;
-#endif
-
-#ifdef OSPFv3
-  ifa->instance_id = ip->instance_id;
-#endif
 
 
   ifa->type = ospf_iface_classify(ip->type, addr);
@@ -586,13 +577,11 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
   int old_type = ifa->type;
   u32 if_multi_flag = ip->real_bcast ? IF_BROADCAST : IF_MULTICAST;
 
-#ifdef OSPFv2
-  if ((ifa->type == OSPF_IT_BCAST) && (addr->flags & IA_PEER))
+  if (ospf_is_v2(p) && (ifa->type == OSPF_IT_BCAST) && (addr->flags & IA_PEER))
     ifa->type = OSPF_IT_PTP;
 
-  if ((ifa->type == OSPF_IT_NBMA) && (addr->flags & IA_PEER))
+  if (ospf_is_v2(p) && (ifa->type == OSPF_IT_NBMA) && (addr->flags & IA_PEER))
     ifa->type = OSPF_IT_PTMP;
-#endif
 
   if ((ifa->type == OSPF_IT_BCAST) && !(iface->flags & if_multi_flag))
     ifa->type = OSPF_IT_NBMA;
@@ -602,8 +591,11 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
 
   if (ifa->type != old_type)
     log(L_WARN "%s: Cannot use interface %s as %s, forcing %s",
-       p->name, iface->name, ospf_it[old_type], ospf_it[ifa->type]);
+       p->p.name, iface->name, ospf_it[old_type], ospf_it[ifa->type]);
+
 
+  if ((ifa->type == OSPF_IT_PTP) || (ifa->type == OSPF_IT_PTMP))
+    ifa->link_lsa_suppression = ip->link_lsa_suppression;
 
   ifa->state = OSPF_IS_DOWN;
   init_list(&ifa->neigh_list);
@@ -617,13 +609,11 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
        should be used). Because OSPFv3 iface is not subnet-specific,
        there is no need for ipa_in_net() check */
 
-#ifdef OSPFv2
-    if (!ipa_in_net(nb->ip, addr->prefix, addr->pxlen))
+    if (ospf_is_v2(p) && !ipa_in_net(nb->ip, addr->prefix, addr->pxlen))
       continue;
-#else
-    if (!ipa_has_link_scope(nb->ip))
+
+    if (ospf_is_v3(p) && !ipa_is_link_local(nb->ip))
       log(L_WARN "In OSPFv3, configured neighbor address (%I) should be link-local", nb->ip);
-#endif
 
     add_nbma_node(ifa, nb, 0);
   }
@@ -637,12 +627,9 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
    * Therefore, we store such info to lock->addr field.
    */
 
+  // XXXX review
   struct object_lock *lock = olock_new(pool);
-#ifdef OSPFv2
-  lock->addr = ifa->addr->prefix;
-#else /* OSPFv3 */
-  lock->addr = _MI(0,0,0,ifa->instance_id);
-#endif
+  lock->addr = ospf_is_v2(p) ? ifa->addr->prefix : _MI6(0,0,0,ifa->instance_id);
   lock->type = OBJLOCK_IP;
   lock->port = OSPF_PROTO;
   lock->iface = iface;
@@ -653,50 +640,43 @@ ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *i
 }
 
 void
-ospf_iface_new_vlink(struct proto_ospf *po, struct ospf_iface_patt *ip)
+ospf_iface_new_vlink(struct ospf_proto *p, struct ospf_iface_patt *ip)
 {
-  struct proto *p = &po->proto;
   struct ospf_iface *ifa;
   struct pool *pool;
 
-  if (!po->vlink_sk)
+  if (!p->vlink_sk)
     return;
 
   OSPF_TRACE(D_EVENTS, "Adding vlink to %R via area %R", ip->vid, ip->voa);
 
   /* Vlink ifname is stored just after the ospf_iface structure */
 
-  pool = rp_new(p->pool, "OSPF Vlink");
+  pool = rp_new(p->p.pool, "OSPF Vlink");
   ifa = mb_allocz(pool, sizeof(struct ospf_iface) + 16);
-  ifa->oa = po->backbone;
+  ifa->oa = p->backbone;
   ifa->cf = ip;
   ifa->pool = pool;
 
   /* Assign iface ID, for vlinks, this is ugly hack */
-  u32 vlink_id = po->last_vlink_id++;
+  u32 vlink_id = p->last_vlink_id++;
   ifa->iface_id = vlink_id + OSPF_VLINK_ID_OFFSET;
   ifa->ifname = (void *) (ifa + 1);
   bsprintf(ifa->ifname, "vlink%d", vlink_id);
 
-  ifa->voa = ospf_find_area(po, ip->voa);
+  ifa->voa = ospf_find_area(p, ip->voa);
   ifa->vid = ip->vid;
-  ifa->sk = po->vlink_sk;
+  ifa->sk = p->vlink_sk;
 
   ifa->helloint = ip->helloint;
   ifa->rxmtint = ip->rxmtint;
   ifa->waitint = ip->waitint;
   ifa->deadint = ip->deadint;
   ifa->inftransdelay = ip->inftransdelay;
-  ifa->tx_length = OSPF_VLINK_MTU;
-
-#ifdef OSPFv2
+  ifa->tx_length = ospf_is_v2(p) ? IP4_MIN_MTU : IP6_MIN_MTU;
   ifa->autype = ip->autype;
   ifa->passwords = ip->passwords;
-#endif
-
-#ifdef OSPFv3
   ifa->instance_id = ip->instance_id;
-#endif
 
   ifa->type = OSPF_IT_VLINK;
 
@@ -704,13 +684,13 @@ ospf_iface_new_vlink(struct proto_ospf *po, struct ospf_iface_patt *ip)
   init_list(&ifa->neigh_list);
   init_list(&ifa->nbma_list);
 
-  add_tail(&po->iface_list, NODE ifa);
+  add_tail(&p->iface_list, NODE ifa);
 
   ifa->hello_timer = tm_new_set(ifa->pool, hello_timer_hook, ifa, 0, ifa->helloint);
 }
 
 static void
-ospf_iface_change_timer(timer *tm, unsigned val)
+ospf_iface_change_timer(timer *tm, uint val)
 {
   if (!tm)
     return;
@@ -724,13 +704,13 @@ ospf_iface_change_timer(timer *tm, unsigned val)
 int
 ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
 {
-  struct proto *p = &ifa->oa->po->proto;
+  struct ospf_proto *p = ifa->oa->po;
   struct ospf_iface_patt *old = ifa->cf;
   char *ifname = ifa->ifname;
 
   /* Type could be changed in ospf_iface_new(),
      but if config values are same then also results are same */
-  int old_type = ospf_iface_classify(old->type, ifa->addr);
+  int old_type = ospf_iface_classify(old->type, ifa->addr);  
   int new_type = ospf_iface_classify(new->type, ifa->addr);
   if (old_type != new_type)
     return 0;
@@ -806,7 +786,6 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
     ifa->inftransdelay = new->inftransdelay;
   }
 
-#ifdef OSPFv2  
   /* AUTHENTICATION */
   if (ifa->autype != new->autype)
   {
@@ -816,7 +795,6 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
 
   /* Update passwords */
   ifa->passwords = new->passwords;
-#endif
 
   /* Remaining options are just for proper interfaces */
   if (ifa->type == OSPF_IT_VLINK)
@@ -837,7 +815,9 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
   {
     OSPF_TRACE(D_EVENTS, "Changing priority on interface %s from %d to %d",
               ifname, ifa->priority, new->priority);
+
     ifa->priority = new->priority;
+    ospf_notify_link_lsa(ifa);
   }
 
   /* STRICT NBMA */
@@ -852,7 +832,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
   /* NBMA LIST - remove or update old */
   WALK_LIST_DELSAFE(nb, nbx, ifa->nbma_list)
   {
-    struct nbma_node *nb2 = find_nbma_node_in(&new->nbma_list, nb->ip);
+    struct nbma_node *nb2 = find_nbma_node_(&new->nbma_list, nb->ip);
     if (nb2)
     {
       if (nb->eligible != nb2->eligible)
@@ -875,13 +855,11 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
   WALK_LIST(nb, new->nbma_list)
   {
     /* See related note in ospf_iface_new() */
-#ifdef OSPFv2
-    if (!ipa_in_net(nb->ip, ifa->addr->prefix, ifa->addr->pxlen))
+    if (ospf_is_v2(p) && !ipa_in_net(nb->ip, ifa->addr->prefix, ifa->addr->pxlen))
       continue;
-#else
-    if (!ipa_has_link_scope(nb->ip))
+
+    if (ospf_is_v3(p) && !ipa_is_link_local(nb->ip))
       log(L_WARN "In OSPFv3, configured neighbor address (%I) should be link-local", nb->ip);
-#endif
 
     if (! find_nbma_node(ifa, nb->ip))
     {
@@ -942,6 +920,17 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
     ifa->ecmp_weight = new->ecmp_weight;
   }
 
+  /* Link LSA suppression */
+  if (((ifa->type == OSPF_IT_PTP) || (ifa->type == OSPF_IT_PTMP)) &&
+      (ifa->link_lsa_suppression != new->link_lsa_suppression))
+  {
+    OSPF_TRACE(D_EVENTS, "Changing link LSA suppression of %s from %d to %d",
+              ifname, ifa->link_lsa_suppression, new->link_lsa_suppression);
+
+    ifa->link_lsa_suppression = new->link_lsa_suppression;
+    ospf_notify_link_lsa(ifa);
+  }
+
   /* BFD */
   if (ifa->bfd != new->bfd)
   {
@@ -961,131 +950,119 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
 }
 
 
-#ifdef OSPFv2
+/*
+ * State for matching iface pattterns walk
+ *
+ * This is significantly different in OSPFv2 and OSPFv3.
+ * In OSPFv2, OSPF ifaces are created for each IP prefix (struct ifa)
+ * In OSPFv3, OSPF ifaces are created based on real iface (struct iface)
+ * We support instance_id for both OSPFv2 (RFC 6549) and OSPFv3.
+ *
+ * We process one ifa/iface and match it for all configured instance IDs. We
+ * maintain bitfields to track whether given instance ID was already matched.
+ * We have two bitfields, one global (active) and one per area (ignore), to
+ * detect misconfigured cases where one iface with one instance ID matches in
+ * multiple areas.
+ */
 
-static inline struct ospf_iface_patt *
-ospf_iface_patt_find(struct ospf_area_config *ac, struct ifa *a)
-{
-  return (struct ospf_iface_patt *) iface_patt_find(&ac->patt_list, a->iface, a);
-}
+struct ospf_mip_walk {
+  u32 active[8];               /* Bitfield of active instance IDs */
+  u32 ignore[8];               /* Bitfield of instance IDs matched in current area */
+  struct ospf_area *oa;                /* Current area */
+  struct ospf_iface_patt *ip;  /* Current iface pattern */
+  struct iface *iface;         /* Specified iface (input) */
+  struct ifa *a;               /* Specified ifa (input) */
+  int warn;                    /* Whether iface matched in multiple areas */
+};
 
-void
-ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a)
+static int
+ospf_walk_matching_iface_patts(struct ospf_proto *p, struct ospf_mip_walk *s)
 {
-  struct proto_ospf *po = (struct proto_ospf *) p;
-
-  if (a->flags & IA_SECONDARY)
-    return;
+  int id;
 
-  if (a->scope <= SCOPE_LINK)
-    return;
+  if (s->ip)
+    goto step;
 
-  /* In OSPFv2, we create OSPF iface for each address. */
-  if (flags & IF_CHANGE_UP)
+  WALK_LIST(s->oa, p->area_list)
   {
-    int done = 0;
-    struct ospf_area *oa;
-    WALK_LIST(oa, po->area_list)
+    WALK_LIST(s->ip, s->oa->ac->patt_list)
     {
-      struct ospf_iface_patt *ip;
-      if (ip = ospf_iface_patt_find(oa->ac, a))
+      id = s->ip->instance_id;
+      if (BIT32_TEST(s->ignore, id))
+       continue;
+
+      if (iface_patt_match(&s->ip->i, s->iface, s->a))
       {
-       if (!done)
-         ospf_iface_new(oa, a, ip);
-       done++;
-      }
-    }
+       /* Now we matched ifa/iface/instance_id for the first time in current area */
+       BIT32_SET(s->ignore, id);
 
-    if (done > 1)
-      log(L_WARN "%s: Interface %s (IP %I) matches for multiple areas", p->name,  a->iface->name, a->ip);
-  }
+       /* If we already found it in previous areas, ignore it and add warning */
+       if (!BIT32_TEST(s->active, id))
+         { s->warn = 1; continue; }
 
-  if (flags & IF_CHANGE_DOWN)
-  {
-    struct ospf_iface *ifa, *ifx;
-    WALK_LIST_DELSAFE(ifa, ifx, po->iface_list)
-    {
-      if ((ifa->type != OSPF_IT_VLINK) && (ifa->addr == a))
-       ospf_iface_remove(ifa);
-      /* See a note in ospf_iface_notify() */
+       BIT32_SET(s->active, id);
+       return 1;
+      step:
+       ;
+      }
     }
+    BIT32_ZERO(s->ignore, 256);
   }
+
+  if (s->warn)
+    log(L_WARN "%s: Interface %s matches for multiple areas", p->p.name, s->iface->name);
+
+  return 0;
 }
 
+
 static struct ospf_iface *
-ospf_iface_find_by_key(struct ospf_area *oa, struct ifa *a)
+ospf_iface_find_by_key(struct ospf_proto *p, struct ifa *a, int instance_id)
 {
   struct ospf_iface *ifa;
-  WALK_LIST(ifa, oa->po->iface_list)
-    if ((ifa->addr == a) && (ifa->oa == oa) && (ifa->type != OSPF_IT_VLINK))
+
+  WALK_LIST(ifa, p->iface_list)
+    if ((ifa->addr == a) && (ifa->instance_id == instance_id) &&
+       (ifa->type != OSPF_IT_VLINK))
       return ifa;
 
   return NULL;
 }
 
+
 void
-ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
+ospf_ifa_notify2(struct proto *P, uint flags, struct ifa *a)
 {
-  struct proto *p = &oa->po->proto;
-  struct ospf_iface_patt *ip;
-  struct iface *iface;
-  struct ifa *a;
-
-  WALK_LIST(iface, iface_list)
-  {
-    if (! (iface->flags & IF_UP))
-      continue;
+  struct ospf_proto *p = (struct ospf_proto *) P;
 
-    WALK_LIST(a, iface->addrs)
-    {
-      if (a->flags & IA_SECONDARY)
-       continue;
-
-      if (a->scope <= SCOPE_LINK)
-       continue;
+  if (a->flags & IA_SECONDARY)
+    return;
 
-      if (ip = ospf_iface_patt_find(oa->ac, a))
-      {
-       /* Main inner loop */
-       struct ospf_iface *ifa = ospf_iface_find_by_key(oa, a);
-       if (ifa)
-       {
-         if (ospf_iface_reconfigure(ifa, ip))
-           continue;
+  if (a->scope <= SCOPE_LINK)
+    return;
 
-         /* Hard restart */
-         log(L_INFO "%s: Restarting interface %s (%I/%d) in area %R",
-             p->name, ifa->ifname, a->prefix, a->pxlen, oa->areaid);
-         ospf_iface_shutdown(ifa);
-         ospf_iface_remove(ifa);
-       }
-       
-       ospf_iface_new(oa, a, ip);
-      }
-    }
+  /* In OSPFv2, we create OSPF iface for each address. */
+  if (flags & IF_CHANGE_UP)
+  {
+    struct ospf_mip_walk s = { .iface = a->iface, .a = a };
+    while (ospf_walk_matching_iface_patts(p, &s))
+      ospf_iface_new(s.oa, s.a, s.ip);
   }
-}
-
-
-#else /* OSPFv3 */
-
-struct ospf_iface_patt *
-ospf_iface_patt_find(struct ospf_area_config *ac, struct iface *iface, int iid)
-{
-  struct ospf_iface_patt *pt, *res = NULL;
-
-  WALK_LIST(pt, ac->patt_list)
-    if ((pt->instance_id >= iid) && (iface_patt_match(&pt->i, iface, NULL)) &&
-       (!res || (pt->instance_id < res->instance_id)))
-      res = pt;
 
-  return res;
+  if (flags & IF_CHANGE_DOWN)
+  {
+    struct ospf_iface *ifa, *ifx;
+    WALK_LIST_DELSAFE(ifa, ifx, p->iface_list)
+      if ((ifa->type != OSPF_IT_VLINK) && (ifa->addr == a))
+       ospf_iface_remove(ifa);
+    /* See a note in ospf_iface_notify() */
+  }
 }
 
 void
-ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a)
+ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a)
 {
-  struct proto_ospf *po = (struct proto_ospf *) p;
+  struct ospf_proto *p = (struct ospf_proto *) P;
 
   if (a->flags & IA_SECONDARY)
     return;
@@ -1099,71 +1076,79 @@ ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a)
   {
     if (flags & IF_CHANGE_UP)
     {
-      int done0 = 0;
-      struct ospf_area *oa;
-
-      WALK_LIST(oa, po->area_list)
-      {
-       int iid = 0;
-
-       struct ospf_iface_patt *ip;
-       while (ip = ospf_iface_patt_find(oa->ac, a->iface, iid))
-       {
-         ospf_iface_new(oa, a, ip);
-         if (ip->instance_id == 0)
-           done0++;
-         iid = ip->instance_id + 1;
-       }
-      }
-
-      if (done0 > 1)
-       log(L_WARN "%s: Interface %s matches for multiple areas",
-           p->name,  a->iface->name);
+      struct ospf_mip_walk s = { .iface = a->iface };
+      while (ospf_walk_matching_iface_patts(p, &s))
+       ospf_iface_new(s.oa, s.a, s.ip);
     }
 
     if (flags & IF_CHANGE_DOWN)
     {
       struct ospf_iface *ifa, *ifx;
-      WALK_LIST_DELSAFE(ifa, ifx, po->iface_list)
-      {
-       if ((ifa->type != OSPF_IT_VLINK) && (ifa->addr == a))
+      WALK_LIST_DELSAFE(ifa, ifx, p->iface_list)
+       if ((ifa->addr == a) && (ifa->type != OSPF_IT_VLINK))
          ospf_iface_remove(ifa);
-       /* See a note in ospf_iface_notify() */
-      }
     }
   }
   else
   {
     struct ospf_iface *ifa;
-    WALK_LIST(ifa, po->iface_list)
-    {
+    WALK_LIST(ifa, p->iface_list)
       if (ifa->iface == a->iface)
       {
-       schedule_rt_lsa(ifa->oa);
-       /* Event 5 from RFC5340 4.4.3. */
-       schedule_link_lsa(ifa);
-       return;
+       /* RFC 5340 4.4.3 Event 5 - prefix added/deleted */
+       ospf_notify_link_lsa(ifa);
+       ospf_notify_rt_lsa(ifa->oa);
       }
-    }
   }
 }
 
-static struct ospf_iface *
-ospf_iface_find_by_key(struct ospf_area *oa, struct ifa *a, int iid)
+
+static void
+ospf_reconfigure_ifaces2(struct ospf_proto *p)
 {
-  struct ospf_iface *ifa;
-  WALK_LIST(ifa, oa->po->iface_list)
-    if ((ifa->addr == a) && (ifa->oa == oa) && (ifa->instance_id == iid) && (ifa->type != OSPF_IT_VLINK))
-      return ifa;
+  struct iface *iface;
+  struct ifa *a;
 
-  return NULL;
+  WALK_LIST(iface, iface_list)
+  {
+    if (! (iface->flags & IF_UP))
+      continue;
+
+    WALK_LIST(a, iface->addrs)
+    {
+      if (a->flags & IA_SECONDARY)
+       continue;
+
+      if (a->scope <= SCOPE_LINK)
+       continue;
+
+      struct ospf_mip_walk s = { .iface = iface, .a = a };
+      while (ospf_walk_matching_iface_patts(p, &s))
+      {
+       /* Main inner loop */
+       struct ospf_iface *ifa = ospf_iface_find_by_key(p, a, s.ip->instance_id);
+       if (ifa)
+       {
+         if ((ifa->oa == s.oa) && (ifa->marked < 2) &&
+             ospf_iface_reconfigure(ifa, s.ip))
+           continue;
+
+         /* Hard restart */
+         log(L_INFO "%s: Restarting interface %s (%I/%d) in area %R",
+             p->p.name, ifa->ifname, a->prefix, a->pxlen, s.oa->areaid);
+         ospf_iface_shutdown(ifa);
+         ospf_iface_remove(ifa);
+       }
+       
+       ospf_iface_new(s.oa, a, s.ip);
+      }
+    }
+  }
 }
 
-void
-ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
+static void
+ospf_reconfigure_ifaces3(struct ospf_proto *p)
 {
-  struct proto *p = &oa->po->proto;
-  struct ospf_iface_patt *ip;
   struct iface *iface;
   struct ifa *a;
 
@@ -1180,38 +1165,43 @@ ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
       if (a->scope != SCOPE_LINK)
        continue;
 
-      int iid = 0;
-      while (ip = ospf_iface_patt_find(nac, iface, iid))
+      struct ospf_mip_walk s = { .iface = iface };
+      while (ospf_walk_matching_iface_patts(p, &s))
       {
-       iid = ip->instance_id + 1;
-
        /* Main inner loop */
-       struct ospf_iface *ifa = ospf_iface_find_by_key(oa, a, ip->instance_id);
+       struct ospf_iface *ifa = ospf_iface_find_by_key(p, a, s.ip->instance_id);
        if (ifa)
        {
-         if (ospf_iface_reconfigure(ifa, ip))
+         if ((ifa->oa == s.oa) && (ifa->marked < 2) &&
+             ospf_iface_reconfigure(ifa, s.ip))
            continue;
 
          /* Hard restart */
          log(L_INFO "%s: Restarting interface %s (IID %d) in area %R",
-             p->name, ifa->ifname, ifa->instance_id, oa->areaid);
+             p->p.name, ifa->ifname, ifa->instance_id, s.oa->areaid);
          ospf_iface_shutdown(ifa);
          ospf_iface_remove(ifa);
        }
 
-       ospf_iface_new(oa, a, ip);
+       ospf_iface_new(s.oa, a, s.ip);
       }
     }
   }
 }
 
-#endif
+void
+ospf_reconfigure_ifaces(struct ospf_proto *p)
+{
+  if (ospf_is_v2(p))
+    ospf_reconfigure_ifaces2(p);
+  else
+    ospf_reconfigure_ifaces3(p);
+}
+
 
 static void
-ospf_iface_change_mtu(struct proto_ospf *po, struct ospf_iface *ifa)
+ospf_iface_change_mtu(struct ospf_proto *p, struct ospf_iface *ifa)
 {
-  struct proto *p = &po->proto;
-
   /* ifa is not vlink */
 
   OSPF_TRACE(D_EVENTS, "Changing MTU on interface %s", ifa->ifname);
@@ -1230,7 +1220,7 @@ ospf_iface_change_mtu(struct proto_ospf *po, struct ospf_iface *ifa)
 }
 
 static void
-ospf_iface_notify(struct proto_ospf *po, unsigned flags, struct ospf_iface *ifa)
+ospf_iface_notify(struct ospf_proto *p, uint flags, struct ospf_iface *ifa)
 {
   /* ifa is not vlink */
 
@@ -1244,13 +1234,13 @@ ospf_iface_notify(struct proto_ospf *po, unsigned flags, struct ospf_iface *ifa)
     ospf_iface_sm(ifa, (ifa->iface->flags & IF_LINK_UP) ? ISM_UNLOOP : ISM_LOOP);
 
   if (flags & IF_CHANGE_MTU)
-    ospf_iface_change_mtu(po, ifa);
+    ospf_iface_change_mtu(p, ifa);
 }
 
 void
-ospf_if_notify(struct proto *p, unsigned flags, struct iface *iface)
+ospf_if_notify(struct proto *P, uint flags, struct iface *iface)
 {
-  struct proto_ospf *po = (struct proto_ospf *) p;
+  struct ospf_proto *p = (struct ospf_proto *) P;
 
   /*
   if (iface->flags & IF_IGNORE)
@@ -1262,9 +1252,9 @@ ospf_if_notify(struct proto *p, unsigned flags, struct iface *iface)
     return;
 
   struct ospf_iface *ifa, *ifx;
-  WALK_LIST_DELSAFE(ifa, ifx, po->iface_list)
+  WALK_LIST_DELSAFE(ifa, ifx, p->iface_list)
     if (ifa->iface == iface)
-      ospf_iface_notify(po, flags, ifa);
+      ospf_iface_notify(p, flags, ifa);
 
   /* We use here that even shutting down iface also shuts down
      the vlinks, but vlinks are not freed and stays in the
@@ -1286,20 +1276,19 @@ ospf_iface_info(struct ospf_iface *ifa)
 
   if (ifa->type == OSPF_IT_VLINK)
   {
-    cli_msg(-1015, "Virtual link %s to %R:", ifa->ifname, ifa->vid);
+    cli_msg(-1015, "Virtual link %s to %R", ifa->ifname, ifa->vid);
     cli_msg(-1015, "\tPeer IP: %I", ifa->vip);
     cli_msg(-1015, "\tTransit area: %R (%u)", ifa->voa->areaid, ifa->voa->areaid);
   }
   else
   {
-#ifdef OSPFv2
-    if (ifa->addr->flags & IA_PEER)
+    if (ospf_is_v3(ifa->oa->po))
+      cli_msg(-1015, "Interface %s (IID %d)", ifa->ifname, ifa->instance_id);
+    else if (ifa->addr->flags & IA_PEER)
       cli_msg(-1015, "Interface %s (peer %I)", ifa->ifname, ifa->addr->opposite);
     else
       cli_msg(-1015, "Interface %s (%I/%d)", ifa->ifname, ifa->addr->prefix, ifa->addr->pxlen);
-#else /* OSPFv3 */
-    cli_msg(-1015, "Interface %s (IID %d)", ifa->ifname, ifa->instance_id);
-#endif
+
     cli_msg(-1015, "\tType: %s%s", ospf_it[ifa->type], more);
     cli_msg(-1015, "\tArea: %R (%u)", ifa->oa->areaid, ifa->oa->areaid);
   }
diff --git a/proto/ospf/iface.h b/proto/ospf/iface.h
deleted file mode 100644 (file)
index 5a250e0..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *      BIRD -- OSPF
- *
- *      (c) 1999--2005 Ondrej Filip <feela@network.cz>
- *
- *      Can be freely distributed and used under the terms of the GNU GPL.
- *
- */
-
-#ifndef _BIRD_OSPF_IFACE_H_
-#define _BIRD_OSPF_IFACE_H_
-
-void ospf_iface_chstate(struct ospf_iface *ifa, u8 state);
-void ospf_iface_sm(struct ospf_iface *ifa, int event);
-struct ospf_iface *ospf_iface_find(struct proto_ospf *p, struct iface *what);
-void ospf_if_notify(struct proto *p, unsigned flags, struct iface *iface);
-void ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a);
-void ospf_iface_info(struct ospf_iface *ifa);
-void ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *ip);
-void ospf_iface_new_vlink(struct proto_ospf *po, struct ospf_iface_patt *ip);
-void ospf_iface_remove(struct ospf_iface *ifa);
-void ospf_iface_shutdown(struct ospf_iface *ifa);
-int ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new);
-void ospf_ifaces_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac);
-
-int ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen);
-
-void ospf_open_vlink_sk(struct proto_ospf *po);
-
-struct nbma_node *find_nbma_node_in(list *nnl, ip_addr ip);
-
-static inline struct nbma_node *
-find_nbma_node(struct ospf_iface *ifa, ip_addr ip)
-{ return find_nbma_node_in(&ifa->nbma_list, ip); }
-
-#endif /* _BIRD_OSPF_IFACE_H_ */
index fd8ead01084133e243b62444d752ca044bcac8cd..aefddfb8f41f077b84748c960f10df5048459916 100644 (file)
@@ -1,7 +1,9 @@
 /*
  *     BIRD -- OSPF
  *
- *     (c) 2000-2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2000--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 #include "ospf.h"
 
 
+/*
 struct ospf_lsack_packet
 {
-  struct ospf_packet ospf_packet;
-  struct ospf_lsa_header lsh[];
-};
+  struct ospf_packet hdr;
+  // union ospf_auth auth;
 
+  struct ospf_lsa_header lsas[];
+};
+*/
 
-char *s_queue[] = { "direct", "delayed" };
+struct lsa_node
+{
+  node n;
+  struct ospf_lsa_header lsa;
+};
 
 
-static void ospf_dump_lsack(struct proto *p, struct ospf_lsack_packet *pkt)
+static inline void
+ospf_lsack_body(struct ospf_proto *p, struct ospf_packet *pkt,
+               struct ospf_lsa_header **body, uint *count)
 {
-  struct ospf_packet *op = &pkt->ospf_packet;
+  uint plen = ntohs(pkt->length);
+  uint hlen = ospf_pkt_hdrlen(p);
 
-  ASSERT(op->type == LSACK_P);
-  ospf_dump_common(p, op);
+  *body = ((void *) pkt) + hlen;
+  *count = (plen - hlen) / sizeof(struct ospf_lsa_header);
+}
 
-  unsigned int i, j;
-  j = (ntohs(op->length) - sizeof(struct ospf_lsack_packet)) /
-    sizeof(struct ospf_lsa_header);
+static void
+ospf_dump_lsack(struct ospf_proto *p, struct ospf_packet *pkt)
+{
+  struct ospf_lsa_header *lsas;
+  uint i, lsa_count;
 
-  for (i = 0; i < j; i++)
-    ospf_dump_lsahdr(p, pkt->lsh + i);
-}
+  ASSERT(pkt->type == LSACK_P);
+  ospf_dump_common(p, pkt);
 
+  ospf_lsack_body(p, pkt, &lsas, &lsa_count);
+  for (i = 0; i < lsa_count; i++)
+    ospf_dump_lsahdr(p, lsas + i);
+}
 
-/*
- * =====================================
- * Note, that h is in network endianity!
- * =====================================
- */
 
 void
-ospf_lsack_enqueue(struct ospf_neighbor *n, struct ospf_lsa_header *h,
-                  int queue)
+ospf_enqueue_lsack(struct ospf_neighbor *n, struct ospf_lsa_header *h_n, int queue)
 {
-  struct lsah_n *no = mb_alloc(n->pool, sizeof(struct lsah_n));
-  memcpy(&no->lsa, h, sizeof(struct ospf_lsa_header));
+  /* Note that h_n is in network endianity */
+  struct lsa_node *no = mb_alloc(n->pool, sizeof(struct lsa_node));
+  memcpy(&no->lsa, h_n, sizeof(struct ospf_lsa_header));
   add_tail(&n->ackl[queue], NODE no);
-  DBG("Adding (%s) ack for %R, ID: %R, RT: %R, Type: %u\n", s_queue[queue],
-      n->rid, ntohl(h->id), ntohl(h->rt), h->type);
+  DBG("Adding %s ack for %R, ID: %R, RT: %R, Type: %u\n",
+      (queue == ACKL_DIRECT) ? "direct" : "delayed",
+      n->rid, ntohl(h_n->id), ntohl(h_n->rt), h_n->type);
 }
 
 void
-ospf_lsack_send(struct ospf_neighbor *n, int queue)
+ospf_reset_lsack_queue(struct ospf_neighbor *n)
 {
-  struct ospf_packet *op;
-  struct ospf_lsack_packet *pk;
-  u16 len, i = 0;
-  struct ospf_lsa_header *h;
-  struct lsah_n *no;
-  struct ospf_iface *ifa = n->ifa;
-  struct proto *p = &n->ifa->oa->po->proto;
+  struct lsa_node *no;
 
-  if (EMPTY_LIST(n->ackl[queue]))
-    return;
+  WALK_LIST_FIRST(no, n->ackl[ACKL_DELAY])
+  {
+    rem_node(NODE no);
+    mb_free(no);
+  }
+}
 
-  pk = ospf_tx_buffer(ifa);
-  op = &pk->ospf_packet;
+static inline void
+ospf_send_lsack(struct ospf_neighbor *n, int queue)
+{
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
+  struct ospf_lsa_header *lsas;
+  struct ospf_packet *pkt;
+  struct lsa_node *no;
+  uint i, lsa_max, length;
 
-  ospf_pkt_fill_hdr(n->ifa, pk, LSACK_P);
-  h = pk->lsh;
+  /* RFC 2328 13.5 */
 
-  while (!EMPTY_LIST(n->ackl[queue]))
+  pkt = ospf_tx_buffer(ifa);
+  ospf_pkt_fill_hdr(ifa, pkt, LSACK_P);
+  ospf_lsack_body(p, pkt, &lsas, &lsa_max);
+
+  for (i = 0; i < lsa_max && !EMPTY_LIST(n->ackl[queue]); i++)
   {
-    no = (struct lsah_n *) HEAD(n->ackl[queue]);
-    memcpy(h + i, &no->lsa, sizeof(struct ospf_lsa_header));
-    DBG("Iter %u ID: %R, RT: %R, Type: %04x\n", i, ntohl((h + i)->id),
-       ntohl((h + i)->rt), (h + i)->type);
-    i++;
+    no = (struct lsa_node *) HEAD(n->ackl[queue]);
+    memcpy(&lsas[i], &no->lsa, sizeof(struct ospf_lsa_header));
+    DBG("Iter %u ID: %R, RT: %R, Type: %04x\n",
+       i, ntohl(lsas[i].id), ntohl(lsas[i].rt), lsas[i].type);
     rem_node(NODE no);
     mb_free(no);
-    if ((i * sizeof(struct ospf_lsa_header) +
-        sizeof(struct ospf_lsack_packet)) > ospf_pkt_maxsize(n->ifa))
-    {
-      if (!EMPTY_LIST(n->ackl[queue]))
-      {
-       len =
-         sizeof(struct ospf_lsack_packet) +
-         i * sizeof(struct ospf_lsa_header);
-       op->length = htons(len);
-       DBG("Sending and continuing! Len=%u\n", len);
-
-       OSPF_PACKET(ospf_dump_lsack, pk, "LSACK packet sent via %s", ifa->ifname);
-
-       if (ifa->type == OSPF_IT_BCAST)
-       {
-         if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP))
-           ospf_send_to_all(ifa);
-         else if (ifa->cf->real_bcast)
-           ospf_send_to_bdr(ifa);
-         else
-           ospf_send_to(ifa, AllDRouters);
-       }
-       else
-       {
-         if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP))
-           ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE);
-         else
-           ospf_send_to_bdr(ifa);
-       }
-
-       ospf_pkt_fill_hdr(n->ifa, pk, LSACK_P);
-       i = 0;
-      }
-    }
   }
 
-  len = sizeof(struct ospf_lsack_packet) + i * sizeof(struct ospf_lsa_header);
-  op->length = htons(len);
-  DBG("Sending! Len=%u\n", len);
+  length = ospf_pkt_hdrlen(p) + i * sizeof(struct ospf_lsa_header);
+  pkt->length = htons(length);
 
-  OSPF_PACKET(ospf_dump_lsack, pk, "LSACK packet sent via %s", ifa->ifname);
+  OSPF_PACKET(ospf_dump_lsack, pkt, "LSACK packet sent via %s", ifa->ifname);
 
   if (ifa->type == OSPF_IT_BCAST)
   {
     if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP))
       ospf_send_to_all(ifa);
-    else if (ifa->cf->real_bcast)
-      ospf_send_to_bdr(ifa);
     else
-      ospf_send_to(ifa, AllDRouters);
+      ospf_send_to_des(ifa);
   }
   else
     ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE);
 }
 
 void
-ospf_lsack_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
+ospf_lsack_send(struct ospf_neighbor *n, int queue)
+{
+  while (!EMPTY_LIST(n->ackl[queue]))
+    ospf_send_lsack(n, queue);
+}
+
+void
+ospf_receive_lsack(struct ospf_packet *pkt, struct ospf_iface *ifa,
                   struct ospf_neighbor *n)
 {
-  struct proto *p = &ifa->oa->po->proto;
-  struct ospf_lsa_header lsa;
-  struct top_hash_entry *en;
-  unsigned int i, lsano;
+  struct ospf_proto *p = ifa->oa->po;
+  struct ospf_lsa_header lsa, *lsas;
+  struct top_hash_entry *ret, *en;
+  uint i, lsa_count;
+  u32 lsa_type, lsa_domain;
 
-  unsigned int size = ntohs(ps_i->length);
-  if (size < sizeof(struct ospf_lsack_packet))
-  {
-    log(L_ERR "Bad OSPF LSACK packet from %I -  too short (%u B)", n->ip, size);
-    return;
-  }
+  /* RFC 2328 13.7 */
 
-  struct ospf_lsack_packet *ps = (void *) ps_i;
-  OSPF_PACKET(ospf_dump_lsack, ps, "LSACK packet received from %I via %s", n->ip, ifa->ifname);
+  /* No need to check length, lsack has only basic header */
 
-  ospf_neigh_sm(n, INM_HELLOREC);
+  OSPF_PACKET(ospf_dump_lsack, pkt, "LSACK packet received from %I via %s", n->ip, ifa->ifname);
 
   if (n->state < NEIGHBOR_EXCHANGE)
     return;
 
-  lsano = (size - sizeof(struct ospf_lsack_packet)) /
-    sizeof(struct ospf_lsa_header);
-  for (i = 0; i < lsano; i++)
+  ospf_neigh_sm(n, INM_HELLOREC);      /* Not in RFC */
+
+  ospf_lsack_body(p, pkt, &lsas, &lsa_count);
+  for (i = 0; i < lsa_count; i++)
   {
-    ntohlsah(ps->lsh + i, &lsa);
-    u32 dom = ospf_lsa_domain(lsa.type, n->ifa);
-    if ((en = ospf_hash_find_header(n->lsrth, dom, &lsa)) == NULL)
-      continue;                        /* pg 155 */
+    lsa_ntoh_hdr(&lsas[i], &lsa);
+    lsa_get_type_domain(&lsa, n->ifa, &lsa_type, &lsa_domain);
 
-    if (lsa_comp(&lsa, &en->lsa) != CMP_SAME)  /* pg 156 */
+    ret = ospf_hash_find(n->lsrth, lsa_domain, lsa.id, lsa.rt, lsa_type);
+    if (!ret)
+      continue;
+
+    if (lsa_comp(&lsa, &ret->lsa) != CMP_SAME)
     {
       if ((lsa.sn == LSA_MAXSEQNO) && (lsa.age == LSA_MAXAGE))
        continue;
 
       OSPF_TRACE(D_PACKETS, "Strange LSACK from %I", n->ip);
       OSPF_TRACE(D_PACKETS, "Type: %04x, Id: %R, Rt: %R",
-                lsa.type, lsa.id, lsa.rt);
+                lsa_type, lsa.id, lsa.rt);
       OSPF_TRACE(D_PACKETS, "I have: Age: %4u, Seq: %08x, Sum: %04x",
-                en->lsa.age, en->lsa.sn, en->lsa.checksum);
+                ret->lsa.age, ret->lsa.sn, ret->lsa.checksum);
       OSPF_TRACE(D_PACKETS, "He has: Age: %4u, Seq: %08x, Sum: %04x",
                 lsa.age, lsa.sn, lsa.checksum);
       continue;
     }
 
-    DBG("Deleting LS Id: %R RT: %R Type: %u from LS Retl for neighbor %R\n",
-       lsa.id, lsa.rt, lsa.type, n->rid);
-    s_rem_node(SNODE en);
-    ospf_hash_delete(n->lsrth, en);
+    en = ospf_hash_find_entry(p->gr, ret);
+    if (en)
+      en->ret_count--;
+
+    DBG("Deleting LSA (Type: %04x Id: %R Rt: %R) from lsrtl for neighbor %R\n",
+       lsa_type, lsa.id, lsa.rt, n->rid);
+    s_rem_node(SNODE ret);
+    ospf_hash_delete(n->lsrth, ret);
   }
 }
diff --git a/proto/ospf/lsack.h b/proto/ospf/lsack.h
deleted file mode 100644 (file)
index 63a436d..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- *      BIRD -- OSPF
- *
- *      (c) 2000--2004 Ondrej Filip <feela@network.cz>
- *
- *      Can be freely distributed and used under the terms of the GNU GPL.
- *
- */
-
-#ifndef _BIRD_OSPF_LSACK_H_
-#define _BIRD_OSPF_LSACK_H_
-
-struct lsah_n
-{
-  node n;
-  struct ospf_lsa_header lsa;
-};
-
-void ospf_lsack_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
-                       struct ospf_neighbor *n);
-void ospf_lsack_send(struct ospf_neighbor *n, int queue);
-void ospf_lsack_enqueue(struct ospf_neighbor *n, struct ospf_lsa_header *h,
-                       int queue);
-
-#endif /* _BIRD_OSPF_LSACK_H_ */
index bcf7bcdddf3e3a7242d61b8b9d221514c1eb27c5..e1af9f467441dcdeeb5fe6901ba092d566523727 100644 (file)
  *     BIRD -- OSPF
  *
  *     (c) 1999--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
 #include "ospf.h"
 
-void
-flush_lsa(struct top_hash_entry *en, struct proto_ospf *po)
-{
-  struct proto *p = &po->proto;
-
-  OSPF_TRACE(D_EVENTS,
-            "Going to remove LSA Type: %04x, Id: %R, Rt: %R, Age: %u, Seqno: 0x%x",
-            en->lsa.type, en->lsa.id, en->lsa.rt, en->lsa.age, en->lsa.sn);
-  s_rem_node(SNODE en);
-  if (en->lsa_body != NULL)
-    mb_free(en->lsa_body);
-  en->lsa_body = NULL;
-  ospf_hash_delete(po->gr, en);
-}
-
-void
-ospf_flush_area(struct proto_ospf *po, u32 areaid)
-{
-  struct top_hash_entry *en, *nxt;
-
-  WALK_SLIST_DELSAFE(en, nxt, po->lsal)
-  {
-    if ((LSA_SCOPE(&en->lsa) == LSA_SCOPE_AREA) && (en->domain == areaid))
-      flush_lsa(en, po);
-  }
-}
-
-/**
- * ospf_age
- * @po: ospf protocol
- *
- * This function is periodicaly invoked from ospf_disp(). It computes the new
- * age of all LSAs and old (@age is higher than %LSA_MAXAGE) LSAs are flushed
- * whenever possible. If an LSA originated by the router itself is older
- * than %LSREFRESHTIME a new instance is originated.
- *
- * The RFC says that a router should check the checksum of every LSA to detect
- * hardware problems. BIRD does not do this to minimalize CPU utilization.
- *
- * If routing table calculation is scheduled, it also invalidates the old routing
- * table calculation results.
- */
-void
-ospf_age(struct proto_ospf *po)
-{
-  struct proto *p = &po->proto;
-  struct top_hash_entry *en, *nxt;
-  int flush = can_flush_lsa(po);
-
-  WALK_SLIST_DELSAFE(en, nxt, po->lsal)
-  {
-    if (en->lsa.age == LSA_MAXAGE)
-    {
-      if (flush)
-       flush_lsa(en, po);
-      continue;
-    }
-    if ((en->lsa.rt == po->router_id) && (en->lsa.age >= LSREFRESHTIME))
-    {
-      OSPF_TRACE(D_EVENTS, "Refreshing my LSA: Type: %u, Id: %R, Rt: %R",
-                en->lsa.type, en->lsa.id, en->lsa.rt);
-      en->lsa.sn++;
-      en->lsa.age = 0;
-      en->inst_t = now;
-      en->ini_age = 0;
-      lsasum_calculate(&en->lsa, en->lsa_body);
-      ospf_lsupd_flood(po, NULL, NULL, &en->lsa, en->domain, 1);
-      continue;
-    }
-    if ((en->lsa.age = (en->ini_age + (now - en->inst_t))) >= LSA_MAXAGE)
-    {
-      if (flush)
-      {
-       flush_lsa(en, po);
-       schedule_rtcalc(po);
-      }
-      else
-       en->lsa.age = LSA_MAXAGE;
-    }
-  }
-}
 
 #ifndef CPU_BIG_ENDIAN
 void
-htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n)
+lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_header *n)
 {
   n->age = htons(h->age);
-#ifdef OSPFv2
-  n->options = h->options;
-#endif
-  n->type = htont(h->type);
+  n->type_raw = htons(h->type_raw);
   n->id = htonl(h->id);
   n->rt = htonl(h->rt);
   n->sn = htonl(h->sn);
@@ -107,13 +25,10 @@ htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n)
 }
 
 void
-ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h)
+lsa_ntoh_hdr(struct ospf_lsa_header *n, struct ospf_lsa_header *h)
 {
   h->age = ntohs(n->age);
-#ifdef OSPFv2
-  h->options = n->options;
-#endif
-  h->type = ntoht(n->type);
+  h->type_raw = ntohs(n->type_raw);
   h->id = ntohl(n->id);
   h->rt = ntohl(n->rt);
   h->sn = ntohl(n->sn);
@@ -122,28 +37,120 @@ ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h)
 }
 
 void
-htonlsab(void *h, void *n, u16 len)
+lsa_hton_body(void *h, void *n, u16 len)
 {
   u32 *hid = h;
   u32 *nid = n;
-  unsigned i;
+  uint i;
 
   for (i = 0; i < (len / sizeof(u32)); i++)
     nid[i] = htonl(hid[i]);
 }
 
 void
-ntohlsab(void *n, void *h, u16 len)
+lsa_ntoh_body(void *n, void *h, u16 len)
 {
   u32 *nid = n;
   u32 *hid = h;
-  unsigned i;
+  uint i;
 
   for (i = 0; i < (len / sizeof(u32)); i++)
     hid[i] = ntohl(nid[i]);
 }
 #endif /* little endian */
 
+
+
+int
+lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa)
+{   
+  /* Handle inactive vlinks */
+  if (ifa->state == OSPF_IS_DOWN)
+    return 0;
+  /* 4.5.2 (Case 2) */
+  switch (LSA_SCOPE(type))
+  {
+  case LSA_SCOPE_LINK:
+    return ifa->iface_id == domain;
+
+  case LSA_SCOPE_AREA:
+    return ifa->oa->areaid == domain;
+
+  case LSA_SCOPE_AS:
+    if (ifa->type == OSPF_IT_VLINK)
+      return 0;
+    if (!oa_is_ext(ifa->oa))
+      return 0;
+    return 1;
+
+  default:
+    log(L_ERR "OSPF: LSA with invalid scope");
+    return 0;
+  }
+}
+
+
+static int
+unknown_lsa_type(u32 type)
+{
+  switch (type)
+  {
+  case LSA_T_RT:
+  case LSA_T_NET:
+  case LSA_T_SUM_NET:
+  case LSA_T_SUM_RT:
+  case LSA_T_EXT:
+  case LSA_T_NSSA:
+  case LSA_T_LINK:
+  case LSA_T_PREFIX:
+    return 0;
+
+  default:
+    return 1;
+  }
+}
+
+#define LSA_V2_TMAX 8
+static const u16 lsa_v2_types[LSA_V2_TMAX] =
+  {0, LSA_T_RT, LSA_T_NET, LSA_T_SUM_NET, LSA_T_SUM_RT, LSA_T_EXT, 0, LSA_T_NSSA};
+
+void
+lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain)
+{
+  if (ospf_is_v2(ifa->oa->po))
+  {
+    itype = itype & LSA_T_V2_MASK;
+    itype = (itype < LSA_V2_TMAX) ? lsa_v2_types[itype] : 0;
+  }
+  else
+  {
+    /* For unkown LSAs without U-bit change scope to LSA_SCOPE_LINK */
+    if (unknown_lsa_type(itype) && !(itype & LSA_UBIT))
+      itype = itype & ~LSA_SCOPE_MASK;
+  }
+
+  *otype = itype;
+
+  switch (LSA_SCOPE(itype))
+  {
+  case LSA_SCOPE_LINK:
+    *domain = ifa->iface_id;
+    return;
+
+  case LSA_SCOPE_AREA:
+    *domain = ifa->oa->areaid;
+    return;
+
+  case LSA_SCOPE_AS:
+  default:
+    *domain = 0;
+    return;
+  }
+}
+
+
+
 /*
 void
 buf_dump(const char *hdr, const byte *buf, int blen)
@@ -188,8 +195,8 @@ lsasum_calculate(struct ospf_lsa_header *h, void *body)
   u16 length = h->length;
 
   //  log(L_WARN "Checksum %R %R %d start (len %d)", h->id, h->rt, h->type, length);
-  htonlsah(h, h);
-  htonlsab1(body, length - sizeof(struct ospf_lsa_header));
+  lsa_hton_hdr(h, h);
+  lsa_hton_body1(body, length - sizeof(struct ospf_lsa_header));
 
   /*
   char buf[1024];
@@ -202,8 +209,8 @@ lsasum_calculate(struct ospf_lsa_header *h, void *body)
 
   //  log(L_WARN "Checksum result %4x", h->checksum);
 
-  ntohlsah(h, h);
-  ntohlsab1(body, length - sizeof(struct ospf_lsa_header));
+  lsa_ntoh_hdr(h, h);
+  lsa_ntoh_body1(body, length - sizeof(struct ospf_lsa_header));
 }
 
 /*
@@ -292,79 +299,231 @@ lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)
   return CMP_SAME;
 }
 
-#define HDRLEN sizeof(struct ospf_lsa_header)
 
-static int
-lsa_validate_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body)
+static inline int
+lsa_walk_rt2(struct ospf_lsa_rt_walk *rt)
 {
-  unsigned int i, max;
-
-  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_rt)))
+  if (rt->buf >= rt->bufend)
     return 0;
 
-  struct ospf_lsa_rt_link *rtl = (struct ospf_lsa_rt_link *) (body + 1);
-  max = lsa_rt_count(lsa);
+  struct ospf_lsa_rt2_link *l = rt->buf;
+  rt->buf += sizeof(struct ospf_lsa_rt2_link) + l->no_tos * sizeof(struct ospf_lsa_rt2_tos);
 
-#ifdef OSPFv2
-  if (body->links != max)
-    return 0;
-#endif  
+  rt->type = l->type;
+  rt->metric = l->metric;
+  rt->id = l->id;
+  rt->data = l->data;
+  return 1;
+}
 
-  for (i = 0; i < max; i++)
+static inline int
+lsa_walk_rt3(struct ospf_lsa_rt_walk *rt)
+{
+  while (rt->buf >= rt->bufend)
   {
-    u8 type = rtl[i].type;
-    if (!((type == LSART_PTP) ||
-         (type == LSART_NET) ||
-#ifdef OSPFv2
-         (type == LSART_STUB) ||
-#endif
-         (type == LSART_VLNK)))
+    rt->en = ospf_hash_find_rt3_next(rt->en);
+    if (!rt->en)
       return 0;
+
+    rt->buf = rt->en->lsa_body;
+    rt->bufend = rt->buf + rt->en->lsa.length - sizeof(struct ospf_lsa_header);
+    rt->buf += sizeof(struct ospf_lsa_rt);
   }
+
+  struct ospf_lsa_rt3_link *l = rt->buf;
+  rt->buf += sizeof(struct ospf_lsa_rt3_link);
+
+  rt->type = l->type;
+  rt->metric = l->metric;
+  rt->lif = l->lif;
+  rt->nif = l->nif;
+  rt->id = l->id;
   return 1;
 }
 
+void
+lsa_walk_rt_init(struct ospf_proto *p, struct top_hash_entry *act, struct ospf_lsa_rt_walk *rt)
+{
+  rt->ospf2 = ospf_is_v2(p);
+  rt->id = rt->data = rt->lif = rt->nif = 0;
+
+  if (rt->ospf2)
+    rt->en = act;
+  else
+    rt->en = ospf_hash_find_rt3_first(p->gr, act->domain, act->lsa.rt);
+
+  rt->buf = rt->en->lsa_body;
+  rt->bufend = rt->buf + rt->en->lsa.length - sizeof(struct ospf_lsa_header);
+  rt->buf += sizeof(struct ospf_lsa_rt);
+}
+
+int
+lsa_walk_rt(struct ospf_lsa_rt_walk *rt)
+{
+  return rt->ospf2 ? lsa_walk_rt2(rt) : lsa_walk_rt3(rt);
+}
+
+
+void
+lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, ip_addr *ip, int *pxlen, u8 *pxopts, u32 *metric)
+{
+  if (ospf2)
+  {
+    struct ospf_lsa_sum2 *ls = en->lsa_body;
+    *ip = ipa_from_u32(en->lsa.id & ls->netmask);
+    *pxlen = u32_masklen(ls->netmask);
+    *pxopts = 0;
+    *metric = ls->metric & LSA_METRIC_MASK;
+  }
+  else
+  {
+    struct ospf_lsa_sum3_net *ls = en->lsa_body;
+    u16 rest;
+    lsa_get_ipv6_prefix(ls->prefix, ip, pxlen, pxopts, &rest);
+    *metric = ls->metric & LSA_METRIC_MASK;
+  }
+}
+
+void
+lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u32 *options)
+{
+  if (ospf2)
+  {
+    struct ospf_lsa_sum2 *ls = en->lsa_body;
+    *drid = en->lsa.id;
+    *metric = ls->metric & LSA_METRIC_MASK;
+    *options = 0;
+  }
+  else
+  {
+    struct ospf_lsa_sum3_rt *ls = en->lsa_body;
+    *drid = ls->drid;
+    *metric = ls->metric & LSA_METRIC_MASK;
+    *options = ls->options & LSA_OPTIONS_MASK;
+  }
+}
+
+void
+lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt)
+{
+  if (ospf2)
+  {
+    struct ospf_lsa_ext2 *ext = en->lsa_body;
+    rt->ip = ipa_from_u32(en->lsa.id & ext->netmask);
+    rt->pxlen = u32_masklen(ext->netmask);
+    rt->pxopts = 0;
+    rt->metric = ext->metric & LSA_METRIC_MASK;
+    rt->ebit = ext->metric & LSA_EXT2_EBIT;
+
+    rt->fbit = ext->fwaddr;
+    rt->fwaddr = ipa_from_u32(ext->fwaddr);
+
+    rt->tag = ext->tag;
+    rt->propagate = lsa_get_options(&en->lsa) & OPT_P;
+  }
+  else
+  {
+    struct ospf_lsa_ext3 *ext = en->lsa_body;
+    u16 rest;
+    u32 *buf = lsa_get_ipv6_prefix(ext->rest, &rt->ip, &rt->pxlen, &rt->pxopts, &rest);
+    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 = lsa_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
-lsa_validate_net(struct ospf_lsa_header *lsa, struct ospf_lsa_net *body UNUSED)
+lsa_validate_rt2(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body)
 {
-  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_net)))
+  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_rt)))
+    return 0;
+
+  uint i = 0;
+  void *buf = body;
+  void *bufend = buf + lsa->length - HDRLEN;
+  buf += sizeof(struct ospf_lsa_rt);
+
+  while (buf < bufend)
+  {
+    struct ospf_lsa_rt2_link *l = buf;
+    buf += sizeof(struct ospf_lsa_rt2_link) + l->no_tos * sizeof(struct ospf_lsa_rt2_tos);
+    i++;
+
+    if (buf > bufend)
+      return 0;
+
+    if (!((l->type == LSART_PTP) ||
+         (l->type == LSART_NET) ||
+         (l->type == LSART_STUB) ||
+         (l->type == LSART_VLNK)))
+      return 0;
+  }
+
+  if ((body->options & LSA_RT2_LINKS) != i)
     return 0;
 
   return 1;
 }
 
-#ifdef OSPFv2
 
 static int
-lsa_validate_sum(struct ospf_lsa_header *lsa, struct ospf_lsa_sum *body)
+lsa_validate_rt3(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body)
 {
-  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum)))
+  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_rt)))
     return 0;
 
-  /* First field should have TOS = 0, we ignore other TOS fields */
-  if ((body->metric & LSA_SUM_TOS) != 0)
+  void *buf = body;
+  void *bufend = buf + lsa->length - HDRLEN;
+  buf += sizeof(struct ospf_lsa_rt);
+
+  while (buf < bufend)
+  {
+    struct ospf_lsa_rt3_link *l = buf;
+    buf += sizeof(struct ospf_lsa_rt3_link);
+
+    if (buf > bufend)
+      return 0;
+
+    if (!((l->type == LSART_PTP) ||
+         (l->type == LSART_NET) ||
+         (l->type == LSART_VLNK)))
+      return 0;
+  }
+  return 1;
+}
+
+static int
+lsa_validate_net(struct ospf_lsa_header *lsa, struct ospf_lsa_net *body UNUSED)
+{
+  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_net)))
     return 0;
 
   return 1;
 }
-#define lsa_validate_sum_net(A,B) lsa_validate_sum(A,B)
-#define lsa_validate_sum_rt(A,B)  lsa_validate_sum(A,B)
 
 static int
-lsa_validate_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body)
+lsa_validate_sum2(struct ospf_lsa_header *lsa, struct ospf_lsa_sum2 *body)
 {
-  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext)))
+  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum2)))
     return 0;
 
   /* First field should have TOS = 0, we ignore other TOS fields */
-  if ((body->metric & LSA_EXT_TOS) != 0)
+  if ((body->metric & LSA_SUM2_TOS) != 0)
     return 0;
 
   return 1;
 }
 
-#else /* OSPFv3 */
-
 static inline int
 pxlen(u32 *buf)
 {
@@ -372,36 +531,48 @@ pxlen(u32 *buf)
 }
 
 static int
-lsa_validate_sum_net(struct ospf_lsa_header *lsa, struct ospf_lsa_sum_net *body)
+lsa_validate_sum3_net(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_net *body)
 {
-  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum_net) + 4))
+  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum3_net) + 4))
     return 0;
 
   u8 pxl = pxlen(body->prefix);
   if (pxl > MAX_PREFIX_LENGTH)
     return 0;
 
-  if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum_net) + 
+  if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum3_net) + 
                      IPV6_PREFIX_SPACE(pxl)))
     return 0;
 
   return 1;
 }
 
+static int
+lsa_validate_sum3_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_sum3_rt *body)
+{
+  if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum3_rt)))
+    return 0;
+
+  return 1;
+}
 
 static int
-lsa_validate_sum_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_sum_rt *body)
+lsa_validate_ext2(struct ospf_lsa_header *lsa, struct ospf_lsa_ext2 *body)
 {
-  if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum_rt)))
+  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext2)))
+    return 0;
+
+  /* First field should have TOS = 0, we ignore other TOS fields */
+  if ((body->metric & LSA_EXT2_TOS) != 0)
     return 0;
 
   return 1;
 }
 
 static int
-lsa_validate_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body)
+lsa_validate_ext3(struct ospf_lsa_header *lsa, struct ospf_lsa_ext3 *body)
 {
-  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext) + 4))
+  if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext3) + 4))
     return 0;
 
   u8 pxl = pxlen(body->rest);
@@ -409,23 +580,23 @@ lsa_validate_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body)
     return 0;
 
   int len = IPV6_PREFIX_SPACE(pxl);
-  if (body->metric & LSA_EXT_FBIT) // forwardinf address
+  if (body->metric & LSA_EXT3_FBIT) // forwardinf address
     len += 16;
-  if (body->metric & LSA_EXT_TBIT) // route tag
+  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_ext) + len))
+  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, unsigned int offset, u8 *pbuf)
+lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, uint offset, u8 *pbuf)
 {
-  unsigned int bound = lsa->length - HDRLEN - 4;
+  uint bound = lsa->length - HDRLEN - 4;
   u32 i;
 
   for (i = 0; i < pxcount; i++)
@@ -464,8 +635,6 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body)
   return lsa_validate_pxlist(lsa, body->pxcount, sizeof(struct ospf_lsa_prefix), (u8 *) body);
 }
 
-#endif
-
 
 /**
  * lsa_validate - check whether given LSA is valid
@@ -477,85 +646,48 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body)
  */
 
 int
-lsa_validate(struct ospf_lsa_header *lsa, void *body)
+lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
 {
-  switch (lsa->type)
+  if (ospf2)
+  {
+    switch (lsa_type)
+    {
+    case LSA_T_RT:
+      return lsa_validate_rt2(lsa, body);
+    case LSA_T_NET:
+      return lsa_validate_net(lsa, body);
+    case LSA_T_SUM_NET:
+      return lsa_validate_sum2(lsa, body);
+    case LSA_T_SUM_RT:
+      return lsa_validate_sum2(lsa, body);
+    case LSA_T_EXT:
+    case LSA_T_NSSA:
+      return lsa_validate_ext2(lsa, body);
+    default:
+      return 0;        /* Should not happen, unknown LSAs are already rejected */
+    }
+  }
+  else
+  {
+    switch (lsa_type)
     {
     case LSA_T_RT:
-      return lsa_validate_rt(lsa, body);
+      return lsa_validate_rt3(lsa, body);
     case LSA_T_NET:
       return lsa_validate_net(lsa, body);
     case LSA_T_SUM_NET:
-      return lsa_validate_sum_net(lsa, body);
+      return lsa_validate_sum3_net(lsa, body);
     case LSA_T_SUM_RT:
-      return lsa_validate_sum_rt(lsa, body);
+      return lsa_validate_sum3_rt(lsa, body);
     case LSA_T_EXT:
     case LSA_T_NSSA:
-      return lsa_validate_ext(lsa, body);
-#ifdef OSPFv3
+      return lsa_validate_ext3(lsa, body);
     case LSA_T_LINK:
       return lsa_validate_link(lsa, body);
     case LSA_T_PREFIX:
       return lsa_validate_prefix(lsa, body);
-#endif
     default:
-      /* In OSPFv3, unknown LSAs are OK,
-        In OSPFv2, unknown LSAs are already rejected
-      */
-      return 1;
+      return 1;        /* Unknown LSAs are OK in OSPFv3 */
     }
-}
-
-/**
- * lsa_install_new - install new LSA into database
- * @po: OSPF protocol
- * @lsa: LSA header
- * @domain: domain of LSA
- * @body: pointer to LSA body
- *
- * This function ensures installing new LSA into LSA database. Old instance is
- * replaced. Several actions are taken to detect if new routing table
- * calculation is necessary. This is described in 13.2 of RFC 2328.
- */
-struct top_hash_entry *
-lsa_install_new(struct proto_ospf *po, struct ospf_lsa_header *lsa, u32 domain, void *body)
-{
-  /* LSA can be temporarrily, but body must be mb_allocated. */
-  int change = 0;
-  struct top_hash_entry *en;
-
-  if ((en = ospf_hash_find_header(po->gr, domain, lsa)) == NULL)
-  {
-    en = ospf_hash_get_header(po->gr, domain, lsa);
-    change = 1;
-  }
-  else
-  {
-    if ((en->lsa.length != lsa->length)
-#ifdef OSPFv2       
-       || (en->lsa.options != lsa->options)
-#endif
-       || (en->lsa.age == LSA_MAXAGE)
-       || (lsa->age == LSA_MAXAGE)
-       || memcmp(en->lsa_body, body, lsa->length - sizeof(struct ospf_lsa_header)))
-      change = 1;
-
-    s_rem_node(SNODE en);
   }
-
-  DBG("Inst lsa: Id: %R, Rt: %R, Type: %u, Age: %u, Sum: %u, Sn: 0x%x\n",
-      lsa->id, lsa->rt, lsa->type, lsa->age, lsa->checksum, lsa->sn);
-
-  s_add_tail(&po->lsal, SNODE en);
-  en->inst_t = now;
-  if (en->lsa_body != NULL)
-    mb_free(en->lsa_body);
-  en->lsa_body = body;
-  memcpy(&en->lsa, lsa, sizeof(struct ospf_lsa_header));
-  en->ini_age = en->lsa.age;
-
-  if (change)
-    schedule_rtcalc(po);
-
-  return en;
 }
index 0b556ec5ead3dfbf97facd8d45d80223f4a36d51..d9e1a610dbda0be212e2748f090c61d7c158c07d 100644 (file)
@@ -1,42 +1,63 @@
 /*
  *      BIRD -- OSPF
  *
- *      (c) 1999 - 2000 Ondrej Filip <feela@network.cz>
+ *      (c) 1999--2000 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *      Can be freely distributed and used under the terms of the GNU GPL.
- *
  */
 
 #ifndef _BIRD_OSPF_LSALIB_H_
 #define _BIRD_OSPF_LSALIB_H_
 
 #ifdef CPU_BIG_ENDIAN
-static inline void htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n) { *n = *h; };
-static inline void ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h) { *h = *n; };
-static inline void htonlsab(void *h, void *n, u16 len) { ASSERT(h != n); memcpy(n, h, len); };
-static inline void ntohlsab(void *n, void *h, u16 len) { ASSERT(n != h); memcpy(h, n, len); };
-static inline void htonlsab1(void *h, u16 len) { };
-static inline void ntohlsab1(void *n, u16 len) { };
+static inline void lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_header *n) { *n = *h; };
+static inline void lsa_ntoh_hdr(struct ospf_lsa_header *n, struct ospf_lsa_header *h) { *h = *n; };
+static inline void lsa_hton_body(void *h, void *n, u16 len) { ASSERT(h != n); memcpy(n, h, len); };
+static inline void lsa_ntoh_body(void *n, void *h, u16 len) { ASSERT(n != h); memcpy(h, n, len); };
+static inline void lsa_hton_body1(void *h, u16 len) { };
+static inline void lsa_ntoh_body1(void *n, u16 len) { };
 #else
-void htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n);
-void ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h);
-void htonlsab(void *h, void *n, u16 len);
-void ntohlsab(void *n, void *h, u16 len);
-static inline void htonlsab1(void *h, u16 len) { htonlsab(h, h, len); };
-static inline void ntohlsab1(void *n, u16 len) { ntohlsab(n, n, len); };
+void lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_header *n);
+void lsa_ntoh_hdr(struct ospf_lsa_header *n, struct ospf_lsa_header *h);
+void lsa_hton_body(void *h, void *n, u16 len);
+void lsa_ntoh_body(void *n, void *h, u16 len);
+static inline void lsa_hton_body1(void *h, u16 len) { lsa_hton_body(h, h, len); };
+static inline void lsa_ntoh_body1(void *n, u16 len) { lsa_ntoh_body(n, n, len); };
 #endif
 
+struct ospf_lsa_rt_walk {
+  struct top_hash_entry *en;
+  void *buf, *bufend;
+  int ospf2;
+  u16 type, metric;
+  u32 id, data, lif, nif;
+};
+
+
+void lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain);
+
+static inline void lsa_get_type_domain(struct ospf_lsa_header *lsa, struct ospf_iface *ifa, u32 *otype, u32 *domain)
+{ lsa_get_type_domain_(lsa->type_raw, ifa, otype, domain); }
+
+static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p)
+{ return ospf_is_v2(p) ? (h->type_raw & LSA_T_V2_MASK) : h->type_raw; }
+
+
+int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
+
 void lsasum_calculate(struct ospf_lsa_header *header, void *body);
 u16 lsasum_check(struct ospf_lsa_header *h, void *body);
 #define CMP_NEWER 1
 #define CMP_SAME 0
 #define CMP_OLDER -1
 int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2);
-int lsa_validate(struct ospf_lsa_header *lsa, void *body);
-struct top_hash_entry * lsa_install_new(struct proto_ospf *po, struct ospf_lsa_header *lsa, u32 domain, void *body);
-void ospf_age(struct proto_ospf *po);
-void flush_lsa(struct top_hash_entry *en, struct proto_ospf *po);
-void ospf_flush_area(struct proto_ospf *po, u32 areaid);
-
+void lsa_walk_rt_init(struct ospf_proto *po, struct top_hash_entry *act, struct ospf_lsa_rt_walk *rt);
+int lsa_walk_rt(struct ospf_lsa_rt_walk *rt);
+void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, ip_addr *ip, int *pxlen, 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);
 
 #endif /* _BIRD_OSPF_LSALIB_H_ */
index 15854ce7b569b2c737bf2c39ec89686ba4cb8b0d..8888f88ee0b3fa2ca0687f2433aa40b73c420244 100644 (file)
@@ -2,6 +2,8 @@
  *     BIRD -- OSPF
  *
  *     (c) 2000--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 #include "ospf.h"
 
 
+/*
 struct ospf_lsreq_packet
 {
-  struct ospf_packet ospf_packet;
-  struct ospf_lsreq_header lsh[];
+  struct ospf_packet hdr;
+  // union ospf_auth auth;
+
+  struct ospf_lsreq_header lsrs[];
 };
+*/
 
 
-static void ospf_dump_lsreq(struct proto *p, struct ospf_lsreq_packet *pkt)
+static inline void
+ospf_lsreq_body(struct ospf_proto *p, struct ospf_packet *pkt,
+               struct ospf_lsreq_header **body, uint *count)
 {
-  struct ospf_packet *op = &pkt->ospf_packet;
+  uint plen = ntohs(pkt->length);
+  uint hlen = ospf_pkt_hdrlen(p);
+
+  *body = ((void *) pkt) + hlen;
+  *count = (plen - hlen) / sizeof(struct ospf_lsreq_header);
+}
 
-  ASSERT(op->type == LSREQ_P);
-  ospf_dump_common(p, op);
+static void
+ospf_dump_lsreq(struct ospf_proto *p, struct ospf_packet *pkt)
+{
+  struct ospf_lsreq_header *lsrs;
+  uint i, lsr_count;
 
-  unsigned int i, j;
-  j = (ntohs(op->length) - sizeof(struct ospf_lsreq_packet)) /
-    sizeof(struct ospf_lsreq_header);
+  ASSERT(pkt->type == LSREQ_P);
+  ospf_dump_common(p, pkt);
 
-  for (i = 0; i < j; i++)
-    log(L_TRACE "%s:     LSR      Type: %04x, Id: %R, Rt: %R", p->name,
-       htonl(pkt->lsh[i].type), htonl(pkt->lsh[i].id), htonl(pkt->lsh[i].rt));
+  ospf_lsreq_body(p, pkt, &lsrs, &lsr_count);
+  for (i = 0; i < lsr_count; i++)
+    log(L_TRACE "%s:     LSR      Type: %04x, Id: %R, Rt: %R", p->p.name,
+       ntohl(lsrs[i].type), ntohl(lsrs[i].id), ntohl(lsrs[i].rt));
 }
 
+
 void
-ospf_lsreq_send(struct ospf_neighbor *n)
+ospf_send_lsreq(struct ospf_proto *p, struct ospf_neighbor *n)
 {
-  snode *sn;
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_lsreq_header *lsrs;
   struct top_hash_entry *en;
-  struct ospf_lsreq_packet *pk;
-  struct ospf_packet *op;
-  struct ospf_lsreq_header *lsh;
-  u16 length;
-  int i, j;
-  struct proto *p = &n->ifa->oa->po->proto;
+  struct ospf_packet *pkt;
+  uint i, lsr_max, length;
 
-  pk = ospf_tx_buffer(n->ifa);
-  op = &pk->ospf_packet;
+  /* RFC 2328 10.9 */
 
-  ospf_pkt_fill_hdr(n->ifa, pk, LSREQ_P);
-
-  sn = SHEAD(n->lsrql);
   if (EMPTY_SLIST(n->lsrql))
   {
     if (n->state == NEIGHBOR_LOADING)
@@ -57,90 +67,80 @@ ospf_lsreq_send(struct ospf_neighbor *n)
     return;
   }
 
-  i = j = (ospf_pkt_maxsize(n->ifa) - sizeof(struct ospf_lsreq_packet)) /
-    sizeof(struct ospf_lsreq_header);
-  lsh = pk->lsh;
+  pkt = ospf_tx_buffer(ifa);
+  ospf_pkt_fill_hdr(ifa, pkt, LSREQ_P);
+  ospf_lsreq_body(p, pkt, &lsrs, &lsr_max);
+
+  //  for (i = 0; i < lsr_max; i++)
 
-  for (; i > 0; i--)
+  i = 0;
+  WALK_SLIST(en, n->lsrql)
   {
-    en = (struct top_hash_entry *) sn;
-    lsh->type = htonl(en->lsa.type);
-    lsh->rt = htonl(en->lsa.rt);
-    lsh->id = htonl(en->lsa.id);
-    DBG("Requesting %uth LSA: Type: %u, ID: %R, RT: %R, SN: 0x%x, Age %u\n",
-       i, en->lsa.type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age);
-    lsh++;
-    if (sn == STAIL(n->lsrql))
+    if (i == lsr_max)
       break;
-    sn = sn->next;
+
+    DBG("Requesting %uth LSA: Type: %04u, ID: %R, RT: %R, SN: 0x%x, Age %u\n",
+       i, en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age);
+
+    u32 etype = lsa_get_etype(&en->lsa, p);
+    lsrs[i].type = htonl(etype);
+    lsrs[i].rt = htonl(en->lsa.rt);
+    lsrs[i].id = htonl(en->lsa.id);
+    i++;
   }
-  if (i != 0)
-    i--;
 
-  length =
-    sizeof(struct ospf_lsreq_packet) + (j -
-                                       i) * sizeof(struct ospf_lsreq_header);
-  op->length = htons(length);
+  length = ospf_pkt_hdrlen(p) + i * sizeof(struct ospf_lsreq_header);
+  pkt->length = htons(length);
 
-  OSPF_PACKET(ospf_dump_lsreq, pk, "LSREQ packet sent to %I via %s", n->ip, n->ifa->ifname);
-  ospf_send_to(n->ifa, n->ip);
+  OSPF_PACKET(ospf_dump_lsreq, pkt, "LSREQ packet sent to %I via %s", n->ip, ifa->ifname);
+  ospf_send_to(ifa, n->ip);
 }
 
+
 void
-ospf_lsreq_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
+ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa,
                   struct ospf_neighbor *n)
 {
-  struct ospf_area *oa = ifa->oa;
-  struct proto_ospf *po = oa->po;
-  struct proto *p = &po->proto;
-  struct ospf_lsreq_header *lsh;
-  struct l_lsr_head *llsh;
-  list uplist;
-  slab *upslab;
-  int i, lsano;
-
-  unsigned int size = ntohs(ps_i->length);
-  if (size < sizeof(struct ospf_lsreq_packet))
-  {
-    log(L_ERR "Bad OSPF LSREQ packet from %I -  too short (%u B)", n->ip, size);
-    return;
-  }
+  struct ospf_proto *p = ifa->oa->po;
+  struct ospf_lsreq_header *lsrs;
+  uint i, lsr_count;
 
-  struct ospf_lsreq_packet *ps = (void *) ps_i;
-  OSPF_PACKET(ospf_dump_lsreq, ps, "LSREQ packet received from %I via %s", n->ip, ifa->ifname);
+  /* RFC 2328 10.7 */
+
+  /* No need to check length, lsreq has only basic header */
+
+  OSPF_PACKET(ospf_dump_lsreq, pkt, "LSREQ packet received from %I via %s", n->ip, ifa->ifname);
 
   if (n->state < NEIGHBOR_EXCHANGE)
     return;
 
-  ospf_neigh_sm(n, INM_HELLOREC);
+  ospf_neigh_sm(n, INM_HELLOREC);      /* Not in RFC */
+
+  ospf_lsreq_body(p, pkt, &lsrs, &lsr_count);
 
-  lsh = ps->lsh;
-  init_list(&uplist);
-  upslab = sl_new(n->pool, sizeof(struct l_lsr_head));
+  struct top_hash_entry *en, *entries[lsr_count];
 
-  lsano = (size - sizeof(struct ospf_lsreq_packet)) /
-    sizeof(struct ospf_lsreq_header);
-  for (i = 0; i < lsano; lsh++, i++)
+  for (i = 0; i < lsr_count; i++)
   {
-    u32 hid = ntohl(lsh->id);
-    u32 hrt = ntohl(lsh->rt);
-    u32 htype = ntohl(lsh->type);
-    u32 dom = ospf_lsa_domain(htype, ifa);
-    DBG("Processing requested LSA: Type: %u, ID: %R, RT: %R\n", lsh->type, hid, hrt);
-    llsh = sl_alloc(upslab);
-    llsh->lsh.id = hid;
-    llsh->lsh.rt = hrt;
-    llsh->lsh.type = htype;
-    add_tail(&uplist, NODE llsh);
-    if (ospf_hash_find(po->gr, dom, hid, hrt, htype) == NULL)
+    u32 id, rt, type, domain;
+
+    id = ntohl(lsrs[i].id);
+    rt = ntohl(lsrs[i].rt);
+    lsa_get_type_domain_(ntohl(lsrs[i].type), ifa, &type, &domain);
+
+    DBG("Processing requested LSA: Type: %04x, Id: %R, Rt: %R\n", type, id, rt);
+
+    en = ospf_hash_find(p->gr, domain, id, rt, type); 
+    if (!en)
     {
-      log(L_WARN "Received bad LSREQ from %I: Type: %04x, Id: %R, Rt: %R",
-         n->ip, htype, hid, hrt);
+      log(L_WARN "%s: Received LSREQ from %I for missing LSA (Type: %04x, Id: %R, Rt: %R)",
+         p->p.name, n->ip, type, id, rt);
       ospf_neigh_sm(n, INM_BADLSREQ);
-      rfree(upslab);
       return;
     }
+
+    entries[i] = en;
   }
-  ospf_lsupd_send_list(n, &uplist);
-  rfree(upslab);
+
+  ospf_send_lsupd(p, entries, lsr_count, n);
 }
diff --git a/proto/ospf/lsreq.h b/proto/ospf/lsreq.h
deleted file mode 100644 (file)
index a12edde..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- *      BIRD -- OSPF
- *
- *      (c) 2000--2004 Ondrej Filip <feela@network.cz>
- *
- *      Can be freely distributed and used under the terms of the GNU GPL.
- *
- */
-
-#ifndef _BIRD_OSPF_LSREQ_H_
-#define _BIRD_OSPF_LSREQ_H_
-
-void ospf_lsreq_send(struct ospf_neighbor *n);
-void ospf_lsreq_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
-                       struct ospf_neighbor *n);
-
-#endif /* _BIRD_OSPF_LSREQ_H_ */
index 8f65c532217cbcc8ed0336a83d92f0474f8e9d74..b0bf21cdf710e5f15b4a224206061ecef5d5f3fa 100644 (file)
@@ -2,6 +2,8 @@
  *     BIRD -- OSPF
  *
  *     (c) 2000--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 #include "ospf.h"
 
 
+/*
 struct ospf_lsupd_packet
 {
-  struct ospf_packet ospf_packet;
-  u32 lsano;                   /* Number of LSA's */
+  struct ospf_packet hdr;
+  // union ospf_auth auth;
+
+  u32 lsa_count;
+  void lsas[];
 };
+*/
 
 
-/* Beware of unaligned access */
-void ospf_dump_lsahdr(struct proto *p, struct ospf_lsa_header *lsa_n)
+void
+ospf_dump_lsahdr(struct ospf_proto *p, struct ospf_lsa_header *lsa_n)
 {
   struct ospf_lsa_header lsa;
-  ntohlsah(lsa_n, &lsa);
+  u32 lsa_etype;
+
+  lsa_ntoh_hdr(lsa_n, &lsa);
+  lsa_etype = lsa_get_etype(&lsa, p);
 
   log(L_TRACE "%s:     LSA      Type: %04x, Id: %R, Rt: %R, Age: %u, Seq: %08x, Sum: %04x",
-      p->name, lsa.type, lsa.id, lsa.rt, lsa.age, lsa.sn, lsa.checksum);
+      p->p.name, lsa_etype, lsa.id, lsa.rt, lsa.age, lsa.sn, lsa.checksum);
 }
 
-void ospf_dump_common(struct proto *p, struct ospf_packet *op)
+void
+ospf_dump_common(struct ospf_proto *p, struct ospf_packet *pkt)
 {
-  log(L_TRACE "%s:     length   %d", p->name, ntohs(op->length));
-  log(L_TRACE "%s:     router   %R", p->name, ntohl(op->routerid));
+  log(L_TRACE "%s:     length   %d", p->p.name, ntohs(pkt->length));
+  log(L_TRACE "%s:     router   %R", p->p.name, ntohl(pkt->routerid));
 }
 
-static void ospf_dump_lsupd(struct proto *p, struct ospf_lsupd_packet *pkt)
+static inline uint
+ospf_lsupd_hdrlen(struct ospf_proto *p)
 {
-  struct ospf_packet *op = &pkt->ospf_packet;
-
-  ASSERT(op->type == LSUPD_P);
-  ospf_dump_common(p, op);
-
-  /* We know that ntohs(op->length) >= sizeof(struct ospf_lsa_header) */
-  u8 *pbuf= (u8 *) pkt;
-  unsigned int offset = sizeof(struct ospf_lsupd_packet);
-  unsigned int bound = ntohs(op->length) - sizeof(struct ospf_lsa_header);
-  unsigned int i, j, lsalen;
+  return ospf_pkt_hdrlen(p) + 4; /* + u32 lsa count field */
+}
 
-  j = ntohl(pkt->lsano);
-  for (i = 0; i < j; i++)
-    {
-      if (offset > bound)
-       {
-         log(L_TRACE "%s:     LSA      invalid", p->name);
-         return;
-       }
+static inline u32
+ospf_lsupd_get_lsa_count(struct ospf_packet *pkt, uint hdrlen)
+{
+  u32 *c = ((void *) pkt) + hdrlen - 4;
+  return ntohl(*c);
+}
 
-      struct ospf_lsa_header *lsa = (void *) (pbuf + offset);
-      ospf_dump_lsahdr(p, lsa);
-      lsalen = ntohs(lsa->length);
-      offset += lsalen;
+static inline void
+ospf_lsupd_set_lsa_count(struct ospf_packet *pkt, uint hdrlen, u32 val)
+{
+  u32 *c = ((void *) pkt) + hdrlen - 4;
+  *c = htonl(val);
+}
 
-      if (((lsalen % 4) != 0) || (lsalen <= sizeof(struct ospf_lsa_header)))
-       {
-         log(L_TRACE "%s:     LSA      invalid", p->name);
-         return;
-       }
-    }
+static inline void
+ospf_lsupd_body(struct ospf_proto *p, struct ospf_packet *pkt,
+               uint *offset, uint *bound, uint *lsa_count)
+{
+  uint hlen = ospf_lsupd_hdrlen(p);
+  *offset = hlen;
+  *bound = ntohs(pkt->length) - sizeof(struct ospf_lsa_header);
+  *lsa_count = ospf_lsupd_get_lsa_count(pkt, hlen);
 }
 
+static void
+ospf_dump_lsupd(struct ospf_proto *p, struct ospf_packet *pkt)
+{
+  uint offset, bound, i, lsa_count, lsalen;
 
-#ifdef OSPFv2
+  ASSERT(pkt->type == LSUPD_P);
+  ospf_dump_common(p, pkt);
 
-int
-ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_iface *ifa)
-{
-  if (lsa->type == LSA_T_EXT)
+  ospf_lsupd_body(p, pkt, &offset, &bound, &lsa_count);
+  for (i = 0; i < lsa_count; i++)
+  {
+    if (offset > bound)
     {
-      if (ifa->type == OSPF_IT_VLINK)
-       return 0;
-      if (!oa_is_ext(ifa->oa))
-       return 0;
-      return 1;
+      log(L_TRACE "%s:     LSA      invalid", p->p.name);
+      return;
     }
-  else
-    return ifa->oa->areaid == domain;
-}
 
-#else /* OSPFv3 */
+    struct ospf_lsa_header *lsa = ((void *) pkt) + offset;
+    ospf_dump_lsahdr(p, lsa);
+    lsalen = ntohs(lsa->length);
+    offset += lsalen;
 
-static int
-unknown_lsa_type(struct ospf_lsa_header *lsa)
-{
-  switch (lsa->type)
+    if (((lsalen % 4) != 0) || (lsalen <= sizeof(struct ospf_lsa_header)))
     {
-    case LSA_T_RT:
-    case LSA_T_NET:
-    case LSA_T_SUM_NET:
-    case LSA_T_SUM_RT:
-    case LSA_T_EXT:
-    case LSA_T_NSSA:
-    case LSA_T_LINK:
-    case LSA_T_PREFIX:
-      return 0;
-
-    default:
-      return 1;
+      log(L_TRACE "%s:     LSA      invalid", p->p.name);
+      return;
     }
+  }
 }
 
-int
-ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_iface *ifa)
-{    
-  u32 scope = LSA_SCOPE(lsa);
 
-  /* Handle inactive vlinks */
-  if (ifa->state == OSPF_IS_DOWN)
-    return 0;
+static inline void
+ospf_lsa_lsrt_up(struct top_hash_entry *en, struct ospf_neighbor *n)
+{
+  struct top_hash_entry *ret = ospf_hash_get_entry(n->lsrth, en);
 
-  /* 4.5.2 (Case 2) */
-  if (unknown_lsa_type(lsa) && !(lsa->type & LSA_UBIT))
-    scope = LSA_SCOPE_LINK;
+  if (!SNODE_VALID(ret))
+  {
+    en->ret_count++;
+    s_add_tail(&n->lsrtl, SNODE ret);
+  }
 
-  switch (scope)
-    {
-    case LSA_SCOPE_LINK:
-      return ifa->iface_id == domain;
+  memcpy(&ret->lsa, &en->lsa, sizeof(struct ospf_lsa_header));
+}
 
-    case LSA_SCOPE_AREA:
-      return ifa->oa->areaid == domain;
+static inline int
+ospf_lsa_lsrt_down(struct top_hash_entry *en, struct ospf_neighbor *n)
+{
+  struct top_hash_entry *ret = ospf_hash_find_entry(n->lsrth, en);
 
-    case LSA_SCOPE_AS:
-      if (ifa->type == OSPF_IT_VLINK)
-       return 0;
-      if (!oa_is_ext(ifa->oa))
-       return 0;
-      return 1;
+  if (ret)
+  {
+    en->ret_count--;
+    s_rem_node(SNODE ret);
+    ospf_hash_delete(n->lsrth, ret);
+    return 1;
+  }
 
-    default:
-      log(L_ERR "LSA with invalid scope");
-      return 0;
-    }
+  return 0;
 }
 
-#endif
+
+static void ospf_lsupd_flood_ifa(struct ospf_proto *p, struct ospf_iface *ifa, struct top_hash_entry *en);
+
 
 /**
- * ospf_lsupd_flood - send received or generated lsa to the neighbors
- * @po: OSPF protocol
- * @n: neighbor than sent this lsa (or NULL if generated)
- * @hn: LSA header followed by lsa body in network endianity (may be NULL) 
- * @hh: LSA header in host endianity (must be filled)
- * @domain: domain of LSA (must be filled)
- * @rtl: add this LSA into retransmission list
- *
+ * ospf_lsupd_flood - send received or generated LSA to the neighbors
+ * @p: OSPF protocol
+ * @en: LSA entry
+ * @from: neighbor than sent this LSA (or NULL if LSA is local)
  *
  * return value - was the LSA flooded back?
  */
 
 int
-ospf_lsupd_flood(struct proto_ospf *po,
-                struct ospf_neighbor *n, struct ospf_lsa_header *hn,
-                struct ospf_lsa_header *hh, u32 domain, int rtl)
+ospf_lsupd_flood(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neighbor *from)
 {
   struct ospf_iface *ifa;
-  struct ospf_neighbor *nn;
-  struct top_hash_entry *en;
-  struct proto *p = &po->proto;
-  int ret, retval = 0;
+  struct ospf_neighbor *n;
 
-  /* pg 148 */
-  WALK_LIST(ifa, po->iface_list)
+  int back = 0;
+  WALK_LIST(ifa, p->iface_list)
   {
     if (ifa->stub)
       continue;
 
-    if (! ospf_lsa_flooding_allowed(hh, domain, ifa))
+    if (! lsa_flooding_allowed(en->lsa_type, en->domain, ifa))
       continue;
 
     DBG("Wanted to flood LSA: Type: %u, ID: %R, RT: %R, SN: 0x%x, Age %u\n",
        hh->type, hh->id, hh->rt, hh->sn, hh->age);
 
-    ret = 0;
-    WALK_LIST(nn, ifa->neigh_list)
+    int used = 0;
+    WALK_LIST(n, ifa->neigh_list)
     {
       /* 13.3 (1a) */
-      if (nn->state < NEIGHBOR_EXCHANGE)
+      if (n->state < NEIGHBOR_EXCHANGE)
        continue;
 
       /* 13.3 (1b) */
-      if (nn->state < NEIGHBOR_FULL)
+      if (n->state < NEIGHBOR_FULL)
       {
-       if ((en = ospf_hash_find_header(nn->lsrqh, domain, hh)) != NULL)
+       struct top_hash_entry *req = ospf_hash_find_entry(n->lsrqh, en);
+       if (req != NULL)
        {
-         DBG("That LSA found in lsreq list for neigh %R\n", nn->rid);
+         int cmp = lsa_comp(&en->lsa, &req->lsa);
 
-         switch (lsa_comp(hh, &en->lsa))
+         /* If same or newer, remove LSA from the link state request list */
+         if (cmp > CMP_OLDER)
          {
-         case CMP_OLDER:
-           continue;
-           break;
-         case CMP_SAME:
-           s_rem_node(SNODE en);
-           if (en->lsa_body != NULL)
-             mb_free(en->lsa_body);
-           en->lsa_body = NULL;
-           DBG("Removing from lsreq list for neigh %R\n", nn->rid);
-           ospf_hash_delete(nn->lsrqh, en);
-           if ((EMPTY_SLIST(nn->lsrql)) && (nn->state == NEIGHBOR_LOADING))
-             ospf_neigh_sm(nn, INM_LOADDONE);
-           continue;
-           break;
-         case CMP_NEWER:
-           s_rem_node(SNODE en);
-           if (en->lsa_body != NULL)
-             mb_free(en->lsa_body);
-           en->lsa_body = NULL;
-           DBG("Removing from lsreq list for neigh %R\n", nn->rid);
-           ospf_hash_delete(nn->lsrqh, en);
-           if ((EMPTY_SLIST(nn->lsrql)) && (nn->state == NEIGHBOR_LOADING))
-             ospf_neigh_sm(nn, INM_LOADDONE);
-           break;
-         default:
-           bug("Bug in lsa_comp?");
+           s_rem_node(SNODE req);
+           ospf_hash_delete(n->lsrqh, req);
+           if ((EMPTY_SLIST(n->lsrql)) && (n->state == NEIGHBOR_LOADING))
+             ospf_neigh_sm(n, INM_LOADDONE);
          }
+
+         /* If older or same, skip processing of this neighbor */
+         if (cmp < CMP_NEWER)
+           continue;
        }
       }
 
       /* 13.3 (1c) */
-      if (nn == n)
+      if (n == from)
        continue;
 
-      /* 13.3 (1d) */
-      if (rtl)
-      {
-       /* In OSPFv3, there should be check whether receiving router understand
-          that type of LSA (for LSA types with U-bit == 0). But as we does not support
-          any optional LSA types, this is not needed yet */
+      /* In OSPFv3, there should be check whether receiving router understand
+        that type of LSA (for LSA types with U-bit == 0). But as we do not support
+        any optional LSA types, this is not needed yet */
 
-       if ((en = ospf_hash_find_header(nn->lsrth, domain, hh)) == NULL)
-       {
-         en = ospf_hash_get_header(nn->lsrth, domain, hh);
-       }
-       else
-       {
-         s_rem_node(SNODE en);
-       }
-       s_add_tail(&nn->lsrtl, SNODE en);
-       memcpy(&en->lsa, hh, sizeof(struct ospf_lsa_header));
-       DBG("Adding that LSA for flood to %I\n", nn->ip);
-      }
-      else
-      {
-       if ((en = ospf_hash_find_header(nn->lsrth, domain, hh)) != NULL)
-       {
-         s_rem_node(SNODE en);
-         ospf_hash_delete(nn->lsrth, en);
-       }
-      }
+      /* 13.3 (1d) - add LSA to the link state retransmission list */
+      ospf_lsa_lsrt_up(en, n);
 
-      ret = 1;
+      used = 1;
     }
 
-    if (ret == 0)
-      continue;                        /* pg 150 (2) */
+    /* 13.3 (2) */
+    if (!used)
+      continue;
 
-    if (n && (n->ifa == ifa))
+    if (from && (from->ifa == ifa))
     {
-      if ((n->rid == ifa->drid) || n->rid == ifa->bdrid)
-       continue;               /* pg 150 (3) */
+      /* 13.3 (3) */
+      if ((from->rid == ifa->drid) || (from->rid == ifa->bdrid))
+       continue;
+
+      /* 13.3 (4) */
       if (ifa->state == OSPF_IS_BACKUP)
-       continue;               /* pg 150 (4) */
-      retval = 1;
+       continue;
+
+      back = 1;
     }
 
+    /* 13.3 (5) - finally flood the packet */
+    ospf_lsupd_flood_ifa(p, ifa, en);
+  }
+
+  return back;
+}
+
+static uint
+ospf_prepare_lsupd(struct ospf_proto *p, struct ospf_iface *ifa,
+                  struct top_hash_entry **lsa_list, uint lsa_count)
+{
+  struct ospf_packet *pkt;
+  uint hlen, pos, i, maxsize;
+
+  pkt = ospf_tx_buffer(ifa);
+  hlen = ospf_lsupd_hdrlen(p);
+  maxsize = ospf_pkt_maxsize(ifa);
+
+  ospf_pkt_fill_hdr(ifa, pkt, LSUPD_P);
+  pos = hlen;
+
+  for (i = 0; i < lsa_count; i++)
+  {
+    struct top_hash_entry *en = lsa_list[i];
+    uint len = en->lsa.length;
+
+    if ((pos + len) > maxsize)
     {
-      u16 len, age;
-      struct ospf_lsupd_packet *pk;
-      struct ospf_packet *op;
-      struct ospf_lsa_header *lh;
-
-      /* Check iface buffer size */
-      uint len2 = sizeof(struct ospf_lsupd_packet) + (hn ? ntohs(hn->length) : hh->length);
-      if (ospf_iface_assure_bufsize(ifa, len2) < 0)
+      /* The packet if full, stop adding LSAs and sent it */
+      if (i > 0)
+       break;
+
+      /* LSA is larger than MTU, check buffer size */
+      if (ospf_iface_assure_bufsize(ifa, pos + len) < 0)
       {
-       /* Cannot fit in a tx buffer, skip that iface */
-       log(L_ERR "OSPF: LSA too large to flood on %s (Type: %04x, Id: %R, Rt: %R)", 
-           ifa->ifname, hh->type, hh->id, hh->rt);
-       continue;
+       /* Cannot fit in a tx buffer, skip that */
+       log(L_ERR "%s: LSA too large to send on %s (Type: %04x, Id: %R, Rt: %R)", 
+           p->p.name, ifa->ifname, en->lsa_type, en->lsa.id, en->lsa.rt);
+       break;
       }
 
-      pk = ospf_tx_buffer(ifa);
-      op = &pk->ospf_packet;
+      /* TX buffer could be reallocated */
+      pkt = ospf_tx_buffer(ifa);
+    }
 
-      ospf_pkt_fill_hdr(ifa, pk, LSUPD_P);
-      pk->lsano = htonl(1);
+    struct ospf_lsa_header *buf = ((void *) pkt) + pos;
+    lsa_hton_hdr(&en->lsa, buf);
+    lsa_hton_body(en->lsa_body, ((void *) buf) + sizeof(struct ospf_lsa_header),
+                 len - sizeof(struct ospf_lsa_header));
+    buf->age = htons(MIN(en->lsa.age + ifa->inftransdelay, LSA_MAXAGE));
 
-      lh = (struct ospf_lsa_header *) (pk + 1);
+    pos += len;
+  }
 
-      /* Copy LSA into the packet */
-      if (hn)
-      {
-       memcpy(lh, hn, ntohs(hn->length));
-      }
-      else
-      {
-       u8 *help;
-       struct top_hash_entry *en;
+  ospf_lsupd_set_lsa_count(pkt, hlen, i);
+  pkt->length = htons(pos);
 
-       htonlsah(hh, lh);
-       help = (u8 *) (lh + 1);
-       en = ospf_hash_find_header(po->gr, domain, hh);
-       htonlsab(en->lsa_body, help, hh->length - sizeof(struct ospf_lsa_header));
-      }
+  return i;
+}
 
-      len = sizeof(struct ospf_lsupd_packet) + ntohs(lh->length);
 
-      age = ntohs(lh->age);
-      age += ifa->inftransdelay;
-      if (age > LSA_MAXAGE)
-       age = LSA_MAXAGE;
-      lh->age = htons(age);
+static void
+ospf_lsupd_flood_ifa(struct ospf_proto *p, struct ospf_iface *ifa, struct top_hash_entry *en)
+{
+  uint c = ospf_prepare_lsupd(p, ifa, &en, 1);
 
-      op->length = htons(len);
+  if (!c)      /* Too large LSA */
+    return;
 
-      OSPF_PACKET(ospf_dump_lsupd, pk, "LSUPD packet flooded via %s", ifa->ifname);
+  OSPF_PACKET(ospf_dump_lsupd, ospf_tx_buffer(ifa),
+             "LSUPD packet flooded via %s", ifa->ifname);
 
-      switch (ifa->type)
-      {
-      case OSPF_IT_BCAST:
-       if ((ifa->state == OSPF_IS_BACKUP) || (ifa->state == OSPF_IS_DR))
-         ospf_send_to_all(ifa);
-       else if (ifa->cf->real_bcast)
-         ospf_send_to_bdr(ifa);
-       else
-         ospf_send_to(ifa, AllDRouters);
-       break;
+  if (ifa->type == OSPF_IT_BCAST)
+  {
+    if ((ifa->state == OSPF_IS_DR) || (ifa->state == OSPF_IS_BACKUP))
+      ospf_send_to_all(ifa);
+    else
+      ospf_send_to_des(ifa);
+  }
+  else
+    ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE);
+}
 
-      case OSPF_IT_NBMA:
-       if ((ifa->state == OSPF_IS_BACKUP) || (ifa->state == OSPF_IS_DR))
-         ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE);
-       else
-         ospf_send_to_bdr(ifa);
-       break;
+int
+ospf_send_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_list, uint lsa_count, struct ospf_neighbor *n)
+{
+  struct ospf_iface *ifa = n->ifa;
+  uint i, c;
 
-      case OSPF_IT_PTP:
-       ospf_send_to_all(ifa);
-       break;
+  for (i = 0; i < lsa_count; i += c)
+  {
+    c = ospf_prepare_lsupd(p, ifa, lsa_list + i, lsa_count - i);
 
-      case OSPF_IT_PTMP:
-       ospf_send_to_agt(ifa, NEIGHBOR_EXCHANGE);
-       break;
+    if (!c)    /* Too large LSA */
+      { i++; continue; }
 
-      case OSPF_IT_VLINK:
-       ospf_send_to(ifa, ifa->vip);
-       break;
+    OSPF_PACKET(ospf_dump_lsupd, ospf_tx_buffer(ifa),
+               "LSUPD packet sent to %I via %s", n->ip, ifa->ifname);
 
-      default:
-       bug("Bug in ospf_lsupd_flood()");
-      }
-    }
+    ospf_send_to(ifa, n->ip);
   }
-  return retval;
+
+  return lsa_count;
 }
 
-void                           /* I send all I received in LSREQ */
-ospf_lsupd_send_list(struct ospf_neighbor *n, list * l)
+void
+ospf_rxmt_lsupd(struct ospf_proto *p, struct ospf_neighbor *n)
 {
-  struct ospf_area *oa = n->ifa->oa;
-  struct proto *p = &oa->po->proto;
-  struct l_lsr_head *lsr;
-  struct top_hash_entry *en;
-  struct ospf_lsupd_packet *pkt;
-  u32 len, len2, lsano;
-  char *buf;
-
-  pkt = ospf_tx_buffer(n->ifa);
-  buf = (void *) pkt;
-
-  lsr = HEAD(*l);
-  while(NODE_NEXT(lsr))
+  const uint max = 128;
+  struct top_hash_entry *entries[max];
+  struct top_hash_entry *ret, *nxt, *en;
+  uint i = 0;
+
+  WALK_SLIST_DELSAFE(ret, nxt, n->lsrtl)
   {
-    /* Prepare the packet */
-    ospf_pkt_fill_hdr(n->ifa, pkt, LSUPD_P);
-    len = sizeof(struct ospf_lsupd_packet);
-    lsano = 0;
+    if (i == max)
+      break;
 
-    /* Fill the packet with LSAs */
-    while(NODE_NEXT(lsr))
+    en = ospf_hash_find_entry(p->gr, ret);
+    if (!en)
     {
-      u32 domain = ospf_lsa_domain(lsr->lsh.type, n->ifa);
-      en = ospf_hash_find(oa->po->gr, domain, lsr->lsh.id, lsr->lsh.rt, lsr->lsh.type);
-      if (en == NULL)
-      {
-       /* Probably flushed LSA, this should not happen */
-       // log(L_WARN "OSPF: LSA disappeared (Type: %04x, Id: %R, Rt: %R)", lsr->lsh.type, lsr->lsh.id, lsr->lsh.rt);
-       lsr = NODE_NEXT(lsr);
-       continue;                       
-      }
+      /* Probably flushed LSA, this should not happen */
+      log(L_WARN "%s: LSA disappeared (Type: %04x, Id: %R, Rt: %R)",
+         p->p.name, ret->lsa_type, ret->lsa.id, ret->lsa.rt);
 
-      len2 = len + en->lsa.length;
-      if (len2 > ospf_pkt_maxsize(n->ifa))
-      {
-       /* The packet if full, stop adding LSAs and sent it */
-       if (lsano > 0)
-         break;
+      s_rem_node(SNODE ret);
+      ospf_hash_delete(n->lsrth, ret);
 
-       /* LSA is larger than MTU, check buffer size */
-       if (ospf_iface_assure_bufsize(n->ifa, len2) < 0)
-       {
-         /* Cannot fit in a tx buffer, skip that */
-         log(L_ERR "OSPF: LSA too large to send (Type: %04x, Id: %R, Rt: %R)", 
-             lsr->lsh.type, lsr->lsh.id, lsr->lsh.rt);
-         lsr = NODE_NEXT(lsr);
-         continue;
-       }
+      continue;
+    }
 
-       /* TX buffer could be reallocated */
-       pkt = ospf_tx_buffer(n->ifa);
-       buf = (void *) pkt;
-      }
+    entries[i] = en;
+    i++;
+  }
 
-      /* Copy the LSA to the packet */
-      htonlsah(&(en->lsa), (struct ospf_lsa_header *) (buf + len));
-      htonlsab(en->lsa_body, buf + len + sizeof(struct ospf_lsa_header),
-              en->lsa.length - sizeof(struct ospf_lsa_header));
-      len = len2;
-      lsano++;
-      lsr = NODE_NEXT(lsr);
-    }
+  ospf_send_lsupd(p, entries, i, n);
+}
 
-    if (lsano == 0)
-      break;
 
-    /* Send the packet */
-    pkt->lsano = htonl(lsano);
-    pkt->ospf_packet.length = htons(len);
-    OSPF_PACKET(ospf_dump_lsupd, pkt, "LSUPD packet sent to %I via %s",
-               n->ip, n->ifa->ifname);
-    ospf_send_to(n->ifa, n->ip);
-  }
+static inline int
+ospf_addr_is_local(struct ospf_proto *p, struct ospf_area *oa, ip_addr ip)
+{
+  struct ospf_iface *ifa;
+  WALK_LIST(ifa, p->iface_list)
+    if ((ifa->oa == oa) && ifa->addr && ipa_equal(ifa->addr->ip, ip))
+      return 1;
+
+  return 0;
 }
 
 void
-ospf_lsupd_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
+ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
                   struct ospf_neighbor *n)
 {
+  struct ospf_proto *p = ifa->oa->po;
 
-  struct ospf_neighbor *ntmp;
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
-  unsigned int i, max, sendreq = 1;
+  /* RFC 2328 13. */
 
-  unsigned int size = ntohs(ps_i->length);
-  if (size < (sizeof(struct ospf_lsupd_packet) + sizeof(struct ospf_lsa_header)))
+  int sendreq = 1;       /* XXXX: review sendreq */
+
+  uint plen = ntohs(pkt->length);
+  if (plen < (ospf_lsupd_hdrlen(p) + sizeof(struct ospf_lsa_header)))
   {
-    log(L_ERR "OSPF: Bad LSUPD packet from %I - too short (%u B)", n->ip, size);
+    log(L_ERR "OSPF: Bad LSUPD packet from %I - too short (%u B)", n->ip, plen);
     return;
   }
 
-  struct ospf_lsupd_packet *ps = (void *) ps_i;
-  OSPF_PACKET(ospf_dump_lsupd, ps, "LSUPD packet received from %I via %s", n->ip, ifa->ifname);
+  OSPF_PACKET(ospf_dump_lsupd, pkt, "LSUPD packet received from %I via %s", n->ip, ifa->ifname);
 
   if (n->state < NEIGHBOR_EXCHANGE)
   {
@@ -468,296 +403,206 @@ ospf_lsupd_receive(struct ospf_packet *ps_i, struct ospf_iface *ifa,
 
   ospf_neigh_sm(n, INM_HELLOREC);      /* Questionable */
 
-  unsigned int offset = sizeof(struct ospf_lsupd_packet);
-  unsigned int bound = size - sizeof(struct ospf_lsa_header);
+  uint offset, bound, i, lsa_count;
+  ospf_lsupd_body(p, pkt, &offset, &bound, &lsa_count);
 
-  max = ntohl(ps->lsano);
-  for (i = 0; i < max; i++)
+  for (i = 0; i < lsa_count; i++)
   {
-    struct ospf_lsa_header lsatmp;
-    struct top_hash_entry *lsadb;
+    struct ospf_lsa_header lsa, *lsa_n;
+    struct top_hash_entry *en;
+    u32 lsa_len, lsa_type, lsa_domain;
 
     if (offset > bound)
     {
-      log(L_WARN "Received lsupd from %I is too short!", n->ip);
+      log(L_WARN "%s: Received LSUPD from %I is too short", p->p.name, n->ip);
       ospf_neigh_sm(n, INM_BADLSREQ);
       return;
     }
 
-    struct ospf_lsa_header *lsa = (void *) (((u8 *) ps) + offset);
-    unsigned int lsalen = ntohs(lsa->length);
-    offset += lsalen;
+    /* LSA header in network order */
+    lsa_n = ((void *) pkt) + offset;
+    lsa_len = ntohs(lsa_n->length);
+    offset += lsa_len;
  
-    if ((offset > size) || ((lsalen % 4) != 0) ||
-       (lsalen <= sizeof(struct ospf_lsa_header)))
+    if ((offset > plen) || ((lsa_len % 4) != 0) ||
+       (lsa_len <= sizeof(struct ospf_lsa_header)))
     {
-      log(L_WARN "Received LSA from %I with bad length", n->ip);
+      log(L_WARN "%s: Received LSA from %I with bad length", p->p.name, n->ip);
       ospf_neigh_sm(n, INM_BADLSREQ);
-      break;
+      return;
     }
 
-    /* pg 143 (1) */
-    u16 chsum = lsa->checksum;
-    if (chsum != lsasum_check(lsa, NULL))
+    /* RFC 2328 13. (1) - validate LSA checksum */
+    u16 chsum = lsa_n->checksum;
+    if (chsum != lsasum_check(lsa_n, NULL))
     {
-      log(L_WARN "Received bad lsa checksum from %I: %x %x", n->ip, chsum, lsa->checksum);
+      log(L_WARN "%s: Received LSA from %I with bad checskum: %x %x",
+         p->p.name, n->ip, chsum, lsa_n->checksum);
       continue;
     }
 
-#ifdef OSPFv2
-    /* pg 143 (2) */
-    if ((lsa->type == 0) || (lsa->type == 6) || (lsa->type > LSA_T_NSSA))
+    /* LSA header in host order */
+    lsa_ntoh_hdr(lsa_n, &lsa);
+    lsa_get_type_domain(&lsa, ifa, &lsa_type, &lsa_domain);
+
+    DBG("Update Type: %04x, Id: %R, Rt: %R, Sn: 0x%08x, Age: %u, Sum: %u\n",
+       lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum);
+
+    /* RFC 2328 13. (2) */
+    if (!lsa_type)
     {
-      log(L_WARN "Unknown LSA type from %I", n->ip);
+      log(L_WARN "%s: Received unknown LSA type from %I", p->p.name, n->ip);
       continue;
     }
 
-    /* pg 143 (3) */
-    if ((lsa->type == LSA_T_EXT) && !oa_is_ext(ifa->oa))
+    /* RFC 5340 4.5.1 (2) and RFC 2328 13. (3) */
+    if (!oa_is_ext(ifa->oa) && (LSA_SCOPE(lsa_type) == LSA_SCOPE_AS))
     {
-      log(L_WARN "Received External LSA in stub area from %I", n->ip);
+      log(L_WARN "%s: Received LSA with AS scope in stub area from %I", p->p.name, n->ip);
       continue;
     }
-#else /* OSPFv3 */
-    u16 scope = ntoht(lsa->type) & LSA_SCOPE_MASK;
 
-    /* 4.5.1 (2) */
-    if ((scope == LSA_SCOPE_AS) && !oa_is_ext(ifa->oa))
+    /* Errata 3746 to RFC 2328 - rt-summary-LSAs forbidden in stub areas */
+    if (!oa_is_ext(ifa->oa) && (lsa_type == LSA_T_SUM_RT))
     {
-      log(L_WARN "Received LSA with AS scope in stub area from %I", n->ip);
+      log(L_WARN "%s: Received rt-summary-LSA in stub area from %I", p->p.name, n->ip);
       continue;
     }
 
-    /* 4.5.1 (3) */
-    if (scope == LSA_SCOPE_RES)
+    /* RFC 5340 4.5.1 (3) */
+    if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES)
     {
-      log(L_WARN "Received LSA with invalid scope from %I", n->ip);
+      log(L_WARN "%s: Received LSA with invalid scope from %I", p->p.name, n->ip);
       continue;
     }
-#endif
-
-    ntohlsah(lsa, &lsatmp);
-
-    DBG("Update Type: %u ID: %R RT: %R, Sn: 0x%08x Age: %u, Sum: %u\n",
-       lsatmp.type, lsatmp.id, lsatmp.rt, lsatmp.sn, lsatmp.age, lsatmp.checksum);
 
-    /* FIXME domain should be link id for unknown LSA types with zero Ubit */
-    u32 domain = ospf_lsa_domain(lsatmp.type, ifa);
-    lsadb = ospf_hash_find_header(po->gr, domain, &lsatmp);
+    /* Find local copy of LSA in link state database */
+    en = ospf_hash_find(p->gr, lsa_domain, lsa.id, lsa.rt, lsa_type);
 
 #ifdef LOCAL_DEBUG
-    if (lsadb)
-      DBG("I have Type: %u ID: %R RT: %R, Sn: 0x%08x Age: %u, Sum: %u\n",
-         lsadb->lsa.type, lsadb->lsa.id, lsadb->lsa.rt,
-         lsadb->lsa.sn, lsadb->lsa.age, lsadb->lsa.checksum);
+    if (en)
+      DBG("I have Type: %04x, Id: %R, Rt: %R, Sn: 0x%08x, Age: %u, Sum: %u\n",
+         en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age, en->lsa.checksum);
 #endif
 
-    /* pg 143 (4) */
-    if ((lsatmp.age == LSA_MAXAGE) && (lsadb == NULL) && can_flush_lsa(po))
+    /* 13. (4) - ignore maxage LSA if i have no local copy */
+    if ((lsa.age == LSA_MAXAGE) && !en && (p->padj == 0))
     {
-      ospf_lsack_enqueue(n, lsa, ACKL_DIRECT);
+      /* 13.5. - schedule ACKs (tbl 19, case 5) */ 
+      ospf_enqueue_lsack(n, lsa_n, ACKL_DIRECT);
       continue;
     }
 
-    /* pg 144 (5) */
-    if ((lsadb == NULL) || (lsa_comp(&lsatmp, &lsadb->lsa) == CMP_NEWER))
+    /* 13. (5) - received LSA is newer (or no local copy) */
+    if (!en || (lsa_comp(&lsa, &en->lsa) == CMP_NEWER))
     {
-      struct ospf_iface *ift = NULL;
-      int self = (lsatmp.rt == po->router_id);
-
-      DBG("PG143(5): Received LSA is newer\n");
-
-#ifdef OSPFv2
-      /* 13.4 - check self-originated LSAs of NET type */
-      if ((!self) && (lsatmp.type == LSA_T_NET))
+      /* 13. (5a) - enforce minimum time between updates for received LSAs */
+      /* We also use this to ratelimit reactions to received self-originated LSAs */
+      if (en && ((now - en->inst_time) < MINLSARRIVAL))
       {
-       struct ospf_iface *nifa;
-       WALK_LIST(nifa, po->iface_list)
-       {
-         if (!nifa->iface)
-           continue;
-         if (ipa_equal(nifa->addr->ip, ipa_from_u32(lsatmp.id)))
-         {
-           self = 1;
-           break;
-         }
-       }
+       OSPF_TRACE(D_EVENTS, "Skipping LSA received in less that MinLSArrival");
+       sendreq = 0;
+       continue;
       }
-#endif
-
-      /* pg 145 (5f) - premature aging of self originated lsa */
-      if (self)
-      {
-       if ((lsatmp.age == LSA_MAXAGE) && (lsatmp.sn == LSA_MAXSEQNO))
-       {
-         ospf_lsack_enqueue(n, lsa, ACKL_DIRECT);
-         continue;
-       }
 
-       OSPF_TRACE(D_EVENTS, "Received old self-originated LSA (Type: %04x, Id: %R, Rt: %R)",
-                  lsatmp.type, lsatmp.id, lsatmp.rt);
+      /* Copy and validate LSA body */
+      int blen = lsa.length - sizeof(struct ospf_lsa_header);
+      void *body = mb_alloc(p->p.pool, blen);
+      lsa_ntoh_body(lsa_n + 1, body, blen);
 
-       if (lsadb)
-       {
-         OSPF_TRACE(D_EVENTS, "Reflooding new self-originated LSA with newer sequence number");
-         lsadb->lsa.sn = lsatmp.sn + 1;
-         lsadb->lsa.age = 0;
-         lsadb->inst_t = now;
-         lsadb->ini_age = 0;
-         lsasum_calculate(&lsadb->lsa, lsadb->lsa_body);
-         ospf_lsupd_flood(po, NULL, NULL, &lsadb->lsa, domain, 1);
-       }
-       else
-       {
-         OSPF_TRACE(D_EVENTS, "Premature aging it");
-         lsatmp.age = LSA_MAXAGE;
-         lsatmp.sn = LSA_MAXSEQNO;
-         lsa->age = htons(LSA_MAXAGE);
-         lsa->sn = htonl(LSA_MAXSEQNO);
-         lsasum_check(lsa, (lsa + 1)); /* It also calculates chsum! */
-         lsatmp.checksum = ntohs(lsa->checksum);
-         ospf_lsupd_flood(po, NULL, lsa, &lsatmp, domain, 0);
-       }
+      if (lsa_validate(&lsa, lsa_type, ospf_is_v2(p), body) == 0)
+      {
+       log(L_WARN "%s: Received invalid LSA from %I", p->p.name, n->ip);
+       mb_free(body);
+       sendreq = 0;
        continue;
       }
 
-      /* pg 144 (5a) */
-      if (lsadb && ((now - lsadb->inst_t) <= MINLSARRIVAL))    /* FIXME: test for flooding? */
+      /* 13. (5f) - handle self-originated LSAs, see also 13.4. */
+      if ((lsa.rt == p->router_id) ||
+         (ospf_is_v2(p) && (lsa_type == LSA_T_NET) && ospf_addr_is_local(p, ifa->oa, ipa_from_u32(lsa.id))))
       {
-       OSPF_TRACE(D_EVENTS, "Skipping LSA received in less that MINLSARRIVAL");
-       sendreq = 0;
+       OSPF_TRACE(D_EVENTS, "Received unexpected self-originated LSA");
+       ospf_advance_lsa(p, en, &lsa, lsa_type, lsa_domain, body);
        continue;
       }
 
-      /* Remove old from all ret lists */
-      /* pg 144 (5c) */
+      /* 13. (5c) - remove old LSA from all retransmission lists */
       /* Must be done before (5b), otherwise it also removes the new entries from (5b) */
-      if (lsadb)
-       WALK_LIST(ift, po->iface_list)
-         WALK_LIST(ntmp, ift->neigh_list)
-      {
-       struct top_hash_entry *en;
-       if (ntmp->state > NEIGHBOR_EXSTART)
-         if ((en = ospf_hash_find_header(ntmp->lsrth, domain, &lsadb->lsa)) != NULL)
-         {
-           s_rem_node(SNODE en);
-           ospf_hash_delete(ntmp->lsrth, en);
-         }
-      }
 
-      /* pg 144 (5b) */
-      if (ospf_lsupd_flood(po, n, lsa, &lsatmp, domain, 1) == 0)
+      if (en)
+       ospf_lsa_lsrt_down(en, n);
+
+      /*
       {
-       DBG("Wasn't flooded back\n");   /* ps 144(5e), pg 153 */
-       if (ifa->state == OSPF_IS_BACKUP)
-       {
-         if (ifa->drid == n->rid)
-           ospf_lsack_enqueue(n, lsa, ACKL_DELAY);
-       }
-       else
-         ospf_lsack_enqueue(n, lsa, ACKL_DELAY);
+       struct ospf_iface *ifi;
+       struct ospf_neighbor *ni;
+
+       WALK_LIST(ifi, p->iface_list)
+         WALK_LIST(ni, ifi->neigh_list)
+           if (ni->state > NEIGHBOR_EXSTART)
+             ospf_lsa_lsrt_down(en, ni);
       }
+      */
 
-      if ((lsatmp.age == LSA_MAXAGE) && (lsatmp.sn == LSA_MAXSEQNO)
-         && lsadb && can_flush_lsa(po))
-      {
-       flush_lsa(lsadb, po);
-       schedule_rtcalc(po);
-       continue;
-      }                                /* FIXME lsack? */
+      /* 13. (5d) - install new LSA into database */
+      en = ospf_install_lsa(p, &lsa, lsa_type, lsa_domain, body);
 
-      /* pg 144 (5d) */
-      void *body = mb_alloc(p->pool, lsatmp.length - sizeof(struct ospf_lsa_header));
-      ntohlsab(lsa + 1, body, lsatmp.length - sizeof(struct ospf_lsa_header));
+      /* RFC 5340 4.4.3 Events 6+7 - new Link LSA received */
+      if (lsa_type == LSA_T_LINK)
+       ospf_notify_net_lsa(ifa);
 
-      /* We will do validation check after flooding and
-        acknowledging given LSA to minimize problems
-        when communicating with non-validating peer */
-      if (lsa_validate(&lsatmp, body) == 0)
-      {
-       log(L_WARN "Received invalid LSA from %I", n->ip);
-       mb_free(body);
-       continue;       
-      }
+      /* 13. (5b) - flood new LSA */
+      int flood_back = ospf_lsupd_flood(p, en, n);
 
-      lsadb = lsa_install_new(po, &lsatmp, domain, body);
-      DBG("New LSA installed in DB\n");
+      /* 13.5. - schedule ACKs (tbl 19, cases 1+2) */ 
+      if (! flood_back)
+       if ((ifa->state != OSPF_IS_BACKUP) || (n->rid == ifa->drid))
+         ospf_enqueue_lsack(n, lsa_n, ACKL_DELAY);
 
-#ifdef OSPFv3
-      /* Events 6,7 from RFC5340 4.4.3. */
-      if ((lsatmp.type == LSA_T_LINK) &&
-         (ifa->state == OSPF_IS_DR))
-       schedule_net_lsa(ifa);
-#endif
+      /* FIXME: remove LSA entry if it is LSA_MAXAGE and it is possible? */
 
       continue;
     }
 
     /* FIXME pg145 (6) */
 
-    /* pg145 (7) */
-    if (lsa_comp(&lsatmp, &lsadb->lsa) == CMP_SAME)
+    /* 13. (7) - received LSA is same */
+    if (lsa_comp(&lsa, &en->lsa) == CMP_SAME)
     {
-      struct top_hash_entry *en;
-      DBG("PG145(7) Got the same LSA\n");
-      if ((en = ospf_hash_find_header(n->lsrth, lsadb->domain, &lsadb->lsa)) != NULL)
-      {
-       /* pg145 (7a) */
-       s_rem_node(SNODE en);
-       ospf_hash_delete(n->lsrth, en);
+      /* Duplicate LSA, treat as implicit ACK */
+      int implicit_ack = ospf_lsa_lsrt_down(en, n);
 
-       if (ifa->state == OSPF_IS_BACKUP)
-       {
-         if (n->rid == ifa->drid)
-           ospf_lsack_enqueue(n, lsa, ACKL_DELAY);
-       }
-      }
-      else
+      /* 13.5. - schedule ACKs (tbl 19, cases 3+4) */ 
+      if (implicit_ack)
       {
-       /* pg145 (7b) */
-       ospf_lsack_enqueue(n, lsa, ACKL_DIRECT);
+       if ((ifa->state == OSPF_IS_BACKUP) && (n->rid == ifa->drid))
+         ospf_enqueue_lsack(n, lsa_n, ACKL_DELAY);
       }
+      else
+       ospf_enqueue_lsack(n, lsa_n, ACKL_DIRECT);
+
       sendreq = 0;
       continue;
     }
 
-    /* pg145 (8) */
-    if ((lsadb->lsa.age == LSA_MAXAGE) && (lsadb->lsa.sn == LSA_MAXSEQNO))
+    /* 13. (8) - received LSA is older */
     {
-      continue;
-    }
+      /* Seqnum is wrapping, wait until it is flushed */
+      if ((en->lsa.age == LSA_MAXAGE) && (en->lsa.sn == LSA_MAXSEQNO))
+       continue;
 
-    {
-      list l;
-      struct l_lsr_head ll;
-      init_list(&l);
-      ll.lsh.id = lsadb->lsa.id;
-      ll.lsh.rt = lsadb->lsa.rt;
-      ll.lsh.type = lsadb->lsa.type;
-      add_tail(&l, NODE & ll);
-      ospf_lsupd_send_list(n, &l);
+      /* Send newer local copy back to neighbor */
+      /* FIXME - check for MinLSArrival ? */
+      ospf_send_lsupd(p, &en, 1, n);
     }
   }
 
   /* Send direct LSAs */
   ospf_lsack_send(n, ACKL_DIRECT);
 
+  /* If loading, ask for another part of neighbor's database */
   if (sendreq && (n->state == NEIGHBOR_LOADING))
-  {
-    ospf_lsreq_send(n);                /* Ask for another part of neighbor's database */
-  }
+    ospf_send_lsreq(p, n);
 }
 
-void
-ospf_lsupd_flush_nlsa(struct proto_ospf *po, struct top_hash_entry *en)
-{
-  struct ospf_lsa_header *lsa = &en->lsa;
-  struct proto *p = &po->proto;
-
-  lsa->age = LSA_MAXAGE;
-  lsa->sn = LSA_MAXSEQNO;
-  lsasum_calculate(lsa, en->lsa_body);
-  OSPF_TRACE(D_EVENTS, "Premature aging self originated lsa!");
-  OSPF_TRACE(D_EVENTS, "Type: %04x, Id: %R, Rt: %R", lsa->type, lsa->id, lsa->rt);
-  ospf_lsupd_flood(po, NULL, NULL, lsa, en->domain, 0);
-}
diff --git a/proto/ospf/lsupd.h b/proto/ospf/lsupd.h
deleted file mode 100644 (file)
index 8bacfe6..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- *      BIRD -- OSPF
- *
- *      (c) 2000--2004 Ondrej Filip <feela@network.cz>
- *
- *      Can be freely distributed and used under the terms of the GNU GPL.
- *
- */
-
-#ifndef _BIRD_OSPF_LSUPD_H_
-#define _BIRD_OSPF_LSUPD_H_
-
-void ospf_dump_lsahdr(struct proto *p, struct ospf_lsa_header *lsa_n);
-void ospf_dump_common(struct proto *p, struct ospf_packet *op);
-void ospf_lsupd_send_list(struct ospf_neighbor *n, list * l);
-void ospf_lsupd_receive(struct ospf_packet *ps_i,
-                       struct ospf_iface *ifa, struct ospf_neighbor *n);
-int ospf_lsupd_flood(struct proto_ospf *po,
-                    struct ospf_neighbor *n, struct ospf_lsa_header *hn,
-                    struct ospf_lsa_header *hh, u32 domain, int rtl);
-void ospf_lsupd_flush_nlsa(struct proto_ospf *po, struct top_hash_entry *en);
-int ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_iface *ifa);
-
-
-#endif /* _BIRD_OSPF_LSUPD_H_ */
index faaaf232573a99124d2549682c5b298ea78c6cfa..392f1d64b710ddcbea0e8e6d0dbdd4f1e4db2d40 100644 (file)
@@ -1,7 +1,9 @@
 /*
  *     BIRD -- OSPF
  *
- *     (c) 1999 - 2004 Ondrej Filip <feela@network.cz>
+ *     (c) 1999--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
@@ -26,8 +28,6 @@ const char *ospf_inm[] =
 };
 
 static void neigh_chstate(struct ospf_neighbor *n, u8 state);
-static struct ospf_neighbor *electbdr(list nl);
-static struct ospf_neighbor *electdr(list nl);
 static void neighbor_timer_hook(timer * timer);
 static void rxmt_timer_hook(timer * timer);
 static void ackd_timer_hook(timer * t);
@@ -37,11 +37,9 @@ init_lists(struct ospf_neighbor *n)
 {
   s_init_list(&(n->lsrql));
   n->lsrqh = ospf_top_new(n->pool);
-  s_init(&(n->lsrqi), &(n->lsrql));
 
   s_init_list(&(n->lsrtl));
   n->lsrth = ospf_top_new(n->pool);
-  s_init(&(n->lsrti), &(n->lsrtl));
 }
 
 /* Resets LSA request and retransmit lists.
@@ -59,9 +57,8 @@ reset_lists(struct ospf_neighbor *n)
 struct ospf_neighbor *
 ospf_neighbor_new(struct ospf_iface *ifa)
 {
-  struct proto *p = (struct proto *) (ifa->oa->po);
-  struct proto_ospf *po = ifa->oa->po;
-  struct pool *pool = rp_new(p->pool, "OSPF Neighbor");
+  struct ospf_proto *p = ifa->oa->po;
+  struct pool *pool = rp_new(p->p.pool, "OSPF Neighbor");
   struct ospf_neighbor *n = mb_allocz(pool, sizeof(struct ospf_neighbor));
 
   n->pool = pool;
@@ -72,14 +69,14 @@ ospf_neighbor_new(struct ospf_iface *ifa)
   n->state = NEIGHBOR_DOWN;
 
   init_lists(n);
-  s_init(&(n->dbsi), &(po->lsal));
+  s_init(&(n->dbsi), &(p->lsal));
 
   n->inactim = tm_new(pool);
   n->inactim->data = n;
   n->inactim->randomize = 0;
   n->inactim->hook = neighbor_timer_hook;
   n->inactim->recurrent = 0;
-  DBG("%s: Installing inactivity timer.\n", p->name);
+  DBG("%s: Installing inactivity timer.\n", p->p.name);
 
   n->rxmt_timer = tm_new(pool);
   n->rxmt_timer->data = n;
@@ -87,7 +84,7 @@ ospf_neighbor_new(struct ospf_iface *ifa)
   n->rxmt_timer->hook = rxmt_timer_hook;
   n->rxmt_timer->recurrent = ifa->rxmtint;
   tm_start(n->rxmt_timer, n->ifa->rxmtint);
-  DBG("%s: Installing rxmt timer.\n", p->name);
+  DBG("%s: Installing rxmt timer.\n", p->p.name);
 
   n->ackd_timer = tm_new(pool);
   n->ackd_timer->data = n;
@@ -97,7 +94,7 @@ ospf_neighbor_new(struct ospf_iface *ifa)
   init_list(&n->ackl[ACKL_DIRECT]);
   init_list(&n->ackl[ACKL_DELAY]);
   tm_start(n->ackd_timer, n->ifa->rxmtint / 2);
-  DBG("%s: Installing ackd timer.\n", p->name);
+  DBG("%s: Installing ackd timer.\n", p->p.name);
 
   return (n);
 }
@@ -110,64 +107,73 @@ ospf_neighbor_new(struct ospf_iface *ifa)
  * Many actions have to be taken acording to a change of state of a neighbor. It
  * starts rxmt timers, call interface state machine etc.
  */
-
 static void
 neigh_chstate(struct ospf_neighbor *n, u8 state)
 {
-  u8 oldstate;
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
+  u8 old_state = n->state;
+  int old_fadj = ifa->fadj;
 
-  oldstate = n->state;
+  if (state == old_state)
+    return;
 
-  if (oldstate != state)
-  {
-    struct ospf_iface *ifa = n->ifa;
-    struct proto_ospf *po = ifa->oa->po;
-    struct proto *p = &po->proto;
+  OSPF_TRACE(D_EVENTS, "Neighbor %I changes state from %s to %s",
+            n->ip, ospf_ns[old_state], ospf_ns[state]);
 
-    n->state = state;
+  n->state = state;
 
-    OSPF_TRACE(D_EVENTS, "Neighbor %I changes state from \"%s\" to \"%s\".",
-              n->ip, ospf_ns[oldstate], ospf_ns[state]);
+  if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY))
+    ospf_iface_sm(ifa, ISM_NEICH);
+  if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY))
+    ospf_iface_sm(ifa, ISM_NEICH);
 
-    if ((state == NEIGHBOR_2WAY) && (oldstate < NEIGHBOR_2WAY))
-      ospf_iface_sm(ifa, ISM_NEICH);
-    if ((state < NEIGHBOR_2WAY) && (oldstate >= NEIGHBOR_2WAY))
-      ospf_iface_sm(ifa, ISM_NEICH);
+  /* Increase number of partial adjacencies */
+  if ((state == NEIGHBOR_EXCHANGE) || (state == NEIGHBOR_LOADING))
+    p->padj++;
 
-    if (oldstate == NEIGHBOR_FULL)     /* Decrease number of adjacencies */
-    {
-      ifa->fadj--;
-      schedule_rt_lsa(ifa->oa);
-      if (ifa->type == OSPF_IT_VLINK) schedule_rt_lsa(ifa->voa);
-      schedule_net_lsa(ifa);
-    }
+  /* Decrease number of partial adjacencies */
+  if ((old_state == NEIGHBOR_EXCHANGE) || (old_state == NEIGHBOR_LOADING))
+    p->padj--;
 
-    if (state == NEIGHBOR_FULL)        /* Increase number of adjacencies */
-    {
-      ifa->fadj++;
-      schedule_rt_lsa(ifa->oa);
-      if (ifa->type == OSPF_IT_VLINK) schedule_rt_lsa(ifa->voa);
-      schedule_net_lsa(ifa);
-    }
-    if (state == NEIGHBOR_EXSTART)
-    {
-      if (n->adj == 0)         /* First time adjacency */
-      {
-       n->dds = random_u32();
-      }
-      n->dds++;
-      n->myimms.byte = 0;
-      n->myimms.bit.ms = 1;
-      n->myimms.bit.m = 1;
-      n->myimms.bit.i = 1;
-    }
-    if (state > NEIGHBOR_EXSTART)
-      n->myimms.bit.i = 0;
+  /* Increase number of full adjacencies */
+  if (state == NEIGHBOR_FULL)
+    ifa->fadj++;
+
+  /* Decrease number of full adjacencies */
+  if (old_state == NEIGHBOR_FULL)
+    ifa->fadj--;
+
+  if (ifa->fadj != old_fadj)
+  {
+    /* RFC 2328 12.4 Event 4 - neighbor enters/leaves Full state */
+    ospf_notify_rt_lsa(ifa->oa);
+    ospf_notify_net_lsa(ifa);
+
+    /* RFC 2328 12.4 Event 8 - vlink state change */
+    if (ifa->type == OSPF_IT_VLINK)
+      ospf_notify_rt_lsa(ifa->voa);
+  }
+
+  if (state == NEIGHBOR_EXSTART)
+  {
+    /* First time adjacency */
+    if (n->adj == 0)
+      n->dds = random_u32();
+
+    n->dds++;
+    n->myimms = DBDES_IMMS;
   }
+
+  if (state > NEIGHBOR_EXSTART)
+    n->myimms &= ~DBDES_I;
 }
 
+static inline u32 neigh_get_id(struct ospf_proto *p, struct ospf_neighbor *n)
+{ return ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid; }
+
 static struct ospf_neighbor *
-electbdr(list nl)
+elect_bdr(struct ospf_proto *p, list nl)
 {
   struct ospf_neighbor *neigh, *n1, *n2;
   u32 nid;
@@ -176,11 +182,7 @@ electbdr(list nl)
   n2 = NULL;
   WALK_LIST(neigh, nl)                 /* First try those decl. themselves */
   {
-#ifdef OSPFv2
-    nid = ipa_to_u32(neigh->ip);
-#else /* OSPFv3 */
-    nid = neigh->rid;
-#endif
+    nid = neigh_get_id(p, neigh);
 
     if (neigh->state >= NEIGHBOR_2WAY) /* Higher than 2WAY */
       if (neigh->priority > 0)         /* Eligible */
@@ -225,7 +227,7 @@ electbdr(list nl)
 }
 
 static struct ospf_neighbor *
-electdr(list nl)
+elect_dr(struct ospf_proto *p, list nl)
 {
   struct ospf_neighbor *neigh, *n;
   u32 nid;
@@ -233,11 +235,7 @@ electdr(list nl)
   n = NULL;
   WALK_LIST(neigh, nl)                 /* And now DR */
   {
-#ifdef OSPFv2
-    nid = ipa_to_u32(neigh->ip);
-#else /* OSPFv3 */
-    nid = neigh->rid;
-#endif
+    nid = neigh_get_id(p, neigh);
 
     if (neigh->state >= NEIGHBOR_2WAY) /* Higher than 2WAY */
       if (neigh->priority > 0)         /* Eligible */
@@ -264,13 +262,9 @@ electdr(list nl)
 static int
 can_do_adj(struct ospf_neighbor *n)
 {
-  struct ospf_iface *ifa;
-  struct proto *p;
-  int i;
-
-  ifa = n->ifa;
-  p = (struct proto *) (ifa->oa->po);
-  i = 0;
+  struct ospf_iface *ifa = n->ifa;
+  struct ospf_proto *p = ifa->oa->po;
+  int i = 0;
 
   switch (ifa->type)
   {
@@ -285,10 +279,10 @@ can_do_adj(struct ospf_neighbor *n)
     {
     case OSPF_IS_DOWN:
     case OSPF_IS_LOOP:
-      bug("%s: Iface %s in down state?", p->name, ifa->ifname);
+      bug("%s: Iface %s in down state?", p->p.name, ifa->ifname);
       break;
     case OSPF_IS_WAITING:
-      DBG("%s: Neighbor? on iface %s\n", p->name, ifa->ifname);
+      DBG("%s: Neighbor? on iface %s\n", p->p.name, ifa->ifname);
       break;
     case OSPF_IS_DROTHER:
       if (((n->rid == ifa->drid) || (n->rid == ifa->bdrid))
@@ -302,15 +296,15 @@ can_do_adj(struct ospf_neighbor *n)
        i = 1;
       break;
     default:
-      bug("%s: Iface %s in unknown state?", p->name, ifa->ifname);
+      bug("%s: Iface %s in unknown state?", p->p.name, ifa->ifname);
       break;
     }
     break;
   default:
-    bug("%s: Iface %s is unknown type?", p->name, ifa->ifname);
+    bug("%s: Iface %s is unknown type?", p->p.name, ifa->ifname);
     break;
   }
-  DBG("%s: Iface %s can_do_adj=%d\n", p->name, ifa->ifname, i);
+  DBG("%s: Iface %s can_do_adj=%d\n", p->p.name, ifa->ifname, i);
   return i;
 }
 
@@ -329,8 +323,7 @@ can_do_adj(struct ospf_neighbor *n)
 void
 ospf_neigh_sm(struct ospf_neighbor *n, int event)
 {
-  struct proto_ospf *po = n->ifa->oa->po;
-  struct proto *p = &po->proto;
+  struct ospf_proto *p = n->ifa->oa->po;
 
   DBG("Neighbor state machine for neighbor %I, event '%s'\n", n->ip,
             ospf_inm[event]);
@@ -341,23 +334,23 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event)
     neigh_chstate(n, NEIGHBOR_ATTEMPT);
     /* NBMA are used different way */
     break;
+
   case INM_HELLOREC:
-    switch (n->state)
-    {
-    case NEIGHBOR_ATTEMPT:
-    case NEIGHBOR_DOWN:
+    if ((n->state == NEIGHBOR_DOWN) ||
+       (n->state == NEIGHBOR_ATTEMPT))
       neigh_chstate(n, NEIGHBOR_INIT);
-    default:
-      tm_start(n->inactim, n->ifa->deadint);   /* Restart inactivity timer */
-      break;
-    }
+
+    /* Restart inactivity timer */
+    tm_start(n->inactim, n->ifa->deadint);
     break;
+
   case INM_2WAYREC:
     if (n->state < NEIGHBOR_2WAY)
       neigh_chstate(n, NEIGHBOR_2WAY);
     if ((n->state == NEIGHBOR_2WAY) && can_do_adj(n))
       neigh_chstate(n, NEIGHBOR_EXSTART);
     break;
+
   case INM_NEGDONE:
     if (n->state == NEIGHBOR_EXSTART)
     {
@@ -365,25 +358,22 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event)
 
       /* Reset DB summary list iterator */
       s_get(&(n->dbsi));
-      s_init(&(n->dbsi), &po->lsal);
+      s_init(&(n->dbsi), &p->lsal);
 
-      while (!EMPTY_LIST(n->ackl[ACKL_DELAY]))
-      {
-       struct lsah_n *no;
-       no = (struct lsah_n *) HEAD(n->ackl[ACKL_DELAY]);
-       rem_node(NODE no);
-       mb_free(no);
-      }
+      ospf_reset_lsack_queue(n);
     }
     else
       bug("NEGDONE and I'm not in EXSTART?");
     break;
+
   case INM_EXDONE:
     neigh_chstate(n, NEIGHBOR_LOADING);
     break;
+
   case INM_LOADDONE:
     neigh_chstate(n, NEIGHBOR_FULL);
     break;
+
   case INM_ADJOK:
     switch (n->state)
     {
@@ -404,6 +394,7 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event)
       break;
     }
     break;
+
   case INM_SEQMIS:
   case INM_BADLSREQ:
     if (n->state >= NEIGHBOR_EXCHANGE)
@@ -412,24 +403,27 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event)
       neigh_chstate(n, NEIGHBOR_EXSTART);
     }
     break;
+
   case INM_KILLNBR:
   case INM_LLDOWN:
   case INM_INACTTIM:
     reset_lists(n);
     neigh_chstate(n, NEIGHBOR_DOWN);
     break;
+
   case INM_1WAYREC:
     reset_lists(n);
     neigh_chstate(n, NEIGHBOR_INIT);
     break;
+
   default:
-    bug("%s: INM - Unknown event?", p->name);
+    bug("%s: INM - Unknown event?", p->p.name);
     break;
   }
 }
 
 /**
- * bdr_election - (Backup) Designed Router election
+ * ospf_dr_election - (Backup) Designed Router election
  * @ifa: actual interface
  *
  * When the wait timer fires, it is time to elect (Backup) Designated Router.
@@ -438,12 +432,11 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event)
  * Router. This process is described in 9.4 of RFC 2328.
  */
 void
-bdr_election(struct ospf_iface *ifa)
+ospf_dr_election(struct ospf_iface *ifa)
 {
-  struct proto_ospf *po = ifa->oa->po;
-  u32 myid = po->router_id;
+  struct ospf_proto *p = ifa->oa->po;
   struct ospf_neighbor *neigh, *ndr, *nbdr, me;
-  int doadj;
+  u32 myid = p->router_id;
 
   DBG("(B)DR election.\n");
 
@@ -452,19 +445,14 @@ bdr_election(struct ospf_iface *ifa)
   me.priority = ifa->priority;
   me.ip = ifa->addr->ip;
 
-#ifdef OSPFv2
-  me.dr = ipa_to_u32(ifa->drip);
-  me.bdr = ipa_to_u32(ifa->bdrip);
-#else /* OSPFv3 */
-  me.dr = ifa->drid;
-  me.bdr = ifa->bdrid;
+  me.dr  = ospf_is_v2(p) ? ipa_to_u32(ifa->drip) : ifa->drid;
+  me.bdr = ospf_is_v2(p) ? ipa_to_u32(ifa->bdrip) : ifa->bdrid;
   me.iface_id = ifa->iface_id;
-#endif
 
   add_tail(&ifa->neigh_list, NODE & me);
 
-  nbdr = electbdr(ifa->neigh_list);
-  ndr = electdr(ifa->neigh_list);
+  nbdr = elect_bdr(p, ifa->neigh_list);
+  ndr = elect_dr(p, ifa->neigh_list);
 
   if (ndr == NULL)
     ndr = nbdr;
@@ -475,56 +463,47 @@ bdr_election(struct ospf_iface *ifa)
       || ((ifa->bdrid == myid) && (nbdr != &me))
       || ((ifa->bdrid != myid) && (nbdr == &me)))
   {
-#ifdef OSPFv2
-    me.dr = ndr ? ipa_to_u32(ndr->ip) : 0;
-    me.bdr = nbdr ? ipa_to_u32(nbdr->ip) : 0;
-#else /* OSPFv3 */
-    me.dr = ndr ? ndr->rid : 0;
-    me.bdr = nbdr ? nbdr->rid : 0;
-#endif
+    me.dr = ndr ? neigh_get_id(p, ndr) : 0;
+    me.bdr = nbdr ? neigh_get_id(p, nbdr) : 0;
 
-    nbdr = electbdr(ifa->neigh_list);
-    ndr = electdr(ifa->neigh_list);
+    nbdr = elect_bdr(p, ifa->neigh_list);
+    ndr = elect_dr(p, ifa->neigh_list);
 
     if (ndr == NULL)
       ndr = nbdr;
   }
 
-  u32 odrid = ifa->drid;
-  u32 obdrid = ifa->bdrid;
+  rem_node(NODE & me);
+
+
+  u32 old_drid = ifa->drid;
+  u32 old_bdrid = ifa->bdrid;
  
   ifa->drid = ndr ? ndr->rid : 0;
   ifa->drip = ndr ? ndr->ip  : IPA_NONE;
+  ifa->dr_iface_id = ndr ? ndr->iface_id : 0;
+
   ifa->bdrid = nbdr ? nbdr->rid : 0;
   ifa->bdrip = nbdr ? nbdr->ip  : IPA_NONE;
 
-#ifdef OSPFv3
-  ifa->dr_iface_id = ndr ? ndr->iface_id : 0;
-#endif
-
   DBG("DR=%R, BDR=%R\n", ifa->drid, ifa->bdrid);
 
-  doadj = ((ifa->drid != odrid) || (ifa->bdrid != obdrid));
-
-  if (myid == ifa->drid)
+  if (ifa->drid == myid)
     ospf_iface_chstate(ifa, OSPF_IS_DR);
+  else if (ifa->bdrid == myid)
+    ospf_iface_chstate(ifa, OSPF_IS_BACKUP);
   else
-  {
-    if (myid == ifa->bdrid)
-      ospf_iface_chstate(ifa, OSPF_IS_BACKUP);
-    else
-      ospf_iface_chstate(ifa, OSPF_IS_DROTHER);
-  }
+    ospf_iface_chstate(ifa, OSPF_IS_DROTHER);
 
-  rem_node(NODE & me);
-
-  if (doadj)
-  {
+  /* Review neighbor adjacencies if DR or BDR changed */
+  if ((ifa->drid != old_drid) || (ifa->bdrid != old_bdrid))
     WALK_LIST(neigh, ifa->neigh_list)
-    {
-      ospf_neigh_sm(neigh, INM_ADJOK);
-    }
-  }
+      if (neigh->state >= NEIGHBOR_2WAY)
+       ospf_neigh_sm(neigh, INM_ADJOK);
+
+  /* RFC 2328 12.4 Event 3 - DR change */
+  if (ifa->drid != old_drid)
+    ospf_notify_rt_lsa(ifa->oa);
 }
 
 struct ospf_neighbor *
@@ -553,9 +532,9 @@ neighbor_timer_hook(timer * timer)
 {
   struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data;
   struct ospf_iface *ifa = n->ifa;
-  struct proto *p = &ifa->oa->po->proto;
+  struct ospf_proto *p = ifa->oa->po;
 
-  OSPF_TRACE(D_EVENTS, "Inactivity timer fired on interface %s for neighbor %I.",
+  OSPF_TRACE(D_EVENTS, "Inactivity timer fired on interface %s for neighbor %I",
             ifa->ifname, n->ip);
   ospf_neigh_remove(n);
 }
@@ -564,7 +543,7 @@ void
 ospf_neigh_remove(struct ospf_neighbor *n)
 {
   struct ospf_iface *ifa = n->ifa;
-  struct proto *p = &ifa->oa->po->proto;
+  struct ospf_proto *p = ifa->oa->po;
 
   if ((ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP))
   {
@@ -577,20 +556,18 @@ ospf_neigh_remove(struct ospf_neighbor *n)
   neigh_chstate(n, NEIGHBOR_DOWN);
   rem_node(NODE n);
   rfree(n->pool);
-  OSPF_TRACE(D_EVENTS, "Deleting neigbor.");
+  OSPF_TRACE(D_EVENTS, "Deleting neigbor %R", n->rid);
 }
 
 static void
 ospf_neigh_bfd_hook(struct bfd_request *req)
 {
   struct ospf_neighbor *n = req->data;
-  struct proto *p = &n->ifa->oa->po->proto;
+  struct ospf_proto *p = n->ifa->oa->po;
 
   if (req->down)
   {
-    OSPF_TRACE(D_EVENTS, "BFD session down for %I on %s",
-              n->ip, n->ifa->ifname);
-
+    OSPF_TRACE(D_EVENTS, "BFD session down for %I on %s", n->ip, n->ifa->ifname);
     ospf_neigh_remove(n);
   }
 }
@@ -646,52 +623,32 @@ static void
 rxmt_timer_hook(timer * timer)
 {
   struct ospf_neighbor *n = (struct ospf_neighbor *) timer->data;
-  // struct proto *p = &n->ifa->oa->po->proto;
-  struct top_hash_entry *en;
-
-  DBG("%s: RXMT timer fired on interface %s for neigh: %I.\n",
-      p->name, n->ifa->ifname, n->ip);
+  struct ospf_proto *p = n->ifa->oa->po;
 
-  if(n->state < NEIGHBOR_EXSTART) return;
+  DBG("%s: RXMT timer fired on interface %s for neigh %I\n",
+      p->p.name, n->ifa->ifname, n->ip);
 
-  if (n->state == NEIGHBOR_EXSTART)
+  switch (n->state)
   {
-    ospf_dbdes_send(n, 1);
+  case NEIGHBOR_EXSTART:
+    ospf_send_dbdes(n, 1);
     return;
-  }
-
-  if ((n->state == NEIGHBOR_EXCHANGE) && n->myimms.bit.ms)     /* I'm master */
-    ospf_dbdes_send(n, 0);
 
+  case NEIGHBOR_EXCHANGE:
+  if (n->myimms & DBDES_MS)
+    ospf_send_dbdes(n, 0);
+  case NEIGHBOR_LOADING:
+    ospf_send_lsreq(p, n);
+    return;
 
-  if (n->state < NEIGHBOR_FULL)        
-    ospf_lsreq_send(n);        /* EXCHANGE or LOADING */
-  else
-  {
-    if (!EMPTY_SLIST(n->lsrtl))        /* FULL */
-    {
-      list uplist;
-      slab *upslab;
-      struct l_lsr_head *llsh;
-
-      init_list(&uplist);
-      upslab = sl_new(n->pool, sizeof(struct l_lsr_head));
+  case NEIGHBOR_FULL:
+    /* LSA retransmissions */
+    if (!EMPTY_SLIST(n->lsrtl))
+      ospf_rxmt_lsupd(p, n);
+    return;
 
-      WALK_SLIST(en, n->lsrtl)
-      {
-       if ((SNODE en)->next == (SNODE en))
-         bug("RTList is cycled");
-       llsh = sl_alloc(upslab);
-       llsh->lsh.id = en->lsa.id;
-       llsh->lsh.rt = en->lsa.rt;
-       llsh->lsh.type = en->lsa.type;
-       DBG("Working on ID: %R, RT: %R, Type: %u\n",
-           en->lsa.id, en->lsa.rt, en->lsa.type);
-       add_tail(&uplist, NODE llsh);
-      }
-      ospf_lsupd_send_list(n, &uplist);
-      rfree(upslab);
-    }
+  default:
+    return;
   }
 }
 
diff --git a/proto/ospf/neighbor.h b/proto/ospf/neighbor.h
deleted file mode 100644 (file)
index e674927..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- *      BIRD -- OSPF
- *
- *      (c) 1999 - 2004 Ondrej Filip <feela@network.cz>
- *
- *      Can be freely distributed and used under the terms of the GNU GPL.
- *
- */
-
-#ifndef _BIRD_OSPF_NEIGHBOR_H_
-#define _BIRD_OSPF_NEIGHBOR_H_
-
-struct ospf_neighbor *ospf_neighbor_new(struct ospf_iface *ifa);
-void ospf_neigh_sm(struct ospf_neighbor *n, int event);
-void bdr_election(struct ospf_iface *ifa);
-struct ospf_neighbor *find_neigh(struct ospf_iface *ifa, u32 rid);
-struct ospf_neighbor *find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip);
-void ospf_neigh_remove(struct ospf_neighbor *n);
-void ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd);
-void ospf_sh_neigh_info(struct ospf_neighbor *n);
-
-#endif /* _BIRD_OSPF_NEIGHBOR_H_ */
index 6756ff49063013a8290975f09946bc907554f0f2..abcd527a767bad947c62cbd46cbd9828359f2df9 100644 (file)
@@ -2,6 +2,8 @@
  *     BIRD -- OSPF
  *
  *     (c) 1999--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
@@ -35,7 +37,7 @@
  * One instance of the protocol is able to hold LSA databases for
  * multiple OSPF areas, to exchange routing information between
  * multiple neighbors and to calculate the routing tables. The core
- * structure is &proto_ospf to which multiple &ospf_area and
+ * structure is &ospf_proto to which multiple &ospf_area and
  * &ospf_iface structures are connected. &ospf_area is also connected to
  * &top_hash_graph which is a dynamic hashing structure that
  * describes the link-state database. It allows fast search, addition
@@ -69,7 +71,7 @@
  * ospf_iface_down()).
  *
  * The heart beat of ospf is ospf_disp(). It is called at regular intervals
- * (&proto_ospf->tick). It is responsible for aging and flushing of LSAs in
+ * (&ospf_proto->tick). It is responsible for aging and flushing of LSAs in
  * the database, for routing table calculaction and it call area_disp() of every
  * ospf_area.
  * 
 #include <stdlib.h>
 #include "ospf.h"
 
-
-static int ospf_reload_routes(struct proto *p);
-static void ospf_rt_notify(struct proto *p, struct rtable *table UNUSED, net * n, rte * new, rte * old UNUSED, ea_list * attrs);
+static int ospf_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool);
+static struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
+static void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs);
+static int ospf_reload_routes(struct proto *P);
 static int ospf_rte_better(struct rte *new, struct rte *old);
 static int ospf_rte_same(struct rte *new, struct rte *old);
 static void ospf_disp(timer *timer);
@@ -114,82 +117,78 @@ ospf_area_initfib(struct fib_node *fn)
 {
   struct area_net *an = (struct area_net *) fn;
   an->hidden = 0;
-  an->active = -1;     /* Force to regenerate summary lsa */
-       /* ac->oldactive will be rewritten by ospf_rt_spf() */
+  an->active = 0;
 }
 
 static void
 add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac)
 {
-    struct proto_ospf *po = oa->po;
-    struct area_net_config *anc;
-    struct area_net *an;
+  struct ospf_proto *p = oa->po;
+  struct area_net_config *anc;
+  struct area_net *an;
 
-    fib_init(&oa->net_fib, po->proto.pool, sizeof(struct area_net), 0, ospf_area_initfib);
-    fib_init(&oa->enet_fib, po->proto.pool, sizeof(struct area_net), 0, ospf_area_initfib);
+  fib_init(&oa->net_fib, p->p.pool, sizeof(struct area_net), 0, ospf_area_initfib);
+  fib_init(&oa->enet_fib, p->p.pool, sizeof(struct area_net), 0, ospf_area_initfib);
 
-    WALK_LIST(anc, ac->net_list)
-    {
-      an = (struct area_net *) fib_get(&oa->net_fib, &anc->px.addr, anc->px.len);
-      an->hidden = anc->hidden;
-    }
+  WALK_LIST(anc, ac->net_list)
+  {
+    an = (struct area_net *) fib_get(&oa->net_fib, &anc->px.addr, anc->px.len);
+    an->hidden = anc->hidden;
+  }
 
-    WALK_LIST(anc, ac->enet_list)
-    {
-      an = (struct area_net *) fib_get(&oa->enet_fib, &anc->px.addr, anc->px.len);
-      an->hidden = anc->hidden;
-      an->tag = anc->tag;
-    }
+  WALK_LIST(anc, ac->enet_list)
+  {
+    an = (struct area_net *) fib_get(&oa->enet_fib, &anc->px.addr, anc->px.len);
+    an->hidden = anc->hidden;
+    an->tag = anc->tag;
+  }
 }
 
 static void
-ospf_area_add(struct proto_ospf *po, struct ospf_area_config *ac, int reconf)
+ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac, int reconf)
 {
-  struct proto *p = &po->proto;
   struct ospf_area *oa;
 
   OSPF_TRACE(D_EVENTS, "Adding area %R", ac->areaid);
 
-  oa = mb_allocz(p->pool, sizeof(struct ospf_area));
-  add_tail(&po->area_list, NODE oa);
-  po->areano++;
+  oa = mb_allocz(p->p.pool, sizeof(struct ospf_area));
+  add_tail(&p->area_list, NODE oa);
+  p->areano++;
 
   oa->ac = ac;
   oa->areaid = ac->areaid;
   oa->rt = NULL;
-  oa->po = po;
-  fib_init(&oa->rtr, p->pool, sizeof(ort), 0, ospf_rt_initort);
+  oa->po = p;
+  fib_init(&oa->rtr, p->p.pool, sizeof(ort), 0, ospf_rt_initort);
   add_area_nets(oa, ac);
 
   if (oa->areaid == 0)
-    po->backbone = oa;
+    p->backbone = oa;
 
-#ifdef OSPFv2
-  oa->options = ac->type;
-#else /* OSPFv3 */
-  oa->options = ac->type | OPT_V6 | (po->stub_router ? 0 : OPT_R);
-#endif
+  if (ospf_is_v2(p))
+    oa->options = ac->type;
+  else
+    oa->options = ac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R);
+}
 
-  /*
-   * Set E-bit for NSSA ABR routers. No need to explicitly call
-   * schedule_rt_lsa() for other areas, will be done anyway.
-   * We use cf->abr because po->areano is not yet complete.
-   */
-  if (oa_is_nssa(oa) && ((struct ospf_config *) (p->cf))->abr)
-    po->ebit = 1;
+static void
+ospf_flush_area(struct ospf_proto *p, u32 areaid)
+{
+  struct top_hash_entry *en;
 
-  if (reconf)
-    ospf_ifaces_reconfigure(oa, ac);
+  WALK_SLIST(en, p->lsal)
+    if ((LSA_SCOPE(en->lsa_type) == LSA_SCOPE_AREA) && (en->domain == areaid))
+      ospf_flush_lsa(p, en);
 }
 
 static void
 ospf_area_remove(struct ospf_area *oa)
 {
-  struct proto *p = &oa->po->proto;
+  struct ospf_proto *p = oa->po;
   OSPF_TRACE(D_EVENTS, "Removing area %R", oa->areaid);
 
   /* We suppose that interfaces are already removed */
-  ospf_flush_area(oa->po, oa->areaid);
+  ospf_flush_area(p, oa->areaid);
  
   fib_free(&oa->rtr);
   fib_free(&oa->net_fib);
@@ -198,87 +197,88 @@ ospf_area_remove(struct ospf_area *oa)
   if (oa->translator_timer)
     rfree(oa->translator_timer);
 
-  oa->po->areano--;
+  p->areano--;
   rem_node(NODE oa);
   mb_free(oa);
 }
 
 
 struct ospf_area *
-ospf_find_area(struct proto_ospf *po, u32 aid)
+ospf_find_area(struct ospf_proto *p, u32 aid)
 {
   struct ospf_area *oa;
-  WALK_LIST(oa, po->area_list)
+  WALK_LIST(oa, p->area_list)
     if (((struct ospf_area *) oa)->areaid == aid)
       return oa;
   return NULL;
 }
 
 static struct ospf_iface *
-ospf_find_vlink(struct proto_ospf *po, u32 voa, u32 vid)
+ospf_find_vlink(struct ospf_proto *p, u32 voa, u32 vid)
 {
   struct ospf_iface *ifa;
-  WALK_LIST(ifa, po->iface_list) 
+  WALK_LIST(ifa, p->iface_list) 
     if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa->areaid == voa) && (ifa->vid == vid))
       return ifa;
   return NULL;
 }
 
 static int
-ospf_start(struct proto *p)
+ospf_start(struct proto *P)
 {
-  struct proto_ospf *po = (struct proto_ospf *) p;
-  struct ospf_config *c = (struct ospf_config *) (p->cf);
+  struct ospf_proto *p = (struct ospf_proto *) P;
+  struct ospf_config *c = (struct ospf_config *) (P->cf);
   struct ospf_area_config *ac;
 
-  po->router_id = proto_get_router_id(p->cf);
-  po->rfc1583 = c->rfc1583;
-  po->stub_router = c->stub_router;
-  po->merge_external = c->merge_external;
-  po->ebit = 0;
-  po->ecmp = c->ecmp;
-  po->tick = c->tick;
-  po->disp_timer = tm_new(p->pool);
-  po->disp_timer->data = po;
-  po->disp_timer->randomize = 0;
-  po->disp_timer->hook = ospf_disp;
-  po->disp_timer->recurrent = po->tick;
-  tm_start(po->disp_timer, 1);
-  po->lsab_size = 256;
-  po->lsab_used = 0;
-  po->lsab = mb_alloc(p->pool, po->lsab_size);
-  po->nhpool = lp_new(p->pool, 12*sizeof(struct mpnh));
-  init_list(&(po->iface_list));
-  init_list(&(po->area_list));
-  fib_init(&po->rtf, p->pool, sizeof(ort), 0, ospf_rt_initort);
-  po->areano = 0;
-  po->gr = ospf_top_new(p->pool);
-  s_init_list(&(po->lsal));
+  p->router_id = proto_get_router_id(P->cf);
+  p->ospf2 = c->ospf2;
+  p->rfc1583 = c->rfc1583;
+  p->stub_router = c->stub_router;
+  p->merge_external = c->merge_external;
+  p->asbr = c->asbr;
+  p->ecmp = c->ecmp;
+  p->tick = c->tick;
+  p->disp_timer = tm_new(P->pool);
+  p->disp_timer->data = p;
+  p->disp_timer->randomize = 0;
+  p->disp_timer->hook = ospf_disp;
+  p->disp_timer->recurrent = p->tick;
+  tm_start(p->disp_timer, 1);
+  p->lsab_size = 256;
+  p->lsab_used = 0;
+  p->lsab = mb_alloc(P->pool, p->lsab_size);
+  p->nhpool = lp_new(P->pool, 12*sizeof(struct mpnh));
+  init_list(&(p->iface_list));
+  init_list(&(p->area_list));
+  fib_init(&p->rtf, P->pool, sizeof(ort), 0, ospf_rt_initort);
+  p->areano = 0;
+  p->gr = ospf_top_new(P->pool);
+  s_init_list(&(p->lsal));
 
   WALK_LIST(ac, c->area_list)
-    ospf_area_add(po, ac, 0);
+    ospf_area_add(p, ac, 0);
 
   if (c->abr)
-    ospf_open_vlink_sk(po);
+    ospf_open_vlink_sk(p);
 
   /* Add all virtual links */
   struct ospf_iface_patt *ic;
   WALK_LIST(ic, c->vlink_list)
-    ospf_iface_new_vlink(po, ic);
+    ospf_iface_new_vlink(p, ic);
 
   return PS_UP;
 }
 
 static void
-ospf_dump(struct proto *p)
+ospf_dump(struct proto *P)
 {
+  struct ospf_proto *p = (struct ospf_proto *) P;
   struct ospf_iface *ifa;
   struct ospf_neighbor *n;
-  struct proto_ospf *po = (struct proto_ospf *) p;
 
-  OSPF_TRACE(D_EVENTS, "Area number: %d", po->areano);
+  OSPF_TRACE(D_EVENTS, "Area number: %d", p->areano);
 
-  WALK_LIST(ifa, po->iface_list)
+  WALK_LIST(ifa, p->iface_list)
   {
     OSPF_TRACE(D_EVENTS, "Interface: %s", ifa->ifname);
     OSPF_TRACE(D_EVENTS, "state: %u", ifa->state);
@@ -292,7 +292,7 @@ ospf_dump(struct proto *p)
 
   /*
   OSPF_TRACE(D_EVENTS, "LSA graph dump start:");
-  ospf_top_dump(po->gr, p);
+  ospf_top_dump(p->gr, p);
   OSPF_TRACE(D_EVENTS, "LSA graph dump finished");
   */
   neigh_dump_all();
@@ -301,20 +301,21 @@ ospf_dump(struct proto *p)
 static struct proto *
 ospf_init(struct proto_config *c)
 {
-  struct proto *p = proto_new(c, sizeof(struct proto_ospf));
-
-  p->accept_ra_types = RA_OPTIMAL;
-  p->rt_notify = ospf_rt_notify;
-  p->if_notify = ospf_if_notify;
-  p->ifa_notify = ospf_ifa_notify;
-  p->import_control = ospf_import_control;
-  p->reload_routes = ospf_reload_routes;
-  p->make_tmp_attrs = ospf_make_tmp_attrs;
-  p->store_tmp_attrs = ospf_store_tmp_attrs;
-  p->rte_better = ospf_rte_better;
-  p->rte_same = ospf_rte_same;
-
-  return p;
+  struct ospf_config *oc = (struct ospf_config *) c;  
+  struct proto *P = proto_new(c, sizeof(struct ospf_proto));
+
+  P->accept_ra_types = RA_OPTIMAL;
+  P->rt_notify = ospf_rt_notify;
+  P->if_notify = ospf_if_notify;
+  P->ifa_notify = oc->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3;
+  P->import_control = ospf_import_control;
+  P->reload_routes = ospf_reload_routes;
+  P->make_tmp_attrs = ospf_make_tmp_attrs;
+  P->store_tmp_attrs = ospf_store_tmp_attrs;
+  P->rte_better = ospf_rte_better;
+  P->rte_same = ospf_rte_same;
+
+  return P;
 }
 
 /* If new is better return 1 */
@@ -379,116 +380,53 @@ ospf_build_attrs(ea_list * next, struct linpool *pool, u32 m1, u32 m2,
   return l;
 }
 
-void
-schedule_net_lsa(struct ospf_iface *ifa)
-{
-  struct proto *p = &ifa->oa->po->proto;
-
-  OSPF_TRACE(D_EVENTS, "Scheduling network-LSA origination for iface %s", ifa->ifname);
-  ifa->orignet = 1;
-}
-
-#ifdef OSPFv3
-void
-schedule_link_lsa(struct ospf_iface *ifa)
-{
-  struct proto *p = &ifa->oa->po->proto;
-
-  OSPF_TRACE(D_EVENTS, "Scheduling link-LSA origination for iface %s", ifa->ifname);
-  ifa->origlink = 1;
-}
-#endif
-
-void
-schedule_rt_lsa(struct ospf_area *oa)
-{
-  struct proto *p = &oa->po->proto;
-
-  OSPF_TRACE(D_EVENTS, "Scheduling router-LSA origination for area %R", oa->areaid);
-  oa->origrt = 1;
-}
 
 void
-schedule_rtcalc(struct proto_ospf *po)
+schedule_rtcalc(struct ospf_proto *p)
 {
-  struct proto *p = &po->proto;
-
-  if (po->calcrt)
+  if (p->calcrt)
     return;
 
   OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation");
-  po->calcrt = 1;
+  p->calcrt = 1;
 }
 
 static int
-ospf_reload_routes(struct proto *p)
+ospf_reload_routes(struct proto *P)
 {
-  struct proto_ospf *po = (struct proto_ospf *) p;
+  struct ospf_proto *p = (struct ospf_proto *) P;
 
-  if (po->calcrt != 2)
+  if (p->calcrt != 2)
     OSPF_TRACE(D_EVENTS, "Scheduling routing table calculation with route reload");
 
-  po->calcrt = 2;
+  p->calcrt = 2;
 
   return 1;
 }
 
-/**
- * area_disp - invokes origination of
- * router LSA and routing table cleanup
- * @oa: ospf area
- *
- * It invokes aging and when @ospf_area->origrt is set to 1, start
- * function for origination of router, network LSAs.
- */
-void
-area_disp(struct ospf_area *oa)
-{
-  struct proto_ospf *po = oa->po;
-  struct ospf_iface *ifa;
-
-  /* Now try to originage rt_lsa */
-  if (oa->origrt)
-    update_rt_lsa(oa);
-
-  /* Now try to originate network LSA's */
-  WALK_LIST(ifa, po->iface_list)
-  {
-#ifdef OSPFv3
-    /* Link LSA should be originated before Network LSA */
-    if (ifa->origlink && (ifa->oa == oa))
-      update_link_lsa(ifa);
-#endif
-
-    if (ifa->orignet && (ifa->oa == oa))
-      update_net_lsa(ifa);
-  }
-}
 
 /**
  * ospf_disp - invokes routing table calculation, aging and also area_disp()
- * @timer: timer usually called every @proto_ospf->tick second, @timer->data
- * point to @proto_ospf
+ * @timer: timer usually called every @ospf_proto->tick second, @timer->data
+ * point to @ospf_proto
  */
-void
+static void
 ospf_disp(timer * timer)
 {
-  struct proto_ospf *po = timer->data;
-  struct ospf_area *oa;
+  struct ospf_proto *p = timer->data;
 
-  WALK_LIST(oa, po->area_list)
-    area_disp(oa);
+  /* Originate or flush local topology LSAs */
+  ospf_update_topology(p);
 
   /* Age LSA DB */
-  ospf_age(po);
+  ospf_update_lsadb(p);
 
   /* Calculate routing table */
-  if (po->calcrt)
-    ospf_rt_spf(po);
+  if (p->calcrt)
+    ospf_rt_spf(p);
 }
 
 
-
 /**
  * ospf_import_control - accept or reject new route from nest's routing table
  * @p: current instance of protocol
@@ -499,15 +437,14 @@ ospf_disp(timer * timer)
  * Its quite simple. It does not accept our own routes and leaves the decision on
  * import to the filters.
  */
-
-int
-ospf_import_control(struct proto *p, rte ** new, ea_list ** attrs,
-                   struct linpool *pool)
+static int
+ospf_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool)
 {
-  struct ospf_area *oa = ospf_main_area((struct proto_ospf *) p);
+  struct ospf_proto *p = (struct ospf_proto *) P;
+  struct ospf_area *oa = ospf_main_area(p);
   rte *e = *new;
 
-  if (e->attrs->src->proto == p)
+  if (e->attrs->src->proto == P)
     return -1;                 /* Reject our own routes */
 
   if (oa_is_stub(oa))
@@ -520,14 +457,14 @@ ospf_import_control(struct proto *p, rte ** new, ea_list ** attrs,
   return 0;                    /* Leave decision to the filters */
 }
 
-struct ea_list *
+static struct ea_list *
 ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool)
 {
   return ospf_build_attrs(NULL, pool, rt->u.ospf.metric1, rt->u.ospf.metric2,
                          rt->u.ospf.tag, rt->u.ospf.router_id);
 }
 
-void
+static void
 ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs)
 {
   rt->u.ospf.metric1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY);
@@ -545,20 +482,20 @@ ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs)
  * them hello packet with empty neighbor list. They should start
  * their neighbor state machine with event %NEIGHBOR_1WAY.
  */
-
 static int
-ospf_shutdown(struct proto *p)
+ospf_shutdown(struct proto *P)
 {
-  struct proto_ospf *po = (struct proto_ospf *) p;
+  struct ospf_proto *p = (struct ospf_proto *) P;
   struct ospf_iface *ifa;
+
   OSPF_TRACE(D_EVENTS, "Shutdown requested");
 
   /* And send to all my neighbors 1WAY */
-  WALK_LIST(ifa, po->iface_list)
+  WALK_LIST(ifa, p->iface_list)
     ospf_iface_shutdown(ifa);
 
   /* Cleanup locked rta entries */
-  FIB_WALK(&po->rtf, nftmp)
+  FIB_WALK(&p->rtf, nftmp)
   {
     rta_free(((ort *) nftmp)->old_rta);
   }
@@ -568,49 +505,11 @@ ospf_shutdown(struct proto *p)
 }
 
 static void
-ospf_rt_notify(struct proto *p, rtable *tbl UNUSED, net * n, rte * new, rte * old UNUSED, ea_list * attrs)
+ospf_get_status(struct proto *P, byte * buf)
 {
-  struct proto_ospf *po = (struct proto_ospf *) p;
-  struct ospf_area *oa = ospf_main_area(po);
-  ort *nf = (ort *) fib_get(&po->rtf, &n->n.prefix, n->n.pxlen);
-  struct fib_node *fn = &nf->fn;
-
-  if (!new)
-  {
-    if (fn->x1 != EXT_EXPORT)
-      return;
-
-    flush_ext_lsa(oa, fn, oa_is_nssa(oa));
-
-    /* Old external route might blocked some NSSA translation */
-    if (po->areano > 1)
-      schedule_rtcalc(po);
-
-    return;
-  }
-
-  /* Get route attributes */
-  u32 m1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY);
-  u32 m2 = ea_get_int(attrs, EA_OSPF_METRIC2, 10000);
-  u32 metric = (m1 != LSINFINITY) ? m1 : (m2 | LSA_EXT_EBIT);
-  u32 tag = ea_get_int(attrs, EA_OSPF_TAG, 0);
-  ip_addr gw = IPA_NONE;
-  // FIXME check for gw should be per ifa, not per iface
-  if ((new->attrs->dest == RTD_ROUTER) &&
-      ipa_nonzero(new->attrs->gw) &&
-      !ipa_has_link_scope(new->attrs->gw) &&
-      (ospf_iface_find((struct proto_ospf *) p, new->attrs->iface) != NULL))
-    gw = new->attrs->gw;
-
-  originate_ext_lsa(oa, fn, EXT_EXPORT, metric, gw, tag, 1);
-}
+  struct ospf_proto *p = (struct ospf_proto *) P;
 
-static void
-ospf_get_status(struct proto *p, byte * buf)
-{
-  struct proto_ospf *po = (struct proto_ospf *) p;
-
-  if (p->proto_state == PS_DOWN)
+  if (p->p.proto_state == PS_DOWN)
     buf[0] = 0;
   else
   {
@@ -618,7 +517,7 @@ ospf_get_status(struct proto *p, byte * buf)
     struct ospf_neighbor *n;
     int adj = 0;
 
-    WALK_LIST(ifa, po->iface_list)
+    WALK_LIST(ifa, p->iface_list)
       WALK_LIST(n, ifa->neigh_list) if (n->state == NEIGHBOR_FULL)
       adj = 1;
 
@@ -636,18 +535,18 @@ ospf_get_route_info(rte * rte, byte * buf, ea_list * attrs UNUSED)
 
   switch (rte->attrs->source)
   {
-    case RTS_OSPF:
-      type = "I";
-      break;
-    case RTS_OSPF_IA:
-      type = "IA";
-      break;
-    case RTS_OSPF_EXT1:
-      type = "E1";
-      break;
-    case RTS_OSPF_EXT2:
-      type = "E2";
-      break;
+  case RTS_OSPF:
+    type = "I";
+    break;
+  case RTS_OSPF_IA:
+    type = "IA";
+    break;
+  case RTS_OSPF_EXT1:
+    type = "E1";
+    break;
+  case RTS_OSPF_EXT2:
+    type = "E2";
+    break;
   }
 
   buf += bsprintf(buf, " %s", type);
@@ -688,18 +587,24 @@ ospf_get_attr(eattr * a, byte * buf, int buflen UNUSED)
 static void
 ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
 {
+  struct ospf_proto *p = oa->po;
+  struct ospf_area_config *oac = oa->ac;
+  struct ospf_iface *ifa;
+
   oa->ac = nac;
 
-  // FIXME better area type reconfiguration
-#ifdef OSPFv2
-  oa->options = nac->type;
-#else /* OSPFv3 */
-  oa->options = nac->type | OPT_V6 | (oa->po->stub_router ? 0 : OPT_R);
-#endif
-  if (oa_is_nssa(oa) && (oa->po->areano > 1))
-    oa->po->ebit = 1;
+  if (ospf_is_v2(p))
+    oa->options = nac->type;
+  else
+    oa->options = nac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R);
 
-  ospf_ifaces_reconfigure(oa, nac);
+  if (nac->type != oac->type)
+  {
+    /* Force restart of area interfaces */
+    WALK_LIST(ifa, p->iface_list)
+      if (ifa->oa == oa)
+       ifa->marked = 2;
+  }
 
   /* Handle net_list */
   fib_free(&oa->net_fib);
@@ -709,7 +614,7 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
   /* No need to handle stubnet_list */
 
   oa->marked = 0;
-  schedule_rt_lsa(oa);
+  ospf_notify_rt_lsa(oa);
 }
 
 /**
@@ -723,95 +628,99 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
  * nonbroadcast network, cost of interface, etc.
  */
 static int
-ospf_reconfigure(struct proto *p, struct proto_config *c)
+ospf_reconfigure(struct proto *P, struct proto_config *c)
 {
-  struct proto_ospf *po = (struct proto_ospf *) p;
-  struct ospf_config *old = (struct ospf_config *) (p->cf);
+  struct ospf_proto *p = (struct ospf_proto *) P;
+  struct ospf_config *old = (struct ospf_config *) (P->cf);
   struct ospf_config *new = (struct ospf_config *) c;
   struct ospf_area_config *nac;
   struct ospf_area *oa, *oax;
   struct ospf_iface *ifa, *ifx;
   struct ospf_iface_patt *ip;
 
-  if (proto_get_router_id(c) != po->router_id)
+  if (proto_get_router_id(c) != p->router_id)
     return 0;
 
-  if (po->rfc1583 != new->rfc1583)
+  if (p->rfc1583 != new->rfc1583)
     return 0;
 
   if (old->abr != new->abr)
     return 0;
 
-  po->stub_router = new->stub_router;
-  po->merge_external = new->merge_external;
-  po->ecmp = new->ecmp;
-  po->tick = new->tick;
-  po->disp_timer->recurrent = po->tick;
-  tm_start(po->disp_timer, 1);
+  p->stub_router = new->stub_router;
+  p->merge_external = new->merge_external;
+  p->asbr = new->asbr;
+  p->ecmp = new->ecmp;
+  p->tick = new->tick;
+  p->disp_timer->recurrent = p->tick;
+  tm_start(p->disp_timer, 1);
 
   /* Mark all areas and ifaces */
-  WALK_LIST(oa, po->area_list)
+  WALK_LIST(oa, p->area_list)
     oa->marked = 1;
 
-  WALK_LIST(ifa, po->iface_list)
+  WALK_LIST(ifa, p->iface_list)
     ifa->marked = 1;
 
   /* Add and update areas */
   WALK_LIST(nac, new->area_list)
   {
-    oa = ospf_find_area(po, nac->areaid);
+    oa = ospf_find_area(p, nac->areaid);
     if (oa)
       ospf_area_reconfigure(oa, nac);
     else
-      ospf_area_add(po, nac, 1);
+      ospf_area_add(p, nac, 1);
   }
 
+  /* Add and update interfaces */
+  ospf_reconfigure_ifaces(p);
+
   /* Add and update vlinks */
   WALK_LIST(ip, new->vlink_list)
   {
-    ifa = ospf_find_vlink(po, ip->voa, ip->vid);
+    ifa = ospf_find_vlink(p, ip->voa, ip->vid);
     if (ifa)
       ospf_iface_reconfigure(ifa, ip);
     else
-      ospf_iface_new_vlink(po, ip);
+      ospf_iface_new_vlink(p, ip);
   }
 
   /* Delete remaining ifaces and areas */
-  WALK_LIST_DELSAFE(ifa, ifx, po->iface_list)
+  WALK_LIST_DELSAFE(ifa, ifx, p->iface_list)
     if (ifa->marked)
     {
       ospf_iface_shutdown(ifa);
       ospf_iface_remove(ifa);
     }
 
-  WALK_LIST_DELSAFE(oa, oax, po->area_list)
+  WALK_LIST_DELSAFE(oa, oax, p->area_list)
     if (oa->marked)
       ospf_area_remove(oa);
 
-  schedule_rtcalc(po);
+  schedule_rtcalc(p);
   
   return 1;
 }
 
 
 void
-ospf_sh_neigh(struct proto *p, char *iff)
+ospf_sh_neigh(struct proto *P, char *iff)
 {
+  struct ospf_proto *p = (struct ospf_proto *) P;
   struct ospf_iface *ifa = NULL;
   struct ospf_neighbor *n;
-  struct proto_ospf *po = (struct proto_ospf *) p;
 
-  if (p->proto_state != PS_UP)
+  if (p->p.proto_state != PS_UP)
   {
-    cli_msg(-1013, "%s: is not up", p->name);
+    cli_msg(-1013, "%s: is not up", p->p.name);
     cli_msg(0, "");
     return;
   }
 
-  cli_msg(-1013, "%s:", p->name);
+  cli_msg(-1013, "%s:", p->p.name);
   cli_msg(-1013, "%-12s\t%3s\t%-15s\t%-5s\t%-10s %-12s", "Router ID", "Pri",
          "     State", "DTime", "Interface", "Router IP");
-  WALK_LIST(ifa, po->iface_list)
+  WALK_LIST(ifa, p->iface_list)
     if ((iff == NULL) || patmatch(iff, ifa->ifname))
       WALK_LIST(n, ifa->neigh_list)
        ospf_sh_neigh_info(n);
@@ -819,37 +728,37 @@ ospf_sh_neigh(struct proto *p, char *iff)
 }
 
 void
-ospf_sh(struct proto *p)
+ospf_sh(struct proto *P)
 {
+  struct ospf_proto *p = (struct ospf_proto *) P;
   struct ospf_area *oa;
-  struct proto_ospf *po = (struct proto_ospf *) p;
   struct ospf_iface *ifa;
   struct ospf_neighbor *n;
   int ifano, nno, adjno, firstfib;
   struct area_net *anet;
 
-  if (p->proto_state != PS_UP)
+  if (p->p.proto_state != PS_UP)
   {
-    cli_msg(-1014, "%s: is not up", p->name);
+    cli_msg(-1014, "%s: is not up", p->p.name);
     cli_msg(0, "");
     return;
   }
 
-  cli_msg(-1014, "%s:", p->name);
-  cli_msg(-1014, "RFC1583 compatibility: %s", (po->rfc1583 ? "enable" : "disabled"));
-  cli_msg(-1014, "Stub router: %s", (po->stub_router ? "Yes" : "No"));
-  cli_msg(-1014, "RT scheduler tick: %d", po->tick);
-  cli_msg(-1014, "Number of areas: %u", po->areano);
-  cli_msg(-1014, "Number of LSAs in DB:\t%u", po->gr->hash_entries);
+  cli_msg(-1014, "%s:", p->p.name);
+  cli_msg(-1014, "RFC1583 compatibility: %s", (p->rfc1583 ? "enable" : "disabled"));
+  cli_msg(-1014, "Stub router: %s", (p->stub_router ? "Yes" : "No"));
+  cli_msg(-1014, "RT scheduler tick: %d", p->tick);
+  cli_msg(-1014, "Number of areas: %u", p->areano);
+  cli_msg(-1014, "Number of LSAs in DB:\t%u", p->gr->hash_entries);
 
-  WALK_LIST(oa, po->area_list)
+  WALK_LIST(oa, p->area_list)
   {
     cli_msg(-1014, "\tArea: %R (%u) %s", oa->areaid, oa->areaid,
            oa->areaid == 0 ? "[BACKBONE]" : "");
     ifano = 0;
     nno = 0;
     adjno = 0;
-    WALK_LIST(ifa, po->iface_list)
+    WALK_LIST(ifa, p->iface_list)
     {
       if (oa == ifa->oa)
       {
@@ -907,20 +816,20 @@ ospf_sh(struct proto *p)
 }
 
 void
-ospf_sh_iface(struct proto *p, char *iff)
+ospf_sh_iface(struct proto *P, char *iff)
 {
-  struct proto_ospf *po = (struct proto_ospf *) p;
+  struct ospf_proto *p = (struct ospf_proto *) P;
   struct ospf_iface *ifa = NULL;
 
-  if (p->proto_state != PS_UP)
+  if (p->p.proto_state != PS_UP)
   {
-    cli_msg(-1015, "%s: is not up", p->name);
+    cli_msg(-1015, "%s: is not up", p->p.name);
     cli_msg(0, "");
     return;
   }
 
-  cli_msg(-1015, "%s:", p->name);
-  WALK_LIST(ifa, po->iface_list)
+  cli_msg(-1015, "%s:", p->p.name);
+  WALK_LIST(ifa, p->iface_list)
     if ((iff == NULL) || patmatch(iff, ifa->ifname))
       ospf_iface_info(ifa);
   cli_msg(0, "");
@@ -939,14 +848,12 @@ ospf_sh_iface(struct proto *p, char *iff)
  * values
  */
 
-#ifdef OSPFv3
-
 static struct ospf_lsa_header *
 fake_lsa_from_prefix_lsa(struct ospf_lsa_header *dst, struct ospf_lsa_header *src,
                         struct ospf_lsa_prefix *px)
 {
   dst->age = src->age;
-  dst->type = px->ref_type;
+  dst->type_raw = px->ref_type;
   dst->id = px->ref_id;
   dst->rt = px->ref_rt;
   dst->sn = src->sn;
@@ -954,49 +861,59 @@ fake_lsa_from_prefix_lsa(struct ospf_lsa_header *dst, struct ospf_lsa_header *sr
   return dst;
 }
 
-#endif
+
+static int lsa_compare_ospf3;
 
 static int
 lsa_compare_for_state(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 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;
 
-#ifdef OSPFv3
-  struct ospf_lsa_header lsatmp1, lsatmp2;
 
-  int px1 = (lsa1->type == LSA_T_PREFIX);
-  int px2 = (lsa2->type == LSA_T_PREFIX);
+  /* 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(&lsatmp1, lsa1, he1->lsa_body);
+    lsa1_type = lsa1->type_raw;        /* FIXME: handle unknown ref_type */
+  }
 
   if (px2)
+  {
     lsa2 = fake_lsa_from_prefix_lsa(&lsatmp2, lsa2, he2->lsa_body);
-#endif
+    lsa2_type = lsa2->type_raw;
+  }
+
 
-  int nt1 = (lsa1->type == LSA_T_NET);
-  int nt2 = (lsa2->type == LSA_T_NET);
+  int nt1 = (lsa1_type == LSA_T_NET);
+  int nt2 = (lsa2_type == LSA_T_NET);
 
   if (nt1 != nt2)
     return nt1 - nt2;
 
   if (nt1)
   {
-#ifdef OSPFv3
-    /* In OSPFv3, neworks are named base on ID of DR */
-    if (lsa1->rt < lsa2->rt)
-      return -1;
-    if (lsa1->rt > lsa2->rt)
-      return 1;
-#endif
+    /* In OSPFv3, networks are named based on ID of DR */
+    if (lsa_compare_ospf3)
+    {
+      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 */
@@ -1005,10 +922,8 @@ lsa_compare_for_state(const void *p1, const void *p2)
     if (lsa1->id > lsa2->id)
       return 1;
 
-#ifdef OSPFv3
     if (px1 != px2)
       return px1 - px2;
-#endif
 
     return lsa1->sn - lsa2->sn;
   }
@@ -1019,9 +934,9 @@ lsa_compare_for_state(const void *p1, const void *p2)
     if (lsa1->rt > lsa2->rt)
       return 1;
 
-    if (lsa1->type < lsa2->type)
+    if (lsa1_type < lsa2_type)
       return -1;
-    if (lsa1->type > lsa2->type)
+    if (lsa1_type > lsa2_type)
       return 1;
 
     if (lsa1->id < lsa2->id)
@@ -1029,10 +944,8 @@ lsa_compare_for_state(const void *p1, const void *p2)
     if (lsa1->id > lsa2->id)
       return 1;
 
-#ifdef OSPFv3
     if (px1 != px2)
       return px1 - px2;
-#endif
   
     return lsa1->sn - lsa2->sn;
   }
@@ -1069,79 +982,77 @@ show_lsa_distance(struct top_hash_entry *he)
 }
 
 static inline void
-show_lsa_router(struct proto_ospf *po, struct top_hash_entry *he, int first, int verbose)
+show_lsa_router(struct ospf_proto *p, struct top_hash_entry *he, int verbose)
 {
-  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);
-  int max = lsa_rt_count(lsa);
-  int i;
-
-  if (first)
-  {
-    cli_msg(-1016, "");
-    cli_msg(-1016, "\trouter %R", he->lsa.rt);
-    show_lsa_distance(he);
-  }
+  struct ospf_lsa_rt_walk rtl;
 
+  cli_msg(-1016, "");
+  cli_msg(-1016, "\trouter %R", he->lsa.rt);
+  show_lsa_distance(he);
 
-  for (i = 0; i < max; i++)
-    if (rr[i].type == LSART_VLNK)
-      cli_msg(-1016, "\t\tvlink %R metric %u", rr[i].id, rr[i].metric);
+  lsa_walk_rt_init(p, he, &rtl);
+  while (lsa_walk_rt(&rtl))
+    if (rtl.type == LSART_VLNK)
+      cli_msg(-1016, "\t\tvlink %R metric %u", rtl.id, rtl.metric);
 
-  for (i = 0; i < max; i++)
-    if (rr[i].type == LSART_PTP)
-      cli_msg(-1016, "\t\trouter %R metric %u", rr[i].id, rr[i].metric);
+  lsa_walk_rt_init(p, he, &rtl);
+  while (lsa_walk_rt(&rtl))
+    if (rtl.type == LSART_PTP)
+      cli_msg(-1016, "\t\trouter %R metric %u", rtl.id, rtl.metric);
 
-  for (i = 0; i < max; i++)
-    if (rr[i].type == LSART_NET)
+  lsa_walk_rt_init(p, he, &rtl);
+  while (lsa_walk_rt(&rtl))
+    if (rtl.type == LSART_NET)
     {
-#ifdef OSPFv2
-      struct top_hash_entry *net_he = ospf_hash_find_net(po->gr, he->domain, rr[i].id);
-
-      if (net_he)
+      if (ospf_is_v2(p))
       {
-       struct ospf_lsa_header *net_lsa = &(net_he->lsa);
-       struct ospf_lsa_net *net_ln = net_he->lsa_body;
+       /* 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);
 
-       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);
+       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_from_u32(net_lsa->id & net_ln->optx),
+                 u32_masklen(net_ln->optx), rtl.metric);
+       }
+       else
+         cli_msg(-1016, "\t\tnetwork [%R] metric %u", rtl.id, rtl.metric);
       }
       else
-       cli_msg(-1016, "\t\tnetwork [%R] metric %u", rr[i].id, rr[i].metric);
-
-#else /* OSPFv3 */
-      cli_msg(-1016, "\t\tnetwork [%R-%u] metric %u", rr[i].id, rr[i].nif, rr[i].metric);
-#endif
+       cli_msg(-1016, "\t\tnetwork [%R-%u] metric %u", rtl.id, rtl.nif, rtl.metric);
     }
 
-#ifdef OSPFv2
-  if (!verbose)
-    return;
-
-  for (i = 0; i < max; i++)
-    if (rr[i].type == LSART_STUB)
-      cli_msg(-1016, "\t\tstubnet %I/%d metric %u", ipa_from_u32(rr[i].id),
-             ipa_mklen(ipa_from_u32(rr[i].data)), rr[i].metric);
-#endif
+  if (ospf_is_v2(p) && verbose)
+  {
+    lsa_walk_rt_init(p, he, &rtl);
+    while (lsa_walk_rt(&rtl))
+      if (rtl.type == LSART_STUB)
+       cli_msg(-1016, "\t\tstubnet %I/%d metric %u",
+               ipa_from_u32(rtl.id), u32_masklen(rtl.data), rtl.metric);
+  }
 }
 
 static inline void
-show_lsa_network(struct top_hash_entry *he)
+show_lsa_network(struct top_hash_entry *he, int ospf2)
 {
   struct ospf_lsa_header *lsa = &(he->lsa);
   struct ospf_lsa_net *ln = he->lsa_body;
   u32 i;
 
-#ifdef OSPFv2
-  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 %R", lsa->rt);
-#else /* OSPFv3 */
-  cli_msg(-1016, "");
-  cli_msg(-1016, "\tnetwork [%R-%u]", lsa->rt, lsa->id);
-#endif
+  if (ospf2)
+  {
+    cli_msg(-1016, "");
+    cli_msg(-1016, "\tnetwork %I/%d", ipa_from_u32(lsa->id & ln->optx), u32_masklen(ln->optx));
+    cli_msg(-1016, "\t\tdr %R", lsa->rt);
+  }
+  else
+  {
+    cli_msg(-1016, "");
+    cli_msg(-1016, "\tnetwork [%R-%u]", lsa->rt, lsa->id);
+  }
 
   show_lsa_distance(he);
 
@@ -1150,97 +1061,54 @@ show_lsa_network(struct top_hash_entry *he)
 }
 
 static inline void
-show_lsa_sum_net(struct top_hash_entry *he)
+show_lsa_sum_net(struct top_hash_entry *he, int ospf2)
 {
   ip_addr ip;
   int pxlen;
-
-#ifdef OSPFv2
-  struct ospf_lsa_sum *ls = he->lsa_body;
-  pxlen = ipa_mklen(ls->netmask);
-  ip = ipa_and(ipa_from_u32(he->lsa.id), ls->netmask);
-#else /* OSPFv3 */
   u8 pxopts;
-  u16 rest;
-  struct ospf_lsa_sum_net *ls = he->lsa_body;
-  lsa_get_ipv6_prefix(ls->prefix, &ip, &pxlen, &pxopts, &rest);
-#endif
+  u32 metric;
 
-  cli_msg(-1016, "\t\txnetwork %I/%d metric %u", ip, pxlen, ls->metric);
+  lsa_parse_sum_net(he, ospf2, &ip, &pxlen, &pxopts, &metric);
+  cli_msg(-1016, "\t\txnetwork %I/%d metric %u", ip, pxlen, metric);
 }
 
 static inline void
-show_lsa_sum_rt(struct top_hash_entry *he)
+show_lsa_sum_rt(struct top_hash_entry *he, int ospf2)
 {
+  u32 metric;
   u32 dst_rid;
+  u32 options;
 
-#ifdef OSPFv2
-  struct ospf_lsa_sum *ls = he->lsa_body;
-  dst_rid = he->lsa.id;
-  // options = 0;
-#else /* OSPFv3 */
-  struct ospf_lsa_sum_rt *ls = he->lsa_body;
-  dst_rid = ls->drid; 
-  // options = ls->options & OPTIONS_MASK;
-#endif
-
-  cli_msg(-1016, "\t\txrouter %R metric %u", dst_rid, ls->metric);
+  lsa_parse_sum_rt(he, ospf2, &dst_rid, &metric, &options);
+  cli_msg(-1016, "\t\txrouter %R metric %u", dst_rid, metric);
 }
 
 
 static inline void
-show_lsa_external(struct top_hash_entry *he)
+show_lsa_external(struct top_hash_entry *he, int ospf2)
 {
-  struct ospf_lsa_ext *ext = he->lsa_body;
+  struct ospf_lsa_ext_local rt;
   char str_via[STD_ADDRESS_P_LENGTH + 8] = "";
   char str_tag[16] = "";
-  ip_addr ip, rt_fwaddr;
-  int pxlen, ebit, rt_fwaddr_valid;
-  u32 rt_tag, rt_metric;
 
-  if (he->lsa.type == LSA_T_EXT)
+  if (he->lsa_type == LSA_T_EXT)
     he->domain = 0; /* Unmark the LSA */
 
-  rt_metric = ext->metric & METRIC_MASK;
-  ebit = ext->metric & LSA_EXT_EBIT;
-#ifdef OSPFv2
-  ip = ipa_and(ipa_from_u32(he->lsa.id), ext->netmask);
-  pxlen = ipa_mklen(ext->netmask);
-  rt_fwaddr = ext->fwaddr;
-  rt_fwaddr_valid = !ipa_equal(rt_fwaddr, IPA_NONE);
-  rt_tag = ext->tag;
-#else /* OSPFv3 */
-  u8 pxopts;
-  u16 rest;
-  u32 *buf = ext->rest;
-  buf = lsa_get_ipv6_prefix(buf, &ip, &pxlen, &pxopts, &rest);
-
-  rt_fwaddr_valid = ext->metric & LSA_EXT_FBIT;
-  if (rt_fwaddr_valid)
-    buf = lsa_get_ipv6_addr(buf, &rt_fwaddr);
-  else 
-    rt_fwaddr = IPA_NONE;
-
-  if (ext->metric & LSA_EXT_TBIT)
-    rt_tag = *buf++;
-  else
-    rt_tag = 0;
-#endif
+  lsa_parse_ext(he, ospf2, &rt);
   
-  if (rt_fwaddr_valid)
-    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 %I/%d metric%s %u%s%s",
-         (he->lsa.type == LSA_T_NSSA) ? "nssa-ext" : "external",
-         ip, pxlen, ebit ? "2" : "", rt_metric, str_via, str_tag);
+         (he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external",
+         rt.ip, rt.pxlen, rt.ebit ? "2" : "", rt.metric, str_via, str_tag);
 }
 
-#ifdef OSPFv3
 static inline void
-show_lsa_prefix(struct top_hash_entry *he, struct ospf_lsa_header *cnode)
+show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode)
 {
   struct ospf_lsa_prefix *px = he->lsa_body;
   ip_addr pxa;
@@ -1251,13 +1119,13 @@ show_lsa_prefix(struct top_hash_entry *he, struct ospf_lsa_header *cnode)
   int i;
 
   /* We check whether given prefix-LSA is related to the current node */
-  if ((px->ref_type != cnode->type) || (px->ref_rt != cnode->rt))
+  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->id))
+  if ((px->ref_type == LSA_T_NET) && (px->ref_id != cnode->lsa.id))
     return;
 
   buf = px->rest;
@@ -1271,19 +1139,18 @@ show_lsa_prefix(struct top_hash_entry *he, struct ospf_lsa_header *cnode)
        cli_msg(-1016, "\t\taddress %I/%d", pxa, pxlen);
     }
 }
-#endif
 
 void
-ospf_sh_state(struct proto *p, int verbose, int reachable)
+ospf_sh_state(struct proto *P, int verbose, int reachable)
 {
-  struct proto_ospf *po = (struct proto_ospf *) p;
-  struct ospf_lsa_header *cnode = NULL;
-  unsigned int i, ix, j1, j2, jx;
+  struct ospf_proto *p = (struct ospf_proto *) P;
+  int ospf2 = ospf_is_v2(p);
+  uint i, ix, j1, jx;
   u32 last_area = 0xFFFFFFFF;
 
-  if (p->proto_state != PS_UP)
+  if (p->p.proto_state != PS_UP)
   {
-    cli_msg(-1016, "%s: is not up", p->name);
+    cli_msg(-1016, "%s: is not up", p->p.name);
     cli_msg(0, "");
     return;
   }
@@ -1291,51 +1158,51 @@ ospf_sh_state(struct proto *p, int verbose, int reachable)
   /* We store interesting area-scoped LSAs in array hea and 
      global-scoped (LSA_T_EXT) LSAs in array hex */
 
-  int num = po->gr->hash_entries;
+  int num = p->gr->hash_entries;
   struct top_hash_entry *hea[num];
   struct top_hash_entry *hex[verbose ? num : 0];
   struct top_hash_entry *he;
+  struct top_hash_entry *cnode = NULL;
 
-  j1 = j2 = jx = 0;
-  WALK_SLIST(he, po->lsal)
+  j1 = jx = 0;
+  WALK_SLIST(he, p->lsal)
   {
     int accept;
 
-    switch (he->lsa.type)
-      {
-      case LSA_T_RT:
-      case LSA_T_NET:
-       accept = 1;
-       break;
+    if (he->lsa.age == LSA_MAXAGE)
+      continue;
 
-      case LSA_T_SUM_NET:
-      case LSA_T_SUM_RT:
-      case LSA_T_NSSA:
-#ifdef OSPFv3
-      case LSA_T_PREFIX:
-#endif
-       accept = verbose;
-       break;
+    switch (he->lsa_type)
+    {
+    case LSA_T_RT:
+    case LSA_T_NET:
+      accept = 1;
+      break;
 
-      case LSA_T_EXT:
-       if (verbose)
-       {
-         he->domain = 1; /* Abuse domain field to mark the LSA */
-         hex[jx++] = he;
-       }
-      default:
-       accept = 0;
+    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;
       }
+    default:
+      accept = 0;
+    }
 
     if (accept)
       hea[j1++] = he;
-    else
-      j2++;
   }
 
-  if ((j1 + j2) != num)
-    die("Fatal mismatch");
+  ASSERT(j1 <= num && jx <= num);
 
+  lsa_compare_ospf3 = !ospf2;
   qsort(hea, j1, sizeof(struct top_hash_entry *), lsa_compare_for_state);
   qsort(hex, jx, sizeof(struct top_hash_entry *), ext_compare_for_state);
 
@@ -1366,10 +1233,10 @@ ospf_sh_state(struct proto *p, int verbose, int reachable)
     /* 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))
+      if (((he->lsa_type == LSA_T_RT) || (he->lsa_type == LSA_T_NET))
          && ((he->color == INSPF) || !reachable))
       {
-       cnode = &(he->lsa);
+       cnode = he;
 
        if (he->domain != last_area)
        {
@@ -1383,51 +1250,50 @@ ospf_sh_state(struct proto *p, int verbose, int reachable)
        continue;
     }
 
-    ASSERT(cnode && (he->domain == last_area) && (he->lsa.rt == cnode->rt));
+    ASSERT(cnode && (he->domain == last_area) && (he->lsa.rt == cnode->lsa.rt));
 
-    switch (he->lsa.type)
+    switch (he->lsa_type)
     {
-      case LSA_T_RT:
-       show_lsa_router(po, he, he->lsa.id == cnode->id, verbose);
-       break;
+    case LSA_T_RT:
+      if (he->lsa.id == cnode->lsa.id)
+       show_lsa_router(p, he, verbose);
+      break;
 
-      case LSA_T_NET:
-       show_lsa_network(he);
-       break;
+    case LSA_T_NET:
+      show_lsa_network(he, ospf2);
+      break;
 
-      case LSA_T_SUM_NET:
-       if (cnode->type == LSA_T_RT)
-         show_lsa_sum_net(he);
-       break;
+    case LSA_T_SUM_NET:
+      if (cnode->lsa_type == LSA_T_RT)
+       show_lsa_sum_net(he, ospf2);
+      break;
 
-      case LSA_T_SUM_RT:
-       if (cnode->type == LSA_T_RT)
-         show_lsa_sum_rt(he);
-       break;
+    case LSA_T_SUM_RT:
+      if (cnode->lsa_type == LSA_T_RT)
+       show_lsa_sum_rt(he, ospf2);
+      break;
 
-#ifdef OSPFv3
-      case LSA_T_PREFIX:
-       show_lsa_prefix(he, cnode);
-       break;
-#endif
+    case LSA_T_EXT:
+    case LSA_T_NSSA:
+      show_lsa_external(he, ospf2);
+      break;
 
-      case LSA_T_EXT:
-      case LSA_T_NSSA:
-       show_lsa_external(he);
-       break;
+    case LSA_T_PREFIX:
+      show_lsa_prefix(he, cnode);
+      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->rt)
-       || (hea[i+1]->lsa.type == LSA_T_NET))
+       || (hea[i+1]->lsa.rt != cnode->lsa.rt)
+       || (hea[i+1]->lsa_type == LSA_T_NET))
     {
-      while ((ix < jx) && (hex[ix]->lsa.rt < cnode->rt))
+      while ((ix < jx) && (hex[ix]->lsa.rt < cnode->lsa.rt))
        ix++;
 
-      while ((ix < jx) && (hex[ix]->lsa.rt == cnode->rt))
-       show_lsa_external(hex[ix++]);
+      while ((ix < jx) && (hex[ix]->lsa.rt == cnode->lsa.rt))
+       show_lsa_external(hex[ix++], ospf2);
 
       cnode = NULL;
     }
@@ -1461,7 +1327,7 @@ ospf_sh_state(struct proto *p, int verbose, int reachable)
        last_rt = he->lsa.rt;
       }
 
-      show_lsa_external(he);
+      show_lsa_external(he, ospf2);
     }
   }
 
@@ -1476,8 +1342,8 @@ lsa_compare_for_lsadb(const void *p1, const void *p2)
   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 sc1 = LSA_SCOPE(lsa1);
-  int sc2 = LSA_SCOPE(lsa2);
+  int sc1 = LSA_SCOPE(he1->lsa_type);
+  int sc2 = LSA_SCOPE(he2->lsa_type);
 
   if (sc1 != sc2)
     return sc2 - sc1;
@@ -1491,8 +1357,8 @@ lsa_compare_for_lsadb(const void *p1, const void *p2)
   if (lsa1->id != lsa2->id)
     return lsa1->id - lsa2->id;
 
-  if (lsa1->type != lsa2->type)
-    return lsa1->type - lsa2->type;
+  if (he1->lsa_type != he2->lsa_type)
+    return he1->lsa_type - he2->lsa_type;
 
   return lsa1->sn - lsa2->sn;
 }
@@ -1500,48 +1366,50 @@ lsa_compare_for_lsadb(const void *p1, const void *p2)
 void
 ospf_sh_lsadb(struct lsadb_show_data *ld)
 {
-  struct proto *p = proto_get_named(ld->name, &proto_ospf);
-  struct proto_ospf *po = (struct proto_ospf *) p;
-  int num = po->gr->hash_entries;
+  struct ospf_proto *p = (struct ospf_proto *) proto_get_named(ld->name, &proto_ospf);
+  int num = p->gr->hash_entries;
   unsigned int i, j;
   int last_dscope = -1;
   u32 last_domain = 0;
+  u16 type_mask = ospf_is_v2(p) ?  0x00ff : 0xffff;    /* see lsa_etype() */
 
-  if (p->proto_state != PS_UP)
+  if (p->p.proto_state != PS_UP)
   {
-    cli_msg(-1017, "%s: is not up", p->name);
+    cli_msg(-1017, "%s: is not up", p->p.name);
     cli_msg(0, "");
     return;
   }
 
   if (ld->router == SH_ROUTER_SELF)
-    ld->router = po->router_id;
+    ld->router = p->router_id;
 
   struct top_hash_entry *hea[num];
   struct top_hash_entry *he;
 
   j = 0;
-  WALK_SLIST(he, po->lsal)
-    hea[j++] = he;
+  WALK_SLIST(he, p->lsal)
+    if (he->lsa_body)
+      hea[j++] = he;
 
-  if (j != num)
-    die("Fatal mismatch");
+  ASSERT(j <= num);
 
   qsort(hea, j, sizeof(struct top_hash_entry *), lsa_compare_for_lsadb);
 
   for (i = 0; i < j; i++)
   {
     struct ospf_lsa_header *lsa = &(hea[i]->lsa);
-    int dscope = LSA_SCOPE(lsa);
-
+    u16 lsa_type = lsa->type_raw & type_mask;
+    u16 dscope = LSA_SCOPE(hea[i]->lsa_type);
+    
+    /* Hack: 1 is used for LSA_SCOPE_LINK, fixed by & 0xf000 */
     if (ld->scope && (dscope != (ld->scope & 0xf000)))
       continue;
 
     if ((ld->scope == LSA_SCOPE_AREA) && (hea[i]->domain != ld->area))
       continue;
 
-    /* Ignore high nibble */
-    if (ld->type && ((lsa->type & 0x0fff) != (ld->type & 0x0fff)))
+    /* For user convenience ignore high nibble */
+    if (ld->type && ((lsa_type & 0x0fff) != (ld->type & 0x0fff)))
       continue;
 
     if (ld->lsid && (lsa->id != ld->lsid))
@@ -1555,20 +1423,20 @@ ospf_sh_lsadb(struct lsadb_show_data *ld)
       cli_msg(-1017, "");
       switch (dscope)
       {
-       case LSA_SCOPE_AS:
-         cli_msg(-1017, "Global");
-         break;
-       case LSA_SCOPE_AREA:
-         cli_msg(-1017, "Area %R", hea[i]->domain);
-         break;
-#ifdef OSPFv3
-       case LSA_SCOPE_LINK:
-         {
-           struct iface *ifa = if_find_by_index(hea[i]->domain);
-           cli_msg(-1017, "Link %s", (ifa != NULL) ? ifa->name : "?");
-         }
-         break;
-#endif
+      case LSA_SCOPE_AS:
+       cli_msg(-1017, "Global");
+       break;
+
+      case LSA_SCOPE_AREA:
+       cli_msg(-1017, "Area %R", hea[i]->domain);
+       break;
+
+      case LSA_SCOPE_LINK:
+       {
+         struct iface *ifa = if_find_by_index(hea[i]->domain);
+         cli_msg(-1017, "Link %s", (ifa != NULL) ? ifa->name : "?");
+       }
+       break;
       }
       cli_msg(-1017, "");
       cli_msg(-1017," Type   LS ID           Router           Age  Sequence  Checksum");
@@ -1577,26 +1445,25 @@ ospf_sh_lsadb(struct lsadb_show_data *ld)
       last_domain = hea[i]->domain;
     }
 
-
     cli_msg(-1017," %04x  %-15R %-15R %5u  %08x    %04x",
-           lsa->type, lsa->id, lsa->rt, lsa->age, lsa->sn, lsa->checksum);
+           lsa_type, lsa->id, lsa->rt, lsa->age, lsa->sn, lsa->checksum);
   }
   cli_msg(0, "");
 }
 
 
 struct protocol proto_ospf = {
-  name:                        "OSPF",
-  template:            "ospf%d",
-  attr_class:          EAP_OSPF,
-  preference:          DEF_PREF_OSPF,
-  init:                        ospf_init,
-  dump:                        ospf_dump,
-  start:               ospf_start,
-  shutdown:            ospf_shutdown,
-  reconfigure:         ospf_reconfigure,
-  get_status:          ospf_get_status,
-  get_attr:            ospf_get_attr,
-  get_route_info:      ospf_get_route_info
-  // show_proto_info:  ospf_sh
+  .name =              "OSPF",
+  .template =          "ospf%d",
+  .attr_class =                EAP_OSPF,
+  .preference =                DEF_PREF_OSPF,
+  .init =              ospf_init,
+  .dump =              ospf_dump,
+  .start =             ospf_start,
+  .shutdown =          ospf_shutdown,
+  .reconfigure =       ospf_reconfigure,
+  .get_status =                ospf_get_status,
+  .get_attr =          ospf_get_attr,
+  .get_route_info =    ospf_get_route_info
 };
+
index e705b88b9f2bd3d61360218cc9ee90852a320a3a..34c26b47da2fef230cadba22f426c96182df3fc7 100644 (file)
@@ -2,6 +2,8 @@
  *     BIRD -- OSPF
  *
  *     (c) 1999--2005 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
@@ -9,23 +11,6 @@
 #ifndef _BIRD_OSPF_H_
 #define _BIRD_OSPF_H_
 
-#define MAXNETS 10
-#define OSPF_MIN_PKT_SIZE 256
-#define OSPF_MAX_PKT_SIZE 65535
-
-#ifdef LOCAL_DEBUG
-#define OSPF_FORCE_DEBUG 1
-#else
-#define OSPF_FORCE_DEBUG 0
-#endif
-#define OSPF_TRACE(flags, msg, args...) do { if ((p->debug & flags) || OSPF_FORCE_DEBUG) \
-  log(L_TRACE "%s: " msg, p->name , ## args ); } while(0)
-
-#define OSPF_PACKET(dumpfn, buffer, msg, args...) \
-do { if ((p->debug & D_PACKETS) || OSPF_FORCE_DEBUG) \
-{ log(L_TRACE "%s: " msg, p->name, ## args ); dumpfn(p, buffer); } } while(0)
-
-
 #include "nest/bird.h"
 
 #include "lib/checksum.h"
@@ -44,33 +29,72 @@ do { if ((p->debug & D_PACKETS) || OSPF_FORCE_DEBUG) \
 #include "conf/conf.h"
 #include "lib/string.h"
 
-#define OSPF_PROTO 89
 
-#ifndef IPV6
-#define OSPFv2 1
-#define OSPF_VERSION 2
-#define OSPF_VLINK_MTU 576                     /* RFC 2328 A.1 */
-#define AllSPFRouters ipa_from_u32(0xe0000005) /* 224.0.0.5 */
-#define AllDRouters ipa_from_u32(0xe0000006)   /* 224.0.0.6 */
+#ifdef LOCAL_DEBUG
+#define OSPF_FORCE_DEBUG 1
+#else
+#define OSPF_FORCE_DEBUG 0
+#endif
+
+
+#define IP4_MIN_MTU            576
+#define IP6_MIN_MTU            1280
+
+#define IP4_OSPF_ALL_ROUTERS   ipa_build4(224, 0, 0, 5)
+#define IP4_OSPF_DES_ROUTERS   ipa_build4(224, 0, 0, 6)
+
+#define IP6_OSPF_ALL_ROUTERS   ipa_build6(0xFF020000, 0, 0, 5)
+#define IP6_OSPF_DES_ROUTERS   ipa_build6(0xFF020000, 0, 0, 6)
+
+#ifdef IPV6
+#define ip4_addr u32
+#define ip6_addr ip_addr
+#define _MI6(x1,x2,x3,x4) _MI(x1, x2, x3, x4)
+#define ipa_is_link_local(x) ipa_has_link_scope(x)
+#define ipa_from_u32(x) _MI6(0,0,0xffff,x)
+#define ipa_to_u32(x) _I3(x)
+#define ipa_build4(a,b,c,d) IPA_NONE
+#define ipa_build6(a,b,c,d) _MI6(a,b,c,d)
+#define OSPF_IS_V2 0
 #else
-#define OSPFv3 1
-#define OSPF_VERSION 3
-#define OSPF_VLINK_MTU 1280                    /* RFC 5340 A.1 */
-#define AllSPFRouters _MI(0xFF020000, 0, 0, 5) /* FF02::5 */
-#define AllDRouters   _MI(0xFF020000, 0, 0, 6) /* FF02::6 */
+#define ip4_addr u32
+#define ip6_addr ip_addr
+#define _I0(X) 0
+#define _I1(X) 0
+#define _I2(X) 0
+#define _I3(X) 0
+#define _MI6(x1,x2,x3,x4) IPA_NONE
+#define ipa_is_link_local(x) 0
+#define ipa_build4(a,b,c,d) _MI(((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+#define ipa_build6(a,b,c,d) IPA_NONE
+#define OSPF_IS_V2 1
 #endif
 
+// FIXME: MAX_PREFIX_LENGTH
+
+#define OSPF_TRACE(flags, msg, args...) \
+do { if ((p->p.debug & flags) || OSPF_FORCE_DEBUG) \
+  log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0)
+
+#define OSPF_PACKET(dumpfn, buffer, msg, args...) \
+do { if ((p->p.debug & D_PACKETS) || OSPF_FORCE_DEBUG) \
+  { log(L_TRACE "%s: " msg, p->p.name, ## args ); dumpfn(p, buffer); } } while(0)
+
+
+#define OSPF_PROTO 89
 
 #define LSREFRESHTIME 1800     /* 30 minutes */
 #define MINLSINTERVAL 5
 #define MINLSARRIVAL 1
 #define LSINFINITY 0xffffff
 
-#define DEFAULT_OSPFTICK 1
-#define DEFAULT_RFC1583 0      /* compatibility with rfc1583 */
-#define DEFAULT_STUB_COST 1000
-#define DEFAULT_ECMP_LIMIT 16
-#define DEFAULT_TRANSINT 40
+#define OSPF_DEFAULT_TICK 1
+#define OSPF_DEFAULT_STUB_COST 1000
+#define OSPF_DEFAULT_ECMP_LIMIT 16
+#define OSPF_DEFAULT_TRANSINT 40
+
+#define OSPF_MIN_PKT_SIZE 256
+#define OSPF_MAX_PKT_SIZE 65535
 
 #define OSPF_VLINK_ID_OFFSET 0x80000000
 
@@ -79,10 +103,12 @@ struct ospf_config
 {
   struct proto_config c;
   unsigned tick;
+  byte ospf2;
   byte rfc1583;
   byte stub_router;
   byte merge_external;
   byte abr;
+  byte asbr;
   int ecmp;
   list area_list;              /* list of struct ospf_area_config */
   list vlink_list;             /* list of struct ospf_iface_patt */
@@ -100,32 +126,34 @@ struct area_net_config
 {
   node n;
   struct prefix px;
-  int hidden;
   u32 tag;
+  u8 hidden;
 };
 
 struct area_net
 {
   struct fib_node fn;
-  int hidden;
-  int active;
-  u32 metric;
+  u32 metric;                  /* With possible LSA_EXT3_EBIT for NSSA area nets */
   u32 tag;
+  u8 hidden;
+  u8 active;
 };
 
 struct ospf_stubnet_config
 {
   node n;
   struct prefix px;
-  int hidden, summary;
   u32 cost;
+  u8 hidden;
+  u8 summary;
 };
 
 struct ospf_area_config
 {
   node n;
   u32 areaid;
-  u32 default_cost;            /* Cost of default route for stub areas */
+  u32 default_cost;            /* Cost of default route for stub areas
+                                  (With possible LSA_EXT3_EBIT for NSSA areas) */
   u8 type;                     /* Area type (standard, stub, NSSA), represented
                                   by option flags (OPT_E, OPT_N) */
   u8 summary;                  /* Import summaries to this stub/NSSA area, valid for ABR */
@@ -139,38 +167,57 @@ struct ospf_area_config
 };
 
 
-/* Option flags */
-
-#define OPT_E  0x02
-#define OPT_N  0x08
-#define OPT_DC 0x20
-
-#ifdef OSPFv2
-#define OPT_P  0x08            /* flags P and N share position, see NSSA RFC */
-#define OPT_EA 0x10
-
-/* VEB flags are are stored independently in 'u16 options' */
-#define OPT_RT_B  (0x01 << 8)
-#define OPT_RT_E  (0x02 << 8)
-#define OPT_RT_V  (0x04 << 8)
-#define OPT_RT_NT (0x10 << 8)
-#endif
-
-#ifdef OSPFv3
-#define OPT_V6 0x01
-#define OPT_R  0x10
+/* Generic option flags */
+#define OPT_V6 0x01            /* OSPFv3, LSA relevant for IPv6 routing calculation */
+#define OPT_E  0x02            /* Related to AS-external LSAs */
+#define OPT_MC 0x04            /* Related to MOSPF, not used and obsolete */
+#define OPT_N  0x08            /* Related to NSSA */
+#define OPT_P  0x08            /* OSPFv2, flags P and N share position, see NSSA RFC */
+#define OPT_EA 0x10            /* OSPFv2, external attributes, not used and obsolete */
+#define OPT_R  0x10            /* OSPFv3, originator is active router */
+#define OPT_DC 0x20            /* Related to demand circuits, not used */
 
-/* VEB flags are are stored together with options in 'u32 options' */
+/* Router-LSA VEB flags are are stored together with links (OSPFv2) or options (OSPFv3) */
 #define OPT_RT_B  (0x01 << 24)
 #define OPT_RT_E  (0x02 << 24)
 #define OPT_RT_V  (0x04 << 24)
 #define OPT_RT_NT (0x10 << 24)
 
+/* Prefix flags, specific for OSPFv3 */
 #define OPT_PX_NU 0x01
 #define OPT_PX_LA 0x02
 #define OPT_PX_P  0x08
 #define OPT_PX_DN 0x10
-#endif
+
+
+/* OSPF interface types */
+#define OSPF_IT_BCAST  0
+#define OSPF_IT_NBMA   1
+#define OSPF_IT_PTP    2
+#define OSPF_IT_PTMP   3
+#define OSPF_IT_VLINK  4
+#define OSPF_IT_UNDEF  5
+
+/* OSPF interface states */
+#define OSPF_IS_DOWN   0       /* Not active */
+#define OSPF_IS_LOOP   1       /* Iface with no link */
+#define OSPF_IS_WAITING        2       /* Waiting for Wait timer */
+#define OSPF_IS_PTP    3       /* PTP operational */
+#define OSPF_IS_DROTHER        4       /* I'm on BCAST or NBMA and I'm not DR */
+#define OSPF_IS_BACKUP 5       /* I'm BDR */
+#define OSPF_IS_DR     6       /* I'm DR */
+
+
+/* Default values for interface parameters */
+#define COST_D 10
+#define RXMTINT_D 5
+#define INFTRANSDELAY_D 1
+#define PRIORITY_D 1
+#define HELLOINT_D 10
+#define POLLINT_D 20
+#define DEADC_D 4
+#define WAIT_DMH 4             
+  /* Value of Wait timer - not found it in RFC * - using 4*HELLO */
 
 
 struct ospf_iface
@@ -199,82 +246,50 @@ struct ospf_iface
                                   transmit a Link State Update Packet over this
                                   interface.  LSAs contained in the update */
   u16 helloint;                        /* number of seconds between hello sending */
-
-#ifdef OSPFv2
   list *passwords;
-  u16 autype;
   u32 csn;                      /* Last used crypt seq number */
   bird_clock_t csn_use;         /* Last time when packet with that CSN was sent */
-#endif
-
-  ip_addr all_routers;         /*  */
-  ip_addr drip;                        /* Designated router */
-  ip_addr bdrip;               /* Backup DR */
-  u32 drid;
-  u32 bdrid;
+  ip_addr all_routers;         /* Multicast (or broadcast) address for all routers */
+  ip_addr des_routers;         /* Multicast (or NULL) address for designated routers */
+  ip_addr drip;                        /* Designated router IP */
+  ip_addr bdrip;               /* Backup DR IP */
+  u32 drid;                    /* DR Router ID */
+  u32 bdrid;                   /* BDR Router ID */
   s16 rt_pos_beg;              /* Position of iface in Router-LSA, begin, inclusive */
   s16 rt_pos_end;              /* Position of iface in Router-LSA, end, exclusive */
-
-#ifdef OSPFv3
   s16 px_pos_beg;              /* Position of iface in Rt Prefix-LSA, begin, inclusive */
   s16 px_pos_end;              /* Position of iface in Rt Prefix-LSA, end, exclusive */
-
   u32 dr_iface_id;             /* if drid is valid, this is iface_id of DR (for connecting network) */
   u8 instance_id;              /* Used to differentiate between more OSPF
                                   instances on one interface */
-#endif
-
-  u8 type;                     /* OSPF view of type */
-#define OSPF_IT_BCAST 0
-#define OSPF_IT_NBMA 1
-#define OSPF_IT_PTP 2
-#define OSPF_IT_PTMP 3
-#define OSPF_IT_VLINK 4
-#define OSPF_IT_UNDEF 5
+  u8 autype;                   /* Authentication type (OSPF_AUTH_*) */
+  u8 type;                     /* OSPF view of type (OSPF_IT_*) */
   u8 strictnbma;               /* Can I talk with unknown neighbors? */
   u8 stub;                     /* Inactive interface */
-  u8 state;                    /* Interface state machine */
-#define OSPF_IS_DOWN 0         /* Not working */
-#define OSPF_IS_LOOP 1         /* Iface with no link */
-#define OSPF_IS_WAITING 2      /* Waiting for Wait timer */
-#define OSPF_IS_PTP 3          /* PTP operational */
-#define OSPF_IS_DROTHER 4      /* I'm on BCAST or NBMA and I'm not DR */
-#define OSPF_IS_BACKUP 5       /* I'm BDR */
-#define OSPF_IS_DR 6           /* I'm DR */
+  u8 state;                    /* Interface state machine (OSPF_IS_*) */
   timer *wait_timer;           /* WAIT timer */
   timer *hello_timer;          /* HELLOINT timer */
   timer *poll_timer;           /* Poll Interval - for NBMA */
-/* Default values for interface parameters */
-#define COST_D 10
-#define RXMTINT_D 5
-#define INFTRANSDELAY_D 1
-#define PRIORITY_D 1
-#define HELLOINT_D 10
-#define POLLINT_D 20
-#define DEADC_D 4
-#define WAIT_DMH 4             
-  /* Value of Wait timer - not found it in RFC * - using 4*HELLO */
 
-  struct top_hash_entry *net_lsa;      /* Originated network LSA */
-  int orignet;                         /* Schedule network LSA origination */
-#ifdef OSPFv3
-  int origlink;                                /* Schedule link LSA origination */
   struct top_hash_entry *link_lsa;     /* Originated link LSA */
+  struct top_hash_entry *net_lsa;      /* Originated network LSA */
   struct top_hash_entry *pxn_lsa;      /* Originated prefix LSA */
-#endif
-  int fadj;                            /* Number of full adjacent neigh */
+  u8 update_link_lsa;
+  u8 update_net_lsa;
+  int fadj;                    /* Number of fully adjacent neighbors */
   list nbma_list;
   u8 priority;                 /* A router priority for DR election */
   u8 ioprob;
 #define OSPF_I_OK 0            /* Everything OK */
 #define OSPF_I_SK 1            /* Socket open failed */
 #define OSPF_I_LL 2            /* Missing link-local address (OSPFv3) */
-  u8 sk_dr;                    /* Socket is a member of DRouters group */
-  u8 marked;                   /* Used in OSPF reconfigure */
+  u8 sk_dr;                    /* Socket is a member of designated routers group */
+  u8 marked;                   /* Used in OSPF reconfigure, 2 for force restart */
   u16 rxbuf;                   /* Buffer size */
   u16 tx_length;               /* Soft TX packet length limit, usually MTU */
   u8 check_link;               /* Whether iface link change is used */
   u8 ecmp_weight;              /* Weight used for ECMP */
+  u8 link_lsa_suppression;     /* Suppression of Link-LSA origination */
   u8 ptp_netmask;              /* Send real netmask for P2P */
   u8 check_ttl;                        /* Check incoming packets for TTL 255 */
   u8 bfd;                      /* Use BFD on iface */
@@ -296,42 +311,20 @@ union ospf_auth
 
 
 /* Packet types */
-#define HELLO_P 1              /* Hello */
-#define DBDES_P 2              /* Database description */
-#define LSREQ_P 3              /* Link state request */
-#define LSUPD_P 4              /* Link state update */
-#define LSACK_P 5              /* Link state acknowledgement */
+#define HELLO_P                1       /* Hello */
+#define DBDES_P                2       /* Database description */
+#define LSREQ_P                3       /* Link state request */
+#define LSUPD_P                4       /* Link state update */
+#define LSACK_P                5       /* Link state acknowledgement */
 
 /* Area IDs */
-#define BACKBONE 0
-
-
-struct immsb
-{
-#ifdef CPU_BIG_ENDIAN
-  u8 padding:5;
-  u8 i:1;
-  u8 m:1;
-  u8 ms:1;
-#else
-  u8 ms:1;
-  u8 m:1;
-  u8 i:1;
-  u8 padding:5;
-#endif
-};
-
-union imms
-{
-  u8 byte;
-  struct immsb bit;
-};
-#define DBDES_MS 1
-#define DBDES_M 2
-#define DBDES_I 4
+#define BACKBONE       0
 
+#define DBDES_I                4       /* Init bit */
+#define DBDES_M                2       /* More bit */
+#define DBDES_MS       1       /* Master/Slave bit */
+#define DBDES_IMMS     (DBDES_I | DBDES_M | DBDES_MS)
 
-#ifdef OSPFv2
 
 struct ospf_packet
 {
@@ -341,57 +334,11 @@ struct ospf_packet
   u32 routerid;
   u32 areaid;
   u16 checksum;
-  u16 autype;
-  union ospf_auth u;
+  u8 instance_id;              /* See RFC 6549 */
+  u8 autype;                   /* Undefined for OSPFv3 */
 };
 
 
-#else /* OSPFv3 packet descriptions */
-
-struct ospf_packet
-{
-  u8 version;
-  u8 type;
-  u16 length;
-  u32 routerid;
-  u32 areaid;
-  u16 checksum;
-  u8 instance_id;
-  u8 zero;
-};
-
-
-#endif
-
-
-
-
-struct ospf_lsa_header
-{
-  u16 age;                     /* LS Age */
-#define LSA_MAXAGE 3600                /* 1 hour */
-#define LSA_CHECKAGE 300       /* 5 minutes */
-#define LSA_MAXAGEDIFF 900     /* 15 minutes */
-
-#ifdef OSPFv2
-  u8 options;
-  u8 type;
-
-#define LSA_T_RT       1
-#define LSA_T_NET      2
-#define LSA_T_SUM_NET  3
-#define LSA_T_SUM_RT   4
-#define LSA_T_EXT      5
-#define LSA_T_NSSA     7
-
-#define LSA_SCOPE_AREA 0x2000
-#define LSA_SCOPE_AS   0x4000
-
-#define LSA_SCOPE(lsa) (((lsa)->type == LSA_T_EXT) ? LSA_SCOPE_AS : LSA_SCOPE_AREA)
-
-#else /* OSPFv3 */
-  u16 type;
-
 #define LSA_T_RT       0x2001
 #define LSA_T_NET      0x2002
 #define LSA_T_SUM_NET  0x2003
@@ -401,6 +348,8 @@ struct ospf_lsa_header
 #define LSA_T_LINK     0x0008
 #define LSA_T_PREFIX   0x2009
 
+#define LSA_T_V2_MASK  0x00ff
+
 #define LSA_UBIT       0x8000
 
 #define LSA_SCOPE_LINK 0x0000
@@ -408,91 +357,93 @@ struct ospf_lsa_header
 #define LSA_SCOPE_AS   0x4000
 #define LSA_SCOPE_RES  0x6000
 #define LSA_SCOPE_MASK 0x6000
+#define LSA_SCOPE(type)        ((type) & LSA_SCOPE_MASK)
 
-#define LSA_SCOPE(lsa) ((lsa)->type & LSA_SCOPE_MASK)
-#endif
+
+#define LSA_MAXAGE     3600    /* 1 hour */
+#define LSA_CHECKAGE   300     /* 5 minutes */
+#define LSA_MAXAGEDIFF 900     /* 15 minutes */
+
+#define LSA_ZEROSEQNO  ((s32) 0x80000000)
+#define LSA_INITSEQNO  ((s32) 0x80000001)
+#define LSA_MAXSEQNO   ((s32) 0x7fffffff)
+
+#define LSA_METRIC_MASK  0x00FFFFFF
+#define LSA_OPTIONS_MASK 0x00FFFFFF
+
+
+#define LSART_PTP      1
+#define LSART_NET      2
+#define LSART_STUB     3
+#define LSART_VLNK     4
+
+#define LSA_RT2_LINKS  0x0000FFFF
+
+#define LSA_SUM2_TOS   0xFF000000
+
+#define LSA_EXT2_TOS   0x7F000000
+#define LSA_EXT2_EBIT  0x80000000
+
+#define LSA_EXT3_EBIT  0x4000000
+#define LSA_EXT3_FBIT  0x2000000
+#define LSA_EXT3_TBIT  0x1000000
+
+
+struct ospf_lsa_header
+{
+  u16 age;                     /* LS Age */
+  u16 type_raw;                        /* Type, mixed with options on OSPFv2 */
 
   u32 id;
   u32 rt;                      /* Advertising router */
   s32 sn;                      /* LS Sequence number */
-#define LSA_INITSEQNO ((s32) 0x80000001)
-#define LSA_MAXSEQNO ((s32) 0x7fffffff)
   u16 checksum;
   u16 length;
 };
 
 
-#define LSART_PTP 1
-#define LSART_NET 2
-#define LSART_STUB 3
-#define LSART_VLNK 4
+/* In OSPFv2, options are embedded in higher half of type_raw */
+static inline u8 lsa_get_options(struct ospf_lsa_header *lsa)
+{ return lsa->type_raw >> 8; }
 
+static inline void lsa_set_options(struct ospf_lsa_header *lsa, u16 options)
+{ lsa->type_raw = (lsa->type_raw & 0xff) | (options << 8); }
 
-#ifdef OSPFv2
 
 struct ospf_lsa_rt
 {
-#ifdef CPU_BIG_ENDIAN
-  u16 options; /* VEB flags only */
-  u16 links;
-#else
-  u16 links;
-  u16 options; /* VEB flags only */
-#endif
+  u32 options; /* VEB flags, mixed with link count for OSPFv2 and options for OSPFv3 */
 };
 
-struct ospf_lsa_rt_link
+struct ospf_lsa_rt2_link
 {
   u32 id;
   u32 data;
 #ifdef CPU_BIG_ENDIAN
   u8 type;
-  u8 padding;
+  u8 no_tos;
   u16 metric;
 #else
   u16 metric;
-  u8 padding;
+  u8 no_tos;
   u8 type;
 #endif
 };
 
-struct ospf_lsa_net
-{
-  ip_addr netmask;
-  u32 routers[];
-};
-
-struct ospf_lsa_sum
-{
-  ip_addr netmask;
-  u32 metric;
-};
-
-struct ospf_lsa_ext
-{
-  ip_addr netmask;
-  u32 metric;
-  ip_addr fwaddr;
-  u32 tag;
-};
-
-#define LSA_SUM_TOS  0xFF000000
-#define LSA_EXT_TOS  0x7F000000
-#define LSA_EXT_EBIT 0x80000000
-
-/* Endianity swap for lsa->type */
-#define ntoht(x) x
-#define htont(x) x
-
-
-#else  /* OSPFv3 */
-
-struct ospf_lsa_rt
+struct ospf_lsa_rt2_tos
 {
-  u32 options;
+#ifdef CPU_BIG_ENDIAN
+  u8 tos;
+  u8 padding;
+  u16 metric;
+#else
+  u16 metric;
+  u8 padding;
+  u8 tos;
+#endif
 };
 
-struct ospf_lsa_rt_link
+struct ospf_lsa_rt3_link
 {
 #ifdef CPU_BIG_ENDIAN
   u8 type;
@@ -508,35 +459,58 @@ struct ospf_lsa_rt_link
   u32 id;      /* Neighbor router ID */
 };
 
+
 struct ospf_lsa_net
 {
-  u32 options;
+  u32 optx;    /* Netmask for OSPFv2, options for OSPFv3 */
   u32 routers[];
 };
 
-struct ospf_lsa_sum_net
+struct ospf_lsa_sum2
+{
+  u32 netmask;
+  u32 metric;
+};
+
+struct ospf_lsa_sum3_net
 {
   u32 metric;
   u32 prefix[];
 };
 
-struct ospf_lsa_sum_rt
+struct ospf_lsa_sum3_rt
 {
   u32 options;
   u32 metric;
   u32 drid;
 };
 
-struct ospf_lsa_ext
+struct ospf_lsa_ext2
+{
+  u32 netmask;
+  u32 metric;
+  u32 fwaddr;
+  u32 tag;
+};
+
+struct ospf_lsa_ext3
 {
   u32 metric;
   u32 rest[];
 };
 
+struct ospf_lsa_ext_local
+{
+  ip_addr ip, fwaddr;
+  int pxlen;
+  u32 metric, ebit, fbit, tag, propagate;
+  u8 pxopts;
+};
+
 struct ospf_lsa_link
 {
   u32 options;
-  ip_addr lladdr;
+  ip6_addr lladdr;
   u32 pxcount;
   u32 rest[];
 };
@@ -555,25 +529,6 @@ struct ospf_lsa_prefix
   u32 rest[];
 };
 
-#define LSA_EXT_EBIT 0x4000000
-#define LSA_EXT_FBIT 0x2000000
-#define LSA_EXT_TBIT 0x1000000
-
-/* Endianity swap for lsa->type */
-#define ntoht(x) ntohs(x)
-#define htont(x) htons(x)
-
-#endif
-
-#define METRIC_MASK  0x00FFFFFF
-#define OPTIONS_MASK 0x00FFFFFF
-
-static inline unsigned
-lsa_rt_count(struct ospf_lsa_header *lsa)
-{
-  return (lsa->length - sizeof(struct ospf_lsa_header) - sizeof(struct ospf_lsa_rt))
-    / sizeof(struct ospf_lsa_rt_link);
-}
 
 static inline unsigned
 lsa_net_count(struct ospf_lsa_header *lsa)
@@ -582,8 +537,12 @@ lsa_net_count(struct ospf_lsa_header *lsa)
     / sizeof(u32);
 }
 
+/* In ospf_area->rtr we store paths to routers, but we use RID (and not IP address)
+   as index, so we need to encapsulate RID to IP address */
+
+#define ipa_from_rid(x) ipa_from_u32(x)
+#define ipa_to_rid(x) ipa_to_u32(x)
 
-#ifdef OSPFv3
 
 #define IPV6_PREFIX_SPACE(x) ((((x) + 63) / 32) * 4)
 #define IPV6_PREFIX_WORDS(x) (((x) + 63) / 32)
@@ -645,21 +604,12 @@ put_ipv6_addr(u32 *buf, ip_addr addr)
   return buf + 4;
 }
 
-#endif
-
-
 
 struct ospf_lsreq_header
 {
   u32 type;
   u32 id;
-  u32 rt;                      /* Advertising router */
-};
-
-struct l_lsr_head
-{
-  node n;
-  struct ospf_lsreq_header lsh;
+  u32 rt;
 };
 
 
@@ -678,31 +628,38 @@ struct ospf_neighbor
 #define NEIGHBOR_LOADING 6
 #define NEIGHBOR_FULL 7
   timer *inactim;              /* Inactivity timer */
-  union imms imms;             /* I, M, Master/slave received */
+  u8 imms;                     /* I, M, Master/slave received */
+  u8 myimms;                   /* I, M Master/slave */
   u32 dds;                     /* DD Sequence number being sent */
   u32 ddr;                     /* last Dat Des packet received */
-  union imms myimms;           /* I, M Master/slave */
+
   u32 rid;                     /* Router ID */
   ip_addr ip;                  /* IP of it's interface */
   u8 priority;                 /* Priority */
   u8 adj;                      /* built adjacency? */
   u32 options;                 /* Options received */
 
-  /* dr and bdr store IP address in OSPFv2 and router ID in OSPFv3,
-     we use the same type to simplify handling */
+  /* Entries dr and bdr store IP addresses in OSPFv2 and router IDs in
+     OSPFv3, we use the same type to simplify handling */
   u32 dr;                      /* Neigbour's idea of DR */
   u32 bdr;                     /* Neigbour's idea of BDR */
-
-#ifdef OSPFv3
   u32 iface_id;                        /* ID of Neighbour's iface connected to common network */
-#endif
 
-  siterator dbsi;              /* Database summary list iterator */
-  slist lsrql;                 /* Link state request */
-  struct top_graph *lsrqh;     /* LSA graph */
-  siterator lsrqi;
-  slist lsrtl;                 /* Link state retransmission list */
-  siterator lsrti;
+  /* Database summary list iterator, controls initial dbdes exchange.
+   * Advances in the LSA list as dbdes packets are sent.
+   */
+  siterator dbsi;              /* iterator of po->lsal */
+
+  /* Link state request list, controls initial LSA exchange.
+   * Entries added when received in dbdes packets, removed as sent in lsreq packets.
+   */
+  slist lsrql;                 /* slist of struct top_hash_entry from n->lsrqh */
+  struct top_graph *lsrqh;
+
+  /* Link state retransmission list, controls LSA retransmission during flood.
+   * Entries added as sent in lsupd packets, removed when received in lsack packets.
+   */
+  slist lsrtl;                 /* slist of struct top_hash_entry from n->lsrth */
   struct top_graph *lsrth;
   timer *rxmt_timer;           /* RXMT timer */
   list ackl[2];
@@ -750,12 +707,12 @@ struct ospf_area
   struct fib net_fib;          /* Networks to advertise or not */
   struct fib enet_fib;         /* External networks for NSSAs */
   u32 options;                 /* Optional features */
-  byte origrt;                 /* Rt lsa origination scheduled? */
-  byte trcap;                  /* Transit capability? */
-  byte marked;                 /* Used in OSPF reconfigure */
-  byte translate;              /* Translator state (TRANS_*), for NSSA ABR  */
+  u8 update_rt_lsa;            /* Rt lsa origination scheduled? */
+  u8 trcap;                    /* Transit capability? */
+  u8 marked;                   /* Used in OSPF reconfigure */
+  u8 translate;                        /* Translator state (TRANS_*), for NSSA ABR  */
   timer *translator_timer;     /* For NSSA translator switch */
-  struct proto_ospf *po;
+  struct ospf_proto *po;
   struct fib rtr;              /* Routing tables for routers */
 };
 
@@ -763,9 +720,9 @@ struct ospf_area
 #define TRANS_ON       1
 #define TRANS_WAIT     2       /* Waiting before the end of translation */
 
-struct proto_ospf
+struct ospf_proto
 {
-  struct proto proto;
+  struct proto p;
   timer *disp_timer;           /* OSPF proto dispatcher */
   unsigned tick;
   struct top_graph *gr;                /* LSA graph */
@@ -775,11 +732,13 @@ struct proto_ospf
   list iface_list;             /* Interfaces we really use */
   list area_list;
   int areano;                  /* Number of area I belong to */
+  int padj;                    /* Number of neighbors in Exchange or Loading state */
   struct fib rtf;              /* Routing table */
+  byte ospf2;                  /* OSPF v2 or v3 */
   byte rfc1583;                        /* RFC1583 compatibility */
   byte stub_router;            /* Do not forward transit traffic */
   byte merge_external;         /* Should i merge external routes? */
-  byte ebit;                   /* Did I originate any ext lsa? */
+  byte asbr;                   /* May i originate any ext/NSSA lsa? */
   byte ecmp;                   /* Maximal number of nexthops in ECMP route, or 0 */
   struct ospf_area *backbone;  /* If exists */
   void *lsab;                  /* LSA buffer used when originating router LSAs */
@@ -813,7 +772,8 @@ struct ospf_iface_patt
   u16 rx_buffer;
 
 #define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */
-  u16 autype;                  /* Not really used in OSPFv3 */
+  u8 instance_id;
+  u8 autype;                   /* Not really used in OSPFv3 */
 #define OSPF_AUTH_NONE 0
 #define OSPF_AUTH_SIMPLE 1
 #define OSPF_AUTH_CRYPT 2
@@ -821,51 +781,15 @@ struct ospf_iface_patt
   u8 strictnbma;
   u8 check_link;
   u8 ecmp_weight;
+  u8 link_lsa_suppression;
   u8 real_bcast;               /* Not really used in OSPFv3 */
   u8 ptp_netmask;              /* bool + 2 for unspecified */
   u8 ttl_security;             /* bool + 2 for TX only */
   u8 bfd;
   u8 bsd_secondary;
-
-#ifdef OSPFv2
   list *passwords;
-#endif
-
-#ifdef OSPFv3
-  u8 instance_id;
-#endif
 };
 
-int ospf_import_control(struct proto *p, rte **new, ea_list **attrs,
-                       struct linpool *pool);
-struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
-void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs);
-void schedule_rt_lsa(struct ospf_area *oa);
-void schedule_rtcalc(struct proto_ospf *po);
-void schedule_net_lsa(struct ospf_iface *ifa);
-
-struct ospf_area *ospf_find_area(struct proto_ospf *po, u32 aid);
-static inline struct ospf_area *ospf_main_area(struct proto_ospf *po)
-{ return (po->areano == 1) ? HEAD(po->area_list) : po->backbone; }
-
-static inline int oa_is_stub(struct ospf_area *oa)
-{ return (oa->options & (OPT_E | OPT_N)) == 0; }
-static inline int oa_is_ext(struct ospf_area *oa)
-{ return oa->options & OPT_E; }
-static inline int oa_is_nssa(struct ospf_area *oa)
-{ return oa->options & OPT_N; }
-
-
-#ifdef OSPFv3
-void schedule_link_lsa(struct ospf_iface *ifa);
-#else
-static inline void schedule_link_lsa(struct ospf_iface *ifa UNUSED) {}
-#endif
-
-void ospf_sh_neigh(struct proto *p, char *iff);
-void ospf_sh(struct proto *p);
-void ospf_sh_iface(struct proto *p, char *iff);
-void ospf_sh_state(struct proto *p, int verbose, int reachable);
 
 #define SH_ROUTER_SELF 0xffffffff
 
@@ -878,24 +802,148 @@ struct lsadb_show_data {
   u32 router;          /* Advertising router, 0 -> all */
 };
 
-void ospf_sh_lsadb(struct lsadb_show_data *ld);
-
 
 #define EA_OSPF_METRIC1        EA_CODE(EAP_OSPF, 0)
 #define EA_OSPF_METRIC2        EA_CODE(EAP_OSPF, 1)
 #define EA_OSPF_TAG    EA_CODE(EAP_OSPF, 2)
 #define EA_OSPF_ROUTER_ID EA_CODE(EAP_OSPF, 3)
 
+
+/* ospf.c */
+void schedule_rtcalc(struct ospf_proto *p);
+
+static inline void ospf_notify_rt_lsa(struct ospf_area *oa)
+{ oa->update_rt_lsa = 1; }
+
+static inline void ospf_notify_net_lsa(struct ospf_iface *ifa)
+{ ifa->update_net_lsa = 1; }
+
+static inline void ospf_notify_link_lsa(struct ospf_iface *ifa)
+{ ifa->update_link_lsa = 1; }
+
+
+static inline int ospf_is_v2(struct ospf_proto *p)
+{ return p->ospf2; }
+
+static inline int ospf_is_v3(struct ospf_proto *p)
+{ return ! p->ospf2; }
+
+static inline int ospf_get_version(struct ospf_proto *p)
+{ return ospf_is_v2(p) ? 2 : 3; }
+
+struct ospf_area *ospf_find_area(struct ospf_proto *p, u32 aid);
+
+static inline struct ospf_area *ospf_main_area(struct ospf_proto *p)
+{ return (p->areano == 1) ? HEAD(p->area_list) : p->backbone; }
+
+static inline int oa_is_stub(struct ospf_area *oa)
+{ return (oa->options & (OPT_E | OPT_N)) == 0; }
+
+static inline int oa_is_ext(struct ospf_area *oa)
+{ return oa->options & OPT_E; }
+
+static inline int oa_is_nssa(struct ospf_area *oa)
+{ return oa->options & OPT_N; }
+
+void ospf_sh_neigh(struct proto *P, char *iff);
+void ospf_sh(struct proto *P);
+void ospf_sh_iface(struct proto *P, char *iff);
+void ospf_sh_state(struct proto *P, int verbose, int reachable);
+
+void ospf_sh_lsadb(struct lsadb_show_data *ld);
+
+/* iface.c */
+void ospf_iface_chstate(struct ospf_iface *ifa, u8 state);
+void ospf_iface_sm(struct ospf_iface *ifa, int event);
+struct ospf_iface *ospf_iface_find(struct ospf_proto *p, struct iface *what);
+void ospf_if_notify(struct proto *P, uint flags, struct iface *iface);
+void ospf_ifa_notify2(struct proto *P, uint flags, struct ifa *a);
+void ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a);
+void ospf_iface_info(struct ospf_iface *ifa);
+void ospf_iface_new(struct ospf_area *oa, struct ifa *addr, struct ospf_iface_patt *ip);
+void ospf_iface_new_vlink(struct ospf_proto *p, struct ospf_iface_patt *ip);
+void ospf_iface_remove(struct ospf_iface *ifa);
+void ospf_iface_shutdown(struct ospf_iface *ifa);
+int ospf_iface_assure_bufsize(struct ospf_iface *ifa, uint plen);
+int ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new);
+void ospf_reconfigure_ifaces(struct ospf_proto *p);
+void ospf_open_vlink_sk(struct ospf_proto *p);
+struct nbma_node *find_nbma_node_(list *nnl, ip_addr ip);
+
+static inline struct nbma_node * find_nbma_node(struct ospf_iface *ifa, ip_addr ip)
+{ return find_nbma_node_(&ifa->nbma_list, ip); }
+
+/* neighbor.c */
+struct ospf_neighbor *ospf_neighbor_new(struct ospf_iface *ifa);
+void ospf_neigh_sm(struct ospf_neighbor *n, int event);
+void ospf_dr_election(struct ospf_iface *ifa);
+struct ospf_neighbor *find_neigh(struct ospf_iface *ifa, u32 rid);
+struct ospf_neighbor *find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip);
+void ospf_neigh_remove(struct ospf_neighbor *n);
+void ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd);
+void ospf_sh_neigh_info(struct ospf_neighbor *n);
+
+/* packet.c */
+void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type);
+uint ospf_pkt_maxsize(struct ospf_iface *ifa);
+int ospf_rx_hook(sock * sk, int size);
+// void ospf_tx_hook(sock * sk);
+void ospf_err_hook(sock * sk, int err);
+void ospf_verr_hook(sock *sk, int err);
+void ospf_send_to(struct ospf_iface *ifa, ip_addr ip);
+void ospf_send_to_agt(struct ospf_iface *ifa, u8 state);
+void ospf_send_to_bdr(struct ospf_iface *ifa);
+
+static inline void ospf_send_to_all(struct ospf_iface *ifa)
+{ ospf_send_to(ifa, ifa->all_routers); }
+
+static inline void ospf_send_to_des(struct ospf_iface *ifa)
+{
+  if (ipa_nonzero(ifa->des_routers))
+    ospf_send_to(ifa, ifa->des_routers);
+  else
+    ospf_send_to_bdr(ifa);
+}
+
+static inline uint ospf_pkt_hdrlen(struct ospf_proto *p)
+{ return ospf_is_v2(p) ? (sizeof(struct ospf_packet) + sizeof(union ospf_auth)) : sizeof(struct ospf_packet); }
+
+static inline void * ospf_tx_buffer(struct ospf_iface *ifa)
+{ return ifa->sk->tbuf; }
+
+/* hello.c */
+#define OHS_HELLO    0
+#define OHS_POLL     1
+#define OHS_SHUTDOWN 2
+
+void ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn);
+void ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n, ip_addr faddr);
+
+/* dbdes.c */
+void ospf_send_dbdes(struct ospf_neighbor *n, int next);
+void ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n);
+
+/* lsreq.c */
+void ospf_send_lsreq(struct ospf_proto *p, struct ospf_neighbor *n);
+void ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n);
+
+/* lsupd.c */
+void ospf_dump_lsahdr(struct ospf_proto *p, struct ospf_lsa_header *lsa_n);
+void ospf_dump_common(struct ospf_proto *p, struct ospf_packet *pkt);
+int ospf_lsupd_flood(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neighbor *from);
+int ospf_send_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_list, uint lsa_count, struct ospf_neighbor *n);
+void ospf_rxmt_lsupd(struct ospf_proto *p, struct ospf_neighbor *n);
+void ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n);
+
+/* lsack.c */
+void ospf_enqueue_lsack(struct ospf_neighbor *n, struct ospf_lsa_header *h_n, int queue);
+void ospf_reset_lsack_queue(struct ospf_neighbor *n);
+void ospf_lsack_send(struct ospf_neighbor *n, int queue);
+void ospf_receive_lsack(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n);
+
+
 #include "proto/ospf/rt.h"
-#include "proto/ospf/hello.h"
-#include "proto/ospf/packet.h"
-#include "proto/ospf/iface.h"
-#include "proto/ospf/neighbor.h"
 #include "proto/ospf/topology.h"
-#include "proto/ospf/dbdes.h"
-#include "proto/ospf/lsreq.h"
-#include "proto/ospf/lsupd.h"
-#include "proto/ospf/lsack.h"
 #include "proto/ospf/lsalib.h"
 
 #endif /* _BIRD_OSPF_H_ */
index 1240b05c1acc7a8af4a5c91ad05d50d182dccb44..963511788131fe9974b47e66a831057a3a020283 100644 (file)
@@ -2,6 +2,8 @@
  *     BIRD -- OSPF
  *
  *     (c) 1999--2005 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 void
 ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type)
 {
-  struct proto_ospf *po = ifa->oa->po;
+  struct ospf_proto *p = ifa->oa->po;
   struct ospf_packet *pkt;
 
   pkt = (struct ospf_packet *) buf;
 
-  pkt->version = OSPF_VERSION;
-
+  pkt->version = ospf_get_version(p);
   pkt->type = h_type;
-
-  pkt->routerid = htonl(po->router_id);
+  pkt->length = htons(ospf_pkt_maxsize(ifa));
+  pkt->routerid = htonl(p->router_id);
   pkt->areaid = htonl(ifa->oa->areaid);
-
-#ifdef OSPFv3
-  pkt->instance_id = ifa->instance_id;
-#endif
-
-#ifdef OSPFv2
-  pkt->autype = htons(ifa->autype);
-#endif
-
   pkt->checksum = 0;
+  pkt->instance_id = ifa->instance_id;
+  pkt->autype = ifa->autype;
 }
 
-unsigned
+uint
 ospf_pkt_maxsize(struct ospf_iface *ifa)
 {
-  unsigned headers = SIZE_OF_IP_HEADER;
+  uint headers = SIZE_OF_IP_HEADER;
 
-#ifdef OSPFv2
+  /* Relevant just for OSPFv2 */
   if (ifa->autype == OSPF_AUTH_CRYPT)
     headers += OSPF_AUTH_CRYPT_SIZE;
-#endif
 
   return ifa->tx_length - headers;
 }
 
-#ifdef OSPFv2
-
+/* We assume OSPFv2 in ospf_pkt_finalize() */
 static void
 ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt)
 {
   struct password_item *passwd = NULL;
-  void *tail;
-  struct MD5Context ctxt;
-  char password[OSPF_AUTH_CRYPT_SIZE];
+  union ospf_auth *auth = (void *) (pkt + 1);
+  uint plen = ntohs(pkt->length);
 
   pkt->checksum = 0;
-  pkt->autype = htons(ifa->autype);
-  bzero(&pkt->u, sizeof(union ospf_auth));
+  pkt->autype = ifa->autype;
+  bzero(auth, sizeof(union ospf_auth));
 
-  /* Compatibility note: pkt->u may contain anything if autype is
+  /* Compatibility note: auth may contain anything if autype is
      none, but nonzero values do not work with Mikrotik OSPF */
 
-  switch(ifa->autype)
+  switch (ifa->autype)
   {
-    case OSPF_AUTH_SIMPLE:
-      passwd = password_find(ifa->passwords, 1);
-      if (!passwd)
-      {
-        log( L_ERR "No suitable password found for authentication" );
-        return;
-      }
-      password_cpy(pkt->u.password, passwd->password, sizeof(union ospf_auth));
-    case OSPF_AUTH_NONE:
-      pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet) -
-                                  sizeof(union ospf_auth), (pkt + 1),
-                                 ntohs(pkt->length) -
-                                 sizeof(struct ospf_packet), NULL);
-      break;
-    case OSPF_AUTH_CRYPT:
-      passwd = password_find(ifa->passwords, 0);
-      if (!passwd)
-      {
-        log( L_ERR "No suitable password found for authentication" );
-        return;
-      }
+  case OSPF_AUTH_SIMPLE:
+    passwd = password_find(ifa->passwords, 1);
+    if (!passwd)
+    {
+      log(L_ERR "No suitable password found for authentication");
+      return;
+    }
+    password_cpy(auth->password, passwd->password, sizeof(union ospf_auth));
 
-      /* Perhaps use random value to prevent replay attacks after
-        reboot when system does not have independent RTC? */
-      if (!ifa->csn)
-       {
-         ifa->csn = (u32) now;
-         ifa->csn_use = now;
-       }
+  case OSPF_AUTH_NONE:
+    {
+      void *body = (void *) (auth + 1);
+      uint blen = plen - sizeof(struct ospf_packet) - sizeof(union ospf_auth);
+      pkt->checksum = ipsum_calculate(pkt, sizeof(struct ospf_packet), body, blen, NULL);
+    }
+    break;
 
-      /* We must have sufficient delay between sending a packet and increasing 
-        CSN to prevent reordering of packets (in a network) with different CSNs */
-      if ((now - ifa->csn_use) > 1)
-       ifa->csn++;
+  case OSPF_AUTH_CRYPT:
+    passwd = password_find(ifa->passwords, 0);
+    if (!passwd)
+    {
+      log(L_ERR "No suitable password found for authentication");
+      return;
+    }
 
+    /* Perhaps use random value to prevent replay attacks after
+       reboot when system does not have independent RTC? */
+    if (!ifa->csn)
+    {
+      ifa->csn = (u32) now;
       ifa->csn_use = now;
+    }
+
+    /* We must have sufficient delay between sending a packet and increasing 
+       CSN to prevent reordering of packets (in a network) with different CSNs */
+    if ((now - ifa->csn_use) > 1)
+      ifa->csn++;
+
+    ifa->csn_use = now;
+
+    auth->md5.zero = 0;
+    auth->md5.keyid = passwd->id;
+    auth->md5.len = OSPF_AUTH_CRYPT_SIZE;
+    auth->md5.csn = htonl(ifa->csn);
+
+    void *tail = ((void *) pkt) + plen;
+    char password[OSPF_AUTH_CRYPT_SIZE];
+    password_cpy(password, passwd->password, OSPF_AUTH_CRYPT_SIZE);
 
-      pkt->u.md5.keyid = passwd->id;
-      pkt->u.md5.len = OSPF_AUTH_CRYPT_SIZE;
-      pkt->u.md5.zero = 0;
-      pkt->u.md5.csn = htonl(ifa->csn);
-      tail = ((void *)pkt) + ntohs(pkt->length);
-      MD5Init(&ctxt);
-      MD5Update(&ctxt, (char *) pkt, ntohs(pkt->length));
-      password_cpy(password, passwd->password, OSPF_AUTH_CRYPT_SIZE);
-      MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE);
-      MD5Final(tail, &ctxt);
-      break;
-    default:
-      bug("Unknown authentication type");
+    struct MD5Context ctxt;
+    MD5Init(&ctxt);
+    MD5Update(&ctxt, (char *) pkt, plen);
+    MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE);
+    MD5Final(tail, &ctxt);
+    break;
+
+  default:
+    bug("Unknown authentication type");
   }
 }
 
+/* We assume OSPFv2 in ospf_pkt_checkauth() */
 static int
 ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int size)
 {
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
+  struct ospf_proto *p = ifa->oa->po;
+  union ospf_auth *auth = (void *) (pkt + 1);
   struct password_item *pass = NULL, *ptmp;
-  void *tail;
-  char md5sum[OSPF_AUTH_CRYPT_SIZE];
   char password[OSPF_AUTH_CRYPT_SIZE];
-  struct MD5Context ctxt;
 
+  uint plen = ntohs(pkt->length);
+  u8 autype = pkt->autype;
 
-  if (pkt->autype != htons(ifa->autype))
+  if (autype != ifa->autype)
   {
-    OSPF_TRACE(D_PACKETS, "OSPF_auth: Method differs (%d)", ntohs(pkt->autype));
+    OSPF_TRACE(D_PACKETS, "OSPF_auth: Method differs (%d)", autype);
     return 0;
   }
 
-  switch(ifa->autype)
+  switch (autype)
   {
-    case OSPF_AUTH_NONE:
-      return 1;
-      break;
-    case OSPF_AUTH_SIMPLE:
-      pass = password_find(ifa->passwords, 1);
-      if (!pass)
-      {
-        OSPF_TRACE(D_PACKETS, "OSPF_auth: no password found");
-       return 0;
-      }
-      password_cpy(password, pass->password, sizeof(union ospf_auth));
+  case OSPF_AUTH_NONE:
+    return 1;
 
-      if (memcmp(pkt->u.password, password, sizeof(union ospf_auth)))
-      {
-        char ppass[sizeof(union ospf_auth) + 1];
-        bzero(ppass, (sizeof(union ospf_auth) + 1));
-        memcpy(ppass, pkt->u.password, sizeof(union ospf_auth));
-        OSPF_TRACE(D_PACKETS, "OSPF_auth: different passwords (%s)", ppass);
-       return 0;
-      }
-      return 1;
-      break;
-    case OSPF_AUTH_CRYPT:
-      if (pkt->u.md5.len != OSPF_AUTH_CRYPT_SIZE)
-      {
-        OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong size of md5 digest");
-        return 0;
-      }
+  case OSPF_AUTH_SIMPLE:
+    pass = password_find(ifa->passwords, 1);
+    if (!pass)
+    {
+      OSPF_TRACE(D_PACKETS, "OSPF_auth: no password found");
+      return 0;
+    }
 
-      if (ntohs(pkt->length) + OSPF_AUTH_CRYPT_SIZE > size)
-      {
-        OSPF_TRACE(D_PACKETS, "OSPF_auth: size mismatch (%d vs %d)",
-         ntohs(pkt->length) + OSPF_AUTH_CRYPT_SIZE, size);
-        return 0;
-      }
+    password_cpy(password, pass->password, sizeof(union ospf_auth));
+    if (memcmp(auth->password, password, sizeof(union ospf_auth)))
+    {
+      OSPF_TRACE(D_PACKETS, "OSPF_auth: different passwords");
+      return 0;
+    }
+    return 1;
 
-      tail = ((void *)pkt) + ntohs(pkt->length);
+  case OSPF_AUTH_CRYPT:
+    if (auth->md5.len != OSPF_AUTH_CRYPT_SIZE)
+    {
+      OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong size of md5 digest");
+      return 0;
+    }
 
-      if (ifa->passwords)
-      {
-       WALK_LIST(ptmp, *(ifa->passwords))
-       {
-         if (pkt->u.md5.keyid != ptmp->id) continue;
-         if ((ptmp->accfrom > now_real) || (ptmp->accto < now_real)) continue;
-         pass = ptmp;
-         break;
-       }
-      }
+    if (plen + OSPF_AUTH_CRYPT_SIZE > size)
+    {
+      OSPF_TRACE(D_PACKETS, "OSPF_auth: size mismatch (%d vs %d)",
+                plen + OSPF_AUTH_CRYPT_SIZE, size);
+      return 0;
+    }
 
-      if (!pass)
+    if (n)
+    {
+      u32 rcv_csn = ntohl(auth->md5.csn);
+      if(rcv_csn < n->csn)
       {
-        OSPF_TRACE(D_PACKETS, "OSPF_auth: no suitable md5 password found");
-        return 0;
+       OSPF_TRACE(D_PACKETS, "OSPF_auth: lower sequence number (rcv %d, old %d)", rcv_csn, n->csn);
+       return 0;
       }
 
-      if (n)
-      {
-       u32 rcv_csn = ntohl(pkt->u.md5.csn);
-       if(rcv_csn < n->csn)
-       {
-         OSPF_TRACE(D_PACKETS, "OSPF_auth: lower sequence number (rcv %d, old %d)", rcv_csn, n->csn);
-         return 0;
-       }
-
-       n->csn = rcv_csn;
-      }
+      n->csn = rcv_csn;
+    }
 
-      MD5Init(&ctxt);
-      MD5Update(&ctxt, (char *) pkt, ntohs(pkt->length));
-      password_cpy(password, pass->password, OSPF_AUTH_CRYPT_SIZE);
-      MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE);
-      MD5Final(md5sum, &ctxt);
-      if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE))
+    if (ifa->passwords)
+    {
+      WALK_LIST(ptmp, *(ifa->passwords))
       {
-        OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong md5 digest");
-        return 0;
+       if (auth->md5.keyid != ptmp->id) continue;
+       if ((ptmp->accfrom > now_real) || (ptmp->accto < now_real)) continue;
+       pass = ptmp;
+       break;
       }
-      return 1;
-      break;
-    default:
-      OSPF_TRACE(D_PACKETS, "OSPF_auth: unknown auth type");
+    }
+
+    if (!pass)
+    {
+      OSPF_TRACE(D_PACKETS, "OSPF_auth: no suitable md5 password found");
       return 0;
-  }
-}
+    }
 
-#else
+    void *tail = ((void *) pkt) + plen;
+    char md5sum[OSPF_AUTH_CRYPT_SIZE];
+    password_cpy(password, pass->password, OSPF_AUTH_CRYPT_SIZE);
 
-/* OSPFv3 authentication not yet supported */
+    struct MD5Context ctxt;
+    MD5Init(&ctxt);
+    MD5Update(&ctxt, (char *) pkt, plen);
+    MD5Update(&ctxt, password, OSPF_AUTH_CRYPT_SIZE);
+    MD5Final(md5sum, &ctxt);
 
-static inline void
-ospf_pkt_finalize(struct ospf_iface *ifa, struct ospf_packet *pkt)
-{ }
+    if (memcmp(md5sum, tail, OSPF_AUTH_CRYPT_SIZE))
+    {
+      OSPF_TRACE(D_PACKETS, "OSPF_auth: wrong md5 digest");
+      return 0;
+    }
+    return 1;
 
-static int
-ospf_pkt_checkauth(struct ospf_neighbor *n, struct ospf_iface *ifa, struct ospf_packet *pkt, int size)
-{ return 1; }
-#endif
+  default:
+    OSPF_TRACE(D_PACKETS, "OSPF_auth: unknown auth type");
+    return 0;
+  }
+}
 
 
 /**
@@ -266,43 +251,44 @@ ospf_rx_hook(sock *sk, int size)
 
   /* Initially, the packet is associated with the 'master' iface */
   struct ospf_iface *ifa = sk->data;
-  struct proto_ospf *po = ifa->oa->po;
-  // struct proto *p = &po->proto;
+  struct ospf_proto *p = ifa->oa->po;
 
-  int src_local, dst_local UNUSED, dst_mcast; 
+  int src_local, dst_local, dst_mcast; 
   src_local = ipa_in_net(sk->faddr, ifa->addr->prefix, ifa->addr->pxlen);
   dst_local = ipa_equal(sk->laddr, ifa->addr->ip);
-  dst_mcast = ipa_equal(sk->laddr, ifa->all_routers) || ipa_equal(sk->laddr, AllDRouters);
-
-#ifdef OSPFv2
-  /* First, we eliminate packets with strange address combinations.
-   * In OSPFv2, they might be for other ospf_ifaces (with different IP
-   * prefix) on the same real iface, so we don't log it. We enforce
-   * that (src_local || dst_local), therefore we are eliminating all
-   * such cases. 
-   */
-  if (dst_mcast && !src_local)
-    return 1;
-  if (!dst_mcast && !dst_local)
-    return 1;
+  dst_mcast = ipa_equal(sk->laddr, ifa->all_routers) || ipa_equal(sk->laddr, ifa->des_routers);
 
-  /* Ignore my own broadcast packets */
-  if (ifa->cf->real_bcast && ipa_equal(sk->faddr, ifa->addr->ip))
-    return 1;
-#else /* OSPFv3 */
+  if (ospf_is_v2(p))
+  {
+    /* First, we eliminate packets with strange address combinations.
+     * In OSPFv2, they might be for other ospf_ifaces (with different IP
+     * prefix) on the same real iface, so we don't log it. We enforce
+     * that (src_local || dst_local), therefore we are eliminating all
+     * such cases. 
+     */
+    if (dst_mcast && !src_local)
+      return 1;
+    if (!dst_mcast && !dst_local)
+      return 1;
 
-  /* In OSPFv3, src_local and dst_local mean link-local. 
-   * RFC 5340 says that local (non-vlink) packets use
-   * link-local src address, but does not enforce it. Strange.
-   */
-  if (dst_mcast && !src_local)
-    log(L_WARN "OSPF: Received multicast packet from %I (not link-local)", sk->faddr);
-#endif
+    /* Ignore my own broadcast packets */
+    if (ifa->cf->real_bcast && ipa_equal(sk->faddr, ifa->addr->ip))
+      return 1;
+  }
+  else
+  {
+    /* In OSPFv3, src_local and dst_local mean link-local. 
+     * RFC 5340 says that local (non-vlink) packets use
+     * link-local src address, but does not enforce it. Strange.
+     */
+    if (dst_mcast && !src_local)
+      log(L_WARN "OSPF: Received multicast packet from %I (not link-local)", sk->faddr);
+  }
 
   /* Second, we check packet size, checksum, and the protocol version */
-  struct ospf_packet *ps = (struct ospf_packet *) ip_skip_header(sk->rbuf, &size);
+  struct ospf_packet *pkt = (struct ospf_packet *) ip_skip_header(sk->rbuf, &size);
 
-  if (ps == NULL)
+  if (pkt == NULL)
   {
     log(L_ERR "%s%I - bad IP header", mesg, sk->faddr);
     return 1;
@@ -314,13 +300,13 @@ ospf_rx_hook(sock *sk, int size)
     return 1;
   }
 
-  if ((unsigned) size < sizeof(struct ospf_packet))
+  if ((uint) size < sizeof(struct ospf_packet))
   {
     log(L_ERR "%s%I - too short (%u bytes)", mesg, sk->faddr, size);
     return 1;
   }
 
-  uint plen = ntohs(ps->length);
+  uint plen = ntohs(pkt->length);
   if ((plen < sizeof(struct ospf_packet)) || ((plen % 4) != 0))
   {
     log(L_ERR "%s%I - invalid length (%u)", mesg, sk->faddr, plen);
@@ -348,89 +334,101 @@ ospf_rx_hook(sock *sk, int size)
     return 1;
   }
 
-  if (ps->version != OSPF_VERSION)
+  if (pkt->version != ospf_get_version(p))
   {
-    log(L_ERR "%s%I - version %u", mesg, sk->faddr, ps->version);
+    log(L_ERR "%s%I - version %u", mesg, sk->faddr, pkt->version);
     return 1;
   }
 
-#ifdef OSPFv2
-  if ((ps->autype != htons(OSPF_AUTH_CRYPT)) &&
-      (!ipsum_verify(ps, 16, (void *) ps + sizeof(struct ospf_packet),
-                    plen - sizeof(struct ospf_packet), NULL)))
+  if (ospf_is_v2(p) && (pkt->autype != OSPF_AUTH_CRYPT))
   {
-    log(L_ERR "%s%I - bad checksum", mesg, sk->faddr);
-    return 1;
-  }
-#endif
+    uint hlen = sizeof(struct ospf_packet) + sizeof(union ospf_auth);
+    uint blen = plen - hlen;
+    void *body = ((void *) pkt) + hlen;
 
+    if (! ipsum_verify(pkt, sizeof(struct ospf_packet), body, blen, NULL))
+    {
+      log(L_ERR "%s%I - bad checksum", mesg, sk->faddr);
+      return 1;
+    }
+  }
 
   /* Third, we resolve associated iface and handle vlinks. */
 
-  u32 areaid = ntohl(ps->areaid);
-  u32 rid = ntohl(ps->routerid);
+  u32 areaid = ntohl(pkt->areaid);
+  u32 rid = ntohl(pkt->routerid);
+  u8 instance_id = pkt->instance_id;
 
-  if ((areaid == ifa->oa->areaid)
-#ifdef OSPFv3
-      && (ps->instance_id == ifa->instance_id)
-#endif
-      )
+  if (areaid == ifa->oa->areaid)
   {
+    /* Matching area ID */
+
+    if (instance_id != ifa->instance_id)
+      return 1;
+
     /* It is real iface, source should be local (in OSPFv2) */
-#ifdef OSPFv2
-    if (!src_local)
+    if (ospf_is_v2(p) && !src_local)
+    {
+      log(L_ERR "%s%I - strange source address for %s", mesg, sk->faddr, ifa->ifname);
       return 1;
-#endif
-  }
-  else if (dst_mcast || (areaid != 0))
-  {
-    /* Obvious mismatch */
+    }
 
-#ifdef OSPFv2
-    /* We ignore mismatch in OSPFv3, because there might be
-       other instance with different instance ID */
-    log(L_ERR "%s%I - area does not match (%R vs %R)",
-       mesg, sk->faddr, areaid, ifa->oa->areaid);
-#endif
-    return 1;
+    goto found;
   }
-  else
+  else if ((areaid == 0) && !dst_mcast)
   {
-    /* Some vlink? */
-    struct ospf_iface *iff = NULL;
+    /* Backbone area ID and possible vlink packet */
+
+    if ((p->areano == 1) || !oa_is_ext(ifa->oa))
+      return 1;
 
-    WALK_LIST(iff, po->iface_list)
+    struct ospf_iface *iff = NULL;
+    WALK_LIST(iff, p->iface_list)
     {
       if ((iff->type == OSPF_IT_VLINK) && 
          (iff->voa == ifa->oa) &&
-#ifdef OSPFv3
-         (iff->instance_id == ps->instance_id) &&
-#endif
+         (iff->instance_id == instance_id) &&
          (iff->vid == rid))
-       {
-         /* Vlink should be UP */
-         if (iff->state != OSPF_IS_PTP)
-           return 1;
-         
-         ifa = iff;
-         goto found;
-       }
+      {
+       /* Vlink should be UP */
+       if (iff->state != OSPF_IS_PTP)
+         return 1;
+
+       ifa = iff;
+       goto found;
+      }
     }
 
-#ifdef OSPFv2
-    log(L_WARN "OSPF: Received packet for unknown vlink (ID %R, IP %I)", rid, sk->faddr);
-#endif
+    /*
+     * Cannot find matching vlink. It is either misconfigured vlink; NBMA or
+     * PtMP with misconfigured area ID, or packet for some other instance (that
+     * is possible even if instance_id == ifa->instance_id, because it may be
+     * also vlink packet in the other instance, which is different namespace).
+     */
+
+    return 1;
+  }
+  else
+  {
+    /* Non-matching area ID but cannot be vlink packet */
+
+    if (instance_id != ifa->instance_id)
+      return 1;
+
+    log(L_ERR "%s%I - area does not match (%R vs %R)",
+       mesg, sk->faddr, areaid, ifa->oa->areaid);
     return 1;
   }
 
+
  found:
   if (ifa->stub)           /* This shouldn't happen */
     return 1;
 
-  if (ipa_equal(sk->laddr, AllDRouters) && (ifa->sk_dr == 0))
+  if (ipa_equal(sk->laddr, ifa->des_routers) && (ifa->sk_dr == 0))
     return 1;
 
-  if (rid == po->router_id)
+  if (rid == p->router_id)
   {
     log(L_ERR "%s%I - received my own router ID!", mesg, sk->faddr);
     return 1;
@@ -442,62 +440,51 @@ ospf_rx_hook(sock *sk, int size)
     return 1;
   }
 
-#ifdef OSPFv2
   /* In OSPFv2, neighbors are identified by either IP or Router ID, base on network type */
+  uint t = ifa->type;
   struct ospf_neighbor *n;
-  if ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA) || (ifa->type == OSPF_IT_PTMP))
+  if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP)))
     n = find_neigh_by_ip(ifa, sk->faddr);
   else
     n = find_neigh(ifa, rid);
-#else
-  struct ospf_neighbor *n = find_neigh(ifa, rid);
-#endif
 
-  if(!n && (ps->type != HELLO_P))
+  if (!n && (pkt->type != HELLO_P))
   {
     log(L_WARN "OSPF: Received non-hello packet from unknown neighbor (src %I, iface %s)",
        sk->faddr, ifa->ifname);
     return 1;
   }
 
-  if (!ospf_pkt_checkauth(n, ifa, ps, size))
+  if (ospf_is_v2(p) && !ospf_pkt_checkauth(n, ifa, pkt, size))
   {
     log(L_ERR "%s%I - authentication failed", mesg, sk->faddr);
     return 1;
   }
 
-  /* Dump packet 
-     pu8=(u8 *)(sk->rbuf+5*4);
-     for(i=0;i<ntohs(ps->length);i+=4)
-     DBG("%s: received %u,%u,%u,%u\n",p->name, pu8[i+0], pu8[i+1], pu8[i+2],
-     pu8[i+3]);
-     DBG("%s: received size: %u\n",p->name,size);
-   */
-
-  switch (ps->type)
+  switch (pkt->type)
   {
   case HELLO_P:
-    DBG("%s: Hello received.\n", p->name);
-    ospf_hello_receive(ps, ifa, n, sk->faddr);
+    ospf_receive_hello(pkt, ifa, n, sk->faddr);
     break;
+
   case DBDES_P:
-    DBG("%s: Database description received.\n", p->name);
-    ospf_dbdes_receive(ps, ifa, n);
+    ospf_receive_dbdes(pkt, ifa, n);
     break;
+
   case LSREQ_P:
-    DBG("%s: Link state request received.\n", p->name);
-    ospf_lsreq_receive(ps, ifa, n);
+    ospf_receive_lsreq(pkt, ifa, n);
     break;
+
   case LSUPD_P:
-    DBG("%s: Link state update received.\n", p->name);
-    ospf_lsupd_receive(ps, ifa, n);
+    ospf_receive_lsupd(pkt, ifa, n);
     break;
+
   case LSACK_P:
-    DBG("%s: Link state ack received.\n", p->name);
-    ospf_lsack_receive(ps, ifa, n);
+    ospf_receive_lsack(pkt, ifa, n);
     break;
+
   default:
-    log(L_ERR "%s%I - wrong type %u", mesg, sk->faddr, ps->type);
+    log(L_ERR "%s%I - wrong type %u", mesg, sk->faddr, pkt->type);
     return 1;
   };
   return 1;
@@ -508,7 +495,7 @@ void
 ospf_tx_hook(sock * sk)
 {
   struct ospf_iface *ifa= (struct ospf_iface *) (sk->data);
-//  struct proto *p = (struct proto *) (ifa->oa->po);
+//  struct proto *p = (struct proto *) (ifa->oa->p);
   log(L_ERR "OSPF: TX hook called on %s", ifa->ifname);
 }
 */
@@ -517,16 +504,35 @@ void
 ospf_err_hook(sock * sk, int err)
 {
   struct ospf_iface *ifa= (struct ospf_iface *) (sk->data);
-  struct proto *p = &(ifa->oa->po->proto);
-  log(L_ERR "%s: Socket error on %s: %M", p->name, ifa->ifname, err);
+  struct ospf_proto *p = ifa->oa->po;
+  log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->ifname, err);
 }
 
 void
 ospf_verr_hook(sock *sk, int err)
 {
-  struct proto_ospf *po = (struct proto_ospf *) (sk->data);
-  struct proto *p =  &po->proto;
-  log(L_ERR "%s: Vlink socket error: %M", p->name, err);
+  struct ospf_proto *p = (struct ospf_proto *) (sk->data);
+  log(L_ERR "%s: Vlink socket error: %M", p->p.name, err);
+}
+
+void
+ospf_send_to(struct ospf_iface *ifa, ip_addr dst)
+{
+  sock *sk = ifa->sk;
+  struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf;
+  int plen = ntohs(pkt->length);
+
+  if (ospf_is_v2(ifa->oa->po))
+  {
+    if (ifa->autype == OSPF_AUTH_CRYPT)
+      plen += OSPF_AUTH_CRYPT_SIZE;
+
+    ospf_pkt_finalize(ifa, pkt);
+  }
+
+  int done = sk_send_to(sk, plen, dst, 0);
+  if (!done)
+    log(L_WARN "OSPF: TX queue full on %s", ifa->ifname);
 }
 
 void
@@ -542,28 +548,8 @@ ospf_send_to_agt(struct ospf_iface *ifa, u8 state)
 void
 ospf_send_to_bdr(struct ospf_iface *ifa)
 {
-  if (!ipa_equal(ifa->drip, IPA_NONE))
+  if (ipa_nonzero(ifa->drip))
     ospf_send_to(ifa, ifa->drip);
-  if (!ipa_equal(ifa->bdrip, IPA_NONE))
+  if (ipa_nonzero(ifa->bdrip))
     ospf_send_to(ifa, ifa->bdrip);
 }
-
-void
-ospf_send_to(struct ospf_iface *ifa, ip_addr dst)
-{
-  sock *sk = ifa->sk;
-  struct ospf_packet *pkt = (struct ospf_packet *) sk->tbuf;
-  int len = ntohs(pkt->length);
-
-#ifdef OSPFv2
-  if (ifa->autype == OSPF_AUTH_CRYPT)
-    len += OSPF_AUTH_CRYPT_SIZE;
-#endif
-
-  ospf_pkt_finalize(ifa, pkt);
-
-  int done = sk_send_to(sk, len, dst, 0);
-  if (!done)
-    log(L_WARN "OSPF: TX queue full on %s", ifa->ifname);
-}
-
diff --git a/proto/ospf/packet.h b/proto/ospf/packet.h
deleted file mode 100644 (file)
index 4ba1f08..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- *      BIRD -- OSPF
- *
- *      (c) 1999--2004 Ondrej Filip <feela@network.cz>
- *
- *      Can be freely distributed and used under the terms of the GNU GPL.
- *
- */
-
-#ifndef _BIRD_OSPF_PACKET_H_
-#define _BIRD_OSPF_PACKET_H_
-
-void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type);
-uint ospf_pkt_maxsize(struct ospf_iface *ifa);
-int ospf_rx_hook(sock * sk, int size);
-// void ospf_tx_hook(sock * sk);
-void ospf_err_hook(sock * sk, int err);
-void ospf_verr_hook(sock *sk, int err);
-void ospf_send_to_agt(struct ospf_iface *ifa, u8 state);
-void ospf_send_to_bdr(struct ospf_iface *ifa);
-void ospf_send_to(struct ospf_iface *ifa, ip_addr ip);
-
-static inline void ospf_send_to_all(struct ospf_iface *ifa) { ospf_send_to(ifa, ifa->all_routers); }
-
-static inline void * ospf_tx_buffer(struct ospf_iface *ifa) { return ifa->sk->tbuf; }
-
-
-#endif /* _BIRD_OSPF_PACKET_H_ */
index 2a879c051a688566aaae7c283935ac3eec6954b0..c4340ee565ffa67beb546e589a03528d8ce5b4d4 100644 (file)
@@ -1,9 +1,11 @@
 /*
- * BIRD -- OSPF
+ *     BIRD -- OSPF
  * 
- * (c) 2000--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2000--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  * 
- * Can be freely distributed and used under the terms of the GNU GPL.
+ *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
 #include "ospf.h"
 static void add_cand(list * l, struct top_hash_entry *en, 
                     struct top_hash_entry *par, u32 dist,
                     struct ospf_area *oa, int i);
-static void rt_sync(struct proto_ospf *po);
-
-/* In ospf_area->rtr we store paths to routers, but we use RID (and not IP address)
-   as index, so we need to encapsulate RID to IP address */
-#ifdef OSPFv2
-#define ipa_from_rid(x) _MI(x)
-#else /* OSPFv3 */
-#define ipa_from_rid(x) _MI(0,0,0,x)
-#endif
+static void rt_sync(struct ospf_proto *p);
 
 
 static inline void reset_ri(ort *ort)
@@ -33,7 +27,7 @@ ospf_rt_initort(struct fib_node *fn)
   ort *ri = (ort *) fn;
   reset_ri(ri);
   ri->old_rta = NULL;
-  ri->fn.x0 = ri->fn.x1 = 0;
+  ri->fn.flags = 0;
 }
 
 static inline int
@@ -49,9 +43,9 @@ unresolved_vlink(ort *ort)
 }
 
 static inline struct mpnh *
-new_nexthop(struct proto_ospf *po, ip_addr gw, struct iface *iface, unsigned char weight)
+new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, unsigned char weight)
 {
-  struct mpnh *nh = lp_alloc(po->nhpool, sizeof(struct mpnh));
+  struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh));
   nh->gw = gw;
   nh->iface = iface;
   nh->next = NULL;
@@ -60,9 +54,9 @@ new_nexthop(struct proto_ospf *po, ip_addr gw, struct iface *iface, unsigned cha
 }
 
 static inline struct mpnh *
-copy_nexthop(struct proto_ospf *po, const struct mpnh *src)
+copy_nexthop(struct ospf_proto *p, const struct mpnh *src)
 {
-  struct mpnh *nh = lp_alloc(po->nhpool, sizeof(struct mpnh));
+  struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh));
   nh->gw = src->gw;
   nh->iface = src->iface;
   nh->next = NULL;
@@ -95,11 +89,11 @@ cmp_nhs(struct mpnh *s1, struct mpnh *s2)
 }
 
 static struct mpnh *
-merge_nexthops(struct proto_ospf *po, struct mpnh *s1, struct mpnh *s2, int r1, int r2)
+merge_nexthops(struct ospf_proto *p, struct mpnh *s1, struct mpnh *s2, int r1, int r2)
 {
   struct mpnh *root = NULL;
   struct mpnh **n = &root;
-  int count = po->ecmp;
+  int count = p->ecmp;
 
   /*
    * r1, r2 signalize whether we can reuse nexthops from s1, s2.
@@ -118,17 +112,17 @@ merge_nexthops(struct proto_ospf *po, struct mpnh *s1, struct mpnh *s2, int r1,
     int cmp = cmp_nhs(s1, s2);
     if (cmp < 0)
     {
-      *n = r1 ? s1 : copy_nexthop(po, s1);
+      *n = r1 ? s1 : copy_nexthop(p, s1);
       s1 = s1->next;
     }
     else if (cmp > 0)
     {
-      *n = r2 ? s2 : copy_nexthop(po, s2);
+      *n = r2 ? s2 : copy_nexthop(p, s2);
       s2 = s2->next;
     }
     else
     {
-      *n = r1 ? s1 : (r2 ? s2 : copy_nexthop(po, s1));
+      *n = r1 ? s1 : (r2 ? s2 : copy_nexthop(p, s1));
       s1 = s1->next;
       s2 = s2->next;
     }
@@ -152,7 +146,7 @@ has_device_nexthops(const struct mpnh *n)
 
 /* Replace device nexthops with nexthops to gw */
 static struct mpnh *
-fix_device_nexthops(struct proto_ospf *po, const struct mpnh *n, ip_addr gw)
+fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
 {
   struct mpnh *root1 = NULL;
   struct mpnh *root2 = NULL;
@@ -165,7 +159,7 @@ fix_device_nexthops(struct proto_ospf *po, const struct mpnh *n, ip_addr gw)
 
   for (; n; n = n->next)
   {
-    struct mpnh *nn = new_nexthop(po, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight);
+    struct mpnh *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight);
 
     if (ipa_zero(n->gw))
     {
@@ -179,7 +173,7 @@ fix_device_nexthops(struct proto_ospf *po, const struct mpnh *n, ip_addr gw)
     }
   }
 
-  return merge_nexthops(po, root1, root2, 1, 1);
+  return merge_nexthops(p, root1, root2, 1, 1);
 }
 
 
@@ -235,7 +229,7 @@ orta_prefer_lsa(const orta *new, const orta *old)
  * the old orta.
  */
 static int
-orta_compare(const struct proto_ospf *po, const orta *new, const orta *old)
+orta_compare(const struct ospf_proto *p, const orta *new, const orta *old)
 {
   int r;
 
@@ -270,7 +264,7 @@ orta_compare(const struct proto_ospf *po, const orta *new, const orta *old)
     return 1;
 
 
-  if (po->ecmp)
+  if (p->ecmp)
     return 0;
 
   /* Prefer routes with higher Router ID, just to be more deterministic */
@@ -286,14 +280,14 @@ orta_compare(const struct proto_ospf *po, const orta *new, const orta *old)
  * than 0 if the new ASBR is less or more preferred than the old ASBR.
  */
 static int
-orta_compare_asbr(const struct proto_ospf *po, const orta *new, const orta *old)
+orta_compare_asbr(const struct ospf_proto *p, const orta *new, const orta *old)
 {
   int r;
 
   if (old->type == RTS_DUMMY)
     return 1;
 
-  if (!po->rfc1583)
+  if (!p->rfc1583)
   {
     r = epath_preferred(new) - epath_preferred(old);
     if (r) return r;
@@ -316,7 +310,7 @@ orta_compare_asbr(const struct proto_ospf *po, const orta *new, const orta *old)
  * than 0 if the new orta is less, equal or more preferred than the old orta.
  */
 static int
-orta_compare_ext(const struct proto_ospf *po, const orta *new, const orta *old)
+orta_compare_ext(const struct ospf_proto *p, const orta *new, const orta *old)
 {
   int r;
 
@@ -335,7 +329,7 @@ orta_compare_ext(const struct proto_ospf *po, const orta *new, const orta *old)
   }
 
   /* 16.4 (6c) - if not RFC1583, prefer routes with preferred ASBR/next_hop */
-  if (!po->rfc1583)
+  if (!p->rfc1583)
   {
     r = orta_pref(new) - orta_pref(old);
     if (r) return r;
@@ -346,7 +340,7 @@ orta_compare_ext(const struct proto_ospf *po, const orta *new, const orta *old)
   if (r) return r;
 
 
-  if (po->ecmp && po->merge_external)
+  if (p->ecmp && p->merge_external)
     return 0;
 
   /*
@@ -369,13 +363,13 @@ ort_replace(ort *o, const orta *new)
 }
 
 static void
-ort_merge(struct proto_ospf *po, ort *o, const orta *new)
+ort_merge(struct ospf_proto *p, ort *o, const orta *new)
 {
   orta *old = &o->n;
 
   if (old->nhs != new->nhs)
   {
-    old->nhs = merge_nexthops(po, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse);
+    old->nhs = merge_nexthops(p, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse);
     old->nhs_reuse = 1;
   }
 
@@ -384,13 +378,13 @@ ort_merge(struct proto_ospf *po, ort *o, const orta *new)
 }
 
 static void
-ort_merge_ext(struct proto_ospf *po, ort *o, const orta *new)
+ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new)
 {
   orta *old = &o->n;
 
   if (old->nhs != new->nhs)
   {
-    old->nhs = merge_nexthops(po, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse);
+    old->nhs = merge_nexthops(p, old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse);
     old->nhs_reuse = 1;
   }
 
@@ -415,15 +409,15 @@ ort_merge_ext(struct proto_ospf *po, ort *o, const orta *new)
 
 
 static inline void
-ri_install_net(struct proto_ospf *po, ip_addr prefix, int pxlen, const orta *new)
+ri_install_net(struct ospf_proto *p, ip_addr prefix, int pxlen, const orta *new)
 {
-  ort *old = (ort *) fib_get(&po->rtf, &prefix, pxlen);
-  int cmp = orta_compare(po, new, &old->n);
+  ort *old = (ort *) fib_get(&p->rtf, &prefix, pxlen);
+  int cmp = orta_compare(p, new, &old->n);
 
   if (cmp > 0)
     ort_replace(old, new);
   else if (cmp == 0)
-    ort_merge(po, old, new);
+    ort_merge(p, old, new);
 }
 
 static inline void
@@ -440,51 +434,55 @@ ri_install_rt(struct ospf_area *oa, u32 rid, const orta *new)
 }
 
 static inline void
-ri_install_asbr(struct proto_ospf *po, ip_addr *addr, const orta *new)
+ri_install_asbr(struct ospf_proto *p, ip_addr *addr, const orta *new)
 {
-  ort *old = (ort *) fib_get(&po->backbone->rtr, addr, MAX_PREFIX_LENGTH);
-  if (orta_compare_asbr(po, new, &old->n) > 0)
+  ort *old = (ort *) fib_get(&p->backbone->rtr, addr, MAX_PREFIX_LENGTH);
+  if (orta_compare_asbr(p, new, &old->n) > 0)
     ort_replace(old, new);
 }
 
 static inline void
-ri_install_ext(struct proto_ospf *po, ip_addr prefix, int pxlen, const orta *new)
+ri_install_ext(struct ospf_proto *p, ip_addr prefix, int pxlen, const orta *new)
 {
-  ort *old = (ort *) fib_get(&po->rtf, &prefix, pxlen);
-  int cmp = orta_compare_ext(po, new, &old->n);
+  ort *old = (ort *) fib_get(&p->rtf, &prefix, pxlen);
+  int cmp = orta_compare_ext(p, new, &old->n);
 
   if (cmp > 0)
     ort_replace(old, new);
   else if (cmp == 0)
-    ort_merge_ext(po, old, new);
+    ort_merge_ext(p, old, new);
 }
 
 static inline struct ospf_iface *
 rt_pos_to_ifa(struct ospf_area *oa, int pos)
 {
   struct ospf_iface *ifa;
+
   WALK_LIST(ifa, oa->po->iface_list)
     if (ifa->oa == oa && pos >= ifa->rt_pos_beg && pos < ifa->rt_pos_end)
       return ifa;
+
   return NULL;
 }
 
-#ifdef OSPFv3
 static inline struct ospf_iface *
 px_pos_to_ifa(struct ospf_area *oa, int pos)
 {
   struct ospf_iface *ifa;
+
   WALK_LIST(ifa, oa->po->iface_list)
     if (ifa->oa == oa && pos >= ifa->px_pos_beg && pos < ifa->px_pos_end)
       return ifa;
+
   return NULL;
 }
-#endif
 
 
 static void
 add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_hash_entry *en, int pos)
 {
+  struct ospf_proto *p = oa->po;
+
   orta nf = {
     .type = RTS_OSPF,
     .options = 0,
@@ -499,7 +497,7 @@ add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_
   if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH)
   {
     log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)",
-       oa->po->proto.name, en->lsa.type, en->lsa.id, en->lsa.rt);
+       p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt);
     return;
   }
 
@@ -514,24 +512,117 @@ add_network(struct ospf_area *oa, ip_addr px, int pxlen, int metric, struct top_
      */
 
     struct ospf_iface *ifa;
-#ifdef OSPFv2
-    ifa = rt_pos_to_ifa(oa, pos);
-#else /* OSPFv3 */
-    ifa = px_pos_to_ifa(oa, pos);
-#endif
+    ifa = ospf_is_v2(p) ? rt_pos_to_ifa(oa, pos) : px_pos_to_ifa(oa, pos);
+    nf.nhs = ifa ? new_nexthop(p, IPA_NONE, ifa->iface, ifa->ecmp_weight) : NULL;
+  }
+
+  ri_install_net(p, px, pxlen, &nf);
+}
+
 
-    nf.nhs = ifa ? new_nexthop(oa->po, IPA_NONE, ifa->iface, ifa->ecmp_weight) : NULL;
+
+static inline void
+spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entry *act)
+{
+  struct ospf_lsa_rt *rt = act->lsa_body;
+  struct ospf_lsa_rt_walk rtl;
+  struct top_hash_entry *tmp;
+  ip_addr prefix;
+  int pxlen, i;
+
+  if (rt->options & OPT_RT_V)
+    oa->trcap = 1;
+
+  /*
+   * In OSPFv3, all routers are added to per-area routing
+   * tables. But we use it just for ASBRs and ABRs. For the
+   * purpose of the last step in SPF - prefix-LSA processing in
+   * spfa_process_prefixes(), we use information stored in LSA db.
+   */
+  if (((rt->options & OPT_RT_E) || (rt->options & OPT_RT_B))
+      && (act->lsa.rt != p->router_id))
+  {
+    orta nf = {
+      .type = RTS_OSPF,
+      .options = rt->options,
+      .metric1 = act->dist,
+      .metric2 = LSINFINITY,
+      .tag = 0,
+      .rid = act->lsa.rt,
+      .oa = oa,
+      .nhs = act->nhs
+    };
+    ri_install_rt(oa, act->lsa.rt, &nf);
   }
 
-  ri_install_net(oa->po, px, pxlen, &nf);
+  /* Errata 2078 to RFC 5340 4.8.1 - skip links from non-routing nodes */
+  if (ospf_is_v3(p) && (act != oa->rt) && !(rt->options & OPT_R))
+    break;
+
+  /* Now process Rt links */
+  for (lsa_walk_rt_init(p, act, &rtl), i = 0; lsa_walk_rt(&rtl); i++)
+  {
+    tmp = NULL;
+
+    switch (rtl.type)
+    {
+    case LSART_STUB:
+
+      /* Should not happen, LSART_STUB is not defined in OSPFv3 */
+      if (ospf_is_v3(p))
+       break;
+
+      /*
+       * RFC 2328 in 16.1. (2a) says to handle stub networks in an
+       * second phase after the SPF for an area is calculated. We get
+       * the same result by handing them here because add_network()
+       * will keep the best (not the first) found route.
+       */
+      prefix = ipa_from_u32(rtl.id & rtl.data);
+      pxlen = u32_masklen(rtl.data);
+      add_network(oa, prefix, pxlen, act->dist + rtl.metric, act, i);
+      break;
+
+    case LSART_NET:
+      tmp = ospf_hash_find_net(p->gr, oa->areaid, rtl.id, rtl.nif);
+      break;
+
+    case LSART_VLNK:
+    case LSART_PTP:
+      tmp = ospf_hash_find_rt(p->gr, oa->areaid, rtl.id);
+      break;
+    }
+
+    add_cand(&oa->cand, tmp, act, act->dist + rtl.metric, oa, i);
+  }
 }
 
-#ifdef OSPFv3
-static void
-process_prefixes(struct ospf_area *oa)
+static inline void
+spfa_process_net(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entry *act)
+{
+  struct ospf_lsa_net *ln = act->lsa_body;
+  struct top_hash_entry *tmp;
+  ip_addr prefix;
+  int pxlen, i, cnt;
+
+  if (ospf_is_v2(p))
+  {
+    prefix = ipa_from_u32(act->lsa.id & ln->optx);
+    pxlen = u32_masklen(ln->optx);
+    add_network(oa, prefix, pxlen, act->dist, act, -1);
+  }
+
+  cnt = lsa_net_count(&act->lsa);
+  for (i = 0; i < cnt; i++)
+  {
+    tmp = ospf_hash_find_rt(p->gr, oa->areaid, ln->routers[i]);
+    add_cand(&oa->cand, tmp, act, act->dist, oa, -1);
+  }
+}
+
+static inline void
+spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa)
 {
-  struct proto_ospf *po = oa->po;
-  // struct proto *p = &po->proto;
   struct top_hash_entry *en, *src;
   struct ospf_lsa_prefix *px;
   ip_addr pxa;
@@ -541,9 +632,9 @@ process_prefixes(struct ospf_area *oa)
   u32 *buf;
   int i;
 
-  WALK_SLIST(en, po->lsal)
+  WALK_SLIST(en, p->lsal)
   {
-    if (en->lsa.type != LSA_T_PREFIX)
+    if (en->lsa_type != LSA_T_PREFIX)
       continue;
 
     if (en->domain != oa->areaid)
@@ -556,9 +647,9 @@ process_prefixes(struct ospf_area *oa)
 
     /* For router prefix-LSA, we would like to find the first router-LSA */
     if (px->ref_type == LSA_T_RT)
-      src = ospf_hash_find_rt(po->gr, oa->areaid, px->ref_rt);
+      src = ospf_hash_find_rt(p->gr, oa->areaid, px->ref_rt);
     else
-      src = ospf_hash_find(po->gr, oa->areaid, px->ref_id, px->ref_rt, px->ref_type);
+      src = ospf_hash_find(p->gr, oa->areaid, px->ref_id, px->ref_rt, px->ref_type);
 
     if (!src)
       continue;
@@ -567,7 +658,7 @@ process_prefixes(struct ospf_area *oa)
     if (src->color != INSPF)
       continue;
 
-    if ((src->lsa.type != LSA_T_RT) && (src->lsa.type != LSA_T_NET))
+    if ((src->lsa_type != LSA_T_RT) && (src->lsa_type != LSA_T_NET))
       continue;
 
     buf = px->rest;
@@ -586,80 +677,13 @@ process_prefixes(struct ospf_area *oa)
       }
   }
 }
-#endif
-
-
-static void
-ospf_rt_spfa_rtlinks(struct ospf_area *oa, struct top_hash_entry *act, struct top_hash_entry *en)
-{
-  // struct proto *p = &oa->po->proto;
-  struct proto_ospf *po = oa->po;
-  ip_addr prefix UNUSED;
-  int pxlen UNUSED, i;
-
-  struct ospf_lsa_rt *rt = en->lsa_body;
-  struct ospf_lsa_rt_link *rr = (struct ospf_lsa_rt_link *) (rt + 1);
-
-  for (i = 0; i < lsa_rt_count(&en->lsa); i++)
-    {
-      struct ospf_lsa_rt_link *rtl = rr + i;
-      struct top_hash_entry *tmp = NULL;
-
-      DBG("     Working on link: %R (type: %u)  ", rtl->id, rtl->type);
-      switch (rtl->type)
-       {
-#ifdef OSPFv2
-       case LSART_STUB:
-         /*
-          * RFC 2328 in 16.1. (2a) says to handle stub networks in an
-          * second phase after the SPF for an area is calculated. We get
-          * the same result by handing them here because add_network()
-          * will keep the best (not the first) found route.
-          */
-         prefix = ipa_from_u32(rtl->id & rtl->data);
-         pxlen = ipa_mklen(ipa_from_u32(rtl->data));
-         add_network(oa, prefix, pxlen, act->dist + rtl->metric, act, i);
-         break;
-#endif
-
-       case LSART_NET:
-#ifdef OSPFv2
-         /* In OSPFv2, rtl->id is IP addres of DR, Router ID is not known */
-         tmp = ospf_hash_find_net(po->gr, oa->areaid, rtl->id);
-#else /* OSPFv3 */
-         tmp = ospf_hash_find(po->gr, oa->areaid, rtl->nif, rtl->id, LSA_T_NET);
-#endif
-         break;
-
-       case LSART_VLNK:
-       case LSART_PTP:
-         tmp = ospf_hash_find_rt(po->gr, oa->areaid, rtl->id);
-         break;
-
-       default:
-         log("Unknown link type in router lsa. (rid = %R)", act->lsa.id);
-         break;
-       }
-
-      if (tmp)
-       DBG("Going to add cand, Mydist: %u, Req: %u\n",
-           tmp->dist, act->dist + rtl->metric);
-      add_cand(&oa->cand, tmp, act, act->dist + rtl->metric, oa, i);
-    }
-}
 
 /* RFC 2328 16.1. calculating shortest paths for an area */
 static void
 ospf_rt_spfa(struct ospf_area *oa)
 {
-  struct proto *p = &oa->po->proto;
-  struct proto_ospf *po = oa->po;
-  struct ospf_lsa_rt *rt;
-  struct ospf_lsa_net *ln;
-  struct top_hash_entry *act, *tmp;
-  ip_addr prefix UNUSED;
-  int pxlen UNUSED;
-  u32 i, *rts;
+  struct ospf_proto *p = oa->po;
+  struct top_hash_entry *act;
   node *n;
 
   if (oa->rt == NULL)
@@ -677,7 +701,7 @@ ospf_rt_spfa(struct ospf_area *oa)
   oa->rt->color = CANDIDATE;
   add_head(&oa->cand, &oa->rt->cn);
   DBG("RT LSA: rt: %R, id: %R, type: %u\n",
-      oa->rt->lsa.rt, oa->rt->lsa.id, oa->rt->lsa.type);
+      oa->rt->lsa.rt, oa->rt->lsa.id, oa->rt->lsa_type);
 
   while (!EMPTY_LIST(oa->cand))
   {
@@ -686,89 +710,36 @@ ospf_rt_spfa(struct ospf_area *oa)
     rem_node(n);
 
     DBG("Working on LSA: rt: %R, id: %R, type: %u\n",
-       act->lsa.rt, act->lsa.id, act->lsa.type);
+       act->lsa.rt, act->lsa.id, act->lsa_type);
 
     act->color = INSPF;
-    switch (act->lsa.type)
+    switch (act->lsa_type)
     {
     case LSA_T_RT:
-      rt = (struct ospf_lsa_rt *) act->lsa_body;
-      if (rt->options & OPT_RT_V)
-       oa->trcap = 1;
-
-      /*
-       * In OSPFv3, all routers are added to per-area routing
-       * tables. But we use it just for ASBRs and ABRs. For the
-       * purpose of the last step in SPF - prefix-LSA processing in
-       * process_prefixes(), we use information stored in LSA db.
-       */
-      if (((rt->options & OPT_RT_E) || (rt->options & OPT_RT_B))
-         && (act->lsa.rt != po->router_id))
-      {
-       orta nf = {
-         .type = RTS_OSPF,
-         .options = rt->options,
-         .metric1 = act->dist,
-         .metric2 = LSINFINITY,
-         .tag = 0,
-         .rid = act->lsa.rt,
-         .oa = oa,
-         .nhs = act->nhs
-       };
-       ri_install_rt(oa, act->lsa.rt, &nf);
-      }
-
-#ifdef OSPFv2
-      ospf_rt_spfa_rtlinks(oa, act, act);
-#else /* OSPFv3 */
-      /* Errata 2078 to RFC 5340 4.8.1 - skip links from non-routing nodes */
-      if ((act != oa->rt) && !(rt->options & OPT_R))
-       break;
-
-      for (tmp = ospf_hash_find_rt_first(po->gr, act->domain, act->lsa.rt);
-          tmp; tmp = ospf_hash_find_rt_next(tmp))
-       ospf_rt_spfa_rtlinks(oa, act, tmp);
-#endif
-
+      spfa_process_rt(p, oa, act);
       break;
-    case LSA_T_NET:
-      ln = act->lsa_body;
 
-#ifdef OSPFv2
-      prefix = ipa_and(ipa_from_u32(act->lsa.id), ln->netmask);
-      pxlen = ipa_mklen(ln->netmask);
-      add_network(oa, prefix, pxlen, act->dist, act, -1);
-#endif
-
-      rts = (u32 *) (ln + 1);
-      for (i = 0; i < lsa_net_count(&act->lsa); i++)
-      {
-       DBG("     Working on router %R ", rts[i]);
-       tmp = ospf_hash_find_rt(po->gr, oa->areaid, rts[i]);
-       if (tmp != NULL)
-         DBG("Found :-)\n");
-       else
-         DBG("Not found!\n");
-       add_cand(&oa->cand, tmp, act, act->dist, oa, -1);
-      }
+    case LSA_T_NET:
+      spfa_process_net(p, oa, act);
       break;
+
+    default:
+      log(L_WARN "%s: Unknown LSA type in SPF: %d", p->p.name, act->lsa_type);
     }
   }
 
-#ifdef OSPFv3
-  process_prefixes(oa);
-#endif
+  if (ospf_is_v3(p))
+    spfa_process_prefixes(p, oa);
 }
 
 static int
 link_back(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par)
 {
-  u32 i, *rts;
-  struct ospf_lsa_net *ln;
-  struct ospf_lsa_rt *rt;
-  struct ospf_lsa_rt_link *rtl, *rr;
+  struct ospf_proto *p = oa->po;
+  struct ospf_lsa_rt_walk rtl;
   struct top_hash_entry *tmp;
-  struct proto_ospf *po = oa->po;
+  struct ospf_lsa_net *ln;
+  u32 i, cnt;
 
   if (!en || !par) return 0;
 
@@ -783,65 +754,56 @@ link_back(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry
      it is set in process_prefixes() to any global addres in the area */
 
   en->lb = IPA_NONE;
-#ifdef OSPFv3
   en->lb_id = 0;
-#endif
-  switch (en->lsa.type)
+
+  switch (en->lsa_type)
   {
-    case LSA_T_RT:
-      rt = (struct ospf_lsa_rt *) en->lsa_body;
-      rr = (struct ospf_lsa_rt_link *) (rt + 1);
-      for (i = 0; i < lsa_rt_count(&en->lsa); i++)
+  case LSA_T_RT:
+    lsa_walk_rt_init(p, en, &rtl);
+    while (lsa_walk_rt(&rtl))
+    {
+      switch (rtl.type)
       {
-       rtl = (rr + i);
-       switch (rtl->type)
+      case LSART_STUB:
+       break;
+
+      case LSART_NET:
+       tmp = ospf_hash_find_net(p->gr, oa->areaid, rtl.id, rtl.nif);
+       if (tmp == par)
        {
-       case LSART_STUB:
-         break;
-       case LSART_NET:
-#ifdef OSPFv2
-         /* In OSPFv2, rtl->id is IP addres of DR, Router ID is not known */
-         tmp = ospf_hash_find_net(po->gr, oa->areaid, rtl->id);
-#else /* OSPFv3 */
-         tmp = ospf_hash_find(po->gr, oa->areaid, rtl->nif, rtl->id, LSA_T_NET);
-#endif
-         if (tmp == par)
-         {
-#ifdef OSPFv2
-           en->lb = ipa_from_u32(rtl->data);
-#else /* OSPFv3 */
-           en->lb_id = rtl->lif;
-#endif
-           return 1;
-         }
-
-         break;
-       case LSART_VLNK:
-       case LSART_PTP:
-         /* Not necessary the same link, see RFC 2328 [23] */
-         tmp = ospf_hash_find_rt(po->gr, oa->areaid, rtl->id);
-         if (tmp == par)
-            return 1;
-
-         break;
-       default:
-         log(L_WARN "Unknown link type in router lsa. (rid = %R)", en->lsa.rt);
-         break;
+         if (ospf_is_v2(p))
+           en->lb = ipa_from_u32(rtl.data);
+         else
+           en->lb_id = rtl.lif;
+
+         return 1;
        }
-      }
-      break;
-    case LSA_T_NET:
-      ln = en->lsa_body;
-      rts = (u32 *) (ln + 1);
-      for (i = 0; i < lsa_net_count(&en->lsa); i++)
-      {
-       tmp = ospf_hash_find_rt(po->gr, oa->areaid, rts[i]);
+       break;
+
+      case LSART_VLNK:
+      case LSART_PTP:
+       /* Not necessary the same link, see RFC 2328 [23] */
+       tmp = ospf_hash_find_rt(p->gr, oa->areaid, rtl.id);
        if (tmp == par)
-          return 1;
+         return 1;
+       break;
       }
-      break;
-    default:
-      bug("Unknown lsa type %x.", en->lsa.type);
+    }
+    break;
+
+  case LSA_T_NET:
+    ln = en->lsa_body;
+    cnt = lsa_net_count(&en->lsa);
+    for (i = 0; i < cnt; i++)
+    {
+      tmp = ospf_hash_find_rt(p->gr, oa->areaid, ln->routers[i]);
+      if (tmp == par)
+       return 1;
+    }
+    break;
+
+  default:
+    log(L_WARN "%s: Unknown LSA type in SPF: %d", p->p.name, en->lsa_type);
   }
   return 0;
 }
@@ -851,20 +813,20 @@ link_back(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry
 static void
 ospf_rt_sum(struct ospf_area *oa)
 {
-  struct proto_ospf *po = oa->po;
-  struct proto *p = &po->proto;
+  struct ospf_proto *p = oa->po;
   struct top_hash_entry *en;
-  ip_addr ip = IPA_NONE;
-  u32 dst_rid = 0;
-  u32 metric, options;
+  ip_addr ip, abrip;
+  u32 dst_rid, metric, options;
   ort *abr;
   int pxlen = -1, type = -1;
+  u8 pxopts;
+  
 
   OSPF_TRACE(D_EVENTS, "Starting routing table calculation for inter-area (area %R)", oa->areaid);
 
-  WALK_SLIST(en, po->lsal)
+  WALK_SLIST(en, p->lsal)
   {
-    if ((en->lsa.type != LSA_T_SUM_RT) && (en->lsa.type != LSA_T_SUM_NET))
+    if ((en->lsa_type != LSA_T_SUM_RT) && (en->lsa_type != LSA_T_SUM_NET))
       continue;
 
     if (en->domain != oa->areaid)
@@ -875,55 +837,36 @@ ospf_rt_sum(struct ospf_area *oa)
       continue;
 
     /* 16.2. (2) */
-    if (en->lsa.rt == po->router_id)
+    if (en->lsa.rt == p->router_id)
       continue;
 
     /* 16.2. (3) is handled later in ospf_rt_abr() by resetting such rt entry */
 
-    if (en->lsa.type == LSA_T_SUM_NET)
+    if (en->lsa_type == LSA_T_SUM_NET)
     {
-#ifdef OSPFv2
-      struct ospf_lsa_sum *ls = en->lsa_body;
-      ip = ipa_and(ipa_from_u32(en->lsa.id), ls->netmask);
-      pxlen = ipa_mklen(ls->netmask);
-#else /* OSPFv3 */
-      u8 pxopts;
-      u16 rest;
-      struct ospf_lsa_sum_net *ls = en->lsa_body;
-      lsa_get_ipv6_prefix(ls->prefix, &ip, &pxlen, &pxopts, &rest);
+      lsa_parse_sum_net(en, ospf_is_v2(p), &ip, &pxlen, &pxopts, &metric);
 
       if (pxopts & OPT_PX_NU)
        continue;
-#endif
 
       if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH)
       {
        log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)",
-           p->name, en->lsa.type, en->lsa.id, en->lsa.rt);
+           p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt);
        continue;
       }
 
-      metric = ls->metric & METRIC_MASK;
       options = 0;
       type = ORT_NET;
     }
     else /* LSA_T_SUM_RT */
     {
-#ifdef OSPFv2
-      struct ospf_lsa_sum *ls = en->lsa_body;
-      dst_rid = en->lsa.id;
-      options = 0;
-#else /* OSPFv3 */
-      struct ospf_lsa_sum_rt *ls = en->lsa_body;
-      dst_rid = ls->drid; 
-      options = ls->options & OPTIONS_MASK;
-#endif
+      lsa_parse_sum_rt(en, ospf_is_v2(p), &dst_rid, &metric, &options);
       
       /* We don't want local router in ASBR routing table */
-      if (dst_rid == po->router_id)
+      if (dst_rid == p->router_id)
        continue;
 
-      metric = ls->metric & METRIC_MASK;
       options |= ORTA_ASBR;
       type = ORT_ROUTER;
     }
@@ -933,7 +876,7 @@ ospf_rt_sum(struct ospf_area *oa)
       continue;
 
     /* 16.2. (4) */
-    ip_addr abrip = ipa_from_rid(en->lsa.rt);
+    abrip = ipa_from_rid(en->lsa.rt);
     abr = (ort *) fib_find(&oa->rtr, &abrip, MAX_PREFIX_LENGTH);
     if (!abr || !abr->n.type)
       continue;
@@ -958,7 +901,7 @@ ospf_rt_sum(struct ospf_area *oa)
     };
 
     if (type == ORT_NET)
-      ri_install_net(po, ip, pxlen, &nf);
+      ri_install_net(p, ip, pxlen, &nf);
     else
       ri_install_rt(oa, dst_rid, &nf);
   }
@@ -968,20 +911,22 @@ ospf_rt_sum(struct ospf_area *oa)
 static void
 ospf_rt_sum_tr(struct ospf_area *oa)
 {
-  struct proto *p = &oa->po->proto;
-  struct proto_ospf *po = oa->po;
-  struct ospf_area *bb = po->backbone;
-  ip_addr abrip;
+  struct ospf_proto *p = oa->po;
+  struct ospf_area *bb = p->backbone;
   struct top_hash_entry *en;
-  u32 dst_rid, metric;
-  ort *re = NULL, *abr;
+  ort *re, *abr;
+  ip_addr ip, abrip;
+  u32 dst_rid, metric, options;
+  int pxlen;
+  u8 pxopts;
 
 
-  if (!bb) return;
+  if (!bb)
+    return;
 
-  WALK_SLIST(en, po->lsal)
+  WALK_SLIST(en, p->lsal)
   {
-    if ((en->lsa.type != LSA_T_SUM_RT) && (en->lsa.type != LSA_T_SUM_NET))
+    if ((en->lsa_type != LSA_T_SUM_RT) && (en->lsa_type != LSA_T_SUM_NET))
       continue;
 
     if (en->domain != oa->areaid)
@@ -992,49 +937,30 @@ ospf_rt_sum_tr(struct ospf_area *oa)
       continue;
 
     /* 16.3 (2) */
-    if (en->lsa.rt == po->router_id)
+    if (en->lsa.rt == p->router_id)
       continue;
 
-    if (en->lsa.type == LSA_T_SUM_NET)
+    if (en->lsa_type == LSA_T_SUM_NET)
     {
-      ip_addr ip;
-      int pxlen;
-#ifdef OSPFv2
-      struct ospf_lsa_sum *ls = en->lsa_body;
-      ip = ipa_and(ipa_from_u32(en->lsa.id), ls->netmask);
-      pxlen = ipa_mklen(ls->netmask);
-#else /* OSPFv3 */
-      u8 pxopts;
-      u16 rest;
-      struct ospf_lsa_sum_net *ls = en->lsa_body;
-      lsa_get_ipv6_prefix(ls->prefix, &ip, &pxlen, &pxopts, &rest);
+      lsa_parse_sum_net(en, ospf_is_v2(p), &ip, &pxlen, &pxopts, &metric);
 
       if (pxopts & OPT_PX_NU)
        continue;
-#endif
 
       if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH)
       {
        log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)",
-           p->name, en->lsa.type, en->lsa.id, en->lsa.rt);
+           p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt);
        continue;
       }
 
-      metric = ls->metric & METRIC_MASK;
-      re = fib_find(&po->rtf, &ip, pxlen);
+      re = fib_find(&p->rtf, &ip, pxlen);
     }
-    else // en->lsa.type == LSA_T_SUM_RT
+    else // en->lsa_type == LSA_T_SUM_RT
     {
-#ifdef OSPFv2
-      struct ospf_lsa_sum *ls = en->lsa_body;
-      dst_rid = en->lsa.id;
-#else /* OSPFv3 */
-      struct ospf_lsa_sum_rt *ls = en->lsa_body;
-      dst_rid = ls->drid; 
-#endif
-
-      metric = ls->metric & METRIC_MASK;
-      ip_addr ip = ipa_from_rid(dst_rid);
+      lsa_parse_sum_rt(en, ospf_is_v2(p), &dst_rid, &metric, &options);
+
+      ip = ipa_from_rid(dst_rid);
       re = fib_find(&bb->rtr, &ip, MAX_PREFIX_LENGTH);
     }
 
@@ -1156,20 +1082,20 @@ decide_sum_lsa(struct ospf_area *oa, ort *nf, int dest)
 
 /* RFC 2328 16.7. p1 - originate or flush summary LSAs */
 static inline void
-check_sum_net_lsa(struct proto_ospf *po, ort *nf)
+check_sum_net_lsa(struct ospf_proto *p, ort *nf)
 {
   struct area_net *anet = NULL;
   struct ospf_area *anet_oa = NULL;
 
   /* RT entry marked as area network */
-  if (nf->fn.x0)
+  if (nf->fn.flags & OSPF_RT_PERSISTENT)
   {
     /* It is a default route for stub areas, handled entirely in ospf_rt_abr() */
     if (nf->fn.pxlen == 0)
       return;
 
     /* Find that area network */
-    WALK_LIST(anet_oa, po->area_list)
+    WALK_LIST(anet_oa, p->area_list)
     {
       anet = (struct area_net *) fib_find(&anet_oa->net_fib, &nf->fn.prefix, nf->fn.pxlen);
       if (anet)
@@ -1178,36 +1104,29 @@ check_sum_net_lsa(struct proto_ospf *po, ort *nf)
   }
 
   struct ospf_area *oa;
-  WALK_LIST(oa, po->area_list)
+  WALK_LIST(oa, p->area_list)
   {
     if (anet && decide_anet_lsa(oa, anet, anet_oa))
-      originate_sum_net_lsa(oa, &nf->fn, anet->metric);
+      ospf_originate_sum_net_lsa(p, oa, nf, anet->metric);
     else if (decide_sum_lsa(oa, nf, ORT_NET))
-      originate_sum_net_lsa(oa, &nf->fn, nf->n.metric1);
-    else
-      flush_sum_lsa(oa, &nf->fn, ORT_NET);
+      ospf_originate_sum_net_lsa(p, oa, nf, nf->n.metric1);
   }
 }
 
 static inline void
-check_sum_rt_lsa(struct proto_ospf *po, ort *nf)
+check_sum_rt_lsa(struct ospf_proto *p, ort *nf)
 {
   struct ospf_area *oa;
-  WALK_LIST(oa, po->area_list)
-  {
+  WALK_LIST(oa, p->area_list)
     if (decide_sum_lsa(oa, nf, ORT_ROUTER))
-      originate_sum_rt_lsa(oa, &nf->fn, nf->n.metric1, nf->n.options);
-    else
-      flush_sum_lsa(oa, &nf->fn, ORT_ROUTER);
-  }
+      ospf_originate_sum_rt_lsa(p, oa, nf, nf->n.metric1, nf->n.options);
 }
 
 static inline int
-decide_nssa_lsa(ort *nf, u32 *rt_metric, ip_addr *rt_fwaddr, u32 *rt_tag)
+decide_nssa_lsa(struct ospf_proto *p, ort *nf, struct ospf_lsa_ext_local *rt)
 {
   struct ospf_area *oa = nf->n.oa;
   struct top_hash_entry *en = nf->n.en;
-  int propagate;
 
   if (!rt_is_nssa(nf) || !oa->translate)
     return 0;
@@ -1216,66 +1135,40 @@ decide_nssa_lsa(ort *nf, u32 *rt_metric, ip_addr *rt_fwaddr, u32 *rt_tag)
   if (fib_route(&oa->enet_fib, nf->fn.prefix, nf->fn.pxlen))
     return 0;
 
-  if (!en || (en->lsa.type != LSA_T_NSSA))
+  if (!en || (en->lsa_type != LSA_T_NSSA))
     return 0;
 
   /* We do not store needed data in struct orta, we have to parse the LSA */
-  struct ospf_lsa_ext *le = en->lsa_body;
-
-#ifdef OSPFv2
-  *rt_fwaddr = le->fwaddr;
-  *rt_tag = le->tag;
-  propagate = en->lsa.options & OPT_P;
-#else /* OSPFv3 */
-  u32 *buf = le->rest;
-  u8 pxlen = (*buf >> 24);
-  u8 pxopts = (*buf >> 16);
-  buf += IPV6_PREFIX_WORDS(pxlen);  /* Skip the IP prefix */
-
-  if (pxopts & OPT_PX_NU)
+  lsa_parse_ext(en, ospf_is_v2(p), rt);
+  
+  if (rt->pxopts & OPT_PX_NU)
     return 0;
 
-  if (le->metric & LSA_EXT_FBIT)
-    buf = lsa_get_ipv6_addr(buf, rt_fwaddr);
-  else
-    *rt_fwaddr = IPA_NONE;
-
-  if (le->metric & LSA_EXT_TBIT)
-    *rt_tag = *buf++;
-  else
-    *rt_tag = 0;
-
-  propagate = pxopts & OPT_PX_P;
-#endif
-
-  if (!propagate || ipa_zero(*rt_fwaddr))
+  if (!rt->propagate || ipa_zero(rt->fwaddr))
     return 0;
 
-  *rt_metric = le->metric & (METRIC_MASK | LSA_EXT_EBIT);
   return 1;
 }
 
 /* RFC 3103 3.2 - translating Type-7 LSAs into Type-5 LSAs */
 static inline void
-check_nssa_lsa(struct proto_ospf *po, ort *nf)
+check_nssa_lsa(struct ospf_proto *p, ort *nf)
 {
-  struct fib_node *fn = &nf->fn;
   struct area_net *anet = NULL;
   struct ospf_area *oa = NULL;
-  u32 rt_metric, rt_tag;
-  ip_addr rt_fwaddr;
+  struct ospf_lsa_ext_local rt;
 
   /* Do not translate LSA if there is already the external LSA from route export */
-  if (fn->x1 == EXT_EXPORT)
+  if (nf->external_rte)
     return;
 
   /* RT entry marked as area network */
-  if (fn->x0)
+  if (nf->fn.flags & OSPF_RT_PERSISTENT)
   {
     /* Find that area network */
-    WALK_LIST(oa, po->area_list)
+    WALK_LIST(oa, p->area_list)
     {
-      anet = (struct area_net *) fib_find(&oa->enet_fib, &fn->prefix, fn->pxlen);
+      anet = (struct area_net *) fib_find(&oa->enet_fib, &nf->fn.prefix, nf->fn.pxlen);
       if (anet)
        break;
     }
@@ -1283,33 +1176,29 @@ check_nssa_lsa(struct proto_ospf *po, ort *nf)
 
   /* RFC 3103 3.2 (3) - originate the aggregated address range */
   if (anet && anet->active && !anet->hidden && oa->translate)
-    originate_ext_lsa(po->backbone, fn, EXT_NSSA, anet->metric, IPA_NONE, anet->tag, 0);
+    ospf_originate_ext_lsa(p, NULL, nf, LSA_RTCALC, anet->metric,
+                          (anet->metric & LSA_EXT3_EBIT), IPA_NONE, anet->tag, 0);
 
   /* RFC 3103 3.2 (2) - originate the same network */
-  else if (decide_nssa_lsa(nf, &rt_metric, &rt_fwaddr, &rt_tag))
-    originate_ext_lsa(po->backbone, fn, EXT_NSSA, rt_metric, rt_fwaddr, rt_tag, 0);
-
-  else if (fn->x1 == EXT_NSSA)
-    flush_ext_lsa(po->backbone, fn, 0);
+  else if (decide_nssa_lsa(p, nf, &rt))
+    ospf_originate_ext_lsa(p, NULL, nf, LSA_RTCALC, rt.metric, rt.ebit, rt.fwaddr, rt.tag, 0);
 }
 
 /* RFC 2328 16.7. p2 - find new/lost vlink endpoints */
 static void
-ospf_check_vlinks(struct proto_ospf *po)
+ospf_check_vlinks(struct ospf_proto *p)
 {
-  struct proto *p = &po->proto;
-
   struct ospf_iface *ifa;
-  WALK_LIST(ifa, po->iface_list)
+  WALK_LIST(ifa, p->iface_list)
   {
     if (ifa->type == OSPF_IT_VLINK)
     {
       struct top_hash_entry *tmp;
-      tmp = ospf_hash_find_rt(po->gr, ifa->voa->areaid, ifa->vid);
+      tmp = ospf_hash_find_rt(p->gr, ifa->voa->areaid, ifa->vid);
 
       if (tmp && (tmp->color == INSPF) && ipa_nonzero(tmp->lb) && tmp->nhs)
       {
-       struct ospf_iface *nhi = ospf_iface_find(po, tmp->nhs->iface);
+       struct ospf_iface *nhi = ospf_iface_find(p, tmp->nhs->iface);
 
         if ((ifa->state != OSPF_IS_PTP)
            || (ifa->vifa != nhi)
@@ -1326,7 +1215,9 @@ ospf_check_vlinks(struct proto_ospf *po)
        else if ((ifa->state == OSPF_IS_PTP) && (ifa->cost != tmp->dist))
        {
          ifa->cost = tmp->dist;
-         schedule_rt_lsa(po->backbone);
+
+         /* RFC 2328 12.4 Event 8 - vlink state change */
+         ospf_notify_rt_lsa(ifa->oa);
        }
       }
       else
@@ -1344,13 +1235,13 @@ ospf_check_vlinks(struct proto_ospf *po)
 
 /* Miscellaneous route processing that needs to be done by ABRs */
 static void
-ospf_rt_abr1(struct proto_ospf *po)
+ospf_rt_abr1(struct ospf_proto *p)
 {
   struct area_net *anet;
   ort *nf, *default_nf;
 
   /* RFC 2328 G.3 - incomplete resolution of virtual next hops - routers */
-  FIB_WALK(&po->backbone->rtr, nftmp)
+  FIB_WALK(&p->backbone->rtr, nftmp)
   {
     nf = (ort *) nftmp;
 
@@ -1360,7 +1251,7 @@ ospf_rt_abr1(struct proto_ospf *po)
   FIB_WALK_END;
 
 
-  FIB_WALK(&po->rtf, nftmp)
+  FIB_WALK(&p->rtf, nftmp)
   {
     nf = (ort *) nftmp;
 
@@ -1381,8 +1272,8 @@ ospf_rt_abr1(struct proto_ospf *po)
          anet->active = 1;
 
          /* Get a RT entry and mark it to know that it is an area network */
-         ort *nfi = (ort *) fib_get(&po->rtf, &anet->fn.prefix, anet->fn.pxlen);
-         nfi->fn.x0 = 1; /* mark and keep persistent, to have stable UID */
+         ort *nfi = (ort *) fib_get(&p->rtf, &anet->fn.prefix, anet->fn.pxlen);
+         nfi->fn.flags |= OSPF_RT_PERSISTENT; /* mark persistent, to have stable UID */
 
          /* 16.2. (3) */
          if (nfi->n.type == RTS_OSPF_IA)
@@ -1397,18 +1288,16 @@ ospf_rt_abr1(struct proto_ospf *po)
   FIB_WALK_END;
 
   ip_addr addr = IPA_NONE;
-  default_nf = (ort *) fib_get(&po->rtf, &addr, 0);
-  default_nf->fn.x0 = 1; /* keep persistent */
+  default_nf = (ort *) fib_get(&p->rtf, &addr, 0);
+  default_nf->fn.flags |= OSPF_RT_PERSISTENT; /* keep persistent */
 
   struct ospf_area *oa;
-  WALK_LIST(oa, po->area_list)
+  WALK_LIST(oa, p->area_list)
   {
 
     /* 12.4.3.1. - originate or flush default route for stub/NSSA areas */
     if (oa_is_stub(oa) || (oa_is_nssa(oa) && !oa->ac->summary))
-      originate_sum_net_lsa(oa, &default_nf->fn, oa->ac->default_cost);
-    else
-      flush_sum_lsa(oa, &default_nf->fn, ORT_NET);
+      ospf_originate_sum_net_lsa(p, oa, default_nf, oa->ac->default_cost);
 
     /*
      * Originate type-7 default route for NSSA areas
@@ -1420,10 +1309,8 @@ ospf_rt_abr1(struct proto_ospf *po)
      */
 
     if (oa_is_nssa(oa) && oa->ac->default_nssa)
-      originate_ext_lsa(oa, &default_nf->fn, 0, oa->ac->default_cost, IPA_NONE, 0, 0);
-    else
-      flush_ext_lsa(oa, &default_nf->fn, 1);
-
+      ospf_originate_ext_lsa(p, oa, default_nf, LSA_RTCALC, oa->ac->default_cost,
+                            (oa->ac->default_cost & LSA_EXT3_EBIT), IPA_NONE, 0, 0);
 
     /* RFC 2328 16.4. (3) - precompute preferred ASBR entries */
     if (oa_is_ext(oa))
@@ -1432,7 +1319,7 @@ ospf_rt_abr1(struct proto_ospf *po)
       {
        nf = (ort *) nftmp;
        if (nf->n.options & ORTA_ASBR)
-         ri_install_asbr(po, &nf->fn.prefix, &nf->n);
+         ri_install_asbr(p, &nf->fn.prefix, &nf->n);
       }
       FIB_WALK_END;
     }
@@ -1440,15 +1327,15 @@ ospf_rt_abr1(struct proto_ospf *po)
 
 
   /* Originate or flush ASBR summary LSAs */
-  FIB_WALK(&po->backbone->rtr, nftmp)
+  FIB_WALK(&p->backbone->rtr, nftmp)
   {
-    check_sum_rt_lsa(po, (ort *) nftmp);
+    check_sum_rt_lsa(p, (ort *) nftmp);
   }
   FIB_WALK_END;
 
 
   /* RFC 2328 16.7. p2 - find new/lost vlink endpoints */
-  ospf_check_vlinks(po);
+  ospf_check_vlinks(p);
 }
 
 
@@ -1465,7 +1352,7 @@ translator_timer_hook(timer *timer)
 }
 
 static void
-ospf_rt_abr2(struct proto_ospf *po)
+ospf_rt_abr2(struct ospf_proto *p)
 {
   struct ospf_area *oa;
   struct top_hash_entry *en;
@@ -1473,8 +1360,8 @@ ospf_rt_abr2(struct proto_ospf *po)
 
 
   /* RFC 3103 3.1 - type-7 translator election */
-  struct ospf_area *bb = po->backbone;
-  WALK_LIST(oa, po->area_list)
+  struct ospf_area *bb = p->backbone;
+  WALK_LIST(oa, p->area_list)
     if (oa_is_nssa(oa))
     {
       int translate = 1;
@@ -1492,13 +1379,13 @@ ospf_rt_abr2(struct proto_ospf *po)
        if (!nf2 || !nf2->n.type || !(nf2->n.options & ORTA_ABR))
          continue;
 
-       en = ospf_hash_find_rt(po->gr, oa->areaid, nf->n.rid);
+       en = ospf_hash_find_rt(p->gr, oa->areaid, nf->n.rid);
        if (!en || (en->color != INSPF))
          continue;
 
        struct ospf_lsa_rt *rt = en->lsa_body;
        /* There is better candidate - Nt-bit or higher Router ID */
-       if ((rt->options & OPT_RT_NT) || (po->router_id < nf->n.rid))
+       if ((rt->options & OPT_RT_NT) || (p->router_id < nf->n.rid))
        {
          translate = 0;
          goto decided;
@@ -1518,7 +1405,7 @@ ospf_rt_abr2(struct proto_ospf *po)
       if (!translate && (oa->translate == TRANS_ON))
       {
        if (oa->translator_timer == NULL)
-         oa->translator_timer = tm_new_set(po->proto.pool, translator_timer_hook, oa, 0, 0);
+         oa->translator_timer = tm_new_set(p->p.pool, translator_timer_hook, oa, 0, 0);
 
        /* Schedule the end of translation */
        tm_start(oa->translator_timer, oa->ac->transint);
@@ -1528,7 +1415,7 @@ ospf_rt_abr2(struct proto_ospf *po)
 
 
   /* Compute condensed external networks */
-  FIB_WALK(&po->rtf, nftmp)
+  FIB_WALK(&p->rtf, nftmp)
   {
     nf = (ort *) nftmp;
     if (rt_is_nssa(nf) && (nf->n.options & ORTA_PROP))
@@ -1543,12 +1430,12 @@ ospf_rt_abr2(struct proto_ospf *po)
          anet->active = 1;
 
          /* Get a RT entry and mark it to know that it is an area network */
-         nf2 = (ort *) fib_get(&po->rtf, &anet->fn.prefix, anet->fn.pxlen);
-         nf2->fn.x0 = 1;
+         nf2 = (ort *) fib_get(&p->rtf, &anet->fn.prefix, anet->fn.pxlen);
+         nf2->fn.flags |= OSPF_RT_PERSISTENT; /* keep persistent */
        }
 
        u32 metric = (nf->n.type == RTS_OSPF_EXT1) ?
-         nf->n.metric1 : ((nf->n.metric2 + 1) | LSA_EXT_EBIT);
+         nf->n.metric1 : ((nf->n.metric2 + 1) | LSA_EXT3_EBIT);
 
        if (anet->metric < metric)
          anet->metric = metric;
@@ -1558,12 +1445,12 @@ ospf_rt_abr2(struct proto_ospf *po)
   FIB_WALK_END;
 
 
-  FIB_WALK(&po->rtf, nftmp)
+  FIB_WALK(&p->rtf, nftmp)
   {
     nf = (ort *) nftmp;
 
-    check_sum_net_lsa(po, nf);
-    check_nssa_lsa(po, nf);
+    check_sum_net_lsa(p, nf);
+    check_nssa_lsa(p, nf);
   }
   FIB_WALK_END;
 }
@@ -1589,78 +1476,46 @@ ospf_fib_route(struct fib *f, ip_addr a, int len)
 
 /* RFC 2328 16.4. calculating external routes */
 static void
-ospf_ext_spf(struct proto_ospf *po)
+ospf_ext_spf(struct ospf_proto *p)
 {
+  struct top_hash_entry *en;
+  struct ospf_lsa_ext_local rt;
   ort *nf1, *nf2;
   orta nfa = {};
-  struct top_hash_entry *en;
-  struct proto *p = &po->proto;
-  struct ospf_lsa_ext *le;
-  int pxlen, ebit, rt_fwaddr_valid, rt_propagate;
-  ip_addr ip, rtid, rt_fwaddr;
-  u32 br_metric, rt_metric, rt_tag;
+  ip_addr rtid;
+  u32 br_metric;
   struct ospf_area *atmp;
 
   OSPF_TRACE(D_EVENTS, "Starting routing table calculation for ext routes");
 
-  WALK_SLIST(en, po->lsal)
+  WALK_SLIST(en, p->lsal)
   {
     /* 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_NSSA))
       continue;
 
     if (en->lsa.age == LSA_MAXAGE)
       continue;
 
     /* 16.4. (2) */
-    if (en->lsa.rt == po->router_id)
+    if (en->lsa.rt == p->router_id)
       continue;
 
     DBG("%s: Working on LSA. ID: %R, RT: %R, Type: %u\n",
-       p->name, en->lsa.id, en->lsa.rt, en->lsa.type);
+       p->p.name, en->lsa.id, en->lsa.rt, en->lsa_type);
 
-    le = en->lsa_body;
+    lsa_parse_ext(en, ospf_is_v2(p), &rt);
 
-    rt_metric = le->metric & METRIC_MASK;
-    ebit = le->metric & LSA_EXT_EBIT;
-
-    if (rt_metric == LSINFINITY)
+    if (rt.metric == LSINFINITY)
       continue;
 
-#ifdef OSPFv2
-    ip = ipa_and(ipa_from_u32(en->lsa.id), le->netmask);
-    pxlen = ipa_mklen(le->netmask);
-    rt_fwaddr = le->fwaddr;
-    rt_fwaddr_valid = !ipa_equal(rt_fwaddr, IPA_NONE);
-    rt_tag = le->tag;
-    rt_propagate = en->lsa.options & OPT_P;
-#else /* OSPFv3 */
-    u8 pxopts;
-    u16 rest;
-    u32 *buf = le->rest;
-    buf = lsa_get_ipv6_prefix(buf, &ip, &pxlen, &pxopts, &rest);
-
-    if (pxopts & OPT_PX_NU)
+    if (rt.pxopts & OPT_PX_NU)
       continue;
 
-    rt_fwaddr_valid = le->metric & LSA_EXT_FBIT;
-    if (rt_fwaddr_valid)
-      buf = lsa_get_ipv6_addr(buf, &rt_fwaddr);
-    else 
-      rt_fwaddr = IPA_NONE;
-
-    if (le->metric & LSA_EXT_TBIT)
-      rt_tag = *buf++;
-    else
-      rt_tag = 0;
-
-    rt_propagate = pxopts & OPT_PX_P;
-#endif
-
-    if (pxlen < 0 || pxlen > MAX_PREFIX_LENGTH)
+    if (rt.pxlen < 0 || rt.pxlen > MAX_PREFIX_LENGTH)
     {
       log(L_WARN "%s: Invalid prefix in LSA (Type: %04x, Id: %R, Rt: %R)",
-         p->name, en->lsa.type, en->lsa.id, en->lsa.rt);
+         p->p.name, en->lsa_type, en->lsa.id, en->lsa.rt);
       continue;
     }
 
@@ -1669,10 +1524,10 @@ ospf_ext_spf(struct proto_ospf *po)
     /* 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)
-      atmp = ospf_main_area(po);
+    if (en->lsa_type == LSA_T_EXT)
+      atmp = ospf_main_area(p);
     else /* NSSA */
-      atmp = ospf_find_area(po, en->domain);
+      atmp = ospf_find_area(p, en->domain);
 
     if (!atmp)
       continue;                        /* Should not happen */
@@ -1688,11 +1543,11 @@ ospf_ext_spf(struct proto_ospf *po)
 
     /* 16.4. (3) NSSA - special rule for default routes */
     /* ABR should use default only if P-bit is set and summaries are active */
-    if ((en->lsa.type == LSA_T_NSSA) && ipa_zero(ip) && (pxlen == 0) &&
-       (po->areano > 1) && !(rt_propagate && atmp->ac->summary))
+    if ((en->lsa_type == LSA_T_NSSA) && ipa_zero(rt.ip) && (rt.pxlen == 0) &&
+       (p->areano > 1) && !(rt.propagate && atmp->ac->summary))
       continue;
 
-    if (!rt_fwaddr_valid)
+    if (!rt.fbit)
     {
       nf2 = nf1;
       nfa.nhs = nf1->n.nhs;
@@ -1700,11 +1555,11 @@ ospf_ext_spf(struct proto_ospf *po)
     }
     else
     {
-      nf2 = ospf_fib_route(&po->rtf, rt_fwaddr, MAX_PREFIX_LENGTH);
+      nf2 = ospf_fib_route(&p->rtf, rt.fwaddr, MAX_PREFIX_LENGTH);
       if (!nf2)
        continue;
 
-      if (en->lsa.type == LSA_T_EXT)
+      if (en->lsa_type == LSA_T_EXT)
       {
        /* For ext routes, we accept intra-area or inter-area routes */
        if ((nf2->n.type != RTS_OSPF) && (nf2->n.type != RTS_OSPF_IA))
@@ -1727,21 +1582,21 @@ ospf_ext_spf(struct proto_ospf *po)
       /* Replace device nexthops with nexthops to forwarding address from LSA */
       if (has_device_nexthops(nfa.nhs))
       {
-       nfa.nhs = fix_device_nexthops(po, nfa.nhs, rt_fwaddr);
+       nfa.nhs = fix_device_nexthops(p, nfa.nhs, rt.fwaddr);
        nfa.nhs_reuse = 1;
       }
     }
 
-    if (ebit)
+    if (rt.ebit)
     {
       nfa.type = RTS_OSPF_EXT2;
       nfa.metric1 = br_metric;
-      nfa.metric2 = rt_metric;
+      nfa.metric2 = rt.metric;
     }
     else
     {
       nfa.type = RTS_OSPF_EXT1;
-      nfa.metric1 = br_metric + rt_metric;
+      nfa.metric1 = br_metric + rt.metric;
       nfa.metric2 = LSINFINITY;
     }
 
@@ -1750,25 +1605,25 @@ ospf_ext_spf(struct proto_ospf *po)
 
     /* Whether the route is preferred in route selection according to 16.4.1 */
     nfa.options = epath_preferred(&nf2->n) ? ORTA_PREF : 0;
-    if (en->lsa.type == LSA_T_NSSA)
+    if (en->lsa_type == LSA_T_NSSA)
     {
       nfa.options |= ORTA_NSSA;
-      if (rt_propagate)
+      if (rt.propagate)
        nfa.options |= ORTA_PROP;
     }
 
-    nfa.tag = rt_tag;
+    nfa.tag = rt.tag;
     nfa.rid = en->lsa.rt;
     nfa.oa = atmp; /* undefined in RFC 2328 */
     nfa.en = en; /* store LSA for later (NSSA processing) */
 
-    ri_install_ext(po, ip, pxlen, &nfa);
+    ri_install_ext(p, rt.ip, rt.pxlen, &nfa);
   }
 }
 
 /* Cleanup of routing tables and data */
 void
-ospf_rt_reset(struct proto_ospf *po)
+ospf_rt_reset(struct ospf_proto *p)
 {
   struct ospf_area *oa;
   struct top_hash_entry *en;
@@ -1776,24 +1631,27 @@ ospf_rt_reset(struct proto_ospf *po)
   ort *ri;
 
   /* Reset old routing table */
-  FIB_WALK(&po->rtf, nftmp)
+  FIB_WALK(&p->rtf, nftmp)
   {
     ri = (ort *) nftmp;
-    ri->fn.x0 = 0;
+    ri->fn.flags &= ~OSPF_RT_PERSISTENT;
     reset_ri(ri);
   }
   FIB_WALK_END;
 
   /* Reset SPF data in LSA db */
-  WALK_SLIST(en, po->lsal)
+  WALK_SLIST(en, p->lsal)
   {
     en->color = OUTSPF;
     en->dist = LSINFINITY;
     en->nhs = NULL;
     en->lb = IPA_NONE;
+
+    if (en->rtcalc == LSA_RTCALC)
+      en->rtcalc = LSA_STALE;
   }
 
-  WALK_LIST(oa, po->area_list)
+  WALK_LIST(oa, p->area_list)
   {
     /* Reset ASBR routing tables */
     FIB_WALK(&oa->rtr, nftmp)
@@ -1804,7 +1662,7 @@ ospf_rt_reset(struct proto_ospf *po)
     FIB_WALK_END;
 
     /* Reset condensed area networks */
-    if (po->areano > 1)
+    if (p->areano > 1)
     {
       FIB_WALK(&oa->net_fib, nftmp)
       {
@@ -1825,53 +1683,57 @@ ospf_rt_reset(struct proto_ospf *po)
   }
 }
 
+static void
+ospf_flush_stale(struct ospf_proto *p)
+{
+}
+
 /**
  * ospf_rt_spf - calculate internal routes
- * @po: OSPF protocol
+ * @p: OSPF protocol
  *
  * Calculation of internal paths in an area is described in 16.1 of RFC 2328.
  * It's based on Dijkstra's shortest path tree algorithms.
  * This function is invoked from ospf_disp().
  */
 void
-ospf_rt_spf(struct proto_ospf *po)
+ospf_rt_spf(struct ospf_proto *p)
 {
-  struct proto *p = &po->proto;
   struct ospf_area *oa;
 
-  if (po->areano == 0)
+  if (p->areano == 0)
     return;
 
   OSPF_TRACE(D_EVENTS, "Starting routing table calculation");
 
   /* 16. (1) */
-  ospf_rt_reset(po);
+  ospf_rt_reset(p);
 
   /* 16. (2) */
-  WALK_LIST(oa, po->area_list)
+  WALK_LIST(oa, p->area_list)
     ospf_rt_spfa(oa);
 
   /* 16. (3) */
-  ospf_rt_sum(ospf_main_area(po));
+  ospf_rt_sum(ospf_main_area(p));
 
   /* 16. (4) */
-  WALK_LIST(oa, po->area_list)
+  WALK_LIST(oa, p->area_list)
     if (oa->trcap && (oa->areaid != 0))
       ospf_rt_sum_tr(oa);
 
-  if (po->areano > 1)
-    ospf_rt_abr1(po);
+  if (p->areano > 1)
+    ospf_rt_abr1(p);
 
   /* 16. (5) */
-  ospf_ext_spf(po);
+  ospf_ext_spf(p);
 
-  if (po->areano > 1)
-    ospf_rt_abr2(po);
+  if (p->areano > 1)
+    ospf_rt_abr2(p);
 
-  rt_sync(po);
-  lp_flush(po->nhpool);
+  rt_sync(p);
+  lp_flush(p->nhpool);
   
-  po->calcrt = 0;
+  p->calcrt = 0;
 }
 
 
@@ -1886,15 +1748,14 @@ static struct mpnh *
 calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
              struct top_hash_entry *par, int pos)
 {
-  // struct proto *p = &oa->po->proto;
-  struct proto_ospf *po = oa->po;
+  struct ospf_proto *p = oa->po;
   struct mpnh *pn = par->nhs;
   struct ospf_iface *ifa;
   u32 rid = en->lsa.rt;
 
   /* 16.1.1. The next hop calculation */
   DBG("     Next hop calculating for id: %R rt: %R type: %u\n",
-      en->lsa.id, en->lsa.rt, en->lsa.type);
+      en->lsa.id, en->lsa.rt, en->lsa_type);
 
   /* Usually, we inherit parent nexthops */
   if (inherit_nexthops(pn))
@@ -1908,67 +1769,69 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
    */
 
   /* The first case - local network */
-  if ((en->lsa.type == LSA_T_NET) && (par == oa->rt))
+  if ((en->lsa_type == LSA_T_NET) && (par == oa->rt))
   {
     ifa = rt_pos_to_ifa(oa, pos);
     if (!ifa)
       return NULL;
 
-    return new_nexthop(po, IPA_NONE, ifa->iface, ifa->ecmp_weight);
+    return new_nexthop(p, IPA_NONE, ifa->iface, ifa->ecmp_weight);
   }
 
   /* The second case - ptp or ptmp neighbor */
-  if ((en->lsa.type == LSA_T_RT) && (par == oa->rt))
+  if ((en->lsa_type == LSA_T_RT) && (par == oa->rt))
   {
     ifa = rt_pos_to_ifa(oa, pos);
     if (!ifa)
       return NULL;
 
     if (ifa->type == OSPF_IT_VLINK)
-      return new_nexthop(po, IPA_NONE, NULL, 0);
+      return new_nexthop(p, IPA_NONE, NULL, 0);
 
     struct ospf_neighbor *m = find_neigh(ifa, rid);
     if (!m || (m->state != NEIGHBOR_FULL))
       return NULL;
 
-    return new_nexthop(po, m->ip, ifa->iface, ifa->ecmp_weight);
+    return new_nexthop(p, m->ip, ifa->iface, ifa->ecmp_weight);
   }
 
   /* The third case - bcast or nbma neighbor */
-  if ((en->lsa.type == LSA_T_RT) && (par->lsa.type == LSA_T_NET))
+  if ((en->lsa_type == LSA_T_RT) && (par->lsa_type == LSA_T_NET))
   {
     /* par->nhi should be defined from parent's calc_next_hop() */
     if (!pn)
       goto bad;
 
-#ifdef OSPFv2
-    /*
-     * In this case, next-hop is the same as link-back, which is
-     * already computed in link_back().
-     */
-    if (ipa_zero(en->lb))
-      goto bad;
-
-    return new_nexthop(po, en->lb, pn->iface, pn->weight);
+    if (ospf_is_v2(p))
+    {
+      /*
+       * In this case, next-hop is the same as link-back, which is
+       * already computed in link_back().
+       */
+      if (ipa_zero(en->lb))
+       goto bad;
 
-#else /* OSPFv3 */
-    /*
-     * Next-hop is taken from lladdr field of Link-LSA, en->lb_id
-     * is computed in link_back().
-     */
-    struct top_hash_entry *lhe;
-    lhe = ospf_hash_find(po->gr, pn->iface->index, en->lb_id, rid, LSA_T_LINK);
+      return new_nexthop(p, en->lb, pn->iface, pn->weight);
+    }
+    else /* OSPFv3 */
+    {
+      /*
+       * Next-hop is taken from lladdr field of Link-LSA, en->lb_id
+       * is computed in link_back().
+       */
+      struct top_hash_entry *lhe;
+      lhe = ospf_hash_find(p->gr, pn->iface->index, en->lb_id, rid, LSA_T_LINK);
 
-    if (!lhe)
-      return NULL;
+      if (!lhe)
+       return NULL;
 
-    struct ospf_lsa_link *llsa = lhe->lsa_body;
+      struct ospf_lsa_link *llsa = lhe->lsa_body;
       
-    if (ipa_zero(llsa->lladdr))
-      return NULL;
+      if (ipa_zero(llsa->lladdr))
+       return NULL;
 
-    return new_nexthop(po, llsa->lladdr, pn->iface, pn->weight);
-#endif
+      return new_nexthop(p, llsa->lladdr, pn->iface, pn->weight);
+    }
   }
 
  bad:
@@ -1983,7 +1846,7 @@ static void
 add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
         u32 dist, struct ospf_area *oa, int pos)
 {
-  struct proto_ospf *po = oa->po;
+  struct ospf_proto *p = oa->po;
   node *prev, *n;
   int added = 0;
   struct top_hash_entry *act;
@@ -1994,14 +1857,13 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
   if (en->lsa.age == LSA_MAXAGE)
     return;
 
-#ifdef OSPFv3
-  if (en->lsa.type == LSA_T_RT)
-    {
-      struct ospf_lsa_rt *rt = en->lsa_body;
-      if (!(rt->options & OPT_V6))
-       return;
-    }
-#endif
+  if (ospf_is_v3(p) && (en->lsa_type == LSA_T_RT))
+  {
+    /* In OSPFv3, check V6 flag */
+    struct ospf_lsa_rt *rt = en->lsa_body;
+    if (!(rt->options & OPT_V6))
+      return;
+  }
 
   /* 16.1. (2c) */
   if (en->color == INSPF)
@@ -2019,7 +1881,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
   if (!nhs)
   {
     log(L_WARN "Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)",
-       en->lsa.type, en->lsa.id, en->lsa.rt);
+       en->lsa_type, en->lsa.id, en->lsa.rt);
     return;
   }
 
@@ -2042,18 +1904,18 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
      */
 
     /* Keep old ones */
-    if (!po->ecmp || nh_is_vlink(nhs) || (nhs == en->nhs))
+    if (!p->ecmp || nh_is_vlink(nhs) || (nhs == en->nhs))
       return;
 
     /* Merge old and new */
     int new_reuse = (par->nhs != nhs);
-    en->nhs = merge_nexthops(po, en->nhs, nhs, en->nhs_reuse, new_reuse);
+    en->nhs = merge_nexthops(p, en->nhs, nhs, en->nhs_reuse, new_reuse);
     en->nhs_reuse = 1;
     return;
   }
 
   DBG("     Adding candidate: rt: %R, id: %R, type: %u\n",
-      en->lsa.rt, en->lsa.id, en->lsa.type);
+      en->lsa.rt, en->lsa.id, en->lsa_type);
 
   if (en->color == CANDIDATE)
   {                            /* We found a shorter path */
@@ -2076,7 +1938,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
     {
       act = SKIP_BACK(struct top_hash_entry, cn, n);
       if ((act->dist > dist) ||
-         ((act->dist == dist) && (act->lsa.type == LSA_T_RT)))
+         ((act->dist == dist) && (act->lsa_type == LSA_T_RT)))
       {
        if (prev == NULL)
          add_head(l, &en->cn);
@@ -2108,16 +1970,16 @@ ort_changed(ort *nf, rta *nr)
 }
 
 static void
-rt_sync(struct proto_ospf *po)
+rt_sync(struct ospf_proto *p)
 {
-  struct proto *p = &po->proto;
+  struct top_hash_entry *en;
   struct fib_iterator fit;
-  struct fib *fib = &po->rtf;
+  struct fib *fib = &p->rtf;
   ort *nf;
   struct ospf_area *oa;
 
   /* This is used for forced reload of routes */
-  int reload = (po->calcrt == 2);
+  int reload = (p->calcrt == 2);
 
   OSPF_TRACE(D_EVENTS, "Starting routing table synchronisation");
 
@@ -2135,7 +1997,7 @@ again1:
       for (nh = nf->n.nhs; nh; nh = nh->next)
        if (ipa_nonzero(nh->gw))
        {
-         neighbor *ng = neigh_find2(p, &nh->gw, nh->iface, 0);
+         neighbor *ng = neigh_find2(&p->p, &nh->gw, nh->iface, 0);
          if (!ng || (ng->scope == SCOPE_HOST))
            { reset_ri(nf); break; }
        }
@@ -2148,7 +2010,7 @@ again1:
     if (nf->n.type) /* Add the route */
     {
       rta a0 = {
-       .src = p->main_source,
+       .src = p->p.main_source,
        .source = nf->n.type,
        .scope = SCOPE_UNIVERSE,
        .cast = RTC_UNICAST
@@ -2173,7 +2035,7 @@ again1:
 
       if (reload || ort_changed(nf, &a0))
       {
-       net *ne = net_get(p->table, nf->fn.prefix, nf->fn.pxlen);
+       net *ne = net_get(p->p.table, nf->fn.prefix, nf->fn.pxlen);
        rta *a = rta_lookup(&a0);
        rte *e = rte_get_temp(a);
 
@@ -2185,11 +2047,11 @@ again1:
        e->u.ospf.router_id = nf->old_rid = nf->n.rid;
        e->pflags = 0;
        e->net = ne;
-       e->pref = p->preference;
+       e->pref = p->p.preference;
 
        DBG("Mod rte type %d - %I/%d via %I on iface %s, met %d\n",
            a0.source, nf->fn.prefix, nf->fn.pxlen, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);
-       rte_update(p, ne, e);
+       rte_update(&p->p, ne, e);
       }
     }
     else if (nf->old_rta)
@@ -2198,12 +2060,12 @@ again1:
       rta_free(nf->old_rta);
       nf->old_rta = NULL;
 
-      net *ne = net_get(p->table, nf->fn.prefix, nf->fn.pxlen);
-      rte_update(p, ne, NULL);
+      net *ne = net_get(p->p.table, nf->fn.prefix, nf->fn.pxlen);
+      rte_update(&p->p, ne, NULL);
     }
 
-    /* Remove unused rt entry. Entries with fn.x0 == 1 are persistent. */
-    if (!nf->n.type && !nf->fn.x0 && !nf->fn.x1)
+    /* Remove unused rt entry. Entries with any flags are persistent. */
+    if (!nf->n.type && !nf->external_rte) // XXXX
     {
       FIB_ITERATE_PUT(&fit, nftmp);
       fib_delete(fib, nftmp);
@@ -2213,7 +2075,7 @@ again1:
   FIB_ITERATE_END(nftmp);
 
 
-  WALK_LIST(oa, po->area_list)
+  WALK_LIST(oa, p->area_list)
   {
     /* Cleanup ASBR hash tables */
     FIB_ITERATE_INIT(&fit, &oa->rtr);
@@ -2231,4 +2093,9 @@ again2:
     }
     FIB_ITERATE_END(nftmp);
   }
+
+  /* Cleanup stale LSAs */
+  WALK_SLIST(en, p->lsal)
+    if (en->rtcalc == LSA_STALE)
+      ospf_flush_lsa(p, en);
 }
index a11748fcf41435eb8f5dc53bce555755e3ed6db4..77d7080bf3c9f6affb36e781667a8fa8ff06b371 100644 (file)
@@ -10,9 +10,9 @@
 #ifndef _BIRD_OSPF_RT_H_
 #define _BIRD_OSPF_RT_H_
 
-#define ORT_UNDEF -1
-#define ORT_ROUTER 1
+
 #define ORT_NET 0
+#define ORT_ROUTER 1
 
 typedef struct orta
 {
@@ -51,26 +51,33 @@ typedef struct orta
 }
 orta;
 
+
+/* Values for fn.flags in struct ort */
+#define OSPF_RT_PERSISTENT     0x01
+
 typedef struct ort
 {
   /*
-   * We use fn.x0 to mark persistent rt entries, that are needed for summary
-   * LSAs that don't have 'proper' rt entry (area networks + default to stubs)
-   * to keep uid stable (used for LSA ID in OSPFv3 - see fibnode_to_lsaid()).
+   * We use OSPF_RT_PERSISTENT to mark persistent rt entries, that are
+   * needed for summary LSAs that don't have 'proper' rt entry (area
+   * networks + default to stubs) to keep uid stable (used for LSA ID
+   * in OSPFv3 - see fibnode_to_lsaid()).
    *
-   * We use fn.x1 to note whether the external route was originated
-   * from the route export (in ospf_rt_notify()) or from the NSSA
-   * route translation (in check_nssa_lsa()).
+   * We use ORT_RT_EXPORT and ORT_RT_NSSA to note whether the
+   * external/NSSA route was originated from the route export (in
+   * ospf_rt_notify()) or from the NSSA route translation (in
+   * check_nssa_lsa()).
    *
    * old_* values are here to represent the last route update. old_rta
    * is cached (we keep reference), mainly for multipath nexthops.
-   * old_rta == NULL means route wasn not in the last update, in that
+   * old_rta == NULL means route was not in the last update, in that
    * case other old_* values are not valid.
    */
   struct fib_node fn;
   orta n;
   u32 old_metric1, old_metric2, old_tag, old_rid;
   rta *old_rta;
+  u8 external_rte;
 }
 ort;
 
@@ -114,7 +121,7 @@ static inline int rt_is_nssa(ort *nf)
  * appear in ASBR pre-selection and external routes processing.
  */
 
-void ospf_rt_spf(struct proto_ospf *po);
+void ospf_rt_spf(struct ospf_proto *p);
 void ospf_rt_initort(struct fib_node *fn);
 
 
index 4af5afa51451cd642acbc7c98e83ffed2e8ec9d5..5ed3cf7aaa7b35a263b17c911255a9054c6f20ce 100644 (file)
@@ -3,6 +3,8 @@
  *
  *     (c) 1999       Martin Mares <mj@ucw.cz>
  *     (c) 1999--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
@@ -12,6 +14,7 @@
 
 #include "ospf.h"
 
+
 #define HASH_DEF_ORDER 6
 #define HASH_HI_MARK *4
 #define HASH_HI_STEP 2
 #define HASH_LO_STEP 2
 #define HASH_LO_MIN 8
 
-void originate_prefix_rt_lsa(struct ospf_area *oa);
-void originate_prefix_net_lsa(struct ospf_iface *ifa);
-void flush_prefix_net_lsa(struct ospf_iface *ifa);
+static inline void * lsab_flush(struct ospf_proto *p);
+static inline void lsab_reset(struct ospf_proto *p);
 
-#ifdef OSPFv2
-#define ipa_to_rid(x) _I(x)
-#else /* OSPFv3 */
-#define ipa_to_rid(x) _I3(x)
-#endif
 
+/**
+ * ospf_install_lsa - install new LSA into database
+ * @p: OSPF protocol instance
+ * @lsa: LSA header
+ * @type: type of LSA 
+ * @domain: domain of LSA
+ * @body: pointer to LSA body
+ *
+ * This function ensures installing new LSA received in LS update into LSA
+ * database. Old instance is replaced. Several actions are taken to detect if
+ * new routing table calculation is necessary. This is described in 13.2 of RFC
+ * 2328. This function is for received LSA only, locally originated LSAs are
+ * installed by ospf_originate_lsa().
+ */
+struct top_hash_entry *
+ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body)
+{
+  /* LSA can be temporary, but body must be mb_allocated. */
+  struct top_hash_entry *en;
+  int change = 0;
 
-#ifdef OSPFv2
-static inline u32
-fibnode_to_lsaid(struct proto_ospf *po, struct fib_node *fn)
-{
-  /* We have to map IP prefixes to u32 in such manner that resulting
-     u32 interpreted as IP address is a member of given
-     prefix. Therefore, /32 prefix have to be mapped on itself.
-     All received prefixes have to be mapped on different u32s.
-
-     We have an assumption that if there is nontrivial (non-/32)
-     network prefix, then there is not /32 prefix for the first
-     and the last IP address of the network (these are usually
-     reserved, therefore it is not an important restriction).
-     The network prefix is mapped to the first or the last
-     IP address in the manner that disallow collisions - we
-     use IP address that cannot be used by parent prefix.
-
-     For example:
-     192.168.0.0/24 maps to 192.168.0.255
-     192.168.1.0/24 maps to 192.168.1.0
-     because 192.168.0.0 and 192.168.1.255 might be used by
-     192.168.0.0/23 .
-
-     This is not compatible with older RFC 1583, so we have an option
-     to the RFC 1583 compatible assignment (that always uses the first
-     address) which disallows subnetting.
-
-     Appendig E of RFC 2328 suggests different algorithm, that tries
-     to maximize both compatibility and subnetting. But as it is not
-     possible to have both reliably and the suggested algorithm was
-     unnecessary complicated and it does crazy things like changing
-     LSA ID for a network because different network appeared, we
-     choose a different way. */
-
-  u32 id = _I(fn->prefix);
-
-  if ((po->rfc1583) || (fn->pxlen == 0) || (fn->pxlen == 32))
-    return id;
+  en = ospf_hash_get(p->gr, domain, lsa->id, lsa->rt, type);
 
-  if (id & (1 << (32 - fn->pxlen)))
-    return id;
+  if (!SNODE_VALID(en))
+    s_add_tail(&p->lsal, SNODE en);
+
+  if ((en->lsa_body == NULL) ||                        /* No old LSA */
+      (en->lsa.length != lsa->length) ||
+      (en->lsa.type_raw != lsa->type_raw) ||   /* Check for OSPFv2 options */
+      (en->lsa.age == LSA_MAXAGE) ||
+      (lsa->age == LSA_MAXAGE) ||
+      memcmp(en->lsa_body, body, lsa->length - sizeof(struct ospf_lsa_header)))
+    change = 1;
+
+  DBG("Inst lsa: Id: %R, Rt: %R, Type: %u, Age: %u, Sum: %u, Sn: 0x%x\n",
+      lsa->id, lsa->rt, lsa->type, lsa->age, lsa->checksum, lsa->sn);
+
+  mb_free(en->lsa_body);
+  en->lsa_body = body;
+  en->lsa = *lsa;
+  en->init_age = en->lsa.age;
+  en->inst_time = now;
+
+  if (change)
+    schedule_rtcalc(p);
+
+  return en;
+}
+
+void
+ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body)
+{
+  // OSPF_TRACE(D_EVENTS, "Reflooding new self-originated LSA with newer sequence number");
+
+  if (en && (en->lsa.age < LSA_MAXAGE))
+  {
+    if (lsa->sn != LSA_MAXSEQNO)
+    {
+      /*
+       * We simply advance current LSA to have higher seqnum than received LSA.
+       * The received LSA is ignored and the advanced LSA is propagated instead.
+       *
+       * Although this is an origination of distinct LSA instance and therefore
+       * should be limited by MinLSInterval, we do not enforce it here. Fast
+       * reaction is needed and we are already limited by MinLSArrival.
+       */
+
+      mb_free(body);
+
+      en->lsa.sn = lsa->sn + 1;
+      en->lsa.age = 0;
+      en->init_age = 0;
+      en->inst_time = now;
+      lsasum_calculate(&en->lsa, en->lsa_body);
+    }
+    else
+    {
+      /* 
+       * Received LSA has maximal sequence number, so we cannot simply override
+       * it. We have to install it to the database, immediately flush it to
+       * implement sequence number wrapping, and schedule our current LSA to be
+       * originated after the received instance is flushed.
+       */
+
+      if (en->next_lsa_body == NULL)
+      {
+       /* Schedule current LSA */
+       en->next_lsa_blen = en->lsa.length - sizeof(struct ospf_lsa_header);
+       en->next_lsa_body = en->lsa_body;
+       en->next_lsa_opts = ospf_is_v2(p) ? lsa_get_options(&en->lsa) : 0;
+      }
+      else
+      {
+       /* There is already scheduled LSA, so we just free current one */
+       mb_free(en->lsa_body);
+      }
+
+      en->lsa_body = body;
+      en->lsa = *lsa;
+      en->lsa.age = LSA_MAXAGE;
+      en->init_age = lsa->age;
+      en->inst_time = now;
+    }
+  }
   else
-    return id | ~u32_mkmask(fn->pxlen);
+  {
+    /*
+     * We do not have received LSA in the database. We have to flush the
+     * received LSA. It has to be installed in the database to secure
+     * retransmissions. Note that the received LSA may already be MaxAge.
+     */
+
+    lsa->age = LSA_MAXAGE;
+    en = ospf_install_lsa(p, lsa, type, domain, body);
+  }
+
+  /* 
+   * We flood the updated LSA. Although in some cases the to-be-flooded LSA is
+   * the same as the received LSA, and therefore we should propagate it as
+   * regular received LSA (send the acknowledgement instead of the update to 
+   * the neighbor we received it from), we cheat a bit here.
+   */
+
+  ospf_lsupd_flood(p, en, NULL);
 }
 
-#else /* OSPFv3 */
 
-static inline u32
-fibnode_to_lsaid(struct proto_ospf *po, struct fib_node *fn)
+static int
+ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa_body, u16 lsa_blen, u16 lsa_opts)
 {
+  /* Enforce MinLSInterval */
+  if ((en->init_age == 0) && en->inst_time && ((en->inst_time + MINLSINTERVAL) > now))
+    return 0;
+
+  /* Handle wrapping sequence number */
+  if (en->lsa.sn == LSA_MAXSEQNO)
+  {
+    /* Prepare to flush old LSA */
+    if (en->lsa.age != LSA_MAXAGE)
+    {
+      OSPF_TRACE(D_EVENTS, "Resetting LSA: Type: %04x, Id: %R, Rt: %R",
+                en->lsa_type, en->lsa.id, en->lsa.rt);
+
+      en->lsa.age = LSA_MAXAGE;
+      ospf_lsupd_flood(p, en, NULL);
+      return 0;
+    }
+
+    /* Already flushing */
+    if ((p->padj != 0) || (en->ret_count != 0))
+      return 0;
+
+    /* Flush done, just clean up seqnum, lsa_body is freed below */
+    en->lsa.sn = LSA_ZEROSEQNO;
+  }
+
+  OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R",
+            en->lsa_type, en->lsa.id, en->lsa.rt);
+
   /*
-   * In OSPFv3, it is simpler. There is not a requirement for
-   * membership of the result in the input network, so we just use a
-   * hash-based unique ID of a routing table entry for a route that
-   * originated given LSA. For ext-LSA, it is an imported route in the
-   * nest's routing table (p->table). For summary-LSA, it is a
-   * 'source' route in the protocol internal routing table (po->rtf).
+   * lsa.type_raw is initialized by ospf_hash_get() to OSPFv3 LSA type.
+   * lsa_set_options() implicitly converts it to OSPFv2 LSA type, assuming that
+   * old type is just new type masked by 0xff.  That is not universally true,
+   * but it holds for all OSPFv2 types currently supported by BIRD.
    */
-  return fn->uid;
+
+  if (ospf_is_v2(p))
+    lsa_set_options(&en->lsa, lsa_opts);
+
+  mb_free(en->lsa_body);
+  en->lsa_body = lsa_body;
+  en->lsa.length = sizeof(struct ospf_lsa_header) + lsa_blen;
+  en->lsa.sn++;
+  en->lsa.age = 0;
+  en->init_age = 0;
+  en->inst_time = now;
+  lsasum_calculate(&en->lsa, en->lsa_body);
+
+  ospf_lsupd_flood(p, en, NULL);
+
+  return 1;
 }
 
-#endif
+/**
+ * ospf_originate_lsa - originate new LSA
+ * @p: OSPF protocol instance
+ * @lsa: New LSA specification
+ *
+ * This function prepares a new LSA, installs it into the LSA database and
+ * floods it. If the new LSA cannot be originated now (because the old instance
+ * was originated within MinLSInterval, or because the LSA seqnum is currently
+ * wrapping), the origination is instead scheduled for later. If the new LSA is
+ * equivalent to the current LSA, the origination is skipped. In all cases, the
+ * corresponding LSA entry is returned. The new LSA is based on the LSA
+ * specification (@lsa) and the LSA body from lsab buffer of @p, which is
+ * emptied after the call. The opposite of this function is ospf_flush_lsa().
+ */
+struct top_hash_entry *
+ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa)
+{
+  struct top_hash_entry *en;
+  void *lsa_body = p->lsab;
+  u16 lsa_blen = p->lsab_used;
+  u16 lsa_length = sizeof(struct ospf_lsa_header) + lsa_blen;
 
+  en = ospf_hash_get(p->gr, lsa->dom, lsa->id, p->router_id, lsa->type);
 
-static void *
-lsab_alloc(struct proto_ospf *po, unsigned size)
+  if (!SNODE_VALID(en))
+    s_add_tail(&p->lsal, SNODE en);
+
+  if (en->lsa_body == NULL)
+    en->nf = lsa->nf;
+
+  if (en->nf != lsa->nf)
+  {
+    log(L_ERR "%s: LSA ID collision for %I/%d",
+       p->p.name, lsa->nf->fn.prefix, lsa->nf->fn.pxlen);
+    goto drop;
+  }
+
+  /* XXXX check for maxage or opts change */
+
+  if (en->next_lsa_body)
+  {
+    /* Ignore the new LSA if it is the same as the scheduled one */
+    if ((lsa_blen == en->next_lsa_blen) && !memcmp(lsa_body, en->next_lsa_body, lsa_blen))
+      goto drop;
+
+    /* Free scheduled LSA */
+    mb_free(en->next_lsa_body);
+    en->next_lsa_body = NULL;
+    en->next_lsa_blen = 0;
+    en->next_lsa_opts = 0;
+  }
+
+  /* Ignore the the new LSA if is the same as the current one */
+  if ((lsa_length == en->lsa.length) && !memcmp(lsa_body, en->lsa_body, lsa_blen))
+    goto drop;
+
+  lsa_body = lsab_flush(p);
+
+  if (! ospf_do_originate_lsa(p, en, lsa_body, lsa_blen, lsa->opts))
+  {
+    en->next_lsa_body = lsa_body;
+    en->next_lsa_blen = lsa_blen;
+    en->next_lsa_opts = lsa->opts;
+  }
+
+  return en;
+
+ drop:
+  lsab_reset(p);
+  return en;
+}
+
+static void
+ospf_originate_next_lsa(struct ospf_proto *p, struct top_hash_entry *en)
 {
-  unsigned offset = po->lsab_used;
-  po->lsab_used += size;
-  if (po->lsab_used > po->lsab_size)
+  /* Called by ospf_update_lsadb() to handle scheduled origination */
+
+  if (! ospf_do_originate_lsa(p, en, en->next_lsa_body, en->next_lsa_blen, en->next_lsa_opts))
+    return;
+  
+  en->next_lsa_body = NULL;
+  en->next_lsa_blen = 0;
+  en->next_lsa_opts = 0;
+
+  // XXXX:  schedule_rtcalc(p);
+}
+
+static void
+ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en)
+{
+  /*
+   * Called by ospf_update_lsadb() for periodic LSA refresh.
+   *
+   * We know that lsa.age < LSA_MAXAGE and lsa.rt is our router ID. We can also
+   * assume that there is no scheduled LSA, because inst_time is deep in past,
+   * therefore ospf_originate_next_lsa() called before would either succeed or
+   * switched lsa.age to LSA_MAXAGE.
+   */
+
+  OSPF_TRACE(D_EVENTS, "Refreshing LSA: Type: %04x, Id: %R, Rt: %R",
+            en->lsa_type, en->lsa.id, en->lsa.rt);
+
+  ASSERT(en->next_lsa_body == NULL);
+
+  /* Handle wrapping sequence number */
+  if (en->lsa.sn == LSA_MAXSEQNO)
+  {
+    /* Copy LSA body as next LSA to get automatic origination after flush is finished */
+    en->next_lsa_blen = en->lsa.length - sizeof(struct ospf_lsa_header);
+    en->next_lsa_body = mb_alloc(p->p.pool, en->next_lsa_blen);
+    memcpy(en->next_lsa_body, en->lsa_body, en->next_lsa_blen);
+    en->next_lsa_opts = ospf_is_v2(p) ? lsa_get_options(&en->lsa) : 0;
+
+    en->lsa.age = LSA_MAXAGE;
+    ospf_lsupd_flood(p, en, NULL);
+    return;
+  }
+
+  en->lsa.sn++;
+  en->lsa.age = 0;
+  en->init_age = 0;
+  en->inst_time = now;
+  lsasum_calculate(&en->lsa, en->lsa_body);
+  ospf_lsupd_flood(p, en, NULL);
+}
+
+/**
+ * ospf_flush_lsa - flush LSA from OSPF domain
+ * @p: OSPF protocol instance
+ * @en: LSA entry to flush
+ *
+ * This function flushes @en from the OSPF domain by setting its age to
+ * %LSA_MAXAGE and flooding it. That also triggers subsequent events in LSA
+ * lifecycle leading to removal of the LSA from the LSA database (e.g. the LSA
+ * content is freed when flushing is acknowledged by neighbors). The function
+ * does nothing if the LSA is already being flushed. LSA entries are not
+ * immediately removed when being flushed, the caller may assume that @en still
+ * exists after the call. The function is the opposite of ospf_originate_lsa()
+ * and is supposed to do the right thing even in cases of postponed
+ * origination. Note that this function do not schedule routing table
+ * calculation, the caller is responsible to do it if necessary.
+ */
+void
+ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en)
+{
+  OSPF_TRACE(D_EVENTS, "Flushing LSA: Type: %04x, Id: %R, Rt: %R",
+            en->lsa_type, en->lsa.id, en->lsa.rt);
+
+  en->rtcalc = 0;
+
+  if (en->next_lsa_body)
+  {
+    mb_free(en->next_lsa_body);
+    en->next_lsa_body = NULL;
+    en->next_lsa_blen = 0;
+    en->next_lsa_opts = 0;
+  }
+
+  if (en->lsa.age == LSA_MAXAGE)
+    return;
+
+  en->lsa.age = LSA_MAXAGE;
+  ospf_lsupd_flood(p, en, NULL);
+}
+
+static void
+ospf_clear_lsa(struct ospf_proto *p, struct top_hash_entry *en)
+{
+  /*
+   * Called by ospf_update_lsadb() as part of LSA flushing process.
+   * Flushed LSA was acknowledged by neighbors and we can free its content.
+   */
+
+  if (en->lsa.sn == LSA_MAXSEQNO)
+    en->lsa.sn = LSA_ZEROSEQNO;
+
+  mb_free(en->lsa_body);
+  en->lsa_body = NULL;
+}
+
+static void
+ospf_remove_lsa(struct ospf_proto *p, struct top_hash_entry *en)
+{
+  /*
+   * Called by ospf_update_lsadb() as part of LSA flushing process.
+   * Both lsa_body and next_lsa_body are NULL.
+   */
+
+  s_rem_node(SNODE en);
+  ospf_hash_delete(p->gr, en);
+}
+
+/**
+ * ospf_update_lsadb - update LSA database
+ * @p: OSPF protocol instance
+ *
+ * This function is periodicaly invoked from ospf_disp(). It does some periodic
+ * or postponed processing related to LSA entries. It originates postponed LSAs
+ * scheduled by ospf_originate_lsa(), It continues in flushing processes started
+ * by ospf_flush_lsa(). It also periodically refreshs locally originated LSAs --
+ * when the current instance is older %LSREFRESHTIME, a new instance is originated.
+ * Finally, it also ages stored LSAs and flushes ones that reached %LSA_MAXAGE.
+ *
+ * The RFC 2328 says that a router should periodically check checksums of all
+ * stored LSAs to detect hardware problems. This is not implemented.
+ */
+void
+ospf_update_lsadb(struct ospf_proto *p)
+{
+  struct top_hash_entry *en, *nxt;
+  bird_clock_t real_age;
+
+  WALK_SLIST_DELSAFE(en, nxt, p->lsal)
+  {
+    if (en->next_lsa_body)
+      ospf_originate_next_lsa(p, en);
+
+    real_age = en->init_age + (now - en->inst_time);
+
+    if (en->lsa.age == LSA_MAXAGE)
+    {
+      if (en->lsa_body && (p->padj == 0) && (en->ret_count == 0))
+       ospf_clear_lsa(p, en);
+
+      if ((en->lsa_body == NULL) && (en->next_lsa_body == NULL) &&
+         ((en->lsa.rt != p->router_id) || (real_age >= LSA_MAXAGE)))
+       ospf_remove_lsa(p, en);
+
+      continue;
+    }
+
+    if ((en->lsa.rt == p->router_id) && (real_age >= LSREFRESHTIME))
+    {
+      ospf_refresh_lsa(p, en);
+      continue;
+    }
+
+    if (real_age >= LSA_MAXAGE)
     {
-      po->lsab_size = MAX(po->lsab_used, 2 * po->lsab_size);
-      po->lsab = po->lsab ? mb_realloc(po->lsab, po->lsab_size):
-       mb_alloc(po->proto.pool, po->lsab_size);
+      ospf_flush_lsa(p, en);
+      schedule_rtcalc(p);
+      continue;
     }
-  return ((byte *) po->lsab) + offset;
+
+    en->lsa.age = real_age;
+  }
+}
+
+
+static inline u32
+ort_to_lsaid(struct ospf_proto *p, ort *nf)
+{
+  /*
+   * In OSPFv2, We have to map IP prefixes to u32 in such manner that resulting
+   * u32 interpreted as IP address is a member of given prefix. Therefore, /32
+   * prefix have to be mapped on itself.  All received prefixes have to be
+   * mapped on different u32s.
+   *
+   * We have an assumption that if there is nontrivial (non-/32) network prefix,
+   * then there is not /32 prefix for the first and the last IP address of the
+   * network (these are usually reserved, therefore it is not an important
+   * restriction).  The network prefix is mapped to the first or the last IP
+   * address in the manner that disallow collisions - we use the IP address that
+   * cannot be used by the parent prefix.
+   *
+   * For example:
+   * 192.168.0.0/24 maps to 192.168.0.255
+   * 192.168.1.0/24 maps to 192.168.1.0
+   * because 192.168.0.0 and 192.168.1.255 might be used by 192.168.0.0/23 .
+   *
+   * Appendig E of RFC 2328 suggests different algorithm, that tries to maximize
+   * both compatibility and subnetting. But as it is not possible to have both
+   * reliably and the suggested algorithm was unnecessary complicated and it
+   * does crazy things like changing LSA ID for a network because different
+   * network appeared, we choose a different way.
+   *
+   * In OSPFv3, it is simpler. There is not a requirement for membership of the
+   * result in the input network, so we just use a hash-based unique ID of a
+   * routing table entry for a route that originated given LSA. For ext-LSA, it
+   * is an imported route in the nest's routing table (p->table). For summary-LSA,
+   * it is a 'source' route in the protocol internal routing table (p->rtf).
+   */
+
+  if (ospf_is_v3(p))
+    return nf->fn.uid;
+
+  u32 id = ipa_to_u32(nf->fn.prefix);
+  int pxlen = nf->fn.pxlen;
+
+  if ((pxlen == 0) || (pxlen == 32))
+    return id;
+
+  if (id & (1 << (32 - pxlen)))
+    return id;
+  else
+    return id | ~u32_mkmask(pxlen);
+}
+
+
+static void *
+lsab_alloc(struct ospf_proto *p, unsigned size)
+{
+  unsigned offset = p->lsab_used;
+  p->lsab_used += size;
+  if (p->lsab_used > p->lsab_size)
+  {
+    p->lsab_size = MAX(p->lsab_used, 2 * p->lsab_size);
+    p->lsab = p->lsab ? mb_realloc(p->lsab, p->lsab_size):
+      mb_alloc(p->p.pool, p->lsab_size);
+  }
+  return ((byte *) p->lsab) + offset;
 }
 
 static inline void *
-lsab_allocz(struct proto_ospf *po, unsigned size)
+lsab_allocz(struct ospf_proto *p, unsigned size)
 {
-  void *r = lsab_alloc(po, size);
+  void *r = lsab_alloc(p, size);
   bzero(r, size);
   return r;
 }
 
 static inline void *
-lsab_flush(struct proto_ospf *po)
+lsab_flush(struct ospf_proto *p)
 {
-  void *r = mb_alloc(po->proto.pool, po->lsab_used);
-  memcpy(r, po->lsab, po->lsab_used);
-  po->lsab_used = 0;
+  void *r = mb_alloc(p->p.pool, p->lsab_used);
+  memcpy(r, p->lsab, p->lsab_used);
+  p->lsab_used = 0;
   return r;
 }
 
-static inline void *
-lsab_offset(struct proto_ospf *po, unsigned offset)
+static inline void
+lsab_reset(struct ospf_proto *p)
 {
-  return ((byte *) po->lsab) + offset;
+  p->lsab_used = 0;
 }
 
 static inline void *
-lsab_end(struct proto_ospf *po)
+lsab_offset(struct ospf_proto *p, unsigned offset)
 {
-  return ((byte *) po->lsab) + po->lsab_used;
+  return ((byte *) p->lsab) + offset;
 }
 
-static s32
-get_seqnum(struct top_hash_entry *en)
+static inline void *
+lsab_end(struct ospf_proto *p)
 {
-  if (!en)
-    return LSA_INITSEQNO;
-
-  if (en->lsa.sn == LSA_MAXSEQNO)
-  {
-    log(L_WARN "OSPF: Premature origination of LSA (Type: %04x, Id: %R, Rt: %R)",
-       en->lsa.type, en->lsa.id, en->lsa.rt);
-    return LSA_INITSEQNO;
-  }
-
-  return en->lsa.sn + 1;
+  return ((byte *) p->lsab) + p->lsab_used;
 }
 
 
+/*
+ *     Router-LSA handling
+ *     Type = LSA_T_RT
+ */
+
 static int
 configured_stubnet(struct ospf_area *oa, struct ifa *a)
 {
-  if (!oa->ac)
-    return 0;
-
   /* Does not work for IA_PEER addresses, but it is not called on these */
   struct ospf_stubnet_config *sn;
   WALK_LIST(sn, oa->ac->stubnet_list)
+  {
+    if (sn->summary)
     {
-      if (sn->summary)
-       {
-         if (ipa_in_net(a->prefix, sn->px.addr, sn->px.len) && (a->pxlen >= sn->px.len))
-           return 1;
-       }
-      else
-       {
-         if (ipa_equal(a->prefix, sn->px.addr) && (a->pxlen == sn->px.len))
-           return 1;
-       }
+      if (ipa_in_net(a->prefix, sn->px.addr, sn->px.len) && (a->pxlen >= sn->px.len))
+       return 1;
     }
+    else
+    {
+      if (ipa_equal(a->prefix, sn->px.addr) && (a->pxlen == sn->px.len))
+       return 1;
+    }
+  }
+
   return 0;
 }
 
-int
+static int
 bcast_net_active(struct ospf_iface *ifa)
 {
   struct ospf_neighbor *neigh;
@@ -188,53 +595,66 @@ bcast_net_active(struct ospf_iface *ifa)
     return 0;
 
   WALK_LIST(neigh, ifa->neigh_list)
+  {
+    if (neigh->state == NEIGHBOR_FULL)
     {
-      if (neigh->state == NEIGHBOR_FULL)
-       {
-         if (neigh->rid == ifa->drid)
-           return 1;
+      if (neigh->rid == ifa->drid)
+       return 1;
 
-         if (ifa->state == OSPF_IS_DR)
-           return 1;
-       }
+      if (ifa->state == OSPF_IS_DR)
+       return 1;
     }
+  }
 
   return 0;
 }
 
+static inline u32
+get_rt_options(struct ospf_proto *p, struct ospf_area *oa, int bitv)
+{
+  u32 opts = 0;
 
-#ifdef OSPFv2
+  if (p->areano > 1)
+    opts |= OPT_RT_B;
 
-static void *
-originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
-{
-  struct proto_ospf *po = oa->po;
-  struct ospf_iface *ifa;
-  int i = 0, bitv = 0;
-  struct ospf_lsa_rt *rt;
-  struct ospf_lsa_rt_link *ln;
-  struct ospf_neighbor *neigh;
+  if ((p->areano > 1) && oa_is_nssa(oa) && oa->ac->translator)
+    opts |= OPT_RT_NT;
 
-  ASSERT(po->lsab_used == 0);
-  rt = lsab_allocz(po, sizeof(struct ospf_lsa_rt));
+  if (p->asbr && !oa_is_stub(oa))
+    opts |= OPT_RT_E;
 
-  rt->options = 0;
+  if (bitv)
+    opts |= OPT_RT_V;
 
-  if (po->areano > 1)
-    rt->options |= OPT_RT_B;
+  return opts;
+}
 
-  if ((po->areano > 1) && oa_is_nssa(oa) && oa->ac->translator)
-    rt->options |= OPT_RT_NT;
+static inline void
+add_rt2_lsa_link(struct ospf_proto *p, u8 type, u32 id, u32 data, u16 metric)
+{
+  struct ospf_lsa_rt2_link *ln = lsab_alloc(p, sizeof(struct ospf_lsa_rt2_link));
+  ln->type = type;
+  ln->id = id;
+  ln->data = data;
+  ln->metric = metric;
+  ln->no_tos = 0;
+}
 
-  if (po->ebit && !oa_is_stub(oa))
-    rt->options |= OPT_RT_E;
+static void
+prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
+{
+  struct ospf_iface *ifa;
+  int i = 0, bitv = 0;
+  struct ospf_neighbor *neigh;
 
-  rt = NULL; /* buffer might be reallocated later */
+  ASSERT(p->lsab_used == 0);
+  lsab_allocz(p, sizeof(struct ospf_lsa_rt));
+  /* ospf_lsa_rt header will be filled later */
 
-  WALK_LIST(ifa, po->iface_list)
+  WALK_LIST(ifa, p->iface_list)
   {
     int net_lsa = 0;
-    u32 link_cost = po->stub_router ? 0xffff : ifa->cost;
+    u32 link_cost = p->stub_router ? 0xffff : ifa->cost;
 
     if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) &&
        (!EMPTY_LIST(ifa->neigh_list)))
@@ -249,66 +669,47 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
 
     ifa->rt_pos_beg = i;
 
-    /* RFC2328 - 12.4.1.1-4 */
+    /* RFC 2328 - 12.4.1.1-4 */
     switch (ifa->type)
-      {
-      case OSPF_IT_PTP:
-      case OSPF_IT_PTMP:
-       WALK_LIST(neigh, ifa->neigh_list)
-         if (neigh->state == NEIGHBOR_FULL)
-         {
-           ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
-           ln->type = LSART_PTP;
-           ln->id = neigh->rid;
-
-           /*
-            * ln->data should be ifa->iface_id in case of no/ptp
-            * address (ifa->addr->flags & IA_PEER) on PTP link (see
-            * RFC 2328 12.4.1.1.), but the iface ID value has no use,
-            * while using IP address even in this case is here for
-            * compatibility with some broken implementations that use
-            * this address as a next-hop.
-            */
-           ln->data = ipa_to_u32(ifa->addr->ip);
-           ln->metric = link_cost;
-           ln->padding = 0;
-           i++;
-         }
-       break;
-
-      case OSPF_IT_BCAST:
-      case OSPF_IT_NBMA:
-       if (bcast_net_active(ifa))
-         {
-           ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
-           ln->type = LSART_NET;
-           ln->id = ipa_to_u32(ifa->drip);
-           ln->data = ipa_to_u32(ifa->addr->ip);
-           ln->metric = link_cost;
-           ln->padding = 0;
-           i++;
-           net_lsa = 1;
-         }
-       break;
-
-      case OSPF_IT_VLINK:
-       neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
-       if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
+    {
+    case OSPF_IT_PTP:
+    case OSPF_IT_PTMP:
+      WALK_LIST(neigh, ifa->neigh_list)
+       if (neigh->state == NEIGHBOR_FULL)
        {
-         ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
-         ln->type = LSART_VLNK;
-         ln->id = neigh->rid;
-         ln->data = ipa_to_u32(ifa->addr->ip);
-         ln->metric = link_cost;
-         ln->padding = 0;
+         /*
+          * ln->data should be ifa->iface_id in case of no/ptp
+          * address (ifa->addr->flags & IA_PEER) on PTP link (see
+          * RFC 2328 12.4.1.1.), but the iface ID value has no use,
+          * while using IP address even in this case is here for
+          * compatibility with some broken implementations that use
+          * this address as a next-hop.
+          */
+         add_rt2_lsa_link(p, LSART_PTP, neigh->rid, ipa_to_u32(ifa->addr->ip), link_cost);
          i++;
-        }
-        break;
+       }
+      break;
 
-      default:
-        log("Unknown interface type %s", ifa->ifname);
-        break;
+    case OSPF_IT_BCAST:
+    case OSPF_IT_NBMA:
+      if (bcast_net_active(ifa))
+      {
+       add_rt2_lsa_link(p, LSART_NET, ipa_to_u32(ifa->drip), ipa_to_u32(ifa->addr->ip), link_cost);
+       i++;
+       net_lsa = 1;
       }
+      break;
+
+    case OSPF_IT_VLINK:
+      neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
+      if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
+       add_rt2_lsa_link(p, LSART_VLNK, neigh->rid, ipa_to_u32(ifa->addr->ip), link_cost), i++;
+      break;
+
+    default:
+      log("Unknown interface type %s", ifa->ifname);
+      break;
+    }
 
     ifa->rt_pos_end = i;
 
@@ -319,62 +720,32 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
        configured_stubnet(oa, ifa->addr))
       continue;
 
-    ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
+      /* Host or network stub entry */
     if ((ifa->addr->flags & IA_HOST) ||
        (ifa->state == OSPF_IS_LOOP) ||
        (ifa->type == OSPF_IT_PTMP))
-    {
-      /* Host stub entry */
-      ln->type = LSART_STUB;
-      ln->id = ipa_to_u32(ifa->addr->ip);
-      ln->data = 0xffffffff;
-      ln->metric = 0;
-      ln->padding = 0;
-    }
+      add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->ip), 0xffffffff, 0);
     else 
-    {
-      /* Network stub entry */
-      ln->type = LSART_STUB;
-      ln->id = ipa_to_u32(ifa->addr->prefix);
-      ln->data = ipa_to_u32(ipa_mkmask(ifa->addr->pxlen));
-      ln->metric = ifa->cost;
-      ln->padding = 0;
-    }
+      add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->prefix), u32_mkmask(ifa->addr->pxlen), ifa->cost);
     i++;
 
     ifa->rt_pos_end = i;
   }
 
   struct ospf_stubnet_config *sn;
-  if (oa->ac)
-    WALK_LIST(sn, oa->ac->stubnet_list)
-      if (!sn->hidden)
-      {
-       ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
-       ln->type = LSART_STUB;
-       ln->id = ipa_to_u32(sn->px.addr);
-       ln->data = ipa_to_u32(ipa_mkmask(sn->px.len));
-       ln->metric = sn->cost;
-       ln->padding = 0;
-       i++;
-      }
-
-  rt = po->lsab;
-  rt->links = i;
-
-  if (bitv) 
-    rt->options |= OPT_RT_V;
+  WALK_LIST(sn, oa->ac->stubnet_list)
+    if (!sn->hidden)
+      add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(sn->px.addr), u32_mkmask(sn->px.len), sn->cost), i++;
 
-  *length = po->lsab_used + sizeof(struct ospf_lsa_header);
-  return lsab_flush(po);
+  struct ospf_lsa_rt *rt = p->lsab;
+  /* Store number of links in lower half of options */ 
+  rt->options = get_rt_options(p, oa, bitv) | (u16) i;
 }
 
-#else /* OSPFv3 */
-
-static void
-add_lsa_rt_link(struct proto_ospf *po, struct ospf_iface *ifa, u8 type, u32 nif, u32 id)
+static inline void
+add_rt3_lsa_link(struct ospf_proto *p, u8 type, struct ospf_iface *ifa, u32 nif, u32 id)
 {
-  struct ospf_lsa_rt_link *ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
+  struct ospf_lsa_rt3_link *ln = lsab_alloc(p, sizeof(struct ospf_lsa_rt3_link));
   ln->type = type;
   ln->padding = 0;
   ln->metric = ifa->cost;
@@ -383,33 +754,19 @@ add_lsa_rt_link(struct proto_ospf *po, struct ospf_iface *ifa, u8 type, u32 nif,
   ln->id = id;
 }
 
-static void *
-originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
+static void
+prepare_rt3_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
 {
-  struct proto_ospf *po = oa->po;
   struct ospf_iface *ifa;
+  struct ospf_neighbor *neigh;
   int bitv = 0;
   int i = 0;
-  struct ospf_lsa_rt *rt;
-  struct ospf_neighbor *neigh;
-
-  ASSERT(po->lsab_used == 0);
-  rt = lsab_allocz(po, sizeof(struct ospf_lsa_rt));
-
-  rt->options = oa->options & OPTIONS_MASK;
-
-  if (po->areano > 1)
-    rt->options |= OPT_RT_B;
-
-  if ((po->areano > 1) && oa_is_nssa(oa) && oa->ac->translator)
-    rt->options |= OPT_RT_NT;
 
-  if (po->ebit && !oa_is_stub(oa))
-    rt->options |= OPT_RT_E;
+  ASSERT(p->lsab_used == 0);
+  lsab_allocz(p, sizeof(struct ospf_lsa_rt));
+  /* ospf_lsa_rt header will be filled later */
 
-  rt = NULL; /* buffer might be reallocated later */
-
-  WALK_LIST(ifa, po->iface_list)
+  WALK_LIST(ifa, p->iface_list)
   {
     if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) &&
        (!EMPTY_LIST(ifa->neigh_list)))
@@ -424,50 +781,42 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
 
     ifa->rt_pos_beg = i;
 
-    /* RFC5340 - 4.4.3.2 */
+    /* RFC 5340 - 4.4.3.2 */
     switch (ifa->type)
-      {
-      case OSPF_IT_PTP:
-      case OSPF_IT_PTMP:
-       WALK_LIST(neigh, ifa->neigh_list)
-         if (neigh->state == NEIGHBOR_FULL)
-           add_lsa_rt_link(po, ifa, LSART_PTP, neigh->iface_id, neigh->rid), i++;
-       break;
-
-      case OSPF_IT_BCAST:
-      case OSPF_IT_NBMA:
-       if (bcast_net_active(ifa))
-         add_lsa_rt_link(po, ifa, LSART_NET, ifa->dr_iface_id, ifa->drid), i++;
-       break;
-
-      case OSPF_IT_VLINK:
-       neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
-       if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
-         add_lsa_rt_link(po, ifa, LSART_VLNK, neigh->iface_id, neigh->rid), i++;
-        break;
-
-      default:
-        log("Unknown interface type %s", ifa->ifname);
-        break;
-      }
+    {
+    case OSPF_IT_PTP:
+    case OSPF_IT_PTMP:
+      WALK_LIST(neigh, ifa->neigh_list)
+       if (neigh->state == NEIGHBOR_FULL)
+         add_rt3_lsa_link(p, LSART_PTP, ifa, neigh->iface_id, neigh->rid), i++;
+      break;
 
-    ifa->rt_pos_end = i;
-  }
+    case OSPF_IT_BCAST:
+    case OSPF_IT_NBMA:
+      if (bcast_net_active(ifa))
+       add_rt3_lsa_link(p, LSART_NET, ifa, ifa->dr_iface_id, ifa->drid), i++;
+      break;
 
-  if (bitv)
-    {
-      rt = po->lsab;
-      rt->options |= OPT_RT_V;
+    case OSPF_IT_VLINK:
+      neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
+      if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
+       add_rt3_lsa_link(p, LSART_VLNK, ifa, neigh->iface_id, neigh->rid), i++;
+      break;
+
+    default:
+      log("Unknown interface type %s", ifa->ifname);
+      break;
     }
 
-  *length = po->lsab_used + sizeof(struct ospf_lsa_header);
-  return lsab_flush(po);
-}
+    ifa->rt_pos_end = i;
+  }
 
-#endif
+  struct ospf_lsa_rt *rt = p->lsab;
+  rt->options = get_rt_options(p, oa, bitv) | (oa->options & LSA_OPTIONS_MASK);
+}
 
 /**
- * originate_rt_lsa - build new instance of router LSA
+ * ospf_originate_rt_lsa - build new instance of router LSA
  * @oa: ospf_area which is LSA built to
  *
  * It builds router LSA walking through all OSPF interfaces in
@@ -475,91 +824,80 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
  * area_disp(). Builds new LSA, increases sequence number (if old
  * instance exists) and sets age of LSA to zero.
  */
-void
-originate_rt_lsa(struct ospf_area *oa)
+static void
+ospf_originate_rt_lsa(struct ospf_proto *p, struct ospf_area *oa)
 {
-  struct ospf_lsa_header lsa;
-  struct proto_ospf *po = oa->po;
-  struct proto *p = &po->proto;
-  void *body;
+  struct ospf_new_lsa lsa = {
+    .type = LSA_T_RT,
+    .dom  = oa->areaid,
+    .id   = ospf_is_v2(p) ? p->router_id : 0,
+    .opts = oa->options
+  };
 
-  OSPF_TRACE(D_EVENTS, "Originating router-LSA for area %R", oa->areaid);
+  if (ospf_is_v2(p))
+    prepare_rt2_lsa_body(p, oa);
+  else
+    prepare_rt3_lsa_body(p, oa);
 
-  lsa.age = 0;
-  lsa.type = LSA_T_RT;
-  
-#ifdef OSPFv2
-  lsa.options = oa->options;
-  lsa.id = po->router_id;
-#else /* OSPFv3 */
-  lsa.id = 0;
-#endif
+  oa->rt = ospf_originate_lsa(p, &lsa);
+}
 
-  lsa.rt = po->router_id;
-  lsa.sn = get_seqnum(oa->rt);
-  u32 dom = oa->areaid;
 
-  body = originate_rt_lsa_body(oa, &lsa.length);
-  lsasum_calculate(&lsa, body);
-  oa->rt = lsa_install_new(po, &lsa, dom, body);
-  ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
-}
+/*
+ *     Net-LSA handling
+ *     Type = LSA_T_NET
+ */
 
-void
-update_rt_lsa(struct ospf_area *oa)
+static void
+prepare_net2_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
 {
-  struct proto_ospf *po = oa->po;
+  struct ospf_lsa_net *net;
+  struct ospf_neighbor *n;
+  int nodes = ifa->fadj + 1;
+  u16 i = 1;
 
-  if ((oa->rt) && ((oa->rt->inst_t + MINLSINTERVAL)) > now)
-    return;
-  /*
-   * Tick is probably set to very low value. We cannot
-   * originate new LSA before MINLSINTERVAL. We will
-   * try to do it next tick.
-   */
+  ASSERT(p->lsab_used == 0);
+  net = lsab_alloc(p, sizeof(struct ospf_lsa_net) + 4 * nodes);
 
-  originate_rt_lsa(oa);
-#ifdef OSPFv3
-  originate_prefix_rt_lsa(oa);
-#endif
+  net->optx = u32_mkmask(ifa->addr->pxlen);
+  net->routers[0] = p->router_id;
 
-  schedule_rtcalc(po);
-  oa->origrt = 0;
+  WALK_LIST(n, ifa->neigh_list)
+  {
+    if (n->state == NEIGHBOR_FULL)
+    {
+      net->routers[i] = n->rid;
+      i++;
+    }
+  }
+  ASSERT(i == nodes);
 }
 
-static void *
-originate_net_lsa_body(struct ospf_iface *ifa, u16 *length,
-                      struct proto_ospf *po)
+static void
+prepare_net3_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
 {
-  u16 i = 1;
-  struct ospf_neighbor *n;
   struct ospf_lsa_net *net;
   int nodes = ifa->fadj + 1;
-
-  net = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_net)
-                + nodes * sizeof(u32));
-
-#ifdef OSPFv2
-  net->netmask = ipa_mkmask(ifa->addr->pxlen);
-#endif
-
-#ifdef OSPFv3
-  /* In OSPFv3, we would like to merge options from Link LSAs of added neighbors */
-  struct top_hash_entry *en;
   u32 options = 0;
-#endif
+  u16 i = 1;
+
+  ASSERT(p->lsab_used == 0);
+  net = lsab_alloc(p, sizeof(struct ospf_lsa_net) + 4 * nodes);
 
-  net->routers[0] = po->router_id;
+  net->routers[0] = p->router_id;
 
+  struct ospf_neighbor *n;
   WALK_LIST(n, ifa->neigh_list)
   {
     if (n->state == NEIGHBOR_FULL)
     {
-#ifdef OSPFv3
-      en = ospf_hash_find(po->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK);
+      /* In OSPFv3, we would like to merge options from Link LSAs of added neighbors */
+
+      struct top_hash_entry *en =
+       ospf_hash_find(p->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK);
+
       if (en)
        options |= ((struct ospf_lsa_link *) en->lsa_body)->options;
-#endif
 
       net->routers[i] = n->rid;
       i++;
@@ -567,18 +905,11 @@ originate_net_lsa_body(struct ospf_iface *ifa, u16 *length,
   }
   ASSERT(i == nodes);
 
-#ifdef OSPFv3
-  net->options = options & OPTIONS_MASK;
-#endif
-  
-  *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_net)
-    + nodes * sizeof(u32);
-  return net;
+  net->optx = options & LSA_OPTIONS_MASK;
 }
 
-
 /**
- * originate_net_lsa - originates of deletes network LSA
+ * ospf_originate_net_lsa - originates of deletes network LSA
  * @ifa: interface which is LSA originated for
  *
  * Interface counts number of adjacent neighbors. If this number is
@@ -586,692 +917,438 @@ originate_net_lsa_body(struct ospf_iface *ifa, u16 *length,
  * and premature ages instance of network LSA for specified interface.
  * In other case, new instance of network LSA is originated.
  */
-void
-originate_net_lsa(struct ospf_iface *ifa)
-{
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
-  struct ospf_lsa_header lsa;
-  u32 dom = ifa->oa->areaid;
-  
-  void *body;
-
-  OSPF_TRACE(D_EVENTS, "Originating network-LSA for iface %s", ifa->ifname);
-
-  lsa.age = 0;
-  lsa.type = LSA_T_NET;
-
-#ifdef OSPFv2
-  lsa.options = ifa->oa->options;
-  lsa.id = ipa_to_u32(ifa->addr->ip);
-#else /* OSPFv3 */
-  lsa.id = ifa->iface_id;
-#endif
-
-  lsa.rt = po->router_id;
-  lsa.sn = get_seqnum(ifa->net_lsa);
-
-  body = originate_net_lsa_body(ifa, &lsa.length, po);
-  lsasum_calculate(&lsa, body);
-  ifa->net_lsa = lsa_install_new(po, &lsa, dom, body);
-  ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
-}
-
-void
-flush_net_lsa(struct ospf_iface *ifa)
-{
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
-  u32 dom = ifa->oa->areaid;
-
-  if (ifa->net_lsa == NULL)
-    return;
-
-  OSPF_TRACE(D_EVENTS, "Flushing network-LSA for iface %s", ifa->ifname);
-  ifa->net_lsa->lsa.sn += 1;
-  ifa->net_lsa->lsa.age = LSA_MAXAGE;
-  lsasum_calculate(&ifa->net_lsa->lsa, ifa->net_lsa->lsa_body);
-  ospf_lsupd_flood(po, NULL, NULL, &ifa->net_lsa->lsa, dom, 0);
-  flush_lsa(ifa->net_lsa, po);
-  ifa->net_lsa = NULL;
-}
-
-void
-update_net_lsa(struct ospf_iface *ifa)
-{
-  struct proto_ospf *po = ifa->oa->po;
-  if (ifa->net_lsa && ((ifa->net_lsa->inst_t + MINLSINTERVAL) > now))
-    return;
-  /*
-   * It's too early to originate new network LSA. We will
-   * try to do it next tick
-   */
-
-  if ((ifa->state != OSPF_IS_DR) || (ifa->fadj == 0))
-    {
-      flush_net_lsa(ifa);
-#ifdef OSPFv3
-      flush_prefix_net_lsa(ifa);
-#endif
-    }
+static void
+ospf_originate_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
+{
+  struct ospf_new_lsa lsa = {
+    .type = LSA_T_NET,
+    .dom  = ifa->oa->areaid,
+    .id   = ospf_is_v2(p) ? ipa_to_u32(ifa->addr->ip) : ifa->iface_id,
+    .opts = ifa->oa->options,
+    .ifa  = ifa
+  };
+
+  if (ospf_is_v2(p))
+    prepare_net2_lsa_body(p, ifa);
   else
-    {
-      originate_net_lsa(ifa);
-#ifdef OSPFv3
-      originate_prefix_net_lsa(ifa);
-#endif
-    }
-
-  schedule_rtcalc(po);
-  ifa->orignet = 0;
-}
-
-#ifdef OSPFv2
-
-static inline void *
-originate_sum_lsa_body(struct proto_ospf *po, u16 *length, u32 mlen, u32 metric)
-{
-  struct ospf_lsa_sum *sum = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_sum));
-  *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_sum);
-
-  sum->netmask = ipa_mkmask(mlen);
-  sum->metric = metric;
-
-  return sum;
-}
-
-#define originate_sum_net_lsa_body(po,length,fn,metric) \
-  originate_sum_lsa_body(po, length, (fn)->pxlen, metric)
-
-#define originate_sum_rt_lsa_body(po,length,drid,metric,options) \
-  originate_sum_lsa_body(po, length, 0, metric)
-
-static inline int
-check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en)
-{
-  struct ospf_lsa_sum *sum = en->lsa_body;
-  return fn->pxlen != ipa_mklen(sum->netmask);
-}
+    prepare_net3_lsa_body(p, ifa);
 
-static inline int
-check_sum_lsa_same(struct top_hash_entry *en, u32 metric)
-{
-  /* Netmask already checked in check_sum_net_lsaid_collision() */
-  struct ospf_lsa_sum *sum = en->lsa_body;
-  return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric);
+  ifa->net_lsa = ospf_originate_lsa(p, &lsa);
 }
 
-#define check_sum_net_lsa_same(en,metric) \
-  check_sum_lsa_same(en, metric)
-
-#define check_sum_rt_lsa_same(en,drid,metric,options) \
-  check_sum_lsa_same(en, metric)
-
 
-#else /* OSPFv3 */
+/*
+ *     (Net|Rt)-summary-LSA handling
+ *     (a.k.a. Inter-Area-(Prefix|Router)-LSA)
+ *     Type = LSA_T_SUM_NET, LSA_T_SUM_RT
+ */
 
-static inline void *
-originate_sum_net_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn, u32 metric)
+static inline void
+prepare_sum2_lsa_body(struct ospf_proto *p, uint pxlen, u32 metric)
 {
-  int size = sizeof(struct ospf_lsa_sum_net) + IPV6_PREFIX_SPACE(fn->pxlen);
-  struct ospf_lsa_sum_net *sum = mb_alloc(po->proto.pool, size);
-  *length = sizeof(struct ospf_lsa_header) + size;
+  struct ospf_lsa_sum2 *sum;
 
+  sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum2));
+  sum->netmask = u32_mkmask(pxlen);
   sum->metric = metric;
-  put_ipv6_prefix(sum->prefix, fn->prefix, fn->pxlen, 0, 0);
-
-  return sum;
 }
 
-static inline int
-check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en)
+static inline void
+prepare_sum3_net_lsa_body(struct ospf_proto *p, ort *nf, u32 metric)
 {
-  struct ospf_lsa_sum_net *sum = en->lsa_body;
-  ip_addr prefix;
-  int pxlen;
-  u8 pxopts;
-  u16 rest;
-
-  lsa_get_ipv6_prefix(sum->prefix, &prefix, &pxlen, &pxopts, &rest);
-  return (fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix);
-}
+  struct ospf_lsa_sum3_net *sum;
 
-static inline int
-check_sum_net_lsa_same(struct top_hash_entry *en, u32 metric)
-{
-  /* Prefix already checked in check_sum_net_lsaid_collision() */
-  struct ospf_lsa_sum_net *sum = en->lsa_body;
-  return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric);
+  sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_net) + IPV6_PREFIX_SPACE(nf->fn.pxlen));
+  sum->metric = metric;
+  put_ipv6_prefix(sum->prefix, nf->fn.prefix, nf->fn.pxlen, 0, 0);
 }
 
-static inline void *
-originate_sum_rt_lsa_body(struct proto_ospf *po, u16 *length, u32 drid, u32 metric, u32 options)
+static inline void
+prepare_sum3_rt_lsa_body(struct ospf_proto *p, u32 drid, u32 metric, u32 options)
 {
-  struct ospf_lsa_sum_rt *sum = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_sum_rt));
-  *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_sum_rt);
+  struct ospf_lsa_sum3_rt *sum;
 
+  sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_rt));
   sum->options = options;
   sum->metric = metric;
   sum->drid = drid;
-
-  return sum;
-}
-
-static inline int
-check_sum_rt_lsa_same(struct top_hash_entry *en, u32 drid, u32 metric, u32 options)
-{
-  struct ospf_lsa_sum_rt *sum = en->lsa_body;
-  return (en->lsa.sn != LSA_MAXSEQNO) && (sum->options == options) &&
-    (sum->metric == metric) && (sum->drid == drid);
 }
 
-#endif
-
 void
-originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric)
+ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric)
 {
-  struct proto_ospf *po = oa->po;
-  struct proto *p = &po->proto;
   struct top_hash_entry *en;
-  u32 dom = oa->areaid;
-  struct ospf_lsa_header lsa;
-  void *body;
-
-  OSPF_TRACE(D_EVENTS, "Originating net-summary-LSA for %I/%d (metric %d)",
-            fn->prefix, fn->pxlen, metric);
-
-  /* options argument is used in ORT_NET and OSPFv3 only */
-  lsa.age = 0;
-#ifdef OSPFv2
-  lsa.options = oa->options;
-#endif
-  lsa.type = LSA_T_SUM_NET;
-  lsa.id = fibnode_to_lsaid(po, fn);
-  lsa.rt = po->router_id;
 
-  if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL)
-  {
-    if (check_sum_net_lsaid_collision(fn, en))
-    {
-      log(L_ERR "%s: LSAID collision for %I/%d",
-         p->name, fn->prefix, fn->pxlen);
-      return;
-    }
+  struct ospf_new_lsa lsa = {
+    .type = LSA_T_SUM_NET,
+    .dom  = oa->areaid,
+    .id   = ort_to_lsaid(p, nf),
+    .opts = oa->options,
+    .nf   = nf
+  };
 
-    if (check_sum_net_lsa_same(en, metric))
-      return;
-  }
-  lsa.sn = get_seqnum(en);
+  if (ospf_is_v2(p))
+    prepare_sum2_lsa_body(p, nf->fn.pxlen, metric);
+  else
+    prepare_sum3_net_lsa_body(p, nf, metric);
 
-  body = originate_sum_net_lsa_body(po, &lsa.length, fn, metric);
-  lsasum_calculate(&lsa, body);
-  lsa_install_new(po, &lsa, dom, body);
-  ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+  en = ospf_originate_lsa(p, &lsa);
+  en->rtcalc = LSA_RTCALC;
 }
 
 void
-originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options UNUSED)
+ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options)
 {
-  struct proto_ospf *po = oa->po;
-  struct proto *p = &po->proto;
   struct top_hash_entry *en;
-  u32 dom = oa->areaid;
-  u32 rid = ipa_to_rid(fn->prefix);
-  struct ospf_lsa_header lsa;
-  void *body;
+  u32 rid = ipa_to_rid(nf->fn.prefix);
 
-  OSPF_TRACE(D_EVENTS, "Originating rt-summary-LSA for %R (metric %d)",
-            rid, metric);
-
-  lsa.age = 0;
-#ifdef OSPFv2
-  lsa.options = oa->options;
-#endif
-  lsa.type = LSA_T_SUM_RT;
   /* In OSPFv3, LSA ID is meaningless, but we still use Router ID of ASBR */
-  lsa.id = rid;
-  lsa.rt = po->router_id;
-
-  options &= OPTIONS_MASK;
-  if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL)
-  {
-    if (check_sum_rt_lsa_same(en, lsa.id, metric, options))
-      return;
-  }
-  lsa.sn = get_seqnum(en);
-
-  body = originate_sum_rt_lsa_body(po, &lsa.length, lsa.id, metric, options);
-  lsasum_calculate(&lsa, body);
-  lsa_install_new(po, &lsa, dom, body);
-  ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
-}
 
-void
-flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
-{
-  struct proto_ospf *po = oa->po;
-  struct proto *p = &po->proto;
-  struct top_hash_entry *en;
-  struct ospf_lsa_header lsa;
+  struct ospf_new_lsa lsa = {
+    .type = LSA_T_SUM_RT,
+    .dom  = oa->areaid,
+    .id   = rid,
+    .opts = oa->options
+  };
 
-  lsa.rt = po->router_id;
-  if (type == ORT_NET)
-    {
-      lsa.id = fibnode_to_lsaid(po, fn);
-      lsa.type = LSA_T_SUM_NET;
-    }
+  if (ospf_is_v2(p))
+    prepare_sum2_lsa_body(p, 0, metric);
   else
-    {
-      /* In OSPFv3, LSA ID is meaningless, but we still use Router ID of ASBR */
-      lsa.id = ipa_to_rid(fn->prefix);
-      lsa.type = LSA_T_SUM_RT;
-    }
-
-  if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) != NULL)
-    {
-      OSPF_TRACE(D_EVENTS, "Flushing summary-LSA (id=%R, type=%d)",
-                en->lsa.id, en->lsa.type);
-
-      if ((type == ORT_NET) && check_sum_net_lsaid_collision(fn, en))
-       {
-         log(L_ERR "%s: LSAID collision for %I/%d",
-             p->name, fn->prefix, fn->pxlen);
-         return;
-       }
+    prepare_sum3_rt_lsa_body(p, rid, metric, options & LSA_OPTIONS_MASK);
 
-      struct ospf_lsa_sum *sum = en->lsa_body;
-      en->lsa.age = LSA_MAXAGE;
-      en->lsa.sn = LSA_MAXSEQNO;
-      lsasum_calculate(&en->lsa, sum);
-      ospf_lsupd_flood(po, NULL, NULL, &en->lsa, oa->areaid, 1);
-      if (can_flush_lsa(po)) flush_lsa(en, po);
-    }
+  en = ospf_originate_lsa(p, &lsa);
+  en->rtcalc = LSA_RTCALC;
 }
 
-#ifdef OSPFv2
-
-static inline void *
-originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn,
-                      u32 metric, ip_addr fwaddr, u32 tag, int pbit UNUSED)
-{
-  struct ospf_lsa_ext *ext = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_ext));
-  *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_ext);
-
-  ext->metric = metric; 
-  ext->netmask = ipa_mkmask(fn->pxlen);
-  ext->fwaddr = fwaddr;
-  ext->tag = tag;
-
-  return ext;
-}
 
 /*
- * check_ext_lsa() combines functions of check_*_lsaid_collision() and
- * check_*_lsa_same(). 'en' is existing ext LSA, and rest parameters
- * are parameters of new ext route.  Function returns -1 if there is
- * LSAID collision, returns 1 if the existing LSA is the same and
- * returns 0 otherwise (in that case, we need to originate a new LSA).
- *
- * Really, checking for the same parameters is not as important as in
- * summary LSA origination, because in most cases the duplicate
- * external route propagation would be stopped by the nest. But there
- * are still some cases (route reload, the same route propagated through
- * different protocol) so it is also done here.
+ *     AS-external-LSA and NSSA-LSA handling
+ *     Type = LSA_T_EXT, LSA_T_NSSA
  */
 
-static inline int
-check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag)
+static inline void
+prepare_ext2_lsa_body(struct ospf_proto *p, uint pxlen,
+                     u32 metric, u32 ebit, ip_addr fwaddr, u32 tag)
 {
-  struct ospf_lsa_ext *ext = en->lsa_body;
+  struct ospf_lsa_ext2 *ext;
 
-  /* LSAID collision */
-  if  (fn->pxlen != ipa_mklen(ext->netmask))
-    return -1;
+  ext = lsab_allocz(p, sizeof(struct ospf_lsa_ext2));
+  ext->metric = metric & LSA_METRIC_MASK;
+  ext->netmask = u32_mkmask(pxlen);
+  ext->fwaddr = ipa_to_u32(fwaddr);
+  ext->tag = tag;
 
-  return (en->lsa.sn != LSA_MAXSEQNO) && (ext->metric == metric) &&
-    (ext->tag == tag) && ipa_equal(ext->fwaddr,fwaddr);
+  if (ebit)
+    ext->metric |= LSA_EXT2_EBIT;
 }
 
-#else /* OSPFv3 */
-
-static inline void *
-originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn,
-                      u32 metric, ip_addr fwaddr, u32 tag, int pbit)
+static inline void
+prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf,
+                     u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit)
 {
-  int size = sizeof(struct ospf_lsa_ext)
-    + IPV6_PREFIX_SPACE(fn->pxlen)
+  struct ospf_lsa_ext3 *ext;
+  int bsize = sizeof(struct ospf_lsa_ext3)
+    + IPV6_PREFIX_SPACE(nf->fn.pxlen)
     + (ipa_nonzero(fwaddr) ? 16 : 0)
     + (tag ? 4 : 0);
 
-  struct ospf_lsa_ext *ext = mb_alloc(po->proto.pool, size);
-  *length = sizeof(struct ospf_lsa_header) + size;
+  ext = lsab_allocz(p, bsize);
+  ext->metric = metric & LSA_METRIC_MASK;
+  u32 *buf = ext->rest;
 
-  ext->metric = metric;
+  buf = put_ipv6_prefix(buf, nf->fn.prefix, nf->fn.pxlen, pbit ? OPT_PX_P : 0, 0);
 
-  u32 *buf = ext->rest;
-  buf = put_ipv6_prefix(buf, fn->prefix, fn->pxlen, pbit ? OPT_PX_P : 0, 0);
+  if (ebit)
+    ext->metric |= LSA_EXT3_EBIT;
 
   if (ipa_nonzero(fwaddr))
   {
-    ext->metric |= LSA_EXT_FBIT;
+    ext->metric |= LSA_EXT3_FBIT;
     buf = put_ipv6_addr(buf, fwaddr);
   }
 
   if (tag)
   {
-    ext->metric |= LSA_EXT_TBIT;
+    ext->metric |= LSA_EXT3_TBIT;
     *buf++ = tag;
   }
-
-  return ext;
 }
 
-static inline int
-check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag)
+/**
+ * originate_ext_lsa - new route received from nest and filters
+ * @oa: ospf_area for which LSA is originated
+ * @nf: network prefix and mask
+ * @src: the source of origination of the LSA (EXT_EXPORT/EXT_NSSA)
+ * @metric: the metric of a route (possibly with appropriate E-bit)
+ * @fwaddr: the forwarding address
+ * @tag: the route tag
+ * @pbit: P-bit for NSSA LSAs, ignored for external LSAs
+ *
+ * If I receive a message that new route is installed, I try to originate an
+ * external LSA. If @oa is an NSSA area, NSSA-LSA is originated instead.
+ * @oa should not be a stub area. @src does not specify whether the LSA
+ * is external or NSSA, but it specifies the source of origination - 
+ * the export from ospf_rt_notify(), or the NSSA-EXT translation.
+ */
+void
+ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 rtcalc,
+                      u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit)
 {
-  struct ospf_lsa_ext *ext = en->lsa_body;
-  ip_addr prefix;
-  int pxlen;
-  u8 pxopts;
-  u16 rest;
+  struct top_hash_entry *en;
 
-  u32 *buf = lsa_get_ipv6_prefix(ext->rest, &prefix, &pxlen, &pxopts, &rest);
+  struct ospf_new_lsa lsa = {
+    .type = oa ? LSA_T_NSSA : LSA_T_EXT,
+    .dom  = oa ? oa->areaid : 0,
+    .id   = ort_to_lsaid(p, nf),
+    .opts = oa ? (pbit ? OPT_P : 0) : OPT_E,
+    .nf   = nf
+  };
 
-  /* LSAID collision */
-  if ((fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix))
-    return -1;
+  if (ospf_is_v2(p))
+    prepare_ext2_lsa_body(p, nf->fn.pxlen, metric, ebit, fwaddr, tag);
+  else
+    prepare_ext3_lsa_body(p, nf, metric, ebit, fwaddr, tag, oa && pbit);
 
-  if (en->lsa.sn == LSA_MAXSEQNO)
-    return 0;
+  en = ospf_originate_lsa(p, &lsa);
+  en->rtcalc = rtcalc;
+}
+
+static void
+ospf_flush_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf)
+{
+  struct top_hash_entry *en;
 
-  u32 rt_metric = ext->metric & METRIC_MASK;
-  ip_addr rt_fwaddr = IPA_NONE;
-  u32 rt_tag = 0;
+  u32 type = oa ? LSA_T_NSSA : LSA_T_EXT;
+  u32 dom = oa ? oa->areaid : 0;
+  u32 id = ort_to_lsaid(p, nf);
 
-  if (ext->metric & LSA_EXT_FBIT)
-    buf = lsa_get_ipv6_addr(buf, &rt_fwaddr);
+  en = ospf_hash_find(p->gr, dom, id, p->router_id, type);
 
-  if (ext->metric & LSA_EXT_TBIT)
-    rt_tag = *buf++;
+  if (!en || (en->nf != nf))
+    return;
 
-  return (rt_metric == metric) && ipa_equal(rt_fwaddr, fwaddr) && (rt_tag == tag);
+  ospf_flush_lsa(p, en);
 }
 
+static inline int
+use_gw_for_fwaddr(struct ospf_proto *p, ip_addr gw, struct iface *iface)
+{
+  struct ospf_iface *ifa;
+
+  if (ipa_zero(gw) || ipa_is_link_local(gw))
+    return 0;
 
-#endif
+  WALK_LIST(ifa, p->iface_list)
+    if ((ifa->iface == iface) &&
+       ((ifa->type == OSPF_IT_BCAST) || (ifa->type == OSPF_IT_NBMA)) &&
+       (!ospf_is_v2(p) || ipa_in_net(gw, ifa->addr->prefix, ifa->addr->pxlen)) &&
+       (!ifa->cf->stub))
+      return 1;
+
+  return 0;
+}
 
 static inline ip_addr
-find_surrogate_fwaddr(struct ospf_area *oa)
+find_surrogate_fwaddr(struct ospf_proto *p, struct ospf_area *oa)
 {
-  struct proto_ospf *po = oa->po;
   struct ospf_iface *ifa;
   struct ifa *a, *cur_addr = NULL;
   int np, cur_np = 0;
 
-  WALK_LIST(ifa, po->iface_list)
+  /* RFC 3101 2.3 - surrogate forwarding address selection */
+
+  WALK_LIST(ifa, p->iface_list)
   {
     if ((ifa->oa != oa) ||
        (ifa->type == OSPF_IT_VLINK))
       continue;
 
-#ifdef OSPFv2
-    a = ifa->addr;
-    if (a->flags & IA_PEER)
-      continue;
-
-    np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1;
-    if (np > cur_np)
-    {
-      cur_addr = a;
-      cur_np = np;
-    }
-
-#else /* OSPFv3 */
-    WALK_LIST(a, ifa->iface->addrs)
+    if (ospf_is_v2(p))
     {
-      if ((a->flags & IA_SECONDARY) ||
-         (a->flags & IA_PEER) ||
-         (a->scope <= SCOPE_LINK))
+      a = ifa->addr;
+      if (a->flags & IA_PEER)
        continue;
 
-      np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1;
+      np = (a->flags & IA_HOST) ? 3 : (ifa->stub ? 2 : 1);
       if (np > cur_np)
       {
        cur_addr = a;
        cur_np = np;
       }
     }
-#endif
+    else /* OSPFv3 */
+    {
+      WALK_LIST(a, ifa->iface->addrs)
+      {
+       if ((a->flags & IA_SECONDARY) ||
+           (a->flags & IA_PEER) ||
+           (a->scope <= SCOPE_LINK))
+         continue;
+
+       np = (a->flags & IA_HOST) ? 3 : (ifa->stub ? 2 : 1);
+       if (np > cur_np)
+       {
+         cur_addr = a;
+         cur_np = np;
+       }
+      }
+    }
   }
 
   return cur_addr ? cur_addr->ip : IPA_NONE;
 }
 
-
-/**
- * originate_ext_lsa - new route received from nest and filters
- * @oa: ospf_area for which LSA is originated
- * @fn: network prefix and mask
- * @src: the source of origination of the LSA (EXT_EXPORT/EXT_NSSA)
- * @metric: the metric of a route
- * @fwaddr: the forwarding address
- * @tag: the route tag
- * @pbit: P-bit for NSSA LSAs, ignored for external LSAs
- *
- * If I receive a message that new route is installed, I try to originate an
- * external LSA. If @oa is an NSSA area, NSSA-LSA is originated instead.
- * @oa should not be a stub area. @src does not specify whether the LSA
- * is external or NSSA, but it specifies the source of origination - 
- * the export from ospf_rt_notify(), or the NSSA-EXT translation.
- *
- * The function also sets flag ebit. If it's the first time, the new router lsa
- * origination is necessary.
- */
 void
-originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src,
-                 u32 metric, ip_addr fwaddr, u32 tag, int pbit)
+ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *ea)
 {
-  struct proto_ospf *po = oa->po;
-  struct proto *p = &po->proto;
-  struct ospf_lsa_header lsa;
-  struct top_hash_entry *en = NULL;
-  void *body;
-  int nssa = oa_is_nssa(oa);
-  u32 dom = nssa ? oa->areaid : 0;
+  struct ospf_proto *p = (struct ospf_proto *) P;
+  struct ospf_area *oa = NULL; /* non-NULL for NSSA-LSA */
+  ort *nf;
 
-  OSPF_TRACE(D_EVENTS, "Originating %s-LSA for %I/%d",
-            nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
+  /*
+   * There are several posibilities:
+   * 1) router in regular area - originate external LSA with global scope
+   * 2) router in NSSA area - originate area-specific NSSA-LSA
+   * 3) router in stub area - cannot export routes
+   * 4) area border router - same as (1), it is attached to backbone
+   */
 
-  lsa.age = 0;
-#ifdef OSPFv2
-  lsa.options = nssa ? (pbit ? OPT_P : 0) : OPT_E;
-#endif
-  lsa.type = nssa ? LSA_T_NSSA : LSA_T_EXT;
-  lsa.id = fibnode_to_lsaid(po, fn);
-  lsa.rt = po->router_id;
+  if ((p->areano == 1) && oa_is_nssa(HEAD(p->area_list)))
+    oa = HEAD(p->area_list);
 
-  if (nssa && pbit && ipa_zero(fwaddr))
+  if (!new)
   {
-    /* NSSA-LSA with P-bit set must have non-zero forwarding address */
+    nf = (ort *) fib_find(&p->rtf, &n->n.prefix, n->n.pxlen);
 
-    fwaddr = find_surrogate_fwaddr(oa);
-    if (ipa_zero(fwaddr))
-    {
-      log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d",
-         p->name, fn->prefix, fn->pxlen);
+    if (!nf || !nf->external_rte)
       return;
-    }
-  }
 
-  if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL)
-  {
-    int rv = check_ext_lsa(en, fn, metric, fwaddr, tag);
-    if (rv < 0)
-    {
-      log(L_ERR "%s: LSAID collision for %I/%d",
-         p->name, fn->prefix, fn->pxlen);
-      return;
-    }
+    ospf_flush_ext_lsa(p, oa, nf);
+    nf->external_rte = 0;
 
-    if (rv > 0)
-      return;
-  }
-  lsa.sn = get_seqnum(en);
+    /* Old external route might blocked some NSSA translation */
+    if ((p->areano > 1) && rt_is_nssa(nf) && nf->n.oa->translate)
+      schedule_rtcalc(p);
 
-  body = originate_ext_lsa_body(po, &lsa.length, fn, metric, fwaddr, tag, pbit);
-  lsasum_calculate(&lsa, body);
+    return;
+  }
 
-  if (src) 
-    fn->x1 = src;
+  ASSERT(p->asbr);
 
-  lsa_install_new(po, &lsa, dom, body);
-  ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+  /* Get route attributes */
+  rta *a = new->attrs;
+  u32 m1 = ea_get_int(ea, EA_OSPF_METRIC1, LSINFINITY);
+  u32 m2 = ea_get_int(ea, EA_OSPF_METRIC2, 10000);
+  int ebit = (m1 == LSINFINITY);
+  u32 metric = ebit ? m2 : m1;
+  u32 tag = ea_get_int(ea, EA_OSPF_TAG, 0);
+  ip_addr fwd = IPA_NONE;
 
-  if (po->ebit == 0)
-  {
-    po->ebit = 1;
-    WALK_LIST(oa, po->area_list)
-    {
-      schedule_rt_lsa(oa);
-    }
-  }
-}
 
-void
-flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int nssa)
-{
-  struct proto_ospf *po = oa->po;
-  struct proto *p = &po->proto;
-  struct top_hash_entry *en;
+  if ((a->dest == RTD_ROUTER) && use_gw_for_fwaddr(p, a->gw, a->iface))
+    fwd = a->gw;
 
-  u32 dom = nssa ? oa->areaid : 0;
-  u32 type = nssa ? LSA_T_NSSA : LSA_T_EXT;
-  u32 lsaid = fibnode_to_lsaid(po, fn);
+  /* NSSA-LSA with P-bit set must have non-zero forwarding address */
+  if (oa && ipa_zero(fwd))
+  {
+    fwd = find_surrogate_fwaddr(p, oa);
 
-  if (en = ospf_hash_find(po->gr, dom, lsaid, po->router_id, type))
+    if (ipa_zero(fwd))
     {
-      OSPF_TRACE(D_EVENTS, "Flushing %s-LSA for %I/%d",
-                nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
-
-      if (check_ext_lsa(en, fn, 0, IPA_NONE, 0) < 0)
-       {
-         log(L_ERR "%s: LSAID collision for %I/%d",
-             p->name, fn->prefix, fn->pxlen);
-         return;
-       }
-
-      fn->x1 = 0;
-      ospf_lsupd_flush_nlsa(po, en);
+      log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d",
+         p->p.name, n->n.prefix, n->n.pxlen);
+      return;
     }
+  }
+
+  nf = (ort *) fib_get(&p->rtf, &n->n.prefix, n->n.pxlen);
+  ospf_originate_ext_lsa(p, oa, nf, 0, metric, ebit, fwd, tag, 1);
+  nf->external_rte = 1;
 }
 
 
-#ifdef OSPFv3
+/*
+ *     Link-LSA handling (assume OSPFv3)
+ *     Type = LSA_T_LINK
+ */
 
-static void *
-originate_link_lsa_body(struct ospf_iface *ifa, u16 *length)
+static inline void
+lsab_put_prefix(struct ospf_proto *p, ip_addr prefix, u32 pxlen, u32 cost)
+{
+  void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(pxlen));
+  u8 flags = (pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA;
+  put_ipv6_prefix(buf, prefix, pxlen, flags, cost);
+}
+
+static void
+prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
 {
-  struct proto_ospf *po = ifa->oa->po;
   struct ospf_lsa_link *ll;
   int i = 0;
-  u8 flags;
 
-  ASSERT(po->lsab_used == 0);
-  ll = lsab_allocz(po, sizeof(struct ospf_lsa_link));
+  ASSERT(p->lsab_used == 0);
+  ll = lsab_allocz(p, sizeof(struct ospf_lsa_link));
   ll->options = ifa->oa->options | (ifa->priority << 24);
   ll->lladdr = ifa->addr->ip;
   ll = NULL; /* buffer might be reallocated later */
 
   struct ifa *a;
   WALK_LIST(a, ifa->iface->addrs)
-    {
-      if ((a->flags & IA_SECONDARY) ||
-         (a->scope < SCOPE_SITE))
-       continue;
+  {
+    if ((a->flags & IA_SECONDARY) ||
+       (a->scope < SCOPE_SITE))
+      continue;
 
-      flags = (a->pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA;
-      put_ipv6_prefix(lsab_alloc(po, IPV6_PREFIX_SPACE(a->pxlen)),
-                     a->ip, a->pxlen, flags, 0);
-      i++;
-    }
+    lsab_put_prefix(p, a->prefix, a->pxlen, 0);
+    i++;
+  }
 
-  ll = po->lsab;
+  ll = p->lsab;
   ll->pxcount = i;
-  *length = po->lsab_used + sizeof(struct ospf_lsa_header);
-  return lsab_flush(po);
 }
 
-void
-originate_link_lsa(struct ospf_iface *ifa)
+static void
+ospf_originate_link_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
 {
-  struct ospf_lsa_header lsa;
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
-  void *body;
-
-  /* Vlinks do not have link-LSAs */
-  if (ifa->type == OSPF_IT_VLINK)
+  if (ospf_is_v2(p))
     return;
 
-  OSPF_TRACE(D_EVENTS, "Originating link-LSA for iface %s", ifa->ifname);
-
-  lsa.age = 0;
-  lsa.type = LSA_T_LINK;
-  lsa.id = ifa->iface_id;
-  lsa.rt = po->router_id;
-  lsa.sn = get_seqnum(ifa->link_lsa);
-  u32 dom = ifa->iface_id;
+  struct ospf_new_lsa lsa = {
+    .type = LSA_T_LINK,
+    .dom  = ifa->iface_id,
+    .id   = ifa->iface_id,
+    .ifa  = ifa
+  };
 
-  body = originate_link_lsa_body(ifa, &lsa.length);
-  lsasum_calculate(&lsa, body);
-  ifa->link_lsa = lsa_install_new(po, &lsa, dom, body);
-  ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+  prepare_link_lsa_body(p, ifa);
 
-  /* Just to be sure to not forget on our link LSA */
-  if (ifa->state == OSPF_IS_DR)
-    schedule_net_lsa(ifa);
+  ifa->link_lsa = ospf_originate_lsa(p, &lsa);
 }
 
-void
-update_link_lsa(struct ospf_iface *ifa)
-{
-  if (ifa->link_lsa && ((ifa->link_lsa->inst_t + MINLSINTERVAL) > now))
-    return;
-  /*
-   * It's too early to originate new link LSA. We will
-   * try to do it next tick
-   */
-  originate_link_lsa(ifa);
-  ifa->origlink = 0;
-}
 
-static inline void
-lsa_put_prefix(struct proto_ospf *po, ip_addr prefix, u32 pxlen, u32 cost)
-{
-  put_ipv6_prefix(lsab_alloc(po, IPV6_PREFIX_SPACE(pxlen)), prefix, pxlen,
-                 (pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA, cost);
-}
+/*
+ *     Prefix-Rt-LSA handling (assume OSPFv3)
+ *     Type = LSA_T_PREFIX, referred type = LSA_T_RT
+ */
 
-static void *
-originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length)
+static void
+prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
 {
-  struct proto_ospf *po = oa->po;
-  struct ospf_config *cf = (struct ospf_config *) (po->proto.cf);
+  struct ospf_config *cf = (struct ospf_config *) (p->p.cf);
   struct ospf_iface *ifa;
   struct ospf_lsa_prefix *lp;
   int host_addr = 0;
   int net_lsa;
   int i = 0;
 
-  ASSERT(po->lsab_used == 0);
-  lp = lsab_allocz(po, sizeof(struct ospf_lsa_prefix));
+  ASSERT(p->lsab_used == 0);
+  lp = lsab_allocz(p, sizeof(struct ospf_lsa_prefix));
   lp->ref_type = LSA_T_RT;
   lp->ref_id = 0;
-  lp->ref_rt = po->router_id;
+  lp->ref_rt = p->router_id;
   lp = NULL; /* buffer might be reallocated later */
 
-  WALK_LIST(ifa, po->iface_list)
+  WALK_LIST(ifa, p->iface_list)
   {
     if ((ifa->oa != oa) || (ifa->type == OSPF_IT_VLINK) || (ifa->state == OSPF_IS_DOWN))
       continue;
@@ -1286,47 +1363,46 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length)
 
     struct ifa *a;
     WALK_LIST(a, ifa->iface->addrs)
-      {
-       if ((a->flags & IA_SECONDARY) ||
-           (a->flags & IA_PEER) ||
-           (a->scope <= SCOPE_LINK))
-         continue;
+    {
+      if ((a->flags & IA_SECONDARY) ||
+         (a->flags & IA_PEER) ||
+         (a->scope <= SCOPE_LINK))
+       continue;
 
-       if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) ||
-           configured_stubnet(oa, a))
-         continue;
+      if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) ||
+         configured_stubnet(oa, a))
+       continue;
 
-       if ((a->flags & IA_HOST) ||
-           (ifa->state == OSPF_IS_LOOP) ||
-           (ifa->type == OSPF_IT_PTMP))
-       {
-         lsa_put_prefix(po, a->ip, MAX_PREFIX_LENGTH, 0);
-         host_addr = 1;
-       }
-       else
-         lsa_put_prefix(po, a->prefix, a->pxlen, ifa->cost);
-       i++;
+      if ((a->flags & IA_HOST) ||
+         (ifa->state == OSPF_IS_LOOP) ||
+         (ifa->type == OSPF_IT_PTMP))
+      {
+       lsab_put_prefix(p, a->ip, MAX_PREFIX_LENGTH, 0);
+       host_addr = 1;
       }
+      else
+       lsab_put_prefix(p, a->prefix, a->pxlen, ifa->cost);
+      i++;
+    }
 
     ifa->px_pos_end = i;
   }
 
   struct ospf_stubnet_config *sn;
-  if (oa->ac)
-    WALK_LIST(sn, oa->ac->stubnet_list)
-      if (!sn->hidden)
-      {
-       lsa_put_prefix(po, sn->px.addr, sn->px.len, sn->cost);
-       if (sn->px.len == MAX_PREFIX_LENGTH)
-         host_addr = 1;
-       i++;
-      }
+  WALK_LIST(sn, oa->ac->stubnet_list)
+    if (!sn->hidden)
+    {
+      lsab_put_prefix(p, sn->px.addr, sn->px.len, sn->cost);
+      if (sn->px.len == MAX_PREFIX_LENGTH)
+       host_addr = 1;
+      i++;
+    }
 
   /* If there are some configured vlinks, find some global address
      (even from another area), which will be used as a vlink endpoint. */
   if (!EMPTY_LIST(cf->vlink_list) && !host_addr)
   {
-    WALK_LIST(ifa, po->iface_list)
+    WALK_LIST(ifa, p->iface_list)
     {
       if ((ifa->type == OSPF_IT_VLINK) || (ifa->state == OSPF_IS_DOWN))
        continue;
@@ -1338,7 +1414,7 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length)
          continue;
 
        /* Found some IP */
-       lsa_put_prefix(po, a->ip, MAX_PREFIX_LENGTH, 0);
+       lsab_put_prefix(p, a->ip, MAX_PREFIX_LENGTH, 0);
        i++;
        goto done;
       }
@@ -1346,36 +1422,33 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length)
   }
 
  done:
-  lp = po->lsab;
+  lp = p->lsab;
   lp->pxcount = i;
-  *length = po->lsab_used + sizeof(struct ospf_lsa_header);
-  return lsab_flush(po);
 }
 
-void
-originate_prefix_rt_lsa(struct ospf_area *oa)
+static void
+ospf_originate_prefix_rt_lsa(struct ospf_proto *p, struct ospf_area *oa)
 {
-  struct proto_ospf *po = oa->po;
-  struct proto *p = &po->proto;  
-  struct ospf_lsa_header lsa;
-  void *body;
+  if (ospf_is_v2(p))
+    return;
 
-  OSPF_TRACE(D_EVENTS, "Originating router prefix-LSA for area %R", oa->areaid);
+  struct ospf_new_lsa lsa = {
+    .type = LSA_T_PREFIX,
+    .dom  = oa->areaid,
+    .id   = 0
+  };
 
-  lsa.age = 0;
-  lsa.type = LSA_T_PREFIX;
-  lsa.id = 0;
-  lsa.rt = po->router_id;
-  lsa.sn = get_seqnum(oa->pxr_lsa);
-  u32 dom = oa->areaid;
+  prepare_prefix_rt_lsa_body(p, oa);
 
-  body = originate_prefix_rt_lsa_body(oa, &lsa.length);
-  lsasum_calculate(&lsa, body);
-  oa->pxr_lsa = lsa_install_new(po, &lsa, dom, body);
-  ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+  ospf_originate_lsa(p, &lsa);
 }
 
 
+/*
+ *     Prefix-Net-LSA handling (assume OSPFv3)
+ *     Type = LSA_T_PREFIX, referred type = LSA_T_NET
+ */
+
 static inline int
 prefix_space(u32 *buf)
 {
@@ -1410,9 +1483,9 @@ prefix_advance(u32 *buf)
 
 /* FIXME eliminate items with LA bit set? see 4.4.3.9 */
 static void
-add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc)
+add_prefix(struct ospf_proto *p, u32 *px, int offset, int *pxc)
 {
-  u32 *pxl = lsab_offset(po, offset);
+  u32 *pxl = lsab_offset(p, offset);
   int i;
   for (i = 0; i < *pxc; pxl = prefix_advance(pxl), i++)
     if (prefix_same(px, pxl))
@@ -1422,19 +1495,18 @@ add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc)
       return;
     }
 
-  ASSERT(pxl == lsab_end(po));
+  ASSERT(pxl == lsab_end(p));
 
   int pxspace = prefix_space(px);
-  pxl = lsab_alloc(po, pxspace);
+  pxl = lsab_alloc(p, pxspace);
   memcpy(pxl, px, pxspace);
   *pxl &= 0xFFFF0000;  /* Set metric to zero */
   (*pxc)++;
 }
 
 static void
-add_link_lsa(struct proto_ospf *po, struct top_hash_entry *en, int offset, int *pxc)
+add_link_lsa(struct ospf_proto *p, struct ospf_lsa_link *ll, int offset, int *pxc)
 {
-  struct ospf_lsa_link *ll = en->lsa_body;
   u32 *pxb = ll->rest;
   int j;
 
@@ -1451,92 +1523,110 @@ add_link_lsa(struct proto_ospf *po, struct top_hash_entry *en, int offset, int *
     if ((pxlen >= 10) && ((pxb[1] & 0xffc00000) == 0xfe800000))
       continue;
 
-    add_prefix(po, pxb, offset, pxc);
+    add_prefix(p, pxb, offset, pxc);
   }
 }
 
-
-
-static void *
-originate_prefix_net_lsa_body(struct ospf_iface *ifa, u16 *length)
+static void
+prepare_prefix_net_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
 {
-  struct proto_ospf *po = ifa->oa->po;
   struct ospf_lsa_prefix *lp;
   struct ospf_neighbor *n;
   struct top_hash_entry *en;
   int pxc, offset;
 
-  ASSERT(po->lsab_used == 0);
-  lp = lsab_allocz(po, sizeof(struct ospf_lsa_prefix));
+  ASSERT(p->lsab_used == 0);
+  lp = lsab_allocz(p, sizeof(struct ospf_lsa_prefix));
   lp->ref_type = LSA_T_NET;
   lp->ref_id = ifa->net_lsa->lsa.id;
-  lp->ref_rt = po->router_id;
+  lp->ref_rt = p->router_id;
   lp = NULL; /* buffer might be reallocated later */
 
   pxc = 0;
-  offset = po->lsab_used;
+  offset = p->lsab_used;
 
   /* Find all Link LSAs associated with the link and merge their prefixes */
-  if (ifa->link_lsa)
-    add_link_lsa(po, ifa->link_lsa, offset, &pxc);
+  if (en = ifa->link_lsa)
+    add_link_lsa(p, en->next_lsa_body ?: en->lsa_body, offset, &pxc);
 
   WALK_LIST(n, ifa->neigh_list)
     if ((n->state == NEIGHBOR_FULL) &&
-       (en = ospf_hash_find(po->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK)))
-      add_link_lsa(po, en, offset, &pxc);
+       (en = ospf_hash_find(p->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK)))
+      add_link_lsa(p, en->lsa_body, offset, &pxc);
 
-  lp = po->lsab;
+  lp = p->lsab;
   lp->pxcount = pxc;
-  *length = po->lsab_used + sizeof(struct ospf_lsa_header);
-  return lsab_flush(po);
 }
 
-void
-originate_prefix_net_lsa(struct ospf_iface *ifa)
+static void
+ospf_originate_prefix_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
 {
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
-  struct ospf_lsa_header lsa;
-  void *body;
+  if (ospf_is_v2(p))
+    return;
 
-  OSPF_TRACE(D_EVENTS, "Originating network prefix-LSA for iface %s", ifa->ifname);
+  struct ospf_new_lsa lsa = {
+    .type = LSA_T_PREFIX,
+    .dom  = ifa->oa->areaid,
+    .id   = ifa->iface_id,
+    .ifa  = ifa
+  };
 
-  lsa.age = 0;
-  lsa.type = LSA_T_PREFIX;
-  lsa.id = ifa->iface_id;
-  lsa.rt = po->router_id;
-  lsa.sn = get_seqnum(ifa->pxn_lsa);
-  u32 dom = ifa->oa->areaid;
+  prepare_prefix_net_lsa_body(p, ifa);
 
-  body = originate_prefix_net_lsa_body(ifa, &lsa.length);
-  lsasum_calculate(&lsa, body);
-  ifa->pxn_lsa = lsa_install_new(po, &lsa, dom, body);
-  ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+  ifa->pxn_lsa = ospf_originate_lsa(p, &lsa);
 }
 
+
 void
-flush_prefix_net_lsa(struct ospf_iface *ifa)
+ospf_update_topology(struct ospf_proto *p)
 {
-  struct proto_ospf *po = ifa->oa->po;
-  struct proto *p = &po->proto;
-  struct top_hash_entry *en = ifa->pxn_lsa;
-  u32 dom = ifa->oa->areaid;
+  struct ospf_area *oa;
+  struct ospf_iface *ifa;
 
-  if (en == NULL)
-    return;
+  WALK_LIST(oa, p->area_list)
+  {
+    if (oa->update_rt_lsa)
+    {
+      ospf_originate_rt_lsa(p, oa);
+      ospf_originate_prefix_rt_lsa(p, oa);
+      oa->update_rt_lsa = 0;
+    }
+  }
 
-  OSPF_TRACE(D_EVENTS, "Flushing network prefix-LSA for iface %s", ifa->ifname);
+  WALK_LIST(ifa, p->iface_list)
+  {
+    if (ifa->type == OSPF_IT_VLINK)
+      continue;
 
-  en->lsa.sn += 1;
-  en->lsa.age = LSA_MAXAGE;
-  lsasum_calculate(&en->lsa, en->lsa_body);
-  ospf_lsupd_flood(po, NULL, NULL, &en->lsa, dom, 0);
-  flush_lsa(en, po);
-  ifa->pxn_lsa = NULL;
-}
+    if (ifa->update_link_lsa)
+    {
+      if ((ifa->state > OSPF_IS_LOOP) && !ifa->link_lsa_suppression)
+       ospf_originate_link_lsa(p, ifa);
+      else
+       ospf_flush2_lsa(p, &ifa->link_lsa);
 
+      ifa->update_link_lsa = 0;
+    }
 
-#endif
+    if (ifa->update_net_lsa)
+    {
+      if ((ifa->state == OSPF_IS_DR) && (ifa->fadj > 0))
+      {
+       ospf_originate_net_lsa(p, ifa);
+       ospf_originate_prefix_net_lsa(p, ifa);
+      }
+      else
+      {
+       ospf_flush2_lsa(p, &ifa->net_lsa);
+       ospf_flush2_lsa(p, &ifa->pxn_lsa);
+      }
+
+      ifa->update_net_lsa = 0;
+    }
+  }
+
+  // XXXX schedule_rtcalc(p);
+}
 
 
 static void
@@ -1574,7 +1664,7 @@ ospf_top_hash_u32(u32 a)
   return a;
 }
 
-static inline unsigned
+static unsigned
 ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type)
 {
   /* In OSPFv2, we don't know Router ID when looking for network LSAs.
@@ -1582,14 +1672,8 @@ ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type)
      In both cases, there is (usually) just one (or small number)
      appropriate LSA, so we just clear unknown part of key. */
 
-  return (
-#ifdef OSPFv2
-         ((type == LSA_T_NET) ? 0 : ospf_top_hash_u32(rtrid)) +
-         ospf_top_hash_u32(lsaid) + 
-#else /* OSPFv3 */
-         ospf_top_hash_u32(rtrid) +
-         ((type == LSA_T_RT) ? 0 : ospf_top_hash_u32(lsaid)) +
-#endif
+  return (((f->ospf2 && (type == LSA_T_NET)) ? 0 : ospf_top_hash_u32(rtrid)) +
+         ((!f->ospf2 && (type == LSA_T_RT)) ? 0 : ospf_top_hash_u32(lsaid)) +
          type + domain) & f->hash_mask;
 
   /*
@@ -1600,7 +1684,7 @@ ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type)
 
 /**
  * ospf_top_new - allocated new topology database
- * @p: current instance of ospf
+ * @p: OSPF protocol instance
  *
  * this dynamically hashed structure is often used for keeping lsas. mainly
  * its used in @ospf_area structure.
@@ -1648,7 +1732,7 @@ ospf_top_rehash(struct top_graph *f, int step)
     while (e)
     {
       x = e->next;
-      n = newt + ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa.type);
+      n = newt + ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa_type);
       e->next = *n;
       *n = e;
       e = x;
@@ -1657,124 +1741,82 @@ ospf_top_rehash(struct top_graph *f, int step)
   ospf_top_ht_free(oldt);
 }
 
-#ifdef OSPFv2
-
-u32
-ospf_lsa_domain(u32 type, struct ospf_iface *ifa)
-{
-  return (type == LSA_T_EXT) ? 0 : ifa->oa->areaid;
-}
-
-#else /* OSPFv3 */
-
-u32
-ospf_lsa_domain(u32 type, struct ospf_iface *ifa)
-{
-  switch (type & LSA_SCOPE_MASK)
-    {
-    case LSA_SCOPE_LINK:
-      return ifa->iface_id;
-
-    case LSA_SCOPE_AREA:
-      return ifa->oa->areaid;
-
-    case LSA_SCOPE_AS:
-    default:
-      return 0;
-    }
-}
-
-#endif
-
-struct top_hash_entry *
-ospf_hash_find_header(struct top_graph *f, u32 domain, struct ospf_lsa_header *h)
-{
-  return ospf_hash_find(f, domain, h->id, h->rt, h->type);
-}
-
-struct top_hash_entry *
-ospf_hash_get_header(struct top_graph *f, u32 domain, struct ospf_lsa_header *h)
-{
-  return ospf_hash_get(f, domain, h->id, h->rt, h->type);
-}
-
 struct top_hash_entry *
 ospf_hash_find(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type)
 {
   struct top_hash_entry *e;
   e = f->hash_table[ospf_top_hash(f, domain, lsa, rtr, type)];
 
-  while (e && (e->lsa.id != lsa || e->lsa.type != type || e->lsa.rt != rtr || e->domain != domain))
-    e = e->next;
-
-  return e;
-}
-
-
-#ifdef OSPFv2
-
-/* In OSPFv2, sometimes we don't know Router ID when looking for network LSAs.
-   There should be just one, so we find any match. */
-struct top_hash_entry *
-ospf_hash_find_net(struct top_graph *f, u32 domain, u32 lsa)
-{
-  struct top_hash_entry *e;
-  e = f->hash_table[ospf_top_hash(f, domain, lsa, 0, LSA_T_NET)];
-
-  while (e && (e->lsa.id != lsa || e->lsa.type != LSA_T_NET || e->domain != domain))
+  while (e && (e->lsa.id != lsa || e->lsa.rt != rtr ||
+              e->lsa_type != type || e->domain != domain))
     e = e->next;
 
-  return e;
+  /* Hide hash entry with empty lsa_body */
+  return e->lsa_body ? e : NULL;
 }
 
-#endif
-
-
-#ifdef OSPFv3
-
-/* In OSPFv3, usually we don't know LSA ID when looking for router
-   LSAs. We return matching LSA with smallest LSA ID. */
+/* In OSPFv2, lsa.id is the same as lsa.rt for router LSA. In OSPFv3, we don't know
+   lsa.id when looking for router LSAs. We return matching LSA with smallest lsa.id. */
 struct top_hash_entry *
 ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr)
 {
   struct top_hash_entry *rv = NULL;
   struct top_hash_entry *e;
-  e = f->hash_table[ospf_top_hash(f, domain, 0, rtr, LSA_T_RT)];
-  
+  /* We can put rtr for lsa.id to hash fn, it is ignored in OSPFv3 */
+  e = f->hash_table[ospf_top_hash(f, domain, rtr, rtr, LSA_T_RT)];
+
   while (e)
+  {
+    if (e->lsa.rt == rtr && e->lsa_type == LSA_T_RT && e->domain == domain && e->lsa_body)
     {
-      if (e->lsa.rt == rtr && e->lsa.type == LSA_T_RT && e->domain == domain)
-       if (!rv || e->lsa.id < rv->lsa.id)
-         rv = e;
-      e = e->next;
+      if (f->ospf2 && (e->lsa.id == rtr))
+       return e;
+      if (!f->ospf2 && (!rv || e->lsa.id < rv->lsa.id))
+       rv = e;
     }
+    e = e->next;
+  }
 
   return rv;
 }
 
 static inline struct top_hash_entry *
-find_matching_rt(struct top_hash_entry *e, u32 domain, u32 rtr)
+find_matching_rt3(struct top_hash_entry *e, u32 domain, u32 rtr)
 {
-  while (e && (e->lsa.rt != rtr || e->lsa.type != LSA_T_RT || e->domain != domain))
+  while (e && (e->lsa.rt != rtr || e->lsa_type != LSA_T_RT ||
+              e->domain != domain || e->lsa_body == NULL))
     e = e->next;
   return e;
 }
 
 struct top_hash_entry *
-ospf_hash_find_rt_first(struct top_graph *f, u32 domain, u32 rtr)
+ospf_hash_find_rt3_first(struct top_graph *f, u32 domain, u32 rtr)
 {
   struct top_hash_entry *e;
   e = f->hash_table[ospf_top_hash(f, domain, 0, rtr, LSA_T_RT)];
-  return find_matching_rt(e, domain, rtr);
+  return find_matching_rt3(e, domain, rtr);
 }
 
 struct top_hash_entry *
-ospf_hash_find_rt_next(struct top_hash_entry *e)
+ospf_hash_find_rt3_next(struct top_hash_entry *e)
 {
-  return find_matching_rt(e->next, e->domain, e->lsa.rt);
+  return find_matching_rt3(e->next, e->domain, e->lsa.rt);
 }
 
-#endif
+/* In OSPFv2, we don't know Router ID when looking for network LSAs.
+   There should be just one, so we find any match. */
+struct top_hash_entry *
+ospf_hash_find_net2(struct top_graph *f, u32 domain, u32 id)
+{
+  struct top_hash_entry *e;
+  e = f->hash_table[ospf_top_hash(f, domain, id, 0, LSA_T_NET)];
+
+  while (e && (e->lsa.id != id || e->lsa_type != LSA_T_NET ||
+              e->domain != domain || e->lsa_body == NULL))
+    e = e->next;
+
+  return e;
+}
 
 
 struct top_hash_entry *
@@ -1786,21 +1828,23 @@ ospf_hash_get(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type)
   ee = f->hash_table + ospf_top_hash(f, domain, lsa, rtr, type);
   e = *ee;
 
-  while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa.type != type || e->domain != domain))
+  while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || 
+              e->lsa_type != type || e->domain != domain))
     e = e->next;
 
   if (e)
     return e;
 
   e = sl_alloc(f->hash_slab);
+  bzero(e, sizeof(struct top_hash_entry));
+
   e->color = OUTSPF;
   e->dist = LSINFINITY;
-  e->nhs = NULL;
-  e->lb = IPA_NONE;
+  e->lsa.type_raw = type;
   e->lsa.id = lsa;
   e->lsa.rt = rtr;
-  e->lsa.type = type;
-  e->lsa_body = NULL;
+  e->lsa.sn = LSA_ZEROSEQNO;
+  e->lsa_type = type;
   e->domain = domain;
   e->next = *ee;
   *ee = e;
@@ -1813,7 +1857,7 @@ void
 ospf_hash_delete(struct top_graph *f, struct top_hash_entry *e)
 {
   struct top_hash_entry **ee = f->hash_table + 
-    ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa.type);
+    ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa_type);
 
   while (*ee)
   {
@@ -1885,29 +1929,114 @@ ospf_top_dump(struct top_graph *f, struct proto *p)
 }
 */
 
-/* This is very inefficient, please don't call it often */
 
-/* I should also test for every LSA if it's in some link state
- * retransmission list for every neighbor. I will not test it.
- * It could happen that I'll receive some strange ls ack's.
- */
 
-int
-can_flush_lsa(struct proto_ospf *po)
+#if 0
+
+void
+update_rt_lsa(struct ospf_area *oa)
 {
-  struct ospf_iface *ifa;
-  struct ospf_neighbor *n;
+  struct ospf_proto *po = oa->po;
 
-  WALK_LIST(ifa, po->iface_list)
-  {
-    WALK_LIST(n, ifa->neigh_list)
-    {
-      if ((n->state == NEIGHBOR_EXCHANGE) || (n->state == NEIGHBOR_LOADING))
-        return 0;
+  if ((oa->rt) && ((oa->rt->inst_t + MINLSINTERVAL)) > now)
+    return;
 
-      break;
-    }
-  }
+  originate_rt_lsa(oa);
+  if (ospf_is_v3(p))
+    originate_prefix_rt_lsa(oa);
 
-  return 1;
+  schedule_rtcalc(p);
+  oa->origrt = 0;
+}
+
+
+
+
+static inline int
+check_sum2_net_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric)
+{
+  struct ospf_lsa_sum2 *sum = en->lsa_body;
+
+  if (fn->pxlen != ip4_masklen(sum->netmask))
+    return -1;
+
+  return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric);
+}
+
+static inline int
+check_sum3_net_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric)
+{
+  struct ospf_lsa_sum3_net *sum = en->lsa_body;
+  ip6_addr prefix;
+  int pxlen;
+  u8 pxopts;
+  u16 rest;
+  lsa_get_ipv6_prefix(sum->prefix, &prefix, &pxlen, &pxopts, &rest);
+
+
+  if ((fn->pxlen != pxlen) || !ip6_equal(fn->prefix, prefix))
+    return -1;
+
+  return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric);
+}
+
+
+static int
+check_sum_net_lsa(struct ospf_proto *po, struct top_hash_entry *en, struct fib_node *fn, u32 metric)
+{
+  int rv = ospf_is_v2(po) ?
+    check_sum2_net_lsa(en, fn, metric) :
+    check_sum3_net_lsa(en, fn, metric);
+
+  if (rv < 0)
+    log(L_ERR "%s: LSAID collision for %I/%d", p->p.name, fn->prefix, fn->pxlen);
+
+  return rv;
+}
+
+static int
+check_sum_rt_lsa(struct ospf_proto *po, struct top_hash_entry *en, u32 drid, u32 metric, u32 options)
+{
+  if (en->lsa.sn == LSA_MAXSEQNO)
+    return 0;
+
+  if (ospf_is_v2(po))
+  {
+    struct ospf_lsa_sum2 *sum = en->lsa_body;
+    return (sum->metric == metric);
+  }
+  else
+  {
+    struct ospf_lsa_sum3_rt *sum = en->lsa_body;
+    return (sum->options == options) && (sum->metric == metric) && (sum->drid == drid);
+  }
 }
+
+
+
+
+
+
+
+  OSPF_TRACE(D_EVENTS, "Originating router-LSA for area %R", oa->areaid);
+  OSPF_TRACE(D_EVENTS, "Originating network-LSA for iface %s", ifa->ifname);
+  OSPF_TRACE(D_EVENTS, "Originating net-summary-LSA for %I/%d (metric %d)", fn->prefix, fn->pxlen, metric);
+  OSPF_TRACE(D_EVENTS, "Originating rt-summary-LSA for %R (metric %d)", rid, metric);
+  OSPF_TRACE(D_EVENTS, "Originating %s-LSA for %I/%d",
+            nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
+  OSPF_TRACE(D_EVENTS, "Originating link-LSA for iface %s", ifa->ifname);
+  OSPF_TRACE(D_EVENTS, "Originating router prefix-LSA for area %R", oa->areaid);
+  OSPF_TRACE(D_EVENTS, "Originating network prefix-LSA for iface %s", ifa->ifname);
+
+
+  en = ospf_hash_find(po->gr, lsa.dom, lsa.id, po->router_id, lsa.type);
+  if (en && check_ext_lsa(po, en, fn, metric, fwaddr, tag))
+    return;
+
+  *length = sizeof(struct ospf_lsa_header) + po->lsab_used;
+  return lsab_flush(po);
+
+  *length = po->lsab_used + sizeof(struct ospf_lsa_header);
+  return lsab_flush(po);
+
+#endif
index cb876487845e4084c7301eb61e0921900d95ff0f..e4ea79f70d25525685cddbb10f0d3bf3015c1581 100644 (file)
@@ -2,6 +2,8 @@
  *     BIRD -- OSPF
  *
  *     (c) 1999--2004 Ondrej Filip <feela@network.cz>
+ *     (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2009--2014 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
@@ -16,79 +18,102 @@ struct top_hash_entry
                                   in intra-area routing table calculation */
   struct top_hash_entry *next; /* Next in hash chain */
   struct ospf_lsa_header lsa;
+  u16 lsa_type;                        /* lsa.type processed and converted to common values */ 
+  u16 init_age;                        /* Initial value for lsa.age during inst_time */
   u32 domain;                  /* Area ID for area-wide LSAs, Iface ID for link-wide LSAs */
   //  struct ospf_area *oa;
-  void *lsa_body;
-  bird_clock_t inst_t;         /* Time of installation into DB */
+  void *lsa_body;              /* May be NULL if LSA was flushed but hash entry was kept */
+  void *next_lsa_body;         /* For postponed LSA origination */
+  u16 next_lsa_blen;           /* For postponed LSA origination */
+  u16 next_lsa_opts;           /* For postponed LSA origination */
+  bird_clock_t inst_time;      /* Time of installation into DB */
+  struct ort *nf;              /* Reference fibnode for sum and ext LSAs, NULL for otherwise */
   struct mpnh *nhs;            /* Computed nexthops - valid only in ospf_rt_spf() */
   ip_addr lb;                  /* In OSPFv2, link back address. In OSPFv3, any global address in the area useful for vlinks */
-#ifdef OSPFv3
   u32 lb_id;                   /* Interface ID of link back iface (for bcast or NBMA networks) */
-#endif
   u32 dist;                    /* Distance from the root */
-  u16 ini_age;
+  int ret_count;               /* Number of retransmission lists referencing the entry */
   u8 color;
 #define OUTSPF 0
 #define CANDIDATE 1
 #define INSPF 2
+  u8 rtcalc;                   /* LSA generated during RT calculation (LSA_RTCALC or LSA_STALE)*/
   u8 nhs_reuse;                        /* Whether nhs nodes can be reused during merging.
                                   See a note in rt.c:merge_nexthops() */
 };
 
+#define LSA_RTCALC     1
+#define LSA_STALE      2
+
 struct top_graph
 {
   pool *pool;                  /* Pool we allocate from */
   slab *hash_slab;             /* Slab for hash entries */
   struct top_hash_entry **hash_table;  /* Hashing (modelled a`la fib) */
-  unsigned int hash_size;
-  unsigned int hash_order;
-  unsigned int hash_mask;
-  unsigned int hash_entries;
-  unsigned int hash_entries_min, hash_entries_max;
+  uint ospf2;                  /* Whether it is for OSPFv2 or OSPFv3 */
+  uint hash_size;
+  uint hash_order;
+  uint hash_mask;
+  uint hash_entries;
+  uint hash_entries_min, hash_entries_max;
+};
+
+struct ospf_new_lsa
+{
+  u16 type;
+  u32 dom;
+  u32 id;
+  u16 opts;
+  u16 length;
+  struct ospf_iface *ifa;
+  struct ort *nf;
 };
 
 struct top_graph *ospf_top_new(pool *);
 void ospf_top_free(struct top_graph *);
 void ospf_top_dump(struct top_graph *, struct proto *);
-u32 ospf_lsa_domain(u32 type, struct ospf_iface *ifa);
-struct top_hash_entry *ospf_hash_find_header(struct top_graph *f, u32 domain,
-                                            struct ospf_lsa_header *h);
-struct top_hash_entry *ospf_hash_get_header(struct top_graph *f, u32 domain,
-                                           struct ospf_lsa_header *h);
-
-struct top_hash_entry *ospf_hash_find(struct top_graph *, u32 domain, u32 lsa, u32 rtr,
-                                     u32 type);
-struct top_hash_entry *ospf_hash_get(struct top_graph *, u32 domain, u32 lsa, u32 rtr,
-                                    u32 type);
+
+struct top_hash_entry * ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body);
+struct top_hash_entry * ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa);
+void ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body);
+void ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en);
+void ospf_update_lsadb(struct ospf_proto *p);
+
+static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry **en)
+{ if (*en) { ospf_flush_lsa(p, *en); *en = NULL; } }
+
+void ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric);
+void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options);
+void ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 rtcalc, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit);
+
+void ospf_rt_notify(struct proto *P, rtable *tbl, net *n, rte *new, rte *old, ea_list *attrs);
+void ospf_update_topology(struct ospf_proto *p);
+
+struct top_hash_entry *ospf_hash_find(struct top_graph *, u32 domain, u32 lsa, u32 rtr, u32 type);
+struct top_hash_entry *ospf_hash_get(struct top_graph *, u32 domain, u32 lsa, u32 rtr, u32 type);
 void ospf_hash_delete(struct top_graph *, struct top_hash_entry *);
-void originate_rt_lsa(struct ospf_area *oa);
-void update_rt_lsa(struct ospf_area *oa);
-void originate_net_lsa(struct ospf_iface *ifa);
-void update_net_lsa(struct ospf_iface *ifa);
-void update_link_lsa(struct ospf_iface *ifa);
-int can_flush_lsa(struct proto_ospf *po);
 
-void originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric);
-void originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options UNUSED);
-void flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type);
-void originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src, u32 metric, ip_addr fwaddr, u32 tag, int pbit);
-void flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int nssa);
+static inline struct top_hash_entry * ospf_hash_find_entry(struct top_graph *f, struct top_hash_entry *en)
+{ return ospf_hash_find(f, en->domain, en->lsa.id, en->lsa.rt, en->lsa_type); }
+
+static inline struct top_hash_entry * ospf_hash_get_entry(struct top_graph *f, struct top_hash_entry *en)
+{ return ospf_hash_get(f, en->domain, en->lsa.id, en->lsa.rt, en->lsa_type); }
 
+struct top_hash_entry * ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr);
+struct top_hash_entry * ospf_hash_find_rt3_first(struct top_graph *f, u32 domain, u32 rtr);
+struct top_hash_entry * ospf_hash_find_rt3_next(struct top_hash_entry *e);
 
-#ifdef OSPFv2
-struct top_hash_entry * ospf_hash_find_net(struct top_graph *f, u32 domain, u32 lsa);
+struct top_hash_entry * ospf_hash_find_net2(struct top_graph *f, u32 domain, u32 id);
 
+/* In OSPFv2, id is network IP prefix (lsa.id) while lsa.rt field is unknown
+   In OSPFv3, id is lsa.rt of DR while nif is neighbor iface id (lsa.id) */
 static inline struct top_hash_entry *
-ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr)
+ospf_hash_find_net(struct top_graph *f, u32 domain, u32 id, u32 nif)
 {
-  return ospf_hash_find(f, domain, rtr, rtr, LSA_T_RT);
+  return f->ospf2 ?
+    ospf_hash_find_net2(f, domain, id) :
+    ospf_hash_find(f, domain, nif, id, LSA_T_NET);
 }
 
-#else /* OSPFv3 */
-struct top_hash_entry * ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr);
-struct top_hash_entry * ospf_hash_find_rt_first(struct top_graph *f, u32 domain, u32 rtr);
-struct top_hash_entry * ospf_hash_find_rt_next(struct top_hash_entry *e);
-#endif
-
 
 #endif /* _BIRD_OSPF_TOPOLOGY_H_ */