]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
OSPF: Opaque LSAs and Router Information LSA
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 17 Feb 2019 18:17:31 +0000 (19:17 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 17 Feb 2019 22:02:05 +0000 (23:02 +0100)
Add support for OSPFv2 Opaque LSAs (RFC 5250) and for Router Information
LSA (RFC 7770). The second part is here mainly for testing opaque LSAs.

lib/birdlib.h
proto/ospf/dbdes.c
proto/ospf/lsalib.c
proto/ospf/lsalib.h
proto/ospf/lsreq.c
proto/ospf/lsupd.c
proto/ospf/ospf.c
proto/ospf/ospf.h
proto/ospf/topology.c

index b336ecebe9972c4218c70d132fda08b99dc380b5..779d2f89af2912a450a6162385feb58d024c1c9f 100644 (file)
@@ -56,6 +56,13 @@ static inline int u64_cmp(u64 i1, u64 i2)
 #define BIT32_CLR(b,p)         ((b)[(p)/32] &= ~BIT32_VAL(p))
 #define BIT32_ZERO(b,l)                memset((b), 0, (l)/8)
 
+/* The same, but counting bits from MSB */
+#define BIT32R_VAL(p)          ((((u32) 1) << 31) >> ((p) % 32))
+#define BIT32R_TEST(b,p)       ((b)[(p)/32] & BIT32R_VAL(p))
+#define BIT32R_SET(b,p)                ((b)[(p)/32] |= BIT32R_VAL(p))
+#define BIT32R_CLR(b,p)                ((b)[(p)/32] &= ~BIT32R_VAL(p))
+#define BIT32R_ZERO(b,l)       memset((b), 0, (l)/8)
+
 #ifndef NULL
 #define NULL ((void *) 0)
 #endif
index 3c22850871ce3da378f41585ccceb9ad05e82a1c..f0873da3f1f603762c1bb64a5ba820e0fde7277a 100644 (file)
@@ -121,7 +121,7 @@ ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
   {
     struct ospf_dbdes2_packet *ps = (void *) pkt;
     ps->iface_mtu = htons(iface_mtu);
-    ps->options = ifa->oa->options;
+    ps->options = ifa->oa->options | OPT_O;
     ps->imms = 0;      /* Will be set later */
     ps->ddseq = htonl(n->dds);
     length = sizeof(struct ospf_dbdes2_packet);
@@ -156,7 +156,8 @@ ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
       }
 
       if ((en->lsa.age < LSA_MAXAGE) &&
-         lsa_flooding_allowed(en->lsa_type, en->domain, ifa))
+         lsa_flooding_allowed(en->lsa_type, en->domain, ifa) &&
+         lsa_is_acceptable(en->lsa_type, n, p))
       {
        lsa_hton_hdr(&(en->lsa), lsas + i);
        i++;
index fbfd8d295189e37b89d15fd9aae00332b649d53b..e66d3dc05be78c2e05449a5a4134334520be2cb0 100644 (file)
@@ -91,6 +91,30 @@ lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa)
   }
 }
 
+int
+lsa_is_acceptable(u32 type, struct ospf_neighbor *n, struct ospf_proto *p)
+{
+  if (ospf_is_v2(p))
+  {
+    if (type == LSA_T_NSSA)
+      return !!(n->options & OPT_N);
+
+    if (lsa_is_opaque(type))
+      return !!(n->options & OPT_O);
+
+    return 1;
+  }
+  else
+  {
+    /*
+     * There should be check whether receiving router understands 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.
+     */
+
+    return 1;
+  }
+}
 
 static int
 unknown_lsa_type(u32 type)
@@ -105,6 +129,9 @@ unknown_lsa_type(u32 type)
   case LSA_T_NSSA:
   case LSA_T_LINK:
   case LSA_T_PREFIX:
+  case LSA_T_RI_LINK:
+  case LSA_T_RI_AREA:
+  case LSA_T_RI_AS:
     return 0;
 
   default:
@@ -112,28 +139,47 @@ unknown_lsa_type(u32 type)
   }
 }
 
