]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Do not originate summary or external LSA if it already here and not changed.
authorOndrej Zajicek <santiago@crfreenet.org>
Sun, 16 May 2010 08:27:20 +0000 (10:27 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Sun, 16 May 2010 08:27:20 +0000 (10:27 +0200)
proto/ospf/topology.c

index 8f7563b2945f68bd1afda25d059a9317313bd286..ebcc31d2b7b60b92a4f07c3dcd4c12f80d603e22 100644 (file)
@@ -637,12 +637,20 @@ check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en)
 }
 
 static inline int
-check_ext_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en)
+check_sum_lsa_same(struct top_hash_entry *en, u32 metric)
 {
-  struct ospf_lsa_ext *ext = en->lsa_body;
-  return fn->pxlen != ipa_mklen(ext->netmask);
+  /* Netmask already checked in check_sum_net_lsaid_collision() */
+  struct ospf_lsa_sum *sum = en->lsa_body;
+  return (sum->metric == metric);
 }
 
+#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 */
 
 static inline void *
@@ -672,16 +680,11 @@ check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en)
 }
 
 static inline int
-check_ext_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en)
+check_sum_net_lsa_same(struct top_hash_entry *en, u32 metric)
 {
-  struct ospf_lsa_ext *ext = en->lsa_body;
-  ip_addr prefix;
-  int pxlen;
-  u8 pxopts;
-  u16 rest;
-
-  lsa_get_ipv6_prefix(ext->rest, &prefix, &pxlen, &pxopts, &rest);
-  return (fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix);
+  /* Prefix already checked in check_sum_net_lsaid_collision() */
+  struct ospf_lsa_sum_net *sum = en->lsa_body;
+  return (sum->metric == metric);
 }
 
 static inline void *
@@ -690,13 +693,20 @@ originate_sum_rt_lsa_body(struct proto_ospf *po, u16 *length, u32 drid, u32 metr
   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);
 
-  sum->options = options & OPTIONS_MASK;
+  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 (sum->options == options) && (sum->metric == metric) && (sum->drid == drid);
+}
+
 #endif
 
 void
@@ -725,11 +735,14 @@ originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric)
   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;
-      }
+    {
+      log(L_ERR, "%s: LSAID collision for %I/%d",
+         p->name, fn->prefix, fn->pxlen);
+      return;
+    }
+
+    if (check_sum_net_lsa_same(en, metric))
+      return;
 
     lsa.sn = en->lsa.sn + 1;
   }
@@ -764,8 +777,15 @@ originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32
   lsa.rt = po->router_id;
   lsa.sn = LSA_INITSEQNO;
 
+  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 = en->lsa.sn + 1;
+  }
 
   body = originate_sum_rt_lsa_body(po, &lsa.length, lsa.id, metric, options);
   lsasum_calculate(&lsa, body);
@@ -815,65 +835,114 @@ flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
     }
 }
 
+#ifdef OSPFv2
 
-static void *
-originate_ext_lsa_body(net *n, rte *e, u16 *length, struct proto_ospf *po,
-                      struct ea_list *attrs)
+static inline void *
+originate_ext_lsa_body(struct proto_ospf *po, u16 *length, net *n,
+                      u32 metric, ip_addr fwaddr, u32 tag)
 {
-  struct proto *p = &po->proto;
-  struct ospf_lsa_ext *ext;
-  u32 m1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY);
-  u32 m2 = ea_get_int(attrs, EA_OSPF_METRIC2, 10000);
-  u32 tag = ea_get_int(attrs, EA_OSPF_TAG, 0);
-  int gw = 0;
-  int size = sizeof(struct ospf_lsa_ext);
+  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);
 
-  // FIXME check for gw should be per ifa, not per iface
-  if ((e->attrs->dest == RTD_ROUTER) &&
-      !ipa_equal(e->attrs->gw, IPA_NONE) &&
-      !ipa_has_link_scope(e->attrs->gw) &&
-      (ospf_iface_find((struct proto_ospf *) p, e->attrs->iface) != NULL))
-    gw = 1;
+  ext->metric = metric; 
+  ext->netmask = ipa_mkmask(n->n.pxlen);
+  ext->fwaddr = fwaddr;
+  ext->tag = tag;
 
-#ifdef OSPFv3
-  size += IPV6_PREFIX_SPACE(n->n.pxlen);
+  return ext;
+}
 
-  if (gw)
-    size += 16;
-  
-  if (tag)
-    size += 4;
-#endif
-  
-  ext = mb_alloc(p->pool, size);
-  *length = sizeof(struct ospf_lsa_header) + size;
+/*
+ * 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.
+ */
 
-  ext->metric = (m1 != LSINFINITY) ? m1 : (m2 | LSA_EXT_EBIT); 
+static inline int
+check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag)
+{
+  struct ospf_lsa_ext *ext = en->lsa_body;
+
+  /* LSAID collision */
+  if  (fn->pxlen != ipa_mklen(ext->netmask))
+    return -1;
+
+  return (ext->metric == metric) && (ext->tag == tag) && (ext->fwaddr == fwaddr);
+}
 
