]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Implements RDNSS and DNSSL support for RAdv.
authorOndrej Zajicek <santiago@crfreenet.org>
Sat, 7 Jul 2012 08:40:00 +0000 (10:40 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Sat, 7 Jul 2012 12:26:42 +0000 (14:26 +0200)
doc/bird.sgml
lib/lists.h
proto/radv/config.Y
proto/radv/packets.c
proto/radv/radv.c
proto/radv/radv.h

index b43eb2630d830828f219a5e97a71d60cd14d5ccf..087a4ebff23766d7ad2023739d3fe17af08b4323 100644 (file)
@@ -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>
 
index 6fab12c440082efef46ed0c1b9bdd1455f46fb35..0b0fdbe3674609fcc7cc9ad023612b86a6c68afd 100644 (file)
@@ -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 */
index 495d9a05d36a4d70248eb611185400c25ffb2e9f..abccd2c7d3a03c63c948e4633e62652b64379ef2 100644 (file)
@@ -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
index ac59ce9436dca14451e4cba10aeee59f712faef5..6fdfcaa38b092430fb18eb5e47beec87ae51342c 100644 (file)
@@ -24,8 +24,10 @@ struct radv_ra_packet
 #define OPT_RA_MANAGED 0x80
 #define OPT_RA_OTHER_CFG 0x40
 
-#define OPT_PREFIX 3
-#define OPT_MTU 5
+#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;
@@ -137,7 +295,22 @@ radv_prepare_ra(struct radv_iface *ifa)
     ipa_hton(op->prefix);
     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;
 }
 
index d6fc8da57583072fbe0553e59d8831803b8e8c72..5e7296a3188b23ca16561d492a821e6548e310e9 100644 (file)
  * 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
index 12bfe42e5104d8b5a69ebe579d33280c635bc296..48af8c004f75bc56c92176198d296f218b68670b 100644 (file)
 #define DEFAULT_VALID_LIFETIME 86400
 #define DEFAULT_PREFERRED_LIFETIME 14400
 
+#define DEFAULT_DNS_LIFETIME_MULT 3
+
 
 struct radv_config
 {
   struct proto_config c;
   list patt_list;              /* List of iface configs (struct radv_iface_config) */
   list pref_list;              /* Global list of prefix configs (struct radv_prefix_config) */
+  list rdnss_list;             /* Global list of RDNSS configs (struct radv_rdnss_config) */
+  list dnssl_list;             /* Global list of DNSSL configs (struct radv_dnssl_config) */
 };
 
 struct radv_iface_config
 {
   struct iface_patt i;
   list pref_list;              /* Local list of prefix configs (struct radv_prefix_config) */
+  list rdnss_list;             /* Local list of RDNSS configs (struct radv_rdnss_config) */
+  list dnssl_list;             /* Local list of DNSSL configs (struct radv_dnssl_config) */
 
   u32 min_ra_int;              /* Standard options from RFC 4261 */
   u32 max_ra_int;
   u32 min_delay;
 
-  u8 managed;
+  u8 rdnss_local;              /* Global list is not used for RDNSS */
+  u8 dnssl_local;              /* Global list is not used for DNSSL */
+
+  u8 managed;                  /* Standard options from RFC 4261 */
   u8 other_config;
   u32 link_mtu;
   u32 reachable_time;
@@ -81,6 +90,25 @@ struct radv_prefix_config
   u32 preferred_lifetime;
 };
 
+struct radv_rdnss_config
+{
+  node n;
+  u32 lifetime;                        /* Valid if lifetime_mult is 0 */
+  u16 lifetime_mult;           /* Lifetime specified as multiple of max_ra_int */
+  ip_addr server;              /* IP address of recursive DNS server */
+};
+
+struct radv_dnssl_config
+{
+  node n;
+  u32 lifetime;                        /* Valid if lifetime_mult is 0 */
+  u16 lifetime_mult;           /* Lifetime specified as multiple of max_ra_int */
+  u8 dlen_first;               /* Length of first label in domain */
+  u8 dlen_all;                 /* Both dlen_ filled in radv_process_domain() */
+  char *domain;                        /* Domain for DNS search list, in processed form */
+};
+
+
 struct proto_radv
 {
   struct proto p;
@@ -123,6 +151,7 @@ struct radv_iface
 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, int shutdown);
 int radv_sk_open(struct radv_iface *ifa);