-#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};
+/* Maps OSPFv2 types to OSPFv3 types */
+static const u16 lsa_v2_types[] = {
+  0, LSA_T_RT, LSA_T_NET, LSA_T_SUM_NET, LSA_T_SUM_RT, LSA_T_EXT, 0, LSA_T_NSSA,
+  0, LSA_T_OPAQUE_LINK, LSA_T_OPAQUE_AREA, LSA_T_OPAQUE_AS
+};
+
+/* Maps OSPFv2 opaque types to OSPFv3 function codes */
+static const u16 opaque_lsa_types[] = {
+  [LSA_OT_RI] = LSA_T_RI_,
+};
+
+/* Maps (subset of) OSPFv3 function codes to OSPFv2 opaque types */
+static const u8 opaque_lsa_types_inv[] = {
+  [LSA_T_RI_] = LSA_OT_RI,
+};
+
+#define LOOKUP(a, i) ({ uint _i = (i); (_i < ARRAY_SIZE(a)) ? a[_i] : 0; })
 
 void
-lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain)
+lsa_get_type_domain_(u32 type, u32 id, 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;
+    type = type & LSA_T_V2_MASK;
+    type = LOOKUP(lsa_v2_types, type);
+
+    uint code;
+    if (LSA_FUNCTION(type) == LSA_T_OPAQUE_)
+      if (code = LOOKUP(opaque_lsa_types, id >> 24))
+       type = code | LSA_UBIT | LSA_SCOPE(type);
   }
   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;
+    if (unknown_lsa_type(type) && !(type & LSA_UBIT))
+      type = type & ~LSA_SCOPE_MASK;
   }
 
-  *otype = itype;
+  *otype = type;
 
-  switch (LSA_SCOPE(itype))
+  switch (LSA_SCOPE(type))
   {
   case LSA_SCOPE_LINK:
     *domain = ifa->iface_id;
@@ -150,6 +196,12 @@ lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain)
   }
 }
 
+u32
+lsa_get_opaque_type(u32 type)
+{
+  return LOOKUP(opaque_lsa_types_inv, LSA_FUNCTION(type));
+}
+
 
 void
 lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body)
@@ -548,6 +600,17 @@ 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);
 }
 
+static int
+lsa_validate_ri(struct ospf_lsa_header *lsa UNUSED, struct ospf_lsa_net *body UNUSED)
+{
+  /*
+   * There should be proper validation. But we do not really process RI LSAs, so
+   * we can just accept them like another unknown opaque LSAs.
+   */
+
+  return 1;
+}
+
 
 /**
  * lsa_validate - check whether given LSA is valid
@@ -577,6 +640,14 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
     case LSA_T_EXT:
     case LSA_T_NSSA:
       return lsa_validate_ext2(lsa, body);
+    case LSA_T_RI_LINK:
+    case LSA_T_RI_AREA:
+    case LSA_T_RI_AS:
+      return lsa_validate_ri(lsa, body);
+    case LSA_T_OPAQUE_LINK:
+    case LSA_T_OPAQUE_AREA:
+    case LSA_T_OPAQUE_AS:
+      return 1;        /* Unknown Opaque LSAs */
     default:
       return 0;        /* Should not happen, unknown LSAs are already rejected */
     }
@@ -600,6 +671,10 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
       return lsa_validate_link(lsa, body);
     case LSA_T_PREFIX:
       return lsa_validate_prefix(lsa, body);
+    case LSA_T_RI_LINK:
+    case LSA_T_RI_AREA:
+    case LSA_T_RI_AS:
+      return lsa_validate_ri(lsa, body);
     default:
       return 1;        /* Unknown LSAs are OK in OSPFv3 */
     }
index fca7faec78bd5ef9f3944a25903b23a55f683980..af8901cebe17789f9ccf92f3bd7ba9dd013e20d0 100644 (file)
@@ -36,16 +36,21 @@ struct ospf_lsa_rt_walk {
 };
 
 
