]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
OSPF: Support of address families in OSPFv3
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 8 Oct 2017 23:16:29 +0000 (01:16 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 10 Oct 2017 14:10:02 +0000 (16:10 +0200)
OSPFv3-AF can handle multiple topologies of diferent address families
(IPv4, IPv6, both unicast and multicast) using separate instances
distinguished by instance ID ranges.

proto/ospf/config.Y
proto/ospf/dbdes.c
proto/ospf/hello.c
proto/ospf/iface.c
proto/ospf/lsalib.c
proto/ospf/lsalib.h
proto/ospf/ospf.c
proto/ospf/ospf.h
proto/ospf/rt.c
proto/ospf/topology.c

index 1f379bf4df1ccb7e81dc894d6f709c30d63b537a..85149ea1edf4f8d749c201bdad8a14c554841f99 100644 (file)
@@ -78,18 +78,66 @@ static void
 ospf_proto_finish(void)
 {
   struct ospf_config *cf = OSPF_CFG;
-
-  if (EMPTY_LIST(cf->area_list))
-    cf_error( "No configured areas in OSPF");
+  struct ospf_area_config *ac;
+  struct ospf_iface_patt *ic;
 
   /* Define default channel */
   if (EMPTY_LIST(this_proto->channels))
+  {
+    this_proto->net_type = ospf_cfg_is_v2() ? NET_IP4 : NET_IP6;
     channel_config_new(NULL, this_proto->net_type, this_proto);
+  }
+
+  /* Propagate global instance ID to interfaces */
+  if (cf->instance_id_set)
+  {
+    WALK_LIST(ac, cf->area_list)
+      WALK_LIST(ic, ac->patt_list)
+       if (!ic->instance_id_set)
+       { ic->instance_id = cf->instance_id; ic->instance_id_set = 1; }
+
+    WALK_LIST(ic, cf->vlink_list)
+      if (!ic->instance_id_set)
+      { ic->instance_id = cf->instance_id; ic->instance_id_set = 1; }
+  }
+
+  if (ospf_cfg_is_v3())
+  {
+    uint ipv4 = (this_proto->net_type == NET_IP4);
+    uint base = (ipv4 ? 64 : 0) + (cf->af_mc ? 32 : 0);
+
+    /* RFC 5838 - OSPFv3-AF */
+    if (cf->af_ext)
+    {
+      /* RFC 5838 2.1 - instance IDs based on AFs */
+      WALK_LIST(ac, cf->area_list)
+       WALK_LIST(ic, ac->patt_list)
+       {
+         if (!ic->instance_id_set)
+           ic->instance_id = base;
+         else if (ic->instance_id >= 128)
+           log(L_WARN "Instance ID %d from unassigned/private range", ic->instance_id);
+         else if ((ic->instance_id < base) || (ic->instance_id >= (base + 32)))
+           cf_error("Instance ID %d invalid for given channel type", ic->instance_id);
+       }
+
+      /* RFC 5838 2.8 - vlinks limited to IPv6 unicast */
+      if ((ipv4 || cf->af_mc) && !EMPTY_LIST(cf->vlink_list))
+       cf_error("Vlinks not supported in AFs other than IPv6 unicast");
+    }
+    else
+    {
+      if (ipv4 || cf->af_mc)
+       cf_error("Different channel type");
+    }
+  }
+
+  if (EMPTY_LIST(cf->area_list))
+    cf_error("No configured areas in OSPF");
 
   int areano = 0;
   int backbone = 0;
   int nssa = 0;
-  struct ospf_area_config *ac;
   WALK_LIST(ac, cf->area_list)
   {
     areano++;
@@ -148,10 +196,11 @@ 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, LSA, SUPPRESSION)
+CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION, MULTICAST)
 
 %type <ld> lsadb_args
-%type <i> ospf_variant nbma_eligible
+%type <i> ospf_variant ospf_af_mc nbma_eligible
+%type <cc> ospf_channel_start ospf_channel
 
 CF_GRAMMAR
 
@@ -166,12 +215,13 @@ ospf_variant:
 ospf_proto_start: proto_start ospf_variant
 {
   this_proto = proto_config_new(&proto_ospf, $1);
-  this_proto->net_type = $2 ? NET_IP4 : NET_IP6;
+  this_proto->net_type = $2 ? NET_IP4 : 0;
 
   init_list(&OSPF_CFG->area_list);
   init_list(&OSPF_CFG->vlink_list);
   OSPF_CFG->tick = OSPF_DEFAULT_TICK;
   OSPF_CFG->ospf2 = $2;
+  OSPF_CFG->af_ext = !$2;
 };
 
 ospf_proto:
@@ -179,16 +229,33 @@ ospf_proto:
  | ospf_proto ospf_proto_item ';'
  ;
 
+ospf_af_mc:
+             { $$ = 0; }
+ | MULTICAST { $$ = 1; }
+ ;
+
+/* We redefine proto_channel to add multicast flag */
+ospf_channel_start: net_type ospf_af_mc
+{
+  $$ = this_channel = channel_config_new(NULL, $1, this_proto);
+
+  /* Save the multicast flag */
+  if (this_channel == proto_cf_main_channel(this_proto))
+    OSPF_CFG->af_mc = $2;
+};
+
+ospf_channel: ospf_channel_start channel_opt_list channel_end;
+
 ospf_proto_item:
    proto_item
- | proto_channel
+ | ospf_channel { this_proto->net_type = $1->net_type; }
  | RFC1583COMPAT bool { OSPF_CFG->rfc1583 = $2; }
  | STUB ROUTER bool { OSPF_CFG->stub_router = $3; }
  | ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; }
  | ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; }
  | 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"); }
