Commit fc06fb62 authored by Ondrej Zajicek's avatar Ondrej Zajicek
Browse files

Implements RDNSS and DNSSL support for RAdv.

parent 95127cbb
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -2450,6 +2450,20 @@ protocol radv {
	prefix 2001:0DB8:2000::/48 {
		autonomous off;		# Do not autoconfigure
	};

	rdnss 2001:0DB8:1234::10;	# Short form of RDNSS

	rdnss {
		lifetime mult 10;
		ns 2001:0DB8:1234::11;
		ns 2001:0DB8:1234::12;
	};

	dnssl {
		lifetime 3600;
		domain "abc.com";
		domain "xyz.com";
	};
}
</code>

+2 −1
Original line number Diff line number Diff line
@@ -34,7 +34,8 @@ typedef struct list { /* In fact two overlayed nodes */
#define HEAD(list) ((void *)((list).head))
#define TAIL(list) ((void *)((list).tail))
#define NODE_NEXT(n) ((void *)((NODE (n))->next))
#define WALK_LIST(n,list) for(n=HEAD(list);(NODE (n))->next; n=NODE_NEXT(n))
#define NODE_VALID(n) ((NODE (n))->next)
#define WALK_LIST(n,list) for(n=HEAD(list); NODE_VALID(n); n=NODE_NEXT(n))
#define WALK_LIST_DELSAFE(n,nxt,list) \
     for(n=HEAD(list); nxt=NODE_NEXT(n); n=(void *) nxt)
/* WALK_LIST_FIRST supposes that called code removes each processed node */
+121 −5
Original line number Diff line number Diff line
@@ -14,32 +14,45 @@ CF_DEFINES
#define RADV_CFG ((struct radv_config *) this_proto)
#define RADV_IFACE ((struct radv_iface_config *) this_ipatt)
#define RADV_PREFIX this_radv_prefix
#define RADV_RDNSS (&this_radv_rdnss)
#define RADV_DNSSL (&this_radv_dnssl)

static struct radv_prefix_config *this_radv_prefix;
static struct radv_rdnss_config this_radv_rdnss;
static struct radv_dnssl_config this_radv_dnssl;
static list radv_dns_list;	/* Used by radv_rdnss and radv_dnssl */
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, LINK, MTU, REACHABLE, TIME, RETRANS,
	TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED,
	LIFETIME, SKIP, ONLINK, AUTONOMOUS)
	TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT,
	LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN,
	LOCAL)

%type<i> radv_mult

CF_GRAMMAR

CF_ADDTO(proto, radv_proto '}')
CF_ADDTO(proto, radv_proto)

radv_proto_start: proto_start RADV
{
  this_proto = proto_config_new(&proto_radv, sizeof(struct radv_config), $1);
  init_list(&RADV_CFG->patt_list);
  init_list(&RADV_CFG->pref_list);
  init_list(&RADV_CFG->rdnss_list);
  init_list(&RADV_CFG->dnssl_list);
};

radv_proto_item:
   proto_item
 | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); }
 | INTERFACE radv_iface
 | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); }
 | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_CFG->rdnss_list, &radv_dns_list); }
 | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_CFG->dnssl_list, &radv_dns_list); }
 ;

radv_proto_opts:
@@ -48,7 +61,7 @@ radv_proto_opts:
 ;

radv_proto:
   radv_proto_start proto_name '{' radv_proto_opts;
   radv_proto_start proto_name '{' radv_proto_opts '}';


radv_iface_start:
@@ -57,6 +70,8 @@ radv_iface_start:
  add_tail(&RADV_CFG->patt_list, NODE this_ipatt);
  init_list(&this_ipatt->ipn_list);
  init_list(&RADV_IFACE->pref_list);
  init_list(&RADV_IFACE->rdnss_list);
  init_list(&RADV_IFACE->dnssl_list);

  RADV_IFACE->min_ra_int = -1; /* undefined */
  RADV_IFACE->max_ra_int = DEFAULT_MAX_RA_INT;
@@ -77,6 +92,10 @@ radv_iface_item:
 | CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if (($4 < 0) || ($4 > 255))  cf_error("Current hop limit must be in range 0-255"); }
 | DEFAULT LIFETIME expr { RADV_IFACE->default_lifetime = $3; if (($3 < 0) || ($3 > 9000))  cf_error("Default lifetime must be in range 0-9000"); }
 | PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); }
 | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_IFACE->rdnss_list, &radv_dns_list); }
 | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_IFACE->dnssl_list, &radv_dns_list); }
 | RDNSS LOCAL bool { RADV_IFACE->rdnss_local = $3; }
 | DNSSL LOCAL bool { RADV_IFACE->dnssl_local = $3; }
 ;

radv_iface_finish:
@@ -152,6 +171,103 @@ radv_prefix:
  radv_prefix_start radv_prefix_opt_list radv_prefix_finish;