-void lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain);
+void lsa_get_type_domain_(u32 type, u32 id, 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); }
+{ lsa_get_type_domain_(lsa->type_raw, lsa->id, 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; }
 
+/* Assuming OSPFv2 - All U-bit LSAs are mapped to Opaque LSAs */
+static inline int lsa_is_opaque(u32 type)
+{ return !!(type & LSA_UBIT); }
 
+u32 lsa_get_opaque_type(u32 type);
 int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
+int lsa_is_acceptable(u32 type, struct ospf_neighbor *n, struct ospf_proto *p);
 void lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body);
 u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
 
index b4f5f85abf1adf0719b84ff68b6f872628ef9ebf..9f133a32058256d0a72909fc46479a97fa0c5e6f 100644 (file)
@@ -126,7 +126,7 @@ ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa,
 
     id = ntohl(lsrs[i].id);
     rt = ntohl(lsrs[i].rt);
-    lsa_get_type_domain_(ntohl(lsrs[i].type), ifa, &type, &domain);
+    lsa_get_type_domain_(ntohl(lsrs[i].type), id, ifa, &type, &domain);
 
     DBG("Processing requested LSA: Type: %04x, Id: %R, Rt: %R\n", type, id, rt);
 
index 09a38e3d34982792064846de2dd046406ff1e2fa..36be7f55de07e48006d87cc8d4c7ed2b28b8e5c7 100644 (file)
@@ -173,7 +173,8 @@ ospf_add_flushed_to_lsrt(struct ospf_proto *p, struct ospf_neighbor *n)
 
   WALK_SLIST(en, p->lsal)
     if ((en->lsa.age == LSA_MAXAGE) && (en->lsa_body != NULL) &&
-       lsa_flooding_allowed(en->lsa_type, en->domain, n->ifa))
+       lsa_flooding_allowed(en->lsa_type, en->domain, n->ifa) &&
+        lsa_is_acceptable(en->lsa_type, n, p))
       ospf_lsa_lsrt_up(en, n);
 
   /* If we found any flushed LSA, we send them ASAP */
@@ -289,9 +290,9 @@ ospf_flood_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neig
       if (n == from)
        continue;
 
-      /* 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 */
+      /* Check whether optional LSAs are supported by neighbor */
+      if (!lsa_is_acceptable(en->lsa_type, n, p))
+       continue;
 
       /* 13.3 (1d) - add LSA to the link state retransmission list */
       ospf_lsa_lsrt_up(en, n);
index 5ac75d89997e5b852ea9b386b93cacfdea851a2e..a8b6986249377c1a4ee6c5b9722d3489f3bc5f0b 100644 (file)
  * - RFC 2328 - main OSPFv2 standard
  * - RFC 5340 - main OSPFv3 standard
  * - RFC 3101 - OSPFv2 NSSA areas
+ * - RFC 5250 - OSPFv2 Opaque LSAs
  * - RFC 5709 - OSPFv2 HMAC-SHA Cryptographic Authentication
  * - RFC 5838 - OSPFv3 Support of Address Families
  * - RFC 6549 - OSPFv2 Multi-Instance Extensions
  * - RFC 6987 - OSPF Stub Router Advertisement
+ * - RFC 7770 - OSPF Router Information LSA
  */
 
 #include <stdlib.h>
@@ -238,6 +240,7 @@ ospf_start(struct proto *P)
   p->rfc1583 = c->rfc1583;
   p->stub_router = c->stub_router;
   p->merge_external = c->merge_external;
+  p->instance_id = c->instance_id;
   p->asbr = c->asbr;
   p->ecmp = c->ecmp;
   p->tick = c->tick;
@@ -665,6 +668,9 @@ ospf_reconfigure(struct proto *P, struct proto_config *CF)
   if (p->rfc1583 != new->rfc1583)
     return 0;
 
+  if (p->instance_id != new->instance_id)
+    return 0;
+
   if (old->abr != new->abr)
     return 0;
 
index 6a20300e523f9be2c270526d0611e8dca3589d6d..31440c24afe3078953240fccda7178bf7bf79a2d 100644 (file)
@@ -230,6 +230,7 @@ struct ospf_proto
   u8 rfc1583;                  /* RFC1583 compatibility */
   u8 stub_router;              /* Do not forward transit traffic */
   u8 merge_external;           /* Should i merge external routes? */
