]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
RAdv: Allow solicited RAs to be sent as unicast
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 11 Aug 2019 22:41:36 +0000 (00:41 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 11 Aug 2019 22:43:19 +0000 (00:43 +0200)
Add option to send solicited router advertisements as unicast directly
to soliciting nodes instead of as multicast to all-nodes group.

doc/bird.sgml
lib/socket.h
proto/radv/config.Y
proto/radv/packets.c
proto/radv/radv.c
proto/radv/radv.h

index 83dec4f818f0ff2176f90454bdd0969ee4573f9b..16d03028b08e2f54829cd750a5fb1626989c4945 100644 (file)
@@ -4121,6 +4121,12 @@ definitions, prefix definitions and DNS definitions:
        The minimum delay between two consecutive router advertisements, in
        seconds. Default: 3
 
+       <tag><label id="radv-solicited-ra-unicast">solicited ra unicast <m/switch/</tag>
+       Solicited router advertisements are usually sent to all-nodes multicast
+       group like unsolicited ones, but the router can be configured to send
+       them as unicast directly to soliciting nodes instead. This is especially
+       useful on wireless networks (see <rfc id="7772">). Default: no
+
        <tag><label id="radv-iface-managed">managed <m/switch/</tag>
        This option specifies whether hosts should use DHCPv6 for IP address
        configuration. Default: no
index e53ec5bae873756dee8725349bab888375fe7567..03c15dcdf73cd0460aaaa9fe8ae0aed3b5bf6698 100644 (file)
@@ -97,7 +97,7 @@ void sk_dump_all(void);
 int sk_is_ipv4(sock *s);               /* True if socket is IPv4 */
 int sk_is_ipv6(sock *s);               /* True if socket is IPv6 */
 
-static inline int sk_send_buffer_empty(sock *sk)
+static inline int sk_tx_buffer_empty(sock *sk)
 { return sk->tbuf == sk->tpos; }
 
 int sk_setup_multicast(sock *s);       /* Prepare UDP or IP socket for multicasting */
index 53715f776c31df1ec3f62e7d6a25c3363c081551..dda9cfcd678be5f7f076c3eaa090db6c47e8d36a 100644 (file)
@@ -1,6 +1,8 @@
 /*
  *     BIRD -- Router Advertisement Configuration
  *
+ *     (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2011--2019 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
@@ -26,12 +28,12 @@ static u8 radv_mult_val;    /* Used by radv_mult for second return value */
 
 CF_DECLS
 
-CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL,
-       MANAGED, OTHER, CONFIG, LINGER, LINK, MTU, REACHABLE, TIME, RETRANS,
-       TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT,
-       LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN,
-       LOCAL, TRIGGER, SENSITIVE, PREFERENCE, LOW, MEDIUM, HIGH, PROPAGATE,
-       ROUTE, ROUTES, RA_PREFERENCE, RA_LIFETIME)
+CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL, SOLICITED,
+       UNICAST, MANAGED, OTHER, CONFIG, LINGER, LINK, MTU, REACHABLE, TIME,
+       RETRANS, TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT,
+       LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN, LOCAL,
+       TRIGGER, SENSITIVE, PREFERENCE, LOW, MEDIUM, HIGH, PROPAGATE, ROUTE,
+       ROUTES, RA_PREFERENCE, RA_LIFETIME)
 
 CF_ENUM(T_ENUM_RA_PREFERENCE, RA_PREF_, LOW, MEDIUM, HIGH)
 
@@ -98,6 +100,7 @@ radv_iface_item:
    MIN RA INTERVAL expr { RADV_IFACE->min_ra_int = $4; if ($4 < 3) cf_error("Min RA interval must be at least 3"); }
  | MAX RA INTERVAL expr { RADV_IFACE->max_ra_int = $4; if (($4 < 4) || ($4 > 1800)) cf_error("Max RA interval must be in range 4-1800"); }
  | MIN DELAY expr { RADV_IFACE->min_delay = $3; if ($3 <= 0) cf_error("Min delay must be positive"); }
+ | SOLICITED RA UNICAST bool { RADV_IFACE->solicited_ra_unicast = $4; }
  | MANAGED bool { RADV_IFACE->managed = $2; }
  | OTHER CONFIG bool { RADV_IFACE->other_config = $3; }
  | LINK MTU expr { RADV_IFACE->link_mtu = $3; }
index b12d3a12f52184a5f9e9f6d1cd8eabff5683bf78..3139d32127c795ea2fea15d14f1fbbffd9d6cee9 100644 (file)
@@ -1,6 +1,8 @@
 /*
  *     BIRD -- RAdv Packet Processing
  *
+ *     (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2011--2019 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
@@ -370,19 +372,49 @@ radv_prepare_ra(struct radv_iface *ifa)
 
 
 void
-radv_send_ra(struct radv_iface *ifa)
+radv_send_ra(struct radv_iface *ifa, ip_addr to)
 {
   struct radv_proto *p = ifa->ra;
 
+  /* TX queue is already full */
+  if (!sk_tx_buffer_empty(ifa->sk))
+    return;
+
+  if (ifa->valid_time <= current_time())
+    radv_invalidate(ifa);
+
   /* We store prepared RA in tbuf */
   if (!ifa->plen)
     radv_prepare_ra(ifa);
 
