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