- | INSTANCE ID expr { OSPF_CFG->instance_id = $3; if ($3 > 255) cf_error("Instance ID must be in range 0-255"); }
+ | INSTANCE ID expr { OSPF_CFG->instance_id = $3; OSPF_CFG->instance_id_set = 1; if ($3 > 255) cf_error("Instance ID must be in range 0-255"); }
  | ospf_area
  ;
 
@@ -293,7 +360,6 @@ ospf_vlink_start: VIRTUAL LINK idval
   OSPF_PATT->inftransdelay = INFTRANSDELAY_D;
   OSPF_PATT->deadc = DEADC_D;
   OSPF_PATT->type = OSPF_IT_VLINK;
-  OSPF_PATT->instance_id = OSPF_CFG->instance_id;
   init_list(&OSPF_PATT->nbma_list);
   reset_passwords();
  }
@@ -393,7 +459,6 @@ ospf_iface_start:
   OSPF_PATT->priority = PRIORITY_D;
   OSPF_PATT->deadc = DEADC_D;
   OSPF_PATT->type = OSPF_IT_UNDEF;
-  OSPF_PATT->instance_id = OSPF_CFG->instance_id;
   init_list(&OSPF_PATT->nbma_list);
   OSPF_PATT->ptp_netmask = 2; /* not specified */
   OSPF_PATT->tx_tos = IP_PREC_INTERNET_CONTROL;
@@ -404,7 +469,7 @@ ospf_iface_start:
 
 ospf_instance_id:
    /* empty */
- | INSTANCE expr { OSPF_PATT->instance_id = $2; if ($2 > 255) cf_error("Instance ID must be in range 0-255"); }
+ | INSTANCE expr { OSPF_PATT->instance_id = $2; OSPF_PATT->instance_id_set = 1; if ($2 > 255) cf_error("Instance ID must be in range 0-255"); }
  ;
 
 ospf_iface_patt_list:
index 195b03adefdcad642beaccc4bae1fbff00594dd6..a4452cc82315d49f75f0792057d55f15cadf57a9 100644 (file)
@@ -356,7 +356,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa,
       LOG_PKT_WARN("MTU mismatch with nbr %R on %s (remote %d, local %d)",
                   n->rid, ifa->ifname, rcv_iface_mtu, ifa->iface->mtu);
 
