Commit 70a4320b authored by Ondrej Zajicek (work)'s avatar Ondrej Zajicek (work)
Browse files

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.
parent 9f3e0983
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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
+1 −1
Original line number Diff line number Diff line
@@ -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 */
+9 −6
Original line number Diff line number Diff line
/*
 *	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; }
+40 −7
Original line number Diff line number Diff line
/*
 *	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);

  if (ipa_zero(to))
  {
    to = IP6_ALL_NODES;
    RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name);
  sk_send_to(ifa->sk, ifa->plen, IP6_ALL_NODES, 0);
  }
  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
+7 −12
Original line number Diff line number Diff line
/*
 *	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);
  }
}

Loading