radv_rdnss_node: ipa
{
  struct radv_rdnss_config *cf = cfg_allocz(sizeof(struct radv_rdnss_config));
  add_tail(&radv_dns_list, NODE cf);

  cf->server = $1;
  cf->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
};

radv_rdnss_start:
{
  RADV_RDNSS->lifetime = 0;
  RADV_RDNSS->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
};

radv_rdnss_item:
 | NS radv_rdnss_node
 | LIFETIME radv_mult { RADV_RDNSS->lifetime = $2; RADV_RDNSS->lifetime_mult = radv_mult_val; }
 ;

radv_rdnss_finish:
{
  if (EMPTY_LIST(radv_dns_list))
    cf_error("No nameserver in RDNSS section");

  struct radv_rdnss_config *cf;
  WALK_LIST(cf, radv_dns_list)
  {
    cf->lifetime = RADV_RDNSS->lifetime;
    cf->lifetime_mult = RADV_RDNSS->lifetime_mult;
  }
};

radv_rdnss_opts:
   /* empty */
 | radv_rdnss_opts radv_rdnss_item ';'
 ;

radv_rdnss:
   radv_rdnss_node
 | '{' radv_rdnss_start radv_rdnss_opts '}' radv_rdnss_finish
 ;


radv_dnssl_node: TEXT
{
  struct radv_dnssl_config *cf = cfg_allocz(sizeof(struct radv_dnssl_config));
  add_tail(&radv_dns_list, NODE cf);

  cf->domain = $1;
  cf->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;

  if (radv_process_domain(cf) < 0)
    cf_error("Invalid domain dame");
};

radv_dnssl_start:
{
  RADV_DNSSL->lifetime = 0;
  RADV_DNSSL->lifetime_mult = DEFAULT_DNS_LIFETIME_MULT;
};

radv_dnssl_item:
 | DOMAIN radv_dnssl_node
 | LIFETIME radv_mult { RADV_DNSSL->lifetime = $2; RADV_DNSSL->lifetime_mult = radv_mult_val; }
 ;

radv_dnssl_finish:
{
  if (EMPTY_LIST(radv_dns_list))
    cf_error("No domain in DNSSL section");

  struct radv_dnssl_config *cf;
  WALK_LIST(cf, radv_dns_list)
  {
    cf->lifetime = RADV_DNSSL->lifetime;
    cf->lifetime_mult = RADV_DNSSL->lifetime_mult;
  }
};

radv_dnssl_opts:
   /* empty */
 | radv_dnssl_opts radv_dnssl_item ';'
 ;

radv_dnssl:
   radv_dnssl_node
 | '{' radv_dnssl_start radv_dnssl_opts '}' radv_dnssl_finish
 ;


radv_mult:
   expr { $$ = $1; radv_mult_val = 0; }
 | MULT expr { $$ = 0; radv_mult_val = $2; if (($2 < 1) || ($2 > 254)) cf_error("Multiplier must be in range 1-254"); }
 ;

CF_CODE

CF_END
+177 −4
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ struct radv_ra_packet

#define OPT_PREFIX	3
#define OPT_MTU		5
#define OPT_RDNSS	25
#define OPT_DNSSL	31

struct radv_opt_prefix
{
@@ -50,6 +52,25 @@ struct radv_opt_mtu
  u32 mtu;
};

struct radv_opt_rdnss
{
  u8 type;
  u8 length;
  u16 reserved;
  u32 lifetime;
  ip_addr servers[];
};

struct radv_opt_dnssl
{
  u8 type;
  u8 length;
  u16 reserved;
  u32 lifetime;
  char domain[];
};


static struct radv_prefix_config default_prefix = {
  .onlink = 1,
  .autonomous = 1,
@@ -57,6 +78,7 @@ static struct radv_prefix_config default_prefix = {
  .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
};


static struct radv_prefix_config *
radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
{
@@ -78,10 +100,146 @@ radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
  return &default_prefix;
}

static int
radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend)
{
  struct radv_rdnss_config *rcf = HEAD(*rdnss_list);

  while(NODE_VALID(rcf))
  {
    struct radv_rdnss_config *rcf_base = rcf;
    struct radv_opt_rdnss *op = (void *) *buf;
    int max_i = (bufend - *buf - sizeof(struct radv_opt_rdnss)) / sizeof(ip_addr);
    int i = 0;

    if (max_i < 1)
      goto too_much;

    op->type = OPT_RDNSS;
    op->reserved = 0;

    if (rcf->lifetime_mult)
      op->lifetime = htonl(rcf->lifetime_mult * ifa->cf->max_ra_int);
    else
      op->lifetime = htonl(rcf->lifetime);

    while(NODE_VALID(rcf) && 
	  (rcf->lifetime == rcf_base->lifetime) &&
	  (rcf->lifetime_mult == rcf_base->lifetime_mult))
      {
	if (i >= max_i)
	  goto too_much;

	op->servers[i] = rcf->server;
	ipa_hton(op->servers[i]);
	i++;

	rcf = NODE_NEXT(rcf);
      }
  
    op->length = 1+2*i;
    *buf += 8 * op->length;
  }

  return 0;

 too_much:
  log(L_WARN "%s: Too many RA options on interface %s",
      ifa->ra->p.name, ifa->iface->name);
  return -1;
}

