diff options
author | Simon Kelley <simon@thekelleys.org.uk> | 2013-10-10 20:58:11 +0100 |
---|---|---|
committer | Simon Kelley <simon@thekelleys.org.uk> | 2013-10-10 20:58:11 +0100 |
commit | c4cd95df68b573b63d234ecdb675228657d65353 (patch) | |
tree | c653e10b47d00cb729cccf8d3fbf475a7babac0d /src/radv.c | |
parent | 043c271f8a4bc7be157f938f5d581e79d9c1f4d2 (diff) | |
download | dnsmasq-c4cd95df68b573b63d234ecdb675228657d65353.tar.gz |
Add --ra-param and remove --force-fast-ra
Diffstat (limited to 'src/radv.c')
-rw-r--r-- | src/radv.c | 120 |
1 files changed, 95 insertions, 25 deletions
@@ -32,11 +32,12 @@ struct ra_param { char *if_name; struct dhcp_netid *tags; struct in6_addr link_local, link_global; - unsigned int pref_time; + unsigned int pref_time, adv_interval; }; struct search_param { time_t now; int iface; + char name[IF_NAMESIZE+1]; }; static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest); @@ -47,7 +48,11 @@ static int iface_search(struct in6_addr *local, int prefix, int scope, int if_index, int flags, int prefered, int valid, void *vparam); static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm); -static void new_timeout(struct dhcp_context *context, time_t now); +static void new_timeout(struct dhcp_context *context, char *iface_name, time_t now); +static unsigned int calc_lifetime(struct ra_interface *ra); +static unsigned int calc_interval(struct ra_interface *ra); +static unsigned int calc_prio(struct ra_interface *ra); +static struct ra_interface *find_iface_param(char *iface); static int hop_limit; @@ -198,19 +203,20 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de struct dhcp_context *context, *tmp, **up; struct dhcp_netid iface_id; struct dhcp_opt *opt_cfg; + struct ra_interface *ra_param = find_iface_param(iface_name); int done_dns = 0; #ifdef HAVE_LINUX_NETWORK FILE *f; #endif - + save_counter(0); ra = expand(sizeof(struct ra_packet)); ra->type = ND_ROUTER_ADVERT; ra->code = 0; ra->hop_limit = hop_limit; - ra->flags = 0x00; - ra->lifetime = htons(RA_INTERVAL * 3); /* AdvDefaultLifetime * 3 */ + ra->flags = calc_prio(ra_param); + ra->lifetime = htons(calc_lifetime(ra_param)); ra->reachable_time = 0; ra->retrans_time = 0; @@ -222,6 +228,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de parm.first = 1; parm.now = now; parm.pref_time = 0; + parm.adv_interval = calc_interval(ra_param); /* set tag with name == interface */ iface_id.net = iface_name; @@ -335,7 +342,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de put_opt6_char(ICMP6_OPT_RDNSS); put_opt6_char((opt_cfg->len/8) + 1); put_opt6_short(0); - put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */ + put_opt6_long(parm.adv_interval * 2); /* lifetime - twice RA retransmit */ /* zero means "self" */ for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++) if (IN6_IS_ADDR_UNSPECIFIED(a)) @@ -351,7 +358,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de put_opt6_char(ICMP6_OPT_DNSSL); put_opt6_char(len + 1); put_opt6_short(0); - put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */ + put_opt6_long(parm.adv_interval * 2); /* lifetime - twice RA retransmit */ put_opt6(opt_cfg->val, opt_cfg->len); /* pad */ @@ -366,7 +373,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de put_opt6_char(ICMP6_OPT_RDNSS); put_opt6_char(3); put_opt6_short(0); - put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */ + put_opt6_long(parm.adv_interval * 2); /* lifetime - twice RA retransmit */ put_opt6(&parm.link_global, IN6ADDRSZ); } @@ -455,8 +462,8 @@ static int add_prefixes(struct in6_addr *local, int prefix, if (time > context->lease_time) { time = context->lease_time; - if (time < ((unsigned int)(3 * RA_INTERVAL))) - time = 3 * RA_INTERVAL; + if (time < ((unsigned int)(3 * param->adv_interval))) + time = 3 * param->adv_interval; } if (context->flags & CONTEXT_DEPRECATE) @@ -564,8 +571,7 @@ time_t periodic_ra(time_t now) struct search_param param; struct dhcp_context *context; time_t next_event; - char interface[IF_NAMESIZE+1]; - + param.now = now; param.iface = 0; @@ -586,13 +592,15 @@ time_t periodic_ra(time_t now) if (!context) break; - if ((context->flags & CONTEXT_OLD) && context->if_index != 0) + if ((context->flags & CONTEXT_OLD) && + context->if_index != 0 && + indextoname(daemon->icmp6fd, param.iface, param.name)) { /* A context for an old address. We'll not find the interface by - looking for addresses, but we know it anyway, as long as we - sent at least one RA whilst the address was current. */ + looking for addresses, but we know it anyway, since the context is + constructed */ param.iface = context->if_index; - new_timeout(context, now); + new_timeout(context, param.name, now); } else if (iface_enumerate(AF_INET6, ¶m, iface_search)) /* There's a context overdue, but we can't find an interface @@ -603,15 +611,14 @@ time_t periodic_ra(time_t now) context->ra_time = 0; if (param.iface != 0 && - indextoname(daemon->icmp6fd, param.iface, interface) && - iface_check(AF_LOCAL, NULL, interface, NULL)) + iface_check(AF_LOCAL, NULL, param.name, NULL)) { struct iname *tmp; for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) - if (tmp->name && wildcard_match(tmp->name, interface)) + if (tmp->name && wildcard_match(tmp->name, param.name)) break; if (!tmp) - send_ra(now, param.iface, interface, NULL); + send_ra(now, param.iface, param.name, NULL); } } return next_event; @@ -643,7 +650,14 @@ static int iface_search(struct in6_addr *local, int prefix, if (!(flags & IFACE_TENTATIVE)) param->iface = if_index; - new_timeout(context, param->now); + /* should never fail */ + if (!indextoname(daemon->icmp6fd, if_index, param->name)) + { + param->iface = 0; + return 0; + } + + new_timeout(context, param->name, param->now); /* zero timers for other contexts on the same subnet, so they don't timeout independently */ @@ -659,14 +673,70 @@ static int iface_search(struct in6_addr *local, int prefix, return 1; /* keep searching */ } -static void new_timeout(struct dhcp_context *context, time_t now) +static void new_timeout(struct dhcp_context *context, char *iface_name, time_t now) { - if (difftime(now, context->ra_short_period_start) < 60.0 || option_bool(OPT_FAST_RA)) + if (difftime(now, context->ra_short_period_start) < 60.0) /* range 5 - 20 */ context->ra_time = now + 5 + (rand16()/4400); else - /* range 3/4 - 1 times RA_INTERVAL */ - context->ra_time = now + (3 * RA_INTERVAL)/4 + ((RA_INTERVAL * (unsigned int)rand16()) >> 18); + { + /* range 3/4 - 1 times MaxRtrAdvInterval */ + unsigned int adv_interval = calc_interval(find_iface_param(iface_name)); + context->ra_time = now + (3 * adv_interval)/4 + ((adv_interval * (unsigned int)rand16()) >> 18); + } +} + +static struct ra_interface *find_iface_param(char *iface) +{ + struct ra_interface *ra; + + for (ra = daemon->ra_interfaces; ra; ra = ra->next) + if (wildcard_match(ra->name, iface)) + return ra; + + return NULL; +} + +static unsigned int calc_interval(struct ra_interface *ra) +{ + int interval = 600; + + if (ra && ra->interval != 0) + { + interval = ra->interval; + if (interval > 1800) + interval = 1800; + else if (interval < 4) + interval = 4; + } + + return (unsigned int)interval; +} + +static unsigned int calc_lifetime(struct ra_interface *ra) +{ + int lifetime, interval = (int)calc_interval(ra); + + if (!ra || ra->lifetime == -1) /* not specified */ + lifetime = 3 * interval; + else + { + lifetime = ra->lifetime; + if (lifetime < interval && lifetime != 0) + lifetime = interval; + else if (lifetime > 9000) + lifetime = 9000; + } + + return (unsigned int)lifetime; +} + +static unsigned int calc_prio(struct ra_interface *ra) +{ + if (ra) + return ra->prio; + + return 0; } #endif |