summaryrefslogtreecommitdiff
path: root/src/radv.c
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2013-10-10 20:58:11 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2013-10-10 20:58:11 +0100
commitc4cd95df68b573b63d234ecdb675228657d65353 (patch)
treec653e10b47d00cb729cccf8d3fbf475a7babac0d /src/radv.c
parent043c271f8a4bc7be157f938f5d581e79d9c1f4d2 (diff)
downloaddnsmasq-c4cd95df68b573b63d234ecdb675228657d65353.tar.gz
Add --ra-param and remove --force-fast-ra
Diffstat (limited to 'src/radv.c')
-rw-r--r--src/radv.c120
1 files changed, 95 insertions, 25 deletions
diff --git a/src/radv.c b/src/radv.c
index b1f2bc1..d1147ba 100644
--- a/src/radv.c
+++ b/src/radv.c
@@ -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, &param, 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