]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
OSPF: Allow loopback nexthop in OSPFv3-IPv4
authorOndrej Zajicek <santiago@crfreenet.org>
Thu, 4 Apr 2024 16:37:26 +0000 (18:37 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Thu, 4 Apr 2024 16:37:26 +0000 (18:37 +0200)
In OSPFv3-IPv4 there is no requirement that link-local next hop announced
in Link-LSA must be in interface address range. Therefore, for interfaces
that do not have IPv4 address we can use some loopback IP address and
announce it as a next hop. Also we should accept such address.

nest/neighbor.c
proto/ospf/ospf.h
proto/ospf/rt.c
proto/ospf/topology.c

index 2c2d3adfdee4e5f14895462e5967da450feeb296..63c07f83f800235d62cdb7a1a67b3ad14cb13d19 100644 (file)
@@ -217,7 +217,8 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
   struct ifa *addr = NULL;
 
   WALK_LIST(n, neigh_hash_table[h])    /* Search the cache */
-    if ((n->proto == p) && ipa_equal(n->addr, a) && (n->ifreq == iface))
+    if ((n->proto == p) && ipa_equal(n->addr, a) && (n->ifreq == iface) &&
+       ((n->flags & NEF_ONLINK) == (flags & NEF_ONLINK)))
       return n;
 
   if (flags & NEF_IFACE)
index 3e704ae82e8dc95328ee55fb18c63ec8b59da7de..4949f173f8fbbf38069c0603fea05410b3021066 100644 (file)
@@ -252,6 +252,7 @@ struct ospf_proto
   u32 last_vlink_id;           /* Interface IDs for vlinks (starts at 0x80000000) */
   struct tbf log_pkt_tbf;      /* TBF for packet messages */
   struct tbf log_lsa_tbf;      /* TBF for LSA messages */
+  ip_addr loopback_addr;       /* IP address used as common next hop (in OSPFv3-IPv4) */
 };
 
 struct ospf_area
@@ -331,6 +332,7 @@ struct ospf_iface
   struct top_hash_entry **flood_queue; /* LSAs queued for LSUPD */
   u8 update_link_lsa;
   u8 update_net_lsa;
+  u8 loopback_addr_used;       /* The Link-LSA depends on p->loopback_addr */
   u16 flood_queue_used;                /* The current number of LSAs in flood_queue */
   u16 flood_queue_size;                /* The maximum number of LSAs in flood_queue */
   int fadj;                    /* Number of fully adjacent neighbors */
index 471bb5860d8282878ce046fcc7a7e387d96a58bc..d7753ce0aa16d9ae3cd4b9753fd559ab858d867e 100644 (file)
@@ -2038,6 +2038,14 @@ again1:
        {
          neighbor *nbr = neigh_find(&p->p, nh->gw, nh->iface,
                                    (nh->flags & RNF_ONLINK) ? NEF_ONLINK : 0);
+
+         /* According to RFC 5838 2.5 Direct Interface Address */
+         if (ospf_is_v3(p) && !nbr && ipa_is_ip4(nh->gw))
+         {
+           nh->flags |= RNF_ONLINK;
+           nbr = neigh_find(&p->p, nh->gw, nh->iface, NEF_ONLINK);
+         }
+
          if (!nbr || (nbr->scope == SCOPE_HOST))
            { reset_ri(nf); break; }
        }
index 9fe682646c8406f6a8f3905d14eb41d698b2e51c..7802242328325afe61fa027e00f0ad9082e22c2b 100644 (file)
@@ -1402,6 +1402,46 @@ lsab_put_prefix(struct ospf_proto *p, net_addr *n, u32 cost)
   ospf3_put_prefix(buf, n, flags, cost);
 }
 
+static inline void
+update_loopback_addr(struct ospf_proto *p)
+{
+  ip_addr old_addr = p->loopback_addr;
+  ip_addr best_addr = IPA_NONE;
+  int best_pref = 0;
+
+  struct ospf_iface *ifa;
+  WALK_LIST(ifa, p->iface_list)
+  {
+    if (ifa->type == OSPF_IT_VLINK)
+      continue;
+
+    struct ifa *a;
+    WALK_LIST(a, ifa->iface->addrs)
+    {
+      if ((a->prefix.type != ospf_get_af(p)) ||
+         (a->flags & IA_SECONDARY) ||
+         (a->scope <= SCOPE_LINK))
+       continue;
+
+      int pref = (a->flags & IA_HOST) ? 3 : (ifa->stub ? 2 : 1);
+      if ((pref > best_pref) || ((pref == best_pref) && ipa_equal(a->ip, old_addr)))
+      {
+       best_addr = a->ip;
+       best_pref = pref;
+      }
+    }
+  }
+
+  if (ipa_equal(best_addr, old_addr))
+    return;
+
+  p->loopback_addr = best_addr;
+
+  WALK_LIST(ifa, p->iface_list)
+    if (ifa->loopback_addr_used)
+      ospf_notify_link_lsa(ifa);
+}
+
 static void
 prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
 {
@@ -1427,6 +1467,12 @@ prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
     i++;
   }
 
+  if (ospf_is_ip4(p) && ipa_zero(nh))
+  {
+    nh = p->loopback_addr;
+    ifa->loopback_addr_used = 1;
+  }
+
   /* Filling the preallocated header */
   struct ospf_lsa_link *ll = p->lsab;
   ll->options = ifa->oa->options | (ifa->priority << 24);
@@ -1853,6 +1899,9 @@ ospf_update_topology(struct ospf_proto *p)
     }
   }
 
+  if (ospf_is_v3(p) && ospf_is_ip4(p))
+    update_loopback_addr(p);
+
   WALK_LIST(ifa, p->iface_list)
   {
     if (ifa->type == OSPF_IT_VLINK)
@@ -1860,6 +1909,8 @@ ospf_update_topology(struct ospf_proto *p)
 
     if (ifa->update_link_lsa)
     {
+      ifa->loopback_addr_used = 0;
+
       if ((ifa->state > OSPF_IS_LOOP) && !ifa->link_lsa_suppression)
        ospf_originate_link_lsa(p, ifa);
       else