-    if ((rcv_imms == DBDES_IMMS) &&
+    if (((rcv_imms & DBDES_IMMS) == DBDES_IMMS) &&
        (n->rid > p->router_id) &&
        (plen == ospf_dbdes_hdrlen(p)))
     {
index 2c55155d0f8a69f022f25ae815838185d4fcaa18..e706ea0f63cfdf8dec6c98d1688779d06125346d 100644 (file)
@@ -32,10 +32,7 @@ struct ospf_hello3_packet
   struct ospf_packet hdr;
 
   u32 iface_id;
-  u8 priority;
-  u8 options3;
-  u8 options2;
-  u8 options;
+  u32 options;
   u16 helloint;
   u16 deadint;
   u32 dr;
@@ -91,10 +88,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn)
     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->options = ntohl(ifa->oa->options | (ifa->priority << 24));
     ps->helloint = ntohs(ifa->helloint);
     ps->deadint = htons(ifa->deadint);
     ps->dr = htonl(ifa->drid);
@@ -190,7 +184,8 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa,
   struct ospf_proto *p = ifa->oa->po;
   const char *err_dsc = NULL;
   u32 rcv_iface_id, rcv_helloint, rcv_deadint, rcv_dr, rcv_bdr;
-  u8 rcv_options, rcv_priority;
+  uint rcv_options, rcv_priority;
+  uint loc_options = ifa->oa->options;
   u32 *neighbors;
   u32 neigh_count;
   uint plen, i, err_val = 0;
@@ -245,8 +240,8 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa,
     rcv_deadint = ntohs(ps->deadint);
     rcv_dr = ntohl(ps->dr);
     rcv_bdr = ntohl(ps->bdr);
-    rcv_options = ps->options;
-    rcv_priority = ps->priority;
+    rcv_options = ntohl(ps->options) & 0x00FFFFFF;
+    rcv_priority = ntohl(ps->options) >> 24;
 
     neighbors = ps->neighbors;
     neigh_count = (plen - sizeof(struct ospf_hello3_packet)) / sizeof(u32);
@@ -259,9 +254,13 @@ ospf_receive_hello(struct ospf_packet *pkt, struct ospf_iface *ifa,
     DROP("dead interval mismatch", rcv_deadint);
 
   /* Check whether bits E, N match */
-  if ((rcv_options ^ ifa->oa->options) & (OPT_E | OPT_N))
+  if ((rcv_options ^ loc_options) & (OPT_E | OPT_N))
     DROP("area type mismatch", rcv_options);
 
+  /* RFC 5838 2.4 - AF-bit check unless on IPv6 unicast */
+  if ((loc_options & OPT_AF) && !(loc_options & OPT_V6) && !(rcv_options & OPT_AF))
+    DROP("AF-bit mismatch", rcv_options);
+
   /* Check consistency of existing neighbor entry */
   if (n)
   {
index 675cf76dc8d271391fab05a9ee1378531a823851..98d48aa1e7ece66cd16aa6f9d90ef1f22f1ab409 100644 (file)
@@ -1113,9 +1113,6 @@ ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a)
 {
   struct ospf_proto *p = (struct ospf_proto *) P;
 
-  if (a->prefix.type != NET_IP6)
-    return;
-
   if (a->flags & IA_SECONDARY)
     return;
 
@@ -1126,6 +1123,9 @@ ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a)
      other addresses are used for link-LSA. */
   if (a->scope == SCOPE_LINK)
   {
+    if (a->prefix.type != NET_IP6)
+      return;
+
     if (flags & IF_CHANGE_UP)
     {
       struct ospf_mip_walk s = { .iface = a->iface };
@@ -1143,6 +1143,9 @@ ospf_ifa_notify3(struct proto *P, uint flags, struct ifa *a)
   }
   else
   {
+    if (a->prefix.type != ospf_get_af(p))
+      return;
+
     struct ospf_iface *ifa;
     WALK_LIST(ifa, p->iface_list)
       if (ifa->iface == a->iface)
index b88a114d3e03d2ce675a43b7be44f9b5ae72f0e5..fbfd8d295189e37b89d15fd9aae00332b649d53b 100644 (file)
@@ -280,7 +280,7 @@ lsa_walk_rt(struct ospf_lsa_rt_walk *rt)
 
 
 void
-lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopts, u32 *metric)
+lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, int af, net_addr *net, u8 *pxopts, u32 *metric)
 {
   if (ospf2)
   {
@@ -292,7 +292,7 @@ lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopt
   else
   {
     struct ospf_lsa_sum3_net *ls = en->lsa_body;
-    ospf_get_ipv6_prefix(ls->prefix, net, pxopts, NULL);
+    ospf3_get_prefix(ls->prefix, af, net, pxopts, NULL);
     *metric = ls->metric & LSA_METRIC_MASK;
   }
 }
@@ -317,7 +317,7 @@ lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u
 }
 
 void
-lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt)
+lsa_parse_ext(struct top_hash_entry *en, int ospf2, int af, struct ospf_lsa_ext_local *rt)
 {
   if (ospf2)
   {
@@ -338,13 +338,13 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *r
   else
   {
     struct ospf_lsa_ext3 *ext = en->lsa_body;
-    u32 *buf = ospf_get_ipv6_prefix(ext->rest, &rt->net, &rt->pxopts, NULL);
+    u32 *buf = ospf3_get_prefix(ext->rest, af, &rt->net, &rt->pxopts, NULL);
     rt->metric = ext->metric & LSA_METRIC_MASK;
     rt->ebit = ext->metric & LSA_EXT3_EBIT;
 
     rt->fbit = ext->metric & LSA_EXT3_FBIT;
     if (rt->fbit)
-      buf = ospf_get_ipv6_addr(buf, &rt->fwaddr);
+      buf = ospf3_get_addr(buf, af, &rt->fwaddr);
     else
       rt->fwaddr = IPA_NONE;
 
index c93f029566694a7ee38dda8b60e5c53d415c962f..0d477f58b44db415e3300aa02f1dd51184b55667 100644 (file)
@@ -55,9 +55,9 @@ u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
 int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2);
 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, net_addr *net, u8 *pxopts, u32 *metric);
+void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, int af, net_addr *net, u8 *pxopts, u32 *metric);
 void lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u32 *options);
-void lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt);
+void lsa_parse_ext(struct top_hash_entry *en, int ospf2, int af, 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 daf76ff26e3d033b39c781b0a9adc51abbd4990c..7ce6698e5aaf7943034b514f77c676c672d17005 100644 (file)
  * - RFC 2328 - main OSPFv2 standard
  * - RFC 5340 - main OSPFv3 standard
  * - RFC 3101 - OSPFv2 NSSA areas
- * - RFC 6549 - OSPFv2 multi-instance extensions
- * - RFC 6987 - OSPF stub router advertisement
+ * - 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
  */
 
 #include <stdlib.h>
@@ -115,9 +117,9 @@ add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac)
   struct area_net_config *anc;
   struct area_net *an;
 