-#ifdef OSPFv2
-  ext->netmask = ipa_mkmask(n->n.pxlen);
-  ext->fwaddr = gw ? e->attrs->gw : IPA_NONE;
-  ext->tag = tag;
 #else /* OSPFv3 */
+
+static inline void *
+originate_ext_lsa_body(struct proto_ospf *po, u16 *length, net *n,
+                      u32 metric, ip_addr fwaddr, u32 tag)
+{
+  int size = sizeof(struct ospf_lsa_ext)
+    + IPV6_PREFIX_SPACE(n->n.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->metric = metric;
+
   u32 *buf = ext->rest;
   buf = put_ipv6_prefix(buf, n->n.prefix, n->n.pxlen, 0, 0);
 
-  if (gw)
-    {
-      ext->metric |= LSA_EXT_FBIT;
-      buf = put_ipv6_addr(buf, e->attrs->gw);
-    }
+  if (ipa_nonzero(fwaddr))
+  {
+    ext->metric |= LSA_EXT_FBIT;
+    buf = put_ipv6_addr(buf, fwaddr);
+  }
 
   if (tag)
-    {
-      ext->metric |= LSA_EXT_TBIT;
-      *buf++ = tag;
-    }
-#endif
+  {
+    ext->metric |= LSA_EXT_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)
+{
+  struct ospf_lsa_ext *ext = en->lsa_body;
+  ip_addr prefix;
+  int pxlen;
+  u8 pxopts;
+  u16 rest;
+
+  u32 *buf = lsa_get_ipv6_prefix(ext->rest, &prefix, &pxlen, &pxopts, &rest);
+
+  /* LSAID collision */
+  if ((fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix))
+    return -1;
+
+  u32 rt_metric = ext->metric & METRIC_MASK;
+  ip_addr rt_fwaddr = IPA_NONE;
+  u32 rt_tag = 0;
+
+  if (ext->metric & LSA_EXT_FBIT)
+    buf = lsa_get_ipv6_addr(buf, &rt_fwaddr);
+
+  if (ext->metric & LSA_EXT_TBIT)
+    rt_tag = *buf++;
+
+  return (rt_metric == metric) && ipa_equal(rt_fwaddr, fwaddr) && (rt_tag == tag);
+}
+
+
+#endif
+
 /**
  * originate_ext_lsa - new route received from nest and filters
  * @n: network prefix and mask
@@ -882,9 +951,7 @@ originate_ext_lsa_body(net *n, rte *e, u16 *length, struct proto_ospf *po,
  * @attrs: list of extended attributes
  *
  * If I receive a message that new route is installed, I try to originate an
- * external LSA. The LSA header of such LSA does not contain information about
- * prefix length, so if I have to originate multiple LSAs for route with
- * different prefixes I try to increment prefix id to find a "free" one.
+ * external LSA.
  *
  * The function also sets flag ebit. If it's the first time, the new router lsa
  * origination is necessary.
@@ -912,19 +979,36 @@ originate_ext_lsa(net * n, rte * e, struct proto_ospf *po,
   lsa.rt = po->router_id;
   lsa.sn = LSA_INITSEQNO;
 
+  /* Compute LSA content */
+  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 ((e->attrs->dest == RTD_ROUTER) &&
+      ipa_nonzero(e->attrs->gw) &&
+      !ipa_has_link_scope(e->attrs->gw) &&
+      (ospf_iface_find((struct proto_ospf *) p, e->attrs->iface) != NULL))
+    gw = e->attrs->gw;
+
   if ((en = ospf_hash_find_header(po->gr, 0, &lsa)) != NULL)
+  {
+    int rv = check_ext_lsa(en, fn, metric, gw, tag);
+    if (rv < 0)
     {
-      if (check_ext_lsaid_collision(fn, en))
-       {
-         log(L_ERR, "%s: LSAID collision for %I/%d",
-             p->name, fn->prefix, fn->pxlen);
-         return;
-       }
-
-      lsa.sn = en->lsa.sn + 1;
+      log(L_ERR, "%s: LSAID collision for %I/%d",
+         p->name, fn->prefix, fn->pxlen);
+      return;
     }
 
-  body = originate_ext_lsa_body(n, e, &lsa.length, po, attrs);
+    if (rv > 0)
+      return;
+
+    lsa.sn = en->lsa.sn + 1;
+  }
+
+  body = originate_ext_lsa_body(po, &lsa.length, n, metric, gw, tag);
   lsasum_calculate(&lsa, body);
 
   en = lsa_install_new(po, &lsa, 0, body);
@@ -954,7 +1038,7 @@ flush_ext_lsa(net *n, struct proto_ospf *po)
 
   if (en = ospf_hash_find(po->gr, 0, lsaid, po->router_id, LSA_T_EXT))
     {
-      if (check_ext_lsaid_collision(fn, en))
+      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);