summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2019-08-12 00:41:36 +0200
committerOndrej Zajicek (work) <santiago@crfreenet.org>2019-08-12 00:43:19 +0200
commit70a4320bdd44122d7a93bc71c77a9d684b3c9adc (patch)
tree01e1d376a979ca0e71ebd016f89439c1f3dbd68c
parent9f3e09832081bc029dc98ae6dda49ee86d138fde (diff)
RAdv: Allow solicited RAs to be sent as unicast
Add option to send solicited router advertisements as unicast directly to soliciting nodes instead of as multicast to all-nodes group.
-rw-r--r--doc/bird.sgml6
-rw-r--r--lib/socket.h2
-rw-r--r--proto/radv/config.Y15
-rw-r--r--proto/radv/packets.c47
-rw-r--r--proto/radv/radv.c19
-rw-r--r--proto/radv/radv.h10
6 files changed, 72 insertions, 27 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 83dec4f8..16d03028 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -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
diff --git a/lib/socket.h b/lib/socket.h
index e53ec5ba..03c15dcd 100644
--- a/lib/socket.h
+++ b/lib/socket.h
@@ -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 */
diff --git a/proto/radv/config.Y b/proto/radv/config.Y
index 53715f77..dda9cfcd 100644
--- a/proto/radv/config.Y
+++ b/proto/radv/config.Y
@@ -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; }
diff --git a/proto/radv/packets.c b/proto/radv/packets.c
index b12d3a12..3139d321 100644
--- a/proto/radv/packets.c
+++ b/proto/radv/packets.c
@@ -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
diff --git a/proto/radv/radv.c b/proto/radv/radv.c
index 990b6024..622b3c3c 100644
--- a/proto/radv/radv.c
+++ b/proto/radv/radv.c
@@ -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.
*/
@@ -35,18 +37,14 @@
* 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);
}
}
diff --git a/proto/radv/radv.h b/proto/radv/radv.h
index 719948d5..2c8ad7d4 100644
--- a/proto/radv/radv.h
+++ b/proto/radv/radv.h
@@ -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);