-  fib_init(&oa->net_fib,  p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6,
+  fib_init(&oa->net_fib,  p->p.pool, ospf_get_af(p),
           sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL);
-  fib_init(&oa->enet_fib, p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6,
+  fib_init(&oa->enet_fib, p->p.pool, ospf_get_af(p),
           sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL);
 
   WALK_LIST(anc, ac->net_list)
@@ -134,6 +136,16 @@ add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac)
   }
 }
 
+static inline uint
+ospf_opts(struct ospf_proto *p)
+{
+  if (ospf_is_v2(p))
+    return 0;
+
+  return ((ospf_is_ip6(p) && !p->af_mc) ? OPT_V6 : 0) |
+    (!p->stub_router ? OPT_R : 0) | (p->af_ext ? OPT_AF : 0);
+}
+
 static void
 ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac)
 {
@@ -155,10 +167,7 @@ ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac)
   if (oa->areaid == 0)
     p->backbone = oa;
 
-  if (ospf_is_v2(p))
-    oa->options = ac->type;
-  else
-    oa->options = ac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R);
+  oa->options = ac->type | ospf_opts(p);
 
   ospf_notify_rt_lsa(oa);
 }
@@ -224,6 +233,8 @@ ospf_start(struct proto *P)
 
   p->router_id = proto_get_router_id(P->cf);
   p->ospf2 = c->ospf2;
+  p->af_ext = c->af_ext;
+  p->af_mc = c->af_mc;
   p->rfc1583 = c->rfc1583;
   p->stub_router = c->stub_router;
   p->merge_external = c->merge_external;
@@ -238,8 +249,7 @@ ospf_start(struct proto *P)
   p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop));
   init_list(&(p->iface_list));
   init_list(&(p->area_list));
-  fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6,
-          sizeof(ort), OFFSETOF(ort, fn), 0, NULL);
+  fib_init(&p->rtf, P->pool, ospf_get_af(p), sizeof(ort), OFFSETOF(ort, fn), 0, NULL);
   if (ospf_is_v3(p))
     idm_init(&p->idm, P->pool, 16);
   p->areano = 0;
@@ -601,11 +611,7 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
   struct ospf_iface *ifa;
 
   oa->ac = nac;
-
-  if (ospf_is_v2(p))
-    oa->options = nac->type;
-  else
-    oa->options = nac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R);
+  oa->options = nac->type | ospf_opts(p);
 
   if (nac->type != oac->type)
   {
@@ -659,6 +665,9 @@ ospf_reconfigure(struct proto *P, struct proto_config *CF)
   if (old->abr != new->abr)
     return 0;
 
+  if ((p->af_ext != new->af_ext) || (p->af_mc != new->af_mc))
+    return 0;
+
   if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
     return 0;
 
@@ -1073,13 +1082,13 @@ show_lsa_network(struct top_hash_entry *he, int ospf2)
 }
 
 static inline void
-show_lsa_sum_net(struct top_hash_entry *he, int ospf2)
+show_lsa_sum_net(struct top_hash_entry *he, int ospf2, int af)
 {
   net_addr net;
   u8 pxopts;
   u32 metric;
 
-  lsa_parse_sum_net(he, ospf2, &net, &pxopts, &metric);
+  lsa_parse_sum_net(he, ospf2, af, &net, &pxopts, &metric);
   cli_msg(-1016, "\t\txnetwork %N metric %u", &net, metric);
 }
 
@@ -1096,7 +1105,7 @@ show_lsa_sum_rt(struct top_hash_entry *he, int ospf2)
 
 
 static inline void
-show_lsa_external(struct top_hash_entry *he, int ospf2)
+show_lsa_external(struct top_hash_entry *he, int ospf2, int af)
 {
   struct ospf_lsa_ext_local rt;
   char str_via[IPA_MAX_TEXT_LENGTH + 8] = "";
@@ -1105,7 +1114,7 @@ show_lsa_external(struct top_hash_entry *he, int ospf2)
   if (he->lsa_type == LSA_T_EXT)
     he->domain = 0; /* Unmark the LSA */
 
-  lsa_parse_ext(he, ospf2, &rt);
+  lsa_parse_ext(he, ospf2, af, &rt);
 
   if (rt.fbit)
     bsprintf(str_via, " via %I", rt.fwaddr);
@@ -1119,7 +1128,7 @@ show_lsa_external(struct top_hash_entry *he, int ospf2)
 }
 
 static inline void
