Add --ra-param and remove --force-fast-ra

This commit is contained in:
Simon Kelley
2013-10-10 20:58:11 +01:00
parent 043c271f8a
commit c4cd95df68
5 changed files with 147 additions and 37 deletions

View File

@@ -221,8 +221,7 @@ struct event_desc {
#define OPT_TFTP_LC 38
#define OPT_CLEVERBIND 39
#define OPT_TFTP 40
#define OPT_FAST_RA 41
#define OPT_LAST 42
#define OPT_LAST 41
/* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -701,6 +700,12 @@ struct prefix_class {
};
#endif
struct ra_interface {
char *name;
int interval, lifetime, prio;
struct ra_interface *next;
};
struct dhcp_context {
unsigned int lease_time, addr_epoch;
struct in_addr netmask, broadcast;
@@ -825,6 +830,7 @@ extern struct daemon {
unsigned long local_ttl, neg_ttl, max_ttl, max_cache_ttl, auth_ttl;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp, *dhcp6;
struct ra_interface *ra_interfaces;
struct dhcp_config *dhcp_conf;
struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6;
struct dhcp_vendor *dhcp_vendors;

View File

@@ -132,8 +132,8 @@ struct myoption {
#ifdef OPTION6_PREFIX_CLASS
#define LOPT_PREF_CLSS 321
#endif
#define LOPT_FAST_RA 322
#define LOPT_RELAY 323
#define LOPT_RA_PARAM 324
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -271,8 +271,8 @@ static const struct myoption opts[] =
#ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif
{ "force-fast-ra", 0, 0, LOPT_FAST_RA },
{ "dhcp-relay", 1, 0, LOPT_RELAY },
{ "ra-param", 1, 0, LOPT_RA_PARAM },
{ NULL, 0, 0, 0 }
};
@@ -402,7 +402,6 @@ static struct {
{ LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
{ LOPT_FQDN, OPT_FQDN_UPDATE, NULL, gettext_noop("Allow DHCP clients to do their own DDNS updates."), NULL },
{ LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
{ LOPT_FAST_RA, OPT_FAST_RA, NULL, gettext_noop("Always send frequent router-advertisements"), NULL },
{ LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
{ LOPT_HOST_REC, ARG_DUP, "<name>,<address>", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
{ LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
@@ -418,6 +417,7 @@ static struct {
#ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif
{ LOPT_RA_PARAM, ARG_DUP, "<interface>,[high,|low,]<interval>[,<lifetime>]", gettext_noop("Set priority, resend-interval and router-lifetime"), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -3215,6 +3215,31 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
#endif
#ifdef HAVE_DHCP6
case LOPT_RA_PARAM: /* --ra-param */
if ((comma = split(arg)))
{
struct ra_interface *new = opt_malloc(sizeof(struct ra_interface));
new->lifetime = -1;
new->prio = 0;
new->name = opt_string_alloc(arg);
if (strcasestr(comma, "high") == comma || strcasestr(comma, "low") == comma)
{
if (*comma == 'l' || *comma == 'L')
new->prio = 0x18;
else
new->prio = 0x08;
comma = split(comma);
}
arg = split(comma);
if (!atoi_check(comma, &new->interval) ||
(arg && !atoi_check(arg, &new->lifetime)))
ret_err(_("bad RA-params"));
new->next = daemon->ra_interfaces;
daemon->ra_interfaces = new;
}
break;
case LOPT_DUID: /* --dhcp-duid */
if (!(comma = split(arg)) || !atoi_check(arg, (int *)&daemon->duid_enterprise))
ret_err(_("bad DUID"));

View File

@@ -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