diff options
Diffstat (limited to 'proto/radv')
-rw-r--r-- | proto/radv/config.Y | 126 | ||||
-rw-r--r-- | proto/radv/packets.c | 181 | ||||
-rw-r--r-- | proto/radv/radv.c | 4 | ||||
-rw-r--r-- | proto/radv/radv.h | 31 |
4 files changed, 332 insertions, 10 deletions
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<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 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 <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; } diff --git a/proto/radv/radv.c b/proto/radv/radv.c index d6fc8da5..5e7296a3 100644 --- a/proto/radv/radv.c +++ b/proto/radv/radv.c @@ -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 diff --git a/proto/radv/radv.h b/proto/radv/radv.h index 12bfe42e..48af8c00 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -42,24 +42,33 @@ #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); |