-show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode)
+show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode, int af)
 {
   struct ospf_lsa_prefix *px = he->lsa_body;
   u32 *buf;
@@ -1142,7 +1151,7 @@ show_lsa_prefix(struct top_hash_entry *he, struct top_hash_entry *cnode)
     u8 pxopts;
     u16 metric;
 
-    buf = ospf_get_ipv6_prefix(buf, &net, &pxopts, &metric);
+    buf = ospf3_get_prefix(buf, af, &net, &pxopts, &metric);
 
     if (px->ref_type == LSA_T_RT)
       cli_msg(-1016, "\t\tstubnet %N metric %u", &net, metric);
@@ -1156,6 +1165,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
 {
   struct ospf_proto *p = (struct ospf_proto *) P;
   int ospf2 = ospf_is_v2(p);
+  int af = ospf_get_af(p);
   uint i, ix, j1, jx;
   u32 last_area = 0xFFFFFFFF;
 
@@ -1276,7 +1286,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
 
     case LSA_T_SUM_NET:
       if (cnode->lsa_type == LSA_T_RT)
-       show_lsa_sum_net(he, ospf2);
+       show_lsa_sum_net(he, ospf2, af);
       break;
 
     case LSA_T_SUM_RT:
@@ -1286,11 +1296,11 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
 
     case LSA_T_EXT:
     case LSA_T_NSSA:
-      show_lsa_external(he, ospf2);
+      show_lsa_external(he, ospf2, af);
       break;
 
     case LSA_T_PREFIX:
-      show_lsa_prefix(he, cnode);
+      show_lsa_prefix(he, cnode, af);
       break;
     }
 
@@ -1304,7 +1314,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
        ix++;
 
       while ((ix < jx) && (hex[ix]->lsa.rt == cnode->lsa.rt))
-       show_lsa_external(hex[ix++], ospf2);
+       show_lsa_external(hex[ix++], ospf2, af);
 
       cnode = NULL;
     }
@@ -1338,7 +1348,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable)
        last_rt = he->lsa.rt;
       }
 
-      show_lsa_external(he, ospf2);
+      show_lsa_external(he, ospf2, af);
     }
   }
 
index e3eae2b55171fda98edb730b12c38ed70698287f..1530b26a5c47fdeabe5f0ff5166dd15cefc4313e 100644 (file)
@@ -84,10 +84,13 @@ struct ospf_config
   struct proto_config c;
   uint tick;
   u8 ospf2;
+  u8 af_ext;
+  u8 af_mc;
   u8 rfc1583;
   u8 stub_router;
   u8 merge_external;
   u8 instance_id;
+  u8 instance_id_set;
   u8 abr;
   u8 asbr;
   int ecmp;
@@ -168,9 +171,9 @@ struct ospf_iface_patt
   int tx_priority;
   u16 tx_length;
   u16 rx_buffer;
-
 #define OSPF_RXBUF_MINSIZE 256 /* Minimal allowed size */
   u8 instance_id;
+  u8 instance_id_set;
   u8 autype;                   /* OSPF_AUTH_*, not really used in OSPFv3 */
   u8 strictnbma;
   u8 check_link;
@@ -211,12 +214,14 @@ struct ospf_proto
   int padj;                    /* Number of neighbors in Exchange or Loading state */
   struct fib rtf;              /* Routing table */
   struct idm idm;              /* OSPFv3 LSA ID map */
-  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 asbr;                   /* May i originate any ext/NSSA lsa? */
-  byte ecmp;                   /* Maximal number of nexthops in ECMP route, or 0 */
+  u8 ospf2;                    /* OSPF v2 or v3 */
+  u8 af_ext;                   /* OSPFv3-AF extension */
+  u8 af_mc;                    /* OSPFv3-AF multicast */
+  u8 rfc1583;                  /* RFC1583 compatibility */
+  u8 stub_router;              /* Do not forward transit traffic */
+  u8 merge_external;           /* Should i merge external routes? */
+  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 */
   event *flood_event;          /* Event for flooding LS updates */
   void *lsab;                  /* LSA buffer used when originating router LSAs */
@@ -449,14 +454,15 @@ struct ospf_neighbor
 
 
 /* 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 */
+#define OPT_V6         0x0001  /* OSPFv3, LSA relevant for IPv6 routing calculation */
+#define OPT_E          0x0002  /* Related to AS-external LSAs */
+#define OPT_MC         0x0004  /* Related to MOSPF, not used and obsolete */
+#define OPT_N          0x0008  /* Related to NSSA */
+#define OPT_P          0x0008  /* OSPFv2, flags P and N share position, see NSSA RFC */
+#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_AF         0x0100  /* OSPFv3 Address Families (RFC 5838) */
 
 /* Router-LSA VEB flags are are stored together with links (OSPFv2) or options (OSPFv3) */
 #define OPT_RT_B       (0x01 << 24)
@@ -718,74 +724,96 @@ lsa_net_count(struct ospf_lsa_header *lsa)
 #define IPV6_PREFIX_SPACE(x) ((((x) + 63) / 32) * 4)
 #define IPV6_PREFIX_WORDS(x) (((x) + 63) / 32)
 
-/* FIXME: these functions should be significantly redesigned w.r.t. integration,
-   also should be named as ospf3_* instead of *_ipv6_* */
 
 static inline int
 ospf_valid_prefix(net_addr *n)
 {
-  /* In OSPFv2, prefix is stored as netmask; ip4_masklen() returns 255 for invalid one */
-  return n->pxlen <= IP6_MAX_PREFIX_LENGTH;
+  /*
+   * In OSPFv2, prefix is stored as netmask; ip4_masklen() returns 255 for
+   * invalid one. But OSPFv3-AF may receive IPv4 net with 32 < pxlen < 128.
+   */
+  uint max = (n->type == NET_IP4) ? IP4_MAX_PREFIX_LENGTH : IP6_MAX_PREFIX_LENGTH;
+  return n->pxlen <= max;
 }
 