+  u8 instance_id;              /* Differentiate between more OSPF instances */
   u8 asbr;                     /* May i originate any ext/NSSA lsa? */
   u8 ecmp;                     /* Maximal number of nexthops in ECMP route, or 0 */
   struct ospf_area *backbone;  /* If exists */
@@ -475,6 +476,7 @@ struct ospf_neighbor
 #define OPT_EA         0x0010  /* OSPFv2, external attributes, not used and obsolete */
 #define OPT_R          0x0010  /* OSPFv3, originator is active router */
 #define OPT_DC         0x0020  /* Related to demand circuits, not used */
+#define OPT_O          0x0040  /* OSPFv2 Opaque LSA (RFC 5250) */
 #define OPT_AF         0x0100  /* OSPFv3 Address Families (RFC 5838) */
 
 /* Router-LSA VEB flags are are stored together with links (OSPFv2) or options (OSPFv3) */
@@ -530,25 +532,44 @@ union ospf_auth
 #define DBDES_IMMS     (DBDES_I | DBDES_M | DBDES_MS)
 
 
-#define LSA_T_RT       0x2001
-#define LSA_T_NET      0x2002
-#define LSA_T_SUM_NET  0x2003
-#define LSA_T_SUM_RT   0x2004
-#define LSA_T_EXT      0x4005
-#define LSA_T_NSSA     0x2007
-#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
-#define LSA_SCOPE_AREA 0x2000
-#define LSA_SCOPE_AS   0x4000
-#define LSA_SCOPE_RES  0x6000
-#define LSA_SCOPE_MASK 0x6000
-#define LSA_SCOPE(type)        ((type) & LSA_SCOPE_MASK)
+/* OSPFv3 LSA Types / LSA Function Codes */
+/* https://www.iana.org/assignments/ospfv3-parameters/ospfv3-parameters.xhtml#ospfv3-parameters-3 */
+#define LSA_T_RT               0x2001
+#define LSA_T_NET              0x2002
+#define LSA_T_SUM_NET          0x2003
+#define LSA_T_SUM_RT           0x2004
+#define LSA_T_EXT              0x4005
+#define LSA_T_NSSA             0x2007
+#define LSA_T_LINK             0x0008
+#define LSA_T_PREFIX           0x2009
+#define LSA_T_RI_              0x000C
+#define LSA_T_RI_LINK          0x800C
+#define LSA_T_RI_AREA          0xA00C
+#define LSA_T_RI_AS            0xC00C
+#define LSA_T_OPAQUE_          0x1FFF
+#define LSA_T_OPAQUE_LINK      0x9FFF
+#define LSA_T_OPAQUE_AREA      0xBFFF
+#define LSA_T_OPAQUE_AS                0xDFFF
+
+#define LSA_T_V2_OPAQUE_       0x0009
+#define LSA_T_V2_MASK          0x00ff
+
+/* OSPFv2 Opaque LSA Types */
+/* https://www.iana.org/assignments/ospf-opaque-types/ospf-opaque-types.xhtml#ospf-opaque-types-2 */
+#define LSA_OT_RI              0x04
+
+#define LSA_FUNCTION_MASK      0x1FFF
+#define LSA_FUNCTION(type)     ((type) & LSA_FUNCTION_MASK)
+
+#define LSA_UBIT               0x8000
+
+#define LSA_SCOPE_LINK         0x0000
+#define LSA_SCOPE_AREA         0x2000
+#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_ORDER(type)  (((type) >> 13) & 0x3)
 
 
 #define LSA_MAXAGE     3600    /* 1 hour */
@@ -575,9 +596,20 @@ union ospf_auth
 #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
