version 1.1.1.2, 2014/06/15 16:31:38
|
version 1.1.1.3, 2016/11/02 09:57:01
|
Line 1
|
Line 1
|
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley | /* dnsmasq is Copyright (c) 2000-2016 Simon Kelley |
|
|
This program is free software; you can redistribute it and/or modify |
This program is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
Line 28
|
Line 28
|
|
|
struct ra_param { |
struct ra_param { |
time_t now; |
time_t now; |
int ind, managed, other, found_context, first; | int ind, managed, other, first, adv_router; |
char *if_name; |
char *if_name; |
struct dhcp_netid *tags; |
struct dhcp_netid *tags; |
struct in6_addr link_local, link_global, ula; |
struct in6_addr link_local, link_global, ula; |
unsigned int glob_pref_time, link_pref_time, ula_pref_time, adv_interval; | unsigned int glob_pref_time, link_pref_time, ula_pref_time, adv_interval, prio; |
| struct dhcp_context *found_context; |
}; |
}; |
|
|
struct search_param { |
struct search_param { |
Line 40 struct search_param {
|
Line 41 struct search_param {
|
char name[IF_NAMESIZE+1]; |
char name[IF_NAMESIZE+1]; |
}; |
}; |
|
|
|
struct alias_param { |
|
int iface; |
|
struct dhcp_bridge *bridge; |
|
int num_alias_ifs; |
|
int max_alias_ifs; |
|
int *alias_ifs; |
|
}; |
|
|
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest); |
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest); |
|
static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_addr *dest, |
|
int send_iface); |
|
static int send_ra_to_aliases(int index, unsigned int type, char *mac, size_t maclen, void *parm); |
static int add_prefixes(struct in6_addr *local, int prefix, |
static int add_prefixes(struct in6_addr *local, int prefix, |
int scope, int if_index, int flags, |
int scope, int if_index, int flags, |
unsigned int preferred, unsigned int valid, void *vparam); |
unsigned int preferred, unsigned int valid, void *vparam); |
Line 181 void icmp6_packet(time_t now)
|
Line 193 void icmp6_packet(time_t now)
|
else if (packet[0] == ND_ROUTER_SOLICIT) |
else if (packet[0] == ND_ROUTER_SOLICIT) |
{ |
{ |
char *mac = ""; |
char *mac = ""; |
|
struct dhcp_bridge *bridge, *alias; |
|
|
/* look for link-layer address option for logging */ |
/* look for link-layer address option for logging */ |
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz) |
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz) |
Line 191 void icmp6_packet(time_t now)
|
Line 204 void icmp6_packet(time_t now)
|
|
|
if (!option_bool(OPT_QUIET_RA)) |
if (!option_bool(OPT_QUIET_RA)) |
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac); |
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac); |
/* source address may not be valid in solicit request. */ | |
send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL); | /* If the incoming interface is an alias of some other one (as |
| specified by the --bridge-interface option), send an RA using |
| the context of the aliased interface. */ |
| for (bridge = daemon->bridges; bridge; bridge = bridge->next) |
| { |
| int bridge_index = if_nametoindex(bridge->iface); |
| if (bridge_index) |
| { |
| for (alias = bridge->alias; alias; alias = alias->next) |
| if (wildcard_matchn(alias->iface, interface, IF_NAMESIZE)) |
| { |
| /* Send an RA on if_index with information from |
| bridge_index. */ |
| send_ra_alias(now, bridge_index, bridge->iface, NULL, if_index); |
| break; |
| } |
| if (alias) |
| break; |
| } |
| } |
| |
| /* If the incoming interface wasn't an alias, send an RA using |
| the context of the incoming interface. */ |
| if (!bridge) |
| /* source address may not be valid in solicit request. */ |
| send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL); |
} |
} |
} |
} |
|
|
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest) | static void send_ra_alias(time_t now, int iface, char *iface_name, struct in6_addr *dest, int send_iface) |
{ |
{ |
struct ra_packet *ra; |
struct ra_packet *ra; |
struct ra_param parm; |
struct ra_param parm; |
Line 210 static void send_ra(time_t now, int iface, char *iface
|
Line 248 static void send_ra(time_t now, int iface, char *iface
|
#ifdef HAVE_LINUX_NETWORK |
#ifdef HAVE_LINUX_NETWORK |
FILE *f; |
FILE *f; |
#endif |
#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 = calc_prio(ra_param); |
|
ra->lifetime = htons(calc_lifetime(ra_param)); |
|
ra->reachable_time = 0; |
|
ra->retrans_time = 0; |
|
|
|
parm.ind = iface; |
parm.ind = iface; |
parm.managed = 0; |
parm.managed = 0; |
parm.other = 0; |
parm.other = 0; |
parm.found_context = 0; | parm.found_context = NULL; |
| parm.adv_router = 0; |
parm.if_name = iface_name; |
parm.if_name = iface_name; |
parm.first = 1; |
parm.first = 1; |
parm.now = now; |
parm.now = now; |
parm.glob_pref_time = parm.link_pref_time = parm.ula_pref_time = 0; |
parm.glob_pref_time = parm.link_pref_time = parm.ula_pref_time = 0; |
parm.adv_interval = calc_interval(ra_param); |
parm.adv_interval = calc_interval(ra_param); |
|
parm.prio = calc_prio(ra_param); |
|
|
|
save_counter(0); |
|
ra = expand(sizeof(struct ra_packet)); |
|
|
|
ra->type = ND_ROUTER_ADVERT; |
|
ra->code = 0; |
|
ra->hop_limit = hop_limit; |
|
ra->flags = parm.prio; |
|
ra->lifetime = htons(calc_lifetime(ra_param)); |
|
ra->reachable_time = 0; |
|
ra->retrans_time = 0; |
|
|
/* set tag with name == interface */ |
/* set tag with name == interface */ |
iface_id.net = iface_name; |
iface_id.net = iface_name; |
iface_id.next = NULL; |
iface_id.next = NULL; |
Line 269 static void send_ra(time_t now, int iface, char *iface
|
Line 309 static void send_ra(time_t now, int iface, char *iface
|
unsigned int old = difftime(now, context->address_lost_time); |
unsigned int old = difftime(now, context->address_lost_time); |
|
|
if (old > context->saved_valid) |
if (old > context->saved_valid) |
{ | { |
/* We've advertised this enough, time to go */ |
/* We've advertised this enough, time to go */ |
|
|
|
/* If this context held the timeout, and there's another context in use |
|
transfer the timeout there. */ |
|
if (context->ra_time != 0 && parm.found_context && parm.found_context->ra_time == 0) |
|
new_timeout(parm.found_context, iface_name, now); |
|
|
*up = context->next; |
*up = context->next; |
free(context); |
free(context); |
} |
} |
Line 286 static void send_ra(time_t now, int iface, char *iface
|
Line 332 static void send_ra(time_t now, int iface, char *iface
|
setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU)); |
setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU)); |
|
|
|
|
if ((context->flags & | if (context->flags & CONTEXT_RA) |
(CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS))) | |
{ |
{ |
do_slaac = 1; |
do_slaac = 1; |
if (context->flags & CONTEXT_DHCP) |
if (context->flags & CONTEXT_DHCP) |
Line 312 static void send_ra(time_t now, int iface, char *iface
|
Line 357 static void send_ra(time_t now, int iface, char *iface
|
opt->type = ICMP6_OPT_PREFIX; |
opt->type = ICMP6_OPT_PREFIX; |
opt->len = 4; |
opt->len = 4; |
opt->prefix_len = context->prefix; |
opt->prefix_len = context->prefix; |
/* autonomous only if we're not doing dhcp, always set "on-link" */ | /* autonomous only if we're not doing dhcp, set |
opt->flags = do_slaac ? 0xC0 : 0x80; | "on-link" unless "off-link" was specified */ |
| opt->flags = (do_slaac ? 0x40 : 0) | |
| ((context->flags & CONTEXT_RA_OFF_LINK) ? 0 : 0x80); |
opt->valid_lifetime = htonl(context->saved_valid - old); |
opt->valid_lifetime = htonl(context->saved_valid - old); |
opt->preferred_lifetime = htonl(0); |
opt->preferred_lifetime = htonl(0); |
opt->reserved = 0; |
opt->reserved = 0; |
Line 339 static void send_ra(time_t now, int iface, char *iface
|
Line 386 static void send_ra(time_t now, int iface, char *iface
|
if (!old_prefix && !parm.found_context) |
if (!old_prefix && !parm.found_context) |
return; |
return; |
|
|
|
/* If we're sending router address instead of prefix in at least on prefix, |
|
include the advertisement interval option. */ |
|
if (parm.adv_router) |
|
{ |
|
put_opt6_char(ICMP6_OPT_ADV_INTERVAL); |
|
put_opt6_char(1); |
|
put_opt6_short(0); |
|
/* interval value is in milliseconds */ |
|
put_opt6_long(1000 * calc_interval(find_iface_param(iface_name))); |
|
} |
|
|
#ifdef HAVE_LINUX_NETWORK |
#ifdef HAVE_LINUX_NETWORK |
/* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU |
/* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU |
available from SIOCGIFMTU */ |
available from SIOCGIFMTU */ |
Line 356 static void send_ra(time_t now, int iface, char *iface
|
Line 414 static void send_ra(time_t now, int iface, char *iface
|
} |
} |
#endif |
#endif |
|
|
iface_enumerate(AF_LOCAL, &iface, add_lla); | iface_enumerate(AF_LOCAL, &send_iface, add_lla); |
|
|
/* RDNSS, RFC 6106, use relevant DHCP6 options */ |
/* RDNSS, RFC 6106, use relevant DHCP6 options */ |
(void)option_filter(parm.tags, NULL, daemon->dhcp_opts6); |
(void)option_filter(parm.tags, NULL, daemon->dhcp_opts6); |
Line 464 static void send_ra(time_t now, int iface, char *iface
|
Line 522 static void send_ra(time_t now, int iface, char *iface
|
else |
else |
{ |
{ |
inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr); |
inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr); |
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface)); | setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &send_iface, sizeof(send_iface)); |
} |
} |
|
|
while (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0, | while (retry_send(sendto(daemon->icmp6fd, daemon->outpacket.iov_base, |
(struct sockaddr *)&addr, sizeof(addr)) == -1 && retry_send()); | save_counter(0), 0, (struct sockaddr *)&addr, |
| sizeof(addr)))); |
|
|
} |
} |
|
|
|
static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest) |
|
{ |
|
/* Send an RA on the same interface that the RA content is based |
|
on. */ |
|
send_ra_alias(now, iface, iface_name, dest, iface); |
|
} |
|
|
static int add_prefixes(struct in6_addr *local, int prefix, |
static int add_prefixes(struct in6_addr *local, int prefix, |
int scope, int if_index, int flags, |
int scope, int if_index, int flags, |
unsigned int preferred, unsigned int valid, void *vparam) |
unsigned int preferred, unsigned int valid, void *vparam) |
Line 500 static int add_prefixes(struct in6_addr *local, int p
|
Line 566 static int add_prefixes(struct in6_addr *local, int p
|
int do_slaac = 0; |
int do_slaac = 0; |
int deprecate = 0; |
int deprecate = 0; |
int constructed = 0; |
int constructed = 0; |
|
int adv_router = 0; |
|
int off_link = 0; |
unsigned int time = 0xffffffff; |
unsigned int time = 0xffffffff; |
struct dhcp_context *context; |
struct dhcp_context *context; |
|
|
Line 511 static int add_prefixes(struct in6_addr *local, int p
|
Line 579 static int add_prefixes(struct in6_addr *local, int p
|
{ |
{ |
context->saved_valid = valid; |
context->saved_valid = valid; |
|
|
if ((context->flags & | if (context->flags & CONTEXT_RA) |
(CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS))) | |
{ |
{ |
do_slaac = 1; |
do_slaac = 1; |
if (context->flags & CONTEXT_DHCP) |
if (context->flags & CONTEXT_DHCP) |
Line 530 static int add_prefixes(struct in6_addr *local, int p
|
Line 597 static int add_prefixes(struct in6_addr *local, int p
|
param->managed = 1; |
param->managed = 1; |
param->other = 1; |
param->other = 1; |
} |
} |
| |
| /* Configured to advertise router address, not prefix. See RFC 3775 7.2 |
| In this case we do all addresses associated with a context, |
| hence the real_prefix setting here. */ |
| if (context->flags & CONTEXT_RA_ROUTER) |
| { |
| adv_router = 1; |
| param->adv_router = 1; |
| real_prefix = context->prefix; |
| } |
| |
/* find floor time, don't reduce below 3 * RA interval. */ |
/* find floor time, don't reduce below 3 * RA interval. */ |
if (time > context->lease_time) |
if (time > context->lease_time) |
{ |
{ |
Line 556 static int add_prefixes(struct in6_addr *local, int p
|
Line 633 static int add_prefixes(struct in6_addr *local, int p
|
/* subsequent prefixes on the same interface |
/* subsequent prefixes on the same interface |
and subsequent instances of this prefix don't need timers. |
and subsequent instances of this prefix don't need timers. |
Be careful not to find the same prefix twice with different |
Be careful not to find the same prefix twice with different |
addresses. */ | addresses unless we're advertising the actual addresses. */ |
if (!(context->flags & CONTEXT_RA_DONE)) |
if (!(context->flags & CONTEXT_RA_DONE)) |
{ |
{ |
if (!param->first) |
if (!param->first) |
context->ra_time = 0; |
context->ra_time = 0; |
context->flags |= CONTEXT_RA_DONE; |
context->flags |= CONTEXT_RA_DONE; |
real_prefix = context->prefix; |
real_prefix = context->prefix; |
|
off_link = (context->flags & CONTEXT_RA_OFF_LINK); |
} |
} |
|
|
param->first = 0; | param->first = 0; |
param->found_context = 1; | /* found_context is the _last_ one we found, so if there's |
| more than one, it's not the first. */ |
| param->found_context = context; |
} |
} |
|
|
/* configured time is ceiling */ |
/* configured time is ceiling */ |
Line 607 static int add_prefixes(struct in6_addr *local, int p
|
Line 687 static int add_prefixes(struct in6_addr *local, int p
|
if ((opt = expand(sizeof(struct prefix_opt)))) |
if ((opt = expand(sizeof(struct prefix_opt)))) |
{ |
{ |
/* zero net part of address */ |
/* zero net part of address */ |
setaddr6part(local, addr6part(local) & ~((real_prefix == 64) ? (u64)-1LL : (1LLU << (128 - real_prefix)) - 1LLU)); | if (!adv_router) |
| setaddr6part(local, addr6part(local) & ~((real_prefix == 64) ? (u64)-1LL : (1LLU << (128 - real_prefix)) - 1LLU)); |
|
|
opt->type = ICMP6_OPT_PREFIX; |
opt->type = ICMP6_OPT_PREFIX; |
opt->len = 4; |
opt->len = 4; |
opt->prefix_len = real_prefix; |
opt->prefix_len = real_prefix; |
/* autonomous only if we're not doing dhcp, always set "on-link" */ | /* autonomous only if we're not doing dhcp, set |
opt->flags = do_slaac ? 0xC0 : 0x80; | "on-link" unless "off-link" was specified */ |
| opt->flags = (off_link ? 0 : 0x80); |
| if (do_slaac) |
| opt->flags |= 0x40; |
| if (adv_router) |
| opt->flags |= 0x20; |
opt->valid_lifetime = htonl(valid); |
opt->valid_lifetime = htonl(valid); |
opt->preferred_lifetime = htonl(preferred); |
opt->preferred_lifetime = htonl(preferred); |
opt->reserved = 0; |
opt->reserved = 0; |
Line 655 time_t periodic_ra(time_t now)
|
Line 741 time_t periodic_ra(time_t now)
|
struct search_param param; |
struct search_param param; |
struct dhcp_context *context; |
struct dhcp_context *context; |
time_t next_event; |
time_t next_event; |
|
struct alias_param aparam; |
|
|
param.now = now; |
param.now = now; |
param.iface = 0; |
param.iface = 0; |
Line 702 time_t periodic_ra(time_t now)
|
Line 789 time_t periodic_ra(time_t now)
|
if (tmp->name && wildcard_match(tmp->name, param.name)) |
if (tmp->name && wildcard_match(tmp->name, param.name)) |
break; |
break; |
if (!tmp) |
if (!tmp) |
send_ra(now, param.iface, param.name, NULL); | { |
| send_ra(now, param.iface, param.name, NULL); |
| |
| /* Also send on all interfaces that are aliases of this |
| one. */ |
| for (aparam.bridge = daemon->bridges; |
| aparam.bridge; |
| aparam.bridge = aparam.bridge->next) |
| if ((int)if_nametoindex(aparam.bridge->iface) == param.iface) |
| { |
| /* Count the number of alias interfaces for this |
| 'bridge', by calling iface_enumerate with |
| send_ra_to_aliases and NULL alias_ifs. */ |
| aparam.iface = param.iface; |
| aparam.alias_ifs = NULL; |
| aparam.num_alias_ifs = 0; |
| iface_enumerate(AF_LOCAL, &aparam, send_ra_to_aliases); |
| my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s => %d alias(es)", |
| param.name, daemon->addrbuff, aparam.num_alias_ifs); |
| |
| /* Allocate memory to store the alias interface |
| indices. */ |
| aparam.alias_ifs = (int *)whine_malloc(aparam.num_alias_ifs * |
| sizeof(int)); |
| if (aparam.alias_ifs) |
| { |
| /* Use iface_enumerate again to get the alias |
| interface indices, then send on each of |
| those. */ |
| aparam.max_alias_ifs = aparam.num_alias_ifs; |
| aparam.num_alias_ifs = 0; |
| iface_enumerate(AF_LOCAL, &aparam, send_ra_to_aliases); |
| for (; aparam.num_alias_ifs; aparam.num_alias_ifs--) |
| { |
| my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s => i/f %d", |
| param.name, daemon->addrbuff, |
| aparam.alias_ifs[aparam.num_alias_ifs - 1]); |
| send_ra_alias(now, |
| param.iface, |
| param.name, |
| NULL, |
| aparam.alias_ifs[aparam.num_alias_ifs - 1]); |
| } |
| free(aparam.alias_ifs); |
| } |
| |
| /* The source interface can only appear in at most |
| one --bridge-interface. */ |
| break; |
| } |
| } |
} |
} |
} |
} |
return next_event; |
return next_event; |
} |
} |
| |
| static int send_ra_to_aliases(int index, unsigned int type, char *mac, size_t maclen, void *parm) |
| { |
| struct alias_param *aparam = (struct alias_param *)parm; |
| char ifrn_name[IFNAMSIZ]; |
| struct dhcp_bridge *alias; |
| |
| (void)type; |
| (void)mac; |
| (void)maclen; |
| |
| if (if_indextoname(index, ifrn_name)) |
| for (alias = aparam->bridge->alias; alias; alias = alias->next) |
| if (wildcard_matchn(alias->iface, ifrn_name, IFNAMSIZ)) |
| { |
| if (aparam->alias_ifs && (aparam->num_alias_ifs < aparam->max_alias_ifs)) |
| aparam->alias_ifs[aparam->num_alias_ifs] = index; |
| aparam->num_alias_ifs++; |
| } |
| |
| return 1; |
| } |
| |
static int iface_search(struct in6_addr *local, int prefix, |
static int iface_search(struct in6_addr *local, int prefix, |
int scope, int if_index, int flags, |
int scope, int if_index, int flags, |
int preferred, int valid, void *vparam) |
int preferred, int valid, void *vparam) |