+/*
+ * In OSPFv3-AF (RFC 5835), IPv4 address is encoded by just placing it in the
+ * first 32 bits of IPv6 address and setting remaining bits to zero. Likewise
+ * for IPv4 prefix, where remaining bits do not matter. We use following
+ * functions to convert between IPv4 and IPv4-in-IPv6 representations:
+ */
+
+static inline ip4_addr ospf3_6to4(ip6_addr a)
+{ return _MI4(_I0(a)); }
+
+static inline ip6_addr ospf3_4to6(ip4_addr a)
+{ return _MI6(_I(a), 0, 0, 0); }
+
+
 static inline u32 *
-ospf_get_ipv6_prefix(u32 *buf, net_addr *N, u8 *pxopts, u16 *rest)
+ospf3_get_prefix(u32 *buf, int af, net_addr *n, u8 *pxopts, u16 *rest)
 {
-  net_addr_ip6 *net = (void *) N;
-  u8 pxlen = (*buf >> 24);
+  ip6_addr px = IP6_NONE;
+  uint pxlen = (*buf >> 24);
   *pxopts = (*buf >> 16) & 0xff;
   if (rest) *rest = *buf & 0xffff;
   buf++;
 
-  *net = NET_ADDR_IP6(IP6_NONE, pxlen);
-
   if (pxlen > 0)
-    _I0(net->prefix) = *buf++;
+    _I0(px) = *buf++;
   if (pxlen > 32)
-    _I1(net->prefix) = *buf++;
+    _I1(px) = *buf++;
   if (pxlen > 64)
-    _I2(net->prefix) = *buf++;
+    _I2(px) = *buf++;
   if (pxlen > 96)
-    _I3(net->prefix) = *buf++;
+    _I3(px) = *buf++;
 
   /* Clean up remaining bits */
   if (pxlen < 128)
-    net->prefix.addr[pxlen / 32] &= u32_mkmask(pxlen % 32);
+    px.addr[pxlen / 32] &= u32_mkmask(pxlen % 32);
 
-  return buf;
-}
+  if (af == NET_IP4)
+    net_fill_ip4(n, ospf3_6to4(px), pxlen);
+  else
+    net_fill_ip6(n, px, pxlen);
 
-static inline u32 *
-ospf_get_ipv6_addr(u32 *buf, ip_addr *addr)
-{
-  *addr = ipa_from_ip6(*(ip6_addr *) buf);
-  return buf + 4;
+  return buf;
 }
 
 static inline u32 *
-ospf_put_ipv6_prefix(u32 *buf, net_addr *N, u8 pxopts, u16 rest)
+ospf3_put_prefix(u32 *buf, net_addr *n, u8 pxopts, u16 rest)
 {
-  net_addr_ip6 *net = (void *) N;
-  u32 pxlen = net->pxlen;
+  ip6_addr px = (n->type == NET_IP4) ? ospf3_4to6(net4_prefix(n)) : net6_prefix(n);
+  uint pxlen = n->pxlen;
 
   *buf++ = ((pxlen << 24) | (pxopts << 16) | rest);
 
   if (pxlen > 0)
-    *buf++ = _I0(net->prefix);
+    *buf++ = _I0(px);
   if (pxlen > 32)
-    *buf++ = _I1(net->prefix);
+    *buf++ = _I1(px);
   if (pxlen > 64)
-    *buf++ = _I2(net->prefix);
+    *buf++ = _I2(px);
   if (pxlen > 96)
-    *buf++ = _I3(net->prefix);
+    *buf++ = _I3(px);
 
   return buf;
 }
 
 static inline u32 *
-ospf_put_ipv6_addr(u32 *buf, ip_addr addr)
+ospf3_get_addr(u32 *buf, int af, ip_addr *addr)
+{
+  ip6_addr a;
+  memcpy(&a, buf, 16);
+  *addr = (af == NET_IP4) ? ipa_from_ip4(ospf3_6to4(a)) : ipa_from_ip6(a);
+  return buf + 4;
+}
+
+static inline u32 *
+ospf3_put_addr(u32 *buf, ip_addr addr)
 {
-  *(ip6_addr *) buf = ipa_to_ip6(addr);
+  ip6_addr a = ipa_is_ip4(addr) ? ospf3_4to6(ipa_to_ip4(addr)) : ipa_to_ip6(addr);
+  memcpy(buf, &a, 16);
   return buf + 4;
 }
 
@@ -838,6 +866,15 @@ static inline int ospf_is_v3(struct ospf_proto *p)
 static inline int ospf_get_version(struct ospf_proto *p)
 { return ospf_is_v2(p) ? 2 : 3; }
 
+static inline int ospf_is_ip4(struct ospf_proto *p)
+{ return p->p.net_type == NET_IP4; }
+
+static inline int ospf_is_ip6(struct ospf_proto *p)
+{ return p->p.net_type == NET_IP6; }
+
+static inline int ospf_get_af(struct ospf_proto *p)
+{ return p->p.net_type; }
+
 struct ospf_area *ospf_find_area(struct ospf_proto *p, u32 aid);
 
 static inline struct ospf_area *ospf_main_area(struct ospf_proto *p)