-  RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name);
-  sk_send_to(ifa->sk, ifa->plen, IP6_ALL_NODES, 0);
+  if (ipa_zero(to))
+  {
+    to = IP6_ALL_NODES;
+    RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name);
+  }
+  else
+  {
+    RADV_TRACE(D_PACKETS, "Sending RA to %I via %s", to, ifa->iface->name);
+  }
+
+  int done = sk_send_to(ifa->sk, ifa->plen, to, 0);
+  if (!done)
+    log(L_WARN "%s: TX queue full on %s", p->p.name, ifa->iface->name);
 }
 
 
+static void
+radv_receive_rs(struct radv_proto *p, struct radv_iface *ifa, ip_addr from)
+{
+  RADV_TRACE(D_PACKETS, "Received RS from %I via %s",
+            from, ifa->iface->name);
+
+  if (ifa->cf->solicited_ra_unicast && ipa_nonzero(from))
+    radv_send_ra(ifa, from);
+  else
+    radv_iface_notify(ifa, RA_EV_RS);
+}
+
 static int
 radv_rx_hook(sock *sk, uint size)
 {
@@ -410,9 +442,7 @@ radv_rx_hook(sock *sk, uint size)
   switch (buf[0])
   {
   case ICMPV6_RS:
-    RADV_TRACE(D_PACKETS, "Received RS from %I via %s",
-              sk->faddr, ifa->iface->name);
-    radv_iface_notify(ifa, RA_EV_RS);
+    radv_receive_rs(p, ifa, sk->faddr);
     return 1;
 
   case ICMPV6_RA:
@@ -430,7 +460,10 @@ static void
 radv_tx_hook(sock *sk)
 {
   struct radv_iface *ifa = sk->data;
-  log(L_WARN "%s: TX hook called", ifa->ra->p.name);
+  log(L_INFO "%s: TX queue ready on %s", ifa->ra->p.name, ifa->iface->name);
+
+  /* Some RAs may be missed due to full TX queue */
+  radv_iface_notify(ifa, RA_EV_RS);
 }
 
 static void
index 990b602494540fbadb8f30632573c612e6494edb..622b3c3cacdfb1ec6776407e55fa9bbaa825b4e2 100644 (file)
@@ -1,6 +1,8 @@
 /*
  *     BIRD -- Router Advertisement
  *
+ *     (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2011--2019 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
  * case of the specified trigger prefix was changed.
  *
  * Supported standards:
- * RFC 4861 - main RA standard
- * RFC 4191 - Default Router Preferences and More-Specific Routes
- * RFC 6106 - DNS extensions (RDDNS, DNSSL)
+ * RFC 4861 - main RA standard
+ * RFC 4191 - Default Router Preferences and More-Specific Routes
+ * RFC 6106 - DNS extensions (RDDNS, DNSSL)
  */
 
 static void radv_prune_prefixes(struct radv_iface *ifa);
 static void radv_prune_routes(struct radv_proto *p);
 
-/* Invalidate cached RA packet */
-static inline void radv_invalidate(struct radv_iface *ifa)
-{ ifa->plen = 0; }
-
 static void
 radv_timer(timer *tm)
 {
@@ -56,16 +54,13 @@ radv_timer(timer *tm)
 
   RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
 
-  if (ifa->valid_time <= now)
-    radv_invalidate(ifa);
-
   if (ifa->prune_time <= now)
     radv_prune_prefixes(ifa);
 
   if (p->prune_time <= now)
     radv_prune_routes(p);
 
-  radv_send_ra(ifa);
+  radv_send_ra(ifa, IPA_NONE);
 
   /* Update timer */
   ifa->last = now;
@@ -627,7 +622,7 @@ radv_iface_shutdown(struct radv_iface *ifa)
   if (ifa->sk)
   {
     radv_invalidate(ifa);
-    radv_send_ra(ifa);
+    radv_send_ra(ifa, IPA_NONE);
   }
 }
 
index 719948d575e1c91064d4f1ebc1dce5e938ba2776..2c8ad7d438b1dd01220b1a14bf211f252e0b2122 100644 (file)
@@ -1,6 +1,8 @@
 /*
  *     BIRD -- Router Advertisement
  *
+ *     (c) 2011--2019 Ondrej Zajicek <santiago@crfreenet.org>
+ *     (c) 2011--2019 CZ.NIC z.s.p.o.
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
@@ -66,6 +68,8 @@ struct radv_iface_config
   u32 max_ra_int;
   u32 min_delay;
 
+  u8 solicited_ra_unicast;     /* Send solicited RAs as unicast */
+
   u32 prefix_linger_time;      /* How long we advertise dead prefixes with lifetime 0 */
   u32 route_linger_time;       /* How long we advertise dead routes with lifetime 0 */
 
@@ -204,12 +208,16 @@ struct radv_iface
        log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0)
 
 
+/* Invalidate cached RA packet */
+static inline void radv_invalidate(struct radv_iface *ifa)
+{ ifa->plen = 0; }
+
 /* radv.c */
 void radv_iface_notify(struct radv_iface *ifa, int event);
 
 /* packets.c */
 int radv_process_domain(struct radv_dnssl_config *cf);
-void radv_send_ra(struct radv_iface *ifa);
+void radv_send_ra(struct radv_iface *ifa, ip_addr to);
 int radv_sk_open(struct radv_iface *ifa);