int
radv_process_domain(struct radv_dnssl_config *cf)
{
  /* Format of domain in search list is <size> <label> <size> <label> ... 0 */

  char *dom = cf->domain;
  char *dom_end = dom; /* Just to  */
  u8 *dlen_save = &cf->dlen_first;
  int len;

  while (dom_end)
  {
    dom_end = strchr(dom, '.');
    len = dom_end ? (dom_end - dom) : strlen(dom);

    if (len < 1 || len > 63)
      return -1;

    *dlen_save = len;
    dlen_save = (u8 *) dom_end;

    dom += len + 1;
  }

  len = dom - cf->domain;
  if (len > 254)
    return -1;

  cf->dlen_all = len;

  return 0;
}

static int
radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl_list, char **buf, char *bufend)
{
  struct radv_dnssl_config *dcf = HEAD(*dnssl_list);

  while(NODE_VALID(dcf))
  {
    struct radv_dnssl_config *dcf_base = dcf;
    struct radv_opt_dnssl *op = (void *) *buf;
    int bsize = bufend - *buf - sizeof(struct radv_opt_dnssl);
    int bpos = 0;

    if (bsize < 0)
      goto too_much;

    bsize = bsize & ~7; /* Round down to multiples of 8 */

    op->type = OPT_DNSSL;
    op->reserved = 0;

    if (dcf->lifetime_mult)
      op->lifetime = htonl(dcf->lifetime_mult * ifa->cf->max_ra_int);
    else
      op->lifetime = htonl(dcf->lifetime);

    while(NODE_VALID(dcf) && 
	  (dcf->lifetime == dcf_base->lifetime) &&
	  (dcf->lifetime_mult == dcf_base->lifetime_mult))
      {
	if (bpos + dcf->dlen_all + 1 > bsize)
	  goto too_much;

	op->domain[bpos++] = dcf->dlen_first;
	memcpy(op->domain + bpos, dcf->domain, dcf->dlen_all);
	bpos += dcf->dlen_all;

	dcf = NODE_NEXT(dcf);
      }

    int blen = (bpos + 7) / 8;
    bzero(op->domain + bpos, 8 * blen - bpos);
    op->length = 1 + blen;
    *buf += 8 * op->length;
  }

  return 0;

 too_much:
  log(L_WARN "%s: Too many RA options on interface %s",
      ifa->ra->p.name, ifa->iface->name);
  return -1;
}

static void
radv_prepare_ra(struct radv_iface *ifa)
{
  struct proto_radv *ra = ifa->ra;
  struct radv_config *cf = (struct radv_config *) (ra->p.cf);

  char *buf = ifa->sk->tbuf;
  char *bufstart = buf;
@@ -121,7 +279,7 @@ radv_prepare_ra(struct radv_iface *ifa)
    if (buf + sizeof(struct radv_opt_prefix) > bufend)
    {
      log(L_WARN "%s: Too many prefixes on interface %s", ra->p.name, ifa->iface->name);
      break;
      goto done;
    }

    struct radv_opt_prefix *op = (void *) buf;
@@ -138,6 +296,21 @@ radv_prepare_ra(struct radv_iface *ifa)
    buf += sizeof(*op);
  }

  if (! ifa->cf->rdnss_local)
    if (radv_prepare_rdnss(ifa, &cf->rdnss_list, &buf, bufend) < 0)
      goto done;

  if (radv_prepare_rdnss(ifa, &ifa->cf->rdnss_list, &buf, bufend) < 0)
    goto done;

  if (! ifa->cf->dnssl_local)
    if (radv_prepare_dnssl(ifa, &cf->dnssl_list, &buf, bufend) < 0)
      goto done;

  if (radv_prepare_dnssl(ifa, &ifa->cf->dnssl_list, &buf, bufend) < 0)
    goto done;

 done:
  ifa->plen = buf - bufstart;
}

+4 −0
Original line number Diff line number Diff line
@@ -29,6 +29,10 @@
 * radv_iface_notify(), which processes asynchronous events (specified
 * by RA_EV_* codes), and radv_timer(), which triggers sending RAs and
 * computes the next timeout.
 *
 * Supported standards:
 * - RFC 4861 - main RA standard
 * - RFC 6106 - DNS extensions (RDDNS, DNSSL)
 */

static void
Loading