index df9eb75b0e8dbcdfc56d449464324e9c0136e22b..f57925c37d48119cc5a48b5b10c91b751a6252be 100644 (file)
@@ -576,20 +576,20 @@ spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa)
     buf = px->rest;
     for (i = 0; i < px->pxcount; i++)
     {
-      net_addr_ip6 net;
+      net_addr net;
       u8 pxopts;
       u16 metric;
 
-      buf = ospf_get_ipv6_prefix(buf, (net_addr *) &net, &pxopts, &metric);
+      buf = ospf3_get_prefix(buf, ospf_get_af(p), &net, &pxopts, &metric);
 
       if (pxopts & OPT_PX_NU)
        continue;
 
       /* Store the first global address to use it later as a vlink endpoint */
-      if ((pxopts & OPT_PX_LA) && ipa_zero(src->lb))
-       src->lb = ipa_from_ip6(net.prefix);
+      if ((pxopts & OPT_PX_LA) && (net.type == NET_IP6) && ipa_zero(src->lb))
+       src->lb = ipa_from_ip6(net6_prefix(&net));
 
-      add_network(oa, (net_addr *) &net, src->dist + metric, src, i);
+      add_network(oa, &net, src->dist + metric, src, i);
     }
   }
 }
@@ -761,7 +761,7 @@ ospf_rt_sum(struct ospf_area *oa)
 
     if (en->lsa_type == LSA_T_SUM_NET)
     {
-      lsa_parse_sum_net(en, ospf_is_v2(p), &net, &pxopts, &metric);
+      lsa_parse_sum_net(en, ospf_is_v2(p), ospf_get_af(p), &net, &pxopts, &metric);
 
       if (!ospf_valid_prefix(&net))
       {
@@ -858,7 +858,7 @@ ospf_rt_sum_tr(struct ospf_area *oa)
       net_addr net;
       u8 pxopts;
 
-      lsa_parse_sum_net(en, ospf_is_v2(p), &net, &pxopts, &metric);
+      lsa_parse_sum_net(en, ospf_is_v2(p), ospf_get_af(p), &net, &pxopts, &metric);
 
       if (!ospf_valid_prefix(&net))
       {
@@ -1058,7 +1058,7 @@ decide_nssa_lsa(struct ospf_proto *p, ort *nf, struct ospf_lsa_ext_local *rt)
     return 0;
 
   /* We do not store needed data in struct orta, we have to parse the LSA */
-  lsa_parse_ext(en, ospf_is_v2(p), rt);
+  lsa_parse_ext(en, ospf_is_v2(p), ospf_get_af(p), rt);
 
   if (rt->pxopts & OPT_PX_NU)
     return 0;
@@ -1450,7 +1450,7 @@ ospf_ext_spf(struct ospf_proto *p)
     DBG("%s: Working on LSA. ID: %R, RT: %R, Type: %u\n",
        p->p.name, en->lsa.id, en->lsa.rt, en->lsa_type);
 
-    lsa_parse_ext(en, ospf_is_v2(p), &rt);
+    lsa_parse_ext(en, ospf_is_v2(p), ospf_get_af(p), &rt);
 
     if (!ospf_valid_prefix(&rt.net))
     {
@@ -1765,7 +1765,11 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
       if (ip6_zero(llsa->lladdr))
        return NULL;
 
-      return new_nexthop(p, ipa_from_ip6(llsa->lladdr), pn->iface, pn->weight);
+      ip_addr nh = ospf_is_ip4(p) ?
+       ipa_from_ip4(ospf3_6to4(llsa->lladdr)) :
+       ipa_from_ip6(llsa->lladdr);
+
+      return new_nexthop(p, nh, pn->iface, pn->weight);
     }
   }
 
@@ -1792,9 +1796,9 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
   if (en->lsa.age == LSA_MAXAGE)
     return;
 
-  if (ospf_is_v3(p) && (en->lsa_type == LSA_T_RT))
+  if (ospf_is_v3(p) && (oa->options & OPT_V6) && (en->lsa_type == LSA_T_RT))
   {
-    /* In OSPFv3, check V6 flag */
+    /* In OSPFv3 IPv6 unicast, check V6 flag */
     struct ospf_lsa_rt *rt = en->lsa_body;
     if (!(rt->options & OPT_V6))
       return;
index ce77f57a7c94481880bd638d331ab902f7d1f943..8dd91a407f9e965f8fee4983574691fd7715ac1b 100644 (file)
@@ -1008,7 +1008,7 @@ prepare_sum3_net_lsa_body(struct ospf_proto *p, ort *nf, u32 metric)
   sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_net) +
                    IPV6_PREFIX_SPACE(nf->fn.addr->pxlen));
   sum->metric = metric;
-  ospf_put_ipv6_prefix(sum->prefix, nf->fn.addr, 0, 0);
+  ospf3_put_prefix(sum->prefix, nf->fn.addr, 0, 0);
 }
 
 static inline void