+#define LSA_EXT3_EBIT  0x04000000
+#define LSA_EXT3_FBIT  0x02000000
+#define LSA_EXT3_TBIT  0x01000000
+
+/* OSPF Router Information (RI) TLVs */
+/* https://www.iana.org/assignments/ospf-parameters/ospf-parameters.xhtml#ri-tlv */
+#define LSA_RI_RIC             1
+#define LSA_RI_RFC             2
+
+/* OSPF Router Informational Capability Bits */
+/* https://www.iana.org/assignments/ospf-parameters/ospf-parameters.xhtml#router-informational-capability */
+#define LSA_RIC_GR_CAPABLE     0
+#define LSA_RIC_GR_HELPER      1
+#define LSA_RIC_STUB_ROUTER    2
 
 
 struct ospf_lsa_header
@@ -720,6 +752,18 @@ struct ospf_lsa_prefix
   u32 rest[];
 };
 
+struct ospf_tlv
+{
+#ifdef CPU_BIG_ENDIAN
+  u16 type;
+  u16 length;
+#else
+  u16 length;
+  u16 type;
+#endif
+  u32 data[];
+};
+
 
 static inline uint
 lsa_net_count(struct ospf_lsa_header *lsa)
index b0ff055ae7a6619d8fce9beee5cda0b78a71f272..efe4bd7ccab188d7bea2bdf82a6cc8e9af9a9b75 100644 (file)
@@ -224,12 +224,17 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
   /*
    * 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.
+   * old type is just new type masked by 0xff. That holds for most OSPFv2 types,
+   * but we have to fix it for opaque LSAs.
    */
 
   if (ospf_is_v2(p))
+  {
+    if (lsa_is_opaque(en->lsa_type))
+      en->lsa.type_raw = LSA_T_V2_OPAQUE_ + LSA_SCOPE_ORDER(en->lsa_type);
+
     lsa_set_options(&en->lsa, lsa_opts);
+  }
 
   mb_free(en->lsa_body);
   en->lsa_body = lsa_body;
@@ -273,6 +278,10 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa)
   u16 lsa_blen = p->lsab_used;
   u16 lsa_length = sizeof(struct ospf_lsa_header) + lsa_blen;
 
+  /* For OSPFv2 Opaque LSAs, LS ID consists of Opaque Type and Opaque ID */
+  if (ospf_is_v2(p) && lsa_is_opaque(lsa->type))
+    lsa->id |= (u32) lsa_get_opaque_type(lsa->type) << 24;
+
   en = ospf_hash_get(p->gr, lsa->dom, lsa->id, p->router_id, lsa->type);
 
   if (!SNODE_VALID(en))
@@ -1638,6 +1647,41 @@ ospf_originate_prefix_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
   ifa->pxn_lsa = ospf_originate_lsa(p, &lsa);
 }
 
+
+/*
+ *     Router Information LSA handling
+ *     Type = LSA_T_RI_AREA, opaque type = LSA_OT_RI
+ */
+
+void
+ospf_add_ric_tlv(struct ospf_proto *p)
+{
+  struct ospf_tlv *ri = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+  ri->type = LSA_RI_RIC;
+  ri->length = sizeof(struct ospf_tlv) + sizeof(u32);
+
+  BIT32R_SET(ri->data, LSA_RIC_STUB_ROUTER);
+}
+
+void
+ospf_originate_ri_lsa(struct ospf_proto *p, struct ospf_area *oa)
+{
+  struct ospf_new_lsa lsa = {
+    .type = LSA_T_RI_AREA,
+    .dom  = oa->areaid,
+    .id   = p->instance_id
+  };
+
+  ospf_add_ric_tlv(p);
+
+  ospf_originate_lsa(p, &lsa);
+}
+
+
+/*
+ *     Generic topology code
+ */
+
 static inline int breaks_minlsinterval(struct top_hash_entry *en)
 { return en && (en->lsa.age < LSA_MAXAGE) && (lsa_inst_age(en) < MINLSINTERVAL); }
 
@@ -1672,6 +1716,7 @@ ospf_update_topology(struct ospf_proto *p)
 
       ospf_originate_rt_lsa(p, oa);
       ospf_originate_prefix_rt_lsa(p, oa);
+      // ospf_originate_ri_lsa(p, oa);
       oa->update_rt_lsa = 0;
     }
   }