diff --git a/doc/bird.sgml b/doc/bird.sgml index b43eb263..087a4ebf 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -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"; + }; } diff --git a/lib/lists.h b/lib/lists.h index 6fab12c4..0b0fdbe3 100644 --- a/lib/lists.h +++ b/lib/lists.h @@ -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 */ diff --git a/proto/radv/config.Y b/proto/radv/config.Y index 495d9a05..abccd2c7 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -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 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 diff --git a/proto/radv/packets.c b/proto/radv/packets.c index ac59ce94..6fdfcaa3 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -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