@@ -1097,7 +1097,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf,
   ext->metric = metric & LSA_METRIC_MASK;
   u32 *buf = ext->rest;
 
-  buf = ospf_put_ipv6_prefix(buf, nf->fn.addr, pbit ? OPT_PX_P : 0, 0);
+  buf = ospf3_put_prefix(buf, nf->fn.addr, pbit ? OPT_PX_P : 0, 0);
 
   if (ebit)
     ext->metric |= LSA_EXT3_EBIT;
@@ -1105,7 +1105,7 @@ prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf,
   if (ipa_nonzero(fwaddr))
   {
     ext->metric |= LSA_EXT3_FBIT;
-    buf = ospf_put_ipv6_addr(buf, fwaddr);
+    buf = ospf3_put_addr(buf, fwaddr);
   }
 
   if (tag)
@@ -1222,7 +1222,7 @@ find_surrogate_fwaddr(struct ospf_proto *p, struct ospf_area *oa)
     {
       WALK_LIST(a, ifa->iface->addrs)
       {
-       if ((a->prefix.type != NET_IP6) ||
+       if ((a->prefix.type != ospf_get_af(p)) ||
            (a->flags & IA_SECONDARY) ||
            (a->flags & IA_PEER) ||
            (a->scope <= SCOPE_LINK))
@@ -1316,39 +1316,47 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
  */
 
 static inline void
-lsab_put_prefix(struct ospf_proto *p, net_addr *net, u32 cost)
+lsab_put_prefix(struct ospf_proto *p, net_addr *n, u32 cost)
 {
-  void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(net6_pxlen(net)));
-  u8 flags = (net6_pxlen(net) < IP6_MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA;
-  ospf_put_ipv6_prefix(buf, net, flags, cost);
+  void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(net_pxlen(n)));
+  uint max = (n->type == NET_IP4) ? IP4_MAX_PREFIX_LENGTH : IP6_MAX_PREFIX_LENGTH;
+  u8 flags = (net_pxlen(n) < max) ? 0 : OPT_PX_LA;
+  ospf3_put_prefix(buf, n, flags, cost);
 }
 
 static void
 prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
 {
-  struct ospf_lsa_link *ll;
+  ip_addr nh = ospf_is_ip4(p) ? IPA_NONE : ifa->addr->ip;
   int i = 0;
 
+  /* Preallocating space for header */
   ASSERT(p->lsab_used == 0);
-  ll = lsab_allocz(p, sizeof(struct ospf_lsa_link));
-  ll->options = ifa->oa->options | (ifa->priority << 24);
-  ll->lladdr = ipa_to_ip6(ifa->addr->ip);
-  ll = NULL; /* buffer might be reallocated later */
+  lsab_allocz(p, sizeof(struct ospf_lsa_link));
 
   struct ifa *a;
   WALK_LIST(a, ifa->iface->addrs)
   {
-    if ((a->prefix.type != NET_IP6) ||
+    if ((a->prefix.type != ospf_get_af(p)) ||
        (a->flags & IA_SECONDARY) ||
        (a->scope <= SCOPE_LINK))
       continue;
 
+    if (ospf_is_ip4(p) && ipa_zero(nh))
+      nh = a->ip;
+
     lsab_put_prefix(p, &a->prefix, 0);
     i++;
   }
 
-  ll = p->lsab;
+  /* Filling the preallocated header */
+  struct ospf_lsa_link *ll = p->lsab;
+  ll->options = ifa->oa->options | (ifa->priority << 24);
+  ll->lladdr = ospf_is_ip4(p) ? ospf3_4to6(ipa_to_ip4(nh)) : ipa_to_ip6(nh);
   ll->pxcount = i;
+
+  if (ipa_zero(nh))
+    log(L_ERR "%s: Cannot find next hop address for %s", p->p.name, ifa->ifname);
 }
 
 static void
@@ -1410,7 +1418,7 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
     struct ifa *a;
     WALK_LIST(a, ifa->iface->addrs)
     {
-      if ((a->prefix.type != NET_IP6) ||
+      if ((a->prefix.type != ospf_get_af(p)) ||
          (a->flags & IA_SECONDARY) ||
          (a->flags & IA_PEER) ||
          (a->scope <= SCOPE_LINK))
@@ -1448,7 +1456,7 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
 
   /* 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)
+  if (!EMPTY_LIST(cf->vlink_list) && !host_addr && ospf_is_ip6(p))
   {
     WALK_LIST(ifa, p->iface_list)
     {
@@ -1571,7 +1579,7 @@ add_link_lsa(struct ospf_proto *p, struct ospf_lsa_link *ll, int offset, int *px
       continue;
 
     /* Skip link-local prefixes */
-    if ((pxlen >= 10) && ((pxb[1] & 0xffc00000) == 0xfe800000))
+    if (ospf_is_ip6(p) && (pxlen >= 10) && ((pxb[1] & 0xffc00000) == 0xfe800000))
       continue;
 
     add_prefix(p, pxb, offset, pxc);