mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 10:18:25 +00:00
[fd00::} and [fe80::] special addresses in DHCPv6 options.
This commit is contained in:
@@ -11,6 +11,12 @@ version 2.69
|
||||
--dhcp-option=option6:23,[::] Thanks to Tsachi Kimeldorfer
|
||||
for spotting the problem.
|
||||
|
||||
Add [fd00::] and [fe80::] as special addresses in DHCPv6
|
||||
options, analogous to [::]. [fd00::] is replaced with the
|
||||
actual ULA of the interface on the machine running
|
||||
dnsmasq, [fe80::] with the link-local address.
|
||||
Thanks to Tsachi Kimeldorfer for championing this.
|
||||
|
||||
|
||||
version 2.68
|
||||
Use random addresses for DHCPv6 temporary address
|
||||
|
||||
@@ -917,9 +917,11 @@ and to set the time-server address to 192.168.0.4, do
|
||||
.B --dhcp-option = 42,192.168.0.4
|
||||
or
|
||||
.B --dhcp-option = option:ntp-server, 192.168.0.4
|
||||
The special address 0.0.0.0 (or [::] for DHCPv6) is taken to mean "the address of the
|
||||
machine running dnsmasq". Data types allowed are comma separated
|
||||
dotted-quad IP addresses, a decimal number, colon-separated hex digits
|
||||
The special address 0.0.0.0 is taken to mean "the address of the
|
||||
machine running dnsmasq".
|
||||
|
||||
Data types allowed are comma separated
|
||||
dotted-quad IPv4 addresses, []-wrapped IPv6 addresses, a decimal number, colon-separated hex digits
|
||||
and a text string. If the optional tags are given then
|
||||
this option is only sent when all the tags are matched.
|
||||
|
||||
@@ -935,7 +937,9 @@ keyword, followed by the option number or option name. The IPv6 option
|
||||
name space is disjoint from the IPv4 option name space. IPv6 addresses
|
||||
in options must be bracketed with square brackets, eg.
|
||||
.B --dhcp-option=option6:ntp-server,[1234::56]
|
||||
|
||||
For IPv6, [::] means "the global address of
|
||||
the machine running dnsmasq", whilst [fd00::] is replaced with the
|
||||
ULA, if it exists, and [fe80::] with the link-local address.
|
||||
|
||||
Be careful: no checking is done that the correct type of data for the
|
||||
option number is sent, it is quite possible to
|
||||
|
||||
11
src/dhcp6.c
11
src/dhcp6.c
@@ -23,7 +23,7 @@
|
||||
struct iface_param {
|
||||
struct dhcp_context *current;
|
||||
struct dhcp_relay *relay;
|
||||
struct in6_addr fallback, relay_local;
|
||||
struct in6_addr fallback, relay_local, ll_addr, ula_addr;
|
||||
int ind, addr_match;
|
||||
};
|
||||
|
||||
@@ -158,6 +158,8 @@ void dhcp6_packet(time_t now)
|
||||
parm.ind = if_index;
|
||||
parm.addr_match = 0;
|
||||
memset(&parm.fallback, 0, IN6ADDRSZ);
|
||||
memset(&parm.ll_addr, 0, IN6ADDRSZ);
|
||||
memset(&parm.ula_addr, 0, IN6ADDRSZ);
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
|
||||
@@ -210,7 +212,7 @@ void dhcp6_packet(time_t now)
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
|
||||
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
|
||||
sz, &from.sin6_addr, now);
|
||||
&parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
|
||||
|
||||
lease_update_file(now);
|
||||
lease_update_dns(0);
|
||||
@@ -309,6 +311,11 @@ static int complete_context6(struct in6_addr *local, int prefix,
|
||||
|
||||
if (if_index == param->ind)
|
||||
{
|
||||
if (IN6_IS_ADDR_LINKLOCAL(local))
|
||||
param->ll_addr = *local;
|
||||
else if (IN6_IS_ADDR_ULA(local))
|
||||
param->ula_addr = *local;
|
||||
|
||||
if (!IN6_IS_ADDR_LOOPBACK(local) &&
|
||||
!IN6_IS_ADDR_LINKLOCAL(local) &&
|
||||
!IN6_IS_ADDR_MULTICAST(local))
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include <getopt.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "ip6addr.h"
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
@@ -1314,7 +1315,8 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
|
||||
/* rfc3315.c */
|
||||
#ifdef HAVE_DHCP6
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, size_t sz, struct in6_addr *client_addr, time_t now);
|
||||
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
|
||||
size_t sz, struct in6_addr *client_addr, time_t now);
|
||||
void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id);
|
||||
|
||||
unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface);
|
||||
|
||||
34
src/ip6addr.h
Normal file
34
src/ip6addr.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley
|
||||
|
||||
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
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define IN6_IS_ADDR_ULA(a) \
|
||||
((((__const uint32_t *) (a))[0] & htonl (0xff000000)) \
|
||||
== htonl (0xfd000000))
|
||||
|
||||
#define IN6_IS_ADDR_ULA_ZERO(a) \
|
||||
(((__const uint32_t *) (a))[0] == htonl (0xfd000000) \
|
||||
&& ((__const uint32_t *) (a))[1] == 0 \
|
||||
&& ((__const uint32_t *) (a))[2] == 0 \
|
||||
&& ((__const uint32_t *) (a))[3] == 0)
|
||||
|
||||
#define IN6_IS_ADDR_LINK_LOCAL_ZERO(a) \
|
||||
(((__const uint32_t *) (a))[0] == htonl (0xfe800000) \
|
||||
&& ((__const uint32_t *) (a))[1] == 0 \
|
||||
&& ((__const uint32_t *) (a))[2] == 0 \
|
||||
&& ((__const uint32_t *) (a))[3] == 0)
|
||||
|
||||
104
src/radv.c
104
src/radv.c
@@ -31,8 +31,8 @@ struct ra_param {
|
||||
int ind, managed, other, found_context, first;
|
||||
char *if_name;
|
||||
struct dhcp_netid *tags;
|
||||
struct in6_addr link_local, link_global;
|
||||
unsigned int pref_time, adv_interval;
|
||||
struct in6_addr link_local, link_global, ula;
|
||||
unsigned int glob_pref_time, link_pref_time, ula_pref_time, adv_interval;
|
||||
};
|
||||
|
||||
struct search_param {
|
||||
@@ -206,6 +206,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
struct dhcp_opt *opt_cfg;
|
||||
struct ra_interface *ra_param = find_iface_param(iface_name);
|
||||
int done_dns = 0, old_prefix = 0;
|
||||
unsigned int min_pref_time;
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
FILE *f;
|
||||
#endif
|
||||
@@ -228,7 +229,7 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
parm.if_name = iface_name;
|
||||
parm.first = 1;
|
||||
parm.now = now;
|
||||
parm.pref_time = 0;
|
||||
parm.glob_pref_time = parm.link_pref_time = parm.ula_pref_time = 0;
|
||||
parm.adv_interval = calc_interval(ra_param);
|
||||
|
||||
/* set tag with name == interface */
|
||||
@@ -245,6 +246,18 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
if (!iface_enumerate(AF_INET6, &parm, add_prefixes))
|
||||
return;
|
||||
|
||||
/* Find smallest preferred time within address classes,
|
||||
to use as lifetime for options. This is a rather arbitrary choice. */
|
||||
min_pref_time = 0xffffffff;
|
||||
if (parm.glob_pref_time != 0 && parm.glob_pref_time < min_pref_time)
|
||||
min_pref_time = parm.glob_pref_time;
|
||||
|
||||
if (parm.ula_pref_time != 0 && parm.ula_pref_time < min_pref_time)
|
||||
min_pref_time = parm.ula_pref_time;
|
||||
|
||||
if (parm.link_pref_time != 0 && parm.link_pref_time < min_pref_time)
|
||||
min_pref_time = parm.link_pref_time;
|
||||
|
||||
/* Look for constructed contexts associated with addresses which have gone,
|
||||
and advertise them with preferred_time == 0 RFC 6204 4.3 L-13 */
|
||||
for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
|
||||
@@ -340,22 +353,48 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DNS_SERVER)
|
||||
{
|
||||
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
|
||||
struct in6_addr *a;
|
||||
int len;
|
||||
|
||||
done_dns = 1;
|
||||
if (opt_cfg->len == 0 || (IN6_IS_ADDR_UNSPECIFIED(a) && parm.pref_time == 0))
|
||||
|
||||
if (opt_cfg->len == 0)
|
||||
continue;
|
||||
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char((opt_cfg->len/8) + 1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(parm.pref_time);
|
||||
/* zero means "self" */
|
||||
for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
put_opt6(&parm.link_global, IN6ADDRSZ);
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
/* reduce len for any addresses we can't substitute */
|
||||
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, i = 0;
|
||||
i < opt_cfg->len; i += IN6ADDRSZ, a++)
|
||||
if ((IN6_IS_ADDR_UNSPECIFIED(a) && parm.glob_pref_time == 0) ||
|
||||
(IN6_IS_ADDR_ULA_ZERO(a) && parm.ula_pref_time == 0) ||
|
||||
(IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && parm.link_pref_time == 0))
|
||||
len -= IN6ADDRSZ;
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char((len/8) + 1);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(min_pref_time);
|
||||
|
||||
for (a = (struct in6_addr *)opt_cfg->val, i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
{
|
||||
if (parm.glob_pref_time != 0)
|
||||
put_opt6(&parm.link_global, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_ULA_ZERO(a))
|
||||
{
|
||||
if (parm.ula_pref_time != 0)
|
||||
put_opt6(&parm.ula, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
|
||||
{
|
||||
if (parm.link_pref_time != 0)
|
||||
put_opt6(&parm.link_local, IN6ADDRSZ);
|
||||
}
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
|
||||
@@ -365,7 +404,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(parm.pref_time);
|
||||
put_opt6_long(min_pref_time);
|
||||
put_opt6(opt_cfg->val, opt_cfg->len);
|
||||
|
||||
/* pad */
|
||||
@@ -374,13 +413,13 @@ static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *de
|
||||
}
|
||||
}
|
||||
|
||||
if (daemon->port == NAMESERVER_PORT && !done_dns && parm.pref_time != 0)
|
||||
if (daemon->port == NAMESERVER_PORT && !done_dns && parm.link_pref_time != 0)
|
||||
{
|
||||
/* default == us, as long as we are supplying DNS service. */
|
||||
put_opt6_char(ICMP6_OPT_RDNSS);
|
||||
put_opt6_char(3);
|
||||
put_opt6_short(0);
|
||||
put_opt6_long(parm.pref_time);
|
||||
put_opt6_long(min_pref_time);
|
||||
put_opt6(&parm.link_local, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
@@ -426,7 +465,16 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
if (if_index == param->ind)
|
||||
{
|
||||
if (IN6_IS_ADDR_LINKLOCAL(local))
|
||||
param->link_local = *local;
|
||||
{
|
||||
/* Can there be more than one LL address?
|
||||
Select the one with the longest preferred time
|
||||
if there is. */
|
||||
if (preferred > param->link_pref_time)
|
||||
{
|
||||
param->link_pref_time = preferred;
|
||||
param->link_local = *local;
|
||||
}
|
||||
}
|
||||
else if (!IN6_IS_ADDR_LOOPBACK(local) &&
|
||||
!IN6_IS_ADDR_MULTICAST(local))
|
||||
{
|
||||
@@ -517,10 +565,21 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
if (!constructed || preferred > time)
|
||||
preferred = time;
|
||||
|
||||
if (preferred > param->pref_time)
|
||||
if (IN6_IS_ADDR_ULA(local))
|
||||
{
|
||||
param->pref_time = preferred;
|
||||
param->link_global = *local;
|
||||
if (preferred > param->ula_pref_time)
|
||||
{
|
||||
param->ula_pref_time = preferred;
|
||||
param->ula = *local;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (preferred > param->glob_pref_time)
|
||||
{
|
||||
param->glob_pref_time = preferred;
|
||||
param->link_global = *local;
|
||||
}
|
||||
}
|
||||
|
||||
if (real_prefix != 0)
|
||||
@@ -546,7 +605,6 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
if (!option_bool(OPT_QUIET_RA))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ struct state {
|
||||
int clid_len, iaid, ia_type, interface, hostname_auth, lease_allocate;
|
||||
char *client_hostname, *hostname, *domain, *send_domain;
|
||||
struct dhcp_context *context;
|
||||
struct in6_addr *link_address, *fallback;
|
||||
struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
|
||||
unsigned int xid, fqdn_flags;
|
||||
char *iface_name;
|
||||
void *packet_options, *end;
|
||||
@@ -73,7 +73,8 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time
|
||||
|
||||
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, size_t sz, struct in6_addr *client_addr, time_t now)
|
||||
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
|
||||
size_t sz, struct in6_addr *client_addr, time_t now)
|
||||
{
|
||||
struct dhcp_vendor *vendor;
|
||||
int msg_type;
|
||||
@@ -93,6 +94,8 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
|
||||
state.interface = interface;
|
||||
state.iface_name = iface_name;
|
||||
state.fallback = fallback;
|
||||
state.ll_addr = ll_addr;
|
||||
state.ula_addr = ula_addr;
|
||||
state.mac_len = 0;
|
||||
state.tags = NULL;
|
||||
state.link_address = NULL;
|
||||
@@ -1269,36 +1272,59 @@ static struct dhcp_netid *add_options(struct state *state, int do_refresh)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DNS_SERVER)
|
||||
{
|
||||
done_dns = 1;
|
||||
if (opt_cfg->len == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opt_cfg->opt == OPTION6_REFRESH_TIME)
|
||||
done_refresh = 1;
|
||||
|
||||
o = new_opt6(opt_cfg->opt);
|
||||
if (opt_cfg->flags & DHOPT_ADDR6)
|
||||
{
|
||||
int j;
|
||||
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
|
||||
for (j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
|
||||
{
|
||||
/* zero means "self" (but not in vendorclass options.) */
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
{
|
||||
if (!add_local_addrs(state->context))
|
||||
put_opt6(state->fallback, IN6ADDRSZ);
|
||||
int len, j;
|
||||
struct in6_addr *a;
|
||||
|
||||
if (opt_cfg->opt == OPTION6_DNS_SERVER)
|
||||
done_dns = 1;
|
||||
|
||||
for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0;
|
||||
j < opt_cfg->len; j += IN6ADDRSZ, a++)
|
||||
if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ||
|
||||
(IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ll_addr)))
|
||||
len -= IN6ADDRSZ;
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
|
||||
o = new_opt6(opt_cfg->opt);
|
||||
|
||||
for (a = (struct in6_addr *)opt_cfg->val, j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
|
||||
{
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(a))
|
||||
{
|
||||
if (!add_local_addrs(state->context))
|
||||
put_opt6(state->fallback, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_ULA_ZERO(a))
|
||||
{
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr))
|
||||
put_opt6(state->ula_addr, IN6ADDRSZ);
|
||||
}
|
||||
else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
|
||||
{
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr))
|
||||
put_opt6(state->ll_addr, IN6ADDRSZ);
|
||||
}
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
}
|
||||
else
|
||||
put_opt6(a, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
end_opt6(o);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
o = new_opt6(opt_cfg->opt);
|
||||
if (opt_cfg->val)
|
||||
put_opt6(opt_cfg->val, opt_cfg->len);
|
||||
end_opt6(o);
|
||||
}
|
||||
else if (opt_cfg->val)
|
||||
put_opt6(opt_cfg->val, opt_cfg->len);
|
||||
end_opt6(o);
|
||||
}
|
||||
|
||||
if (daemon->port == NAMESERVER_PORT && !done_dns)
|
||||
|
||||
Reference in New Issue
Block a user