|
|
@@ -0,0 +1,626 @@
|
|
|
+From ae5b7e04a1025167f1b80840e61432a3cea9625c Mon Sep 17 00:00:00 2001
|
|
|
+From: Simon Kelley <[email protected]>
|
|
|
+Date: Wed, 27 Mar 2019 22:33:28 +0000
|
|
|
+Subject: [PATCH 46/57] Add --shared-network DHCP configuration.
|
|
|
+
|
|
|
+Signed-off-by: Kevin Darbyshire-Bryant <[email protected]>
|
|
|
+---
|
|
|
+ CHANGELOG | 5 ++
|
|
|
+ man/dnsmasq.8 | 21 ++++++
|
|
|
+ src/dhcp.c | 76 ++++++++++++++++++----
|
|
|
+ src/dhcp6.c | 175 ++++++++++++++++++++++++++++----------------------
|
|
|
+ src/dnsmasq.h | 11 ++++
|
|
|
+ src/option.c | 41 ++++++++++++
|
|
|
+ src/rfc2131.c | 97 +++++++++++++++++-----------
|
|
|
+ src/rfc3315.c | 40 +++++++++---
|
|
|
+ 8 files changed, 332 insertions(+), 134 deletions(-)
|
|
|
+
|
|
|
+--- a/CHANGELOG
|
|
|
++++ b/CHANGELOG
|
|
|
+@@ -34,6 +34,11 @@ version 2.81
|
|
|
+ tries not to request capabilities not required by its
|
|
|
+ configuration.
|
|
|
+
|
|
|
++ Add --shared-network config. This enables allocation of addresses
|
|
|
++ the DHCP server in subnets where the server (or relay) doesn't
|
|
|
++ have an interface on the network in that subnet. Many thanks to
|
|
|
++ plank.de for sponsoring this feature.
|
|
|
++
|
|
|
+
|
|
|
+ version 2.80
|
|
|
+ Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method
|
|
|
+--- a/man/dnsmasq.8
|
|
|
++++ b/man/dnsmasq.8
|
|
|
+@@ -1741,6 +1741,27 @@ It is permissible to add more than one a
|
|
|
+ \fB--bridge-interface=int1,alias1,alias2\fP is exactly equivalent to
|
|
|
+ \fB--bridge-interface=int1,alias1 --bridge-interface=int1,alias2\fP
|
|
|
+ .TP
|
|
|
++.B --shared-network=<interface>|<addr>,<addr>
|
|
|
++The DHCP server determines which dhcp ranges are useable for allocating and
|
|
|
++address to a DHCP client based on the network from which the DHCP request arrives,
|
|
|
++and the IP configuration of the server's interface on that network. The shared-network
|
|
|
++option extends the available subnets (and therefore dhcp ranges) beyond the
|
|
|
++subnets configured on the arrival interface. The first argument is either the
|
|
|
++name of an interface or an address which is configured on a local interface, and the
|
|
|
++second argument is an address which defines another subnet on which addresses can be allocated.
|
|
|
++To be useful, there must be suitable dhcp-range which allows address allocation on this subnet
|
|
|
++and this dhcp-range MUST include the netmask. Use shared-network also needs extra
|
|
|
++consideration of routing. Dnsmasq doesn't have the usual information which it uses to
|
|
|
++determine the default route, so the default route option (or other routing) MUST be
|
|
|
++manually configured. The client must have a route to the server: if the two-address form
|
|
|
++of shared-network is used, this will be to the first specified address. If the interface,address
|
|
|
++form is used, there must be a route to all of the addresses configured on the interface.
|
|
|
++
|
|
|
++The two-address form of shared-network is also usable with a DHCP relay: the first address
|
|
|
++is the address of the relay and the second, as before, specifies an extra subnet which
|
|
|
++may be allocated.
|
|
|
++
|
|
|
++.TP
|
|
|
+ .B \-s, --domain=<domain>[,<address range>[,local]]
|
|
|
+ Specifies DNS domains for the DHCP server. Domains may be be given
|
|
|
+ unconditionally (without the IP range) or for limited IP ranges. This has two effects;
|
|
|
+--- a/src/dhcp.c
|
|
|
++++ b/src/dhcp.c
|
|
|
+@@ -507,33 +507,83 @@ static int check_listen_addrs(struct in_
|
|
|
+
|
|
|
+ Note that the current chain may be superseded later for configured hosts or those coming via gateways. */
|
|
|
+
|
|
|
+-static int complete_context(struct in_addr local, int if_index, char *label,
|
|
|
+- struct in_addr netmask, struct in_addr broadcast, void *vparam)
|
|
|
++static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
|
|
|
+ {
|
|
|
+ struct dhcp_context *context;
|
|
|
+- struct dhcp_relay *relay;
|
|
|
+- struct iface_param *param = vparam;
|
|
|
+
|
|
|
+- (void)label;
|
|
|
+-
|
|
|
+ for (context = daemon->dhcp; context; context = context->next)
|
|
|
+- {
|
|
|
+- if (!(context->flags & CONTEXT_NETMASK) &&
|
|
|
+- (is_same_net(local, context->start, netmask) ||
|
|
|
+- is_same_net(local, context->end, netmask)))
|
|
|
++ if (!(context->flags & CONTEXT_NETMASK) &&
|
|
|
++ (is_same_net(addr, context->start, netmask) ||
|
|
|
++ is_same_net(addr, context->end, netmask)))
|
|
|
+ {
|
|
|
+ if (context->netmask.s_addr != netmask.s_addr &&
|
|
|
+- !(is_same_net(local, context->start, netmask) &&
|
|
|
+- is_same_net(local, context->end, netmask)))
|
|
|
++ !(is_same_net(addr, context->start, netmask) &&
|
|
|
++ is_same_net(addr, context->end, netmask)))
|
|
|
+ {
|
|
|
+ strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
|
|
|
+ strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
|
|
|
+ my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
|
|
|
+ daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
|
|
|
+ }
|
|
|
+- context->netmask = netmask;
|
|
|
++ context->netmask = netmask;
|
|
|
+ }
|
|
|
++}
|
|
|
++
|
|
|
++static int complete_context(struct in_addr local, int if_index, char *label,
|
|
|
++ struct in_addr netmask, struct in_addr broadcast, void *vparam)
|
|
|
++{
|
|
|
++ struct dhcp_context *context;
|
|
|
++ struct dhcp_relay *relay;
|
|
|
++ struct iface_param *param = vparam;
|
|
|
++ struct shared_network *share;
|
|
|
++
|
|
|
++ (void)label;
|
|
|
++
|
|
|
++ for (share = daemon->shared_networks; share; share = share->next)
|
|
|
++ {
|
|
|
+
|
|
|
++#ifdef HAVE_DHCP6
|
|
|
++ if (share->shared_addr.s_addr == 0)
|
|
|
++ continue;
|
|
|
++#endif
|
|
|
++
|
|
|
++ if (share->if_index != 0)
|
|
|
++ {
|
|
|
++ if (share->if_index != if_index)
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ else
|
|
|
++ {
|
|
|
++ if (share->match_addr.s_addr != local.s_addr)
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++
|
|
|
++ for (context = daemon->dhcp; context; context = context->next)
|
|
|
++ {
|
|
|
++ if (context->netmask.s_addr != 0 &&
|
|
|
++ is_same_net(share->shared_addr, context->start, context->netmask) &&
|
|
|
++ is_same_net(share->shared_addr, context->end, context->netmask))
|
|
|
++ {
|
|
|
++ /* link it onto the current chain if we've not seen it before */
|
|
|
++ if (context->current == context)
|
|
|
++ {
|
|
|
++ /* For a shared network, we have no way to guess what the default route should be. */
|
|
|
++ context->router.s_addr = 0;
|
|
|
++ context->local = local; /* Use configured address for Server Identifier */
|
|
|
++ context->current = param->current;
|
|
|
++ param->current = context;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!(context->flags & CONTEXT_BRDCAST))
|
|
|
++ context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ guess_range_netmask(local, netmask);
|
|
|
++
|
|
|
++ for (context = daemon->dhcp; context; context = context->next)
|
|
|
++ {
|
|
|
+ if (context->netmask.s_addr != 0 &&
|
|
|
+ is_same_net(local, context->start, context->netmask) &&
|
|
|
+ is_same_net(local, context->end, context->netmask))
|
|
|
+--- a/src/dhcp6.c
|
|
|
++++ b/src/dhcp6.c
|
|
|
+@@ -299,89 +299,114 @@ static int complete_context6(struct in6_
|
|
|
+ unsigned int valid, void *vparam)
|
|
|
+ {
|
|
|
+ struct dhcp_context *context;
|
|
|
++ struct shared_network *share;
|
|
|
+ struct dhcp_relay *relay;
|
|
|
+ struct iface_param *param = vparam;
|
|
|
+ struct iname *tmp;
|
|
|
+
|
|
|
+ (void)scope; /* warning */
|
|
|
+
|
|
|
+- 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))
|
|
|
+- {
|
|
|
+- /* if we have --listen-address config, see if the
|
|
|
+- arrival interface has a matching address. */
|
|
|
+- for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
|
|
|
+- if (tmp->addr.sa.sa_family == AF_INET6 &&
|
|
|
+- IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
|
|
|
+- param->addr_match = 1;
|
|
|
+-
|
|
|
+- /* Determine a globally address on the arrival interface, even
|
|
|
+- if we have no matching dhcp-context, because we're only
|
|
|
+- allocating on remote subnets via relays. This
|
|
|
+- is used as a default for the DNS server option. */
|
|
|
+- param->fallback = *local;
|
|
|
+-
|
|
|
+- for (context = daemon->dhcp6; context; context = context->next)
|
|
|
+- {
|
|
|
+- if ((context->flags & CONTEXT_DHCP) &&
|
|
|
+- !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
|
|
|
+- prefix <= context->prefix &&
|
|
|
+- is_same_net6(local, &context->start6, context->prefix) &&
|
|
|
+- is_same_net6(local, &context->end6, context->prefix))
|
|
|
+- {
|
|
|
+-
|
|
|
+-
|
|
|
+- /* link it onto the current chain if we've not seen it before */
|
|
|
+- if (context->current == context)
|
|
|
+- {
|
|
|
+- struct dhcp_context *tmp, **up;
|
|
|
+-
|
|
|
+- /* use interface values only for constructed contexts */
|
|
|
+- if (!(context->flags & CONTEXT_CONSTRUCTED))
|
|
|
+- preferred = valid = 0xffffffff;
|
|
|
+- else if (flags & IFACE_DEPRECATED)
|
|
|
+- preferred = 0;
|
|
|
+-
|
|
|
+- if (context->flags & CONTEXT_DEPRECATE)
|
|
|
+- preferred = 0;
|
|
|
+-
|
|
|
+- /* order chain, longest preferred time first */
|
|
|
+- for (up = ¶m->current, tmp = param->current; tmp; tmp = tmp->current)
|
|
|
+- if (tmp->preferred <= preferred)
|
|
|
+- break;
|
|
|
+- else
|
|
|
+- up = &tmp->current;
|
|
|
+-
|
|
|
+- context->current = *up;
|
|
|
+- *up = context;
|
|
|
+- context->local6 = *local;
|
|
|
+- context->preferred = preferred;
|
|
|
+- context->valid = valid;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- for (relay = daemon->relay6; relay; relay = relay->next)
|
|
|
+- if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay &&
|
|
|
+- (IN6_IS_ADDR_UNSPECIFIED(¶m->relay_local) || IN6_ARE_ADDR_EQUAL(local, ¶m->relay_local)))
|
|
|
++ if (if_index != param->ind)
|
|
|
++ return 1;
|
|
|
++
|
|
|
++ 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))
|
|
|
++ return 1;
|
|
|
++
|
|
|
++ /* if we have --listen-address config, see if the
|
|
|
++ arrival interface has a matching address. */
|
|
|
++ for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
|
|
|
++ if (tmp->addr.sa.sa_family == AF_INET6 &&
|
|
|
++ IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
|
|
|
++ param->addr_match = 1;
|
|
|
++
|
|
|
++ /* Determine a globally address on the arrival interface, even
|
|
|
++ if we have no matching dhcp-context, because we're only
|
|
|
++ allocating on remote subnets via relays. This
|
|
|
++ is used as a default for the DNS server option. */
|
|
|
++ param->fallback = *local;
|
|
|
++
|
|
|
++ for (context = daemon->dhcp6; context; context = context->next)
|
|
|
++ if ((context->flags & CONTEXT_DHCP) &&
|
|
|
++ !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
|
|
|
++ prefix <= context->prefix &&
|
|
|
++ context->current == context)
|
|
|
++ {
|
|
|
++ if (is_same_net6(local, &context->start6, context->prefix) &&
|
|
|
++ is_same_net6(local, &context->end6, context->prefix))
|
|
|
+ {
|
|
|
+- relay->current = param->relay;
|
|
|
+- param->relay = relay;
|
|
|
+- param->relay_local = *local;
|
|
|
++ struct dhcp_context *tmp, **up;
|
|
|
++
|
|
|
++ /* use interface values only for constructed contexts */
|
|
|
++ if (!(context->flags & CONTEXT_CONSTRUCTED))
|
|
|
++ preferred = valid = 0xffffffff;
|
|
|
++ else if (flags & IFACE_DEPRECATED)
|
|
|
++ preferred = 0;
|
|
|
++
|
|
|
++ if (context->flags & CONTEXT_DEPRECATE)
|
|
|
++ preferred = 0;
|
|
|
++
|
|
|
++ /* order chain, longest preferred time first */
|
|
|
++ for (up = ¶m->current, tmp = param->current; tmp; tmp = tmp->current)
|
|
|
++ if (tmp->preferred <= preferred)
|
|
|
++ break;
|
|
|
++ else
|
|
|
++ up = &tmp->current;
|
|
|
++
|
|
|
++ context->current = *up;
|
|
|
++ *up = context;
|
|
|
++ context->local6 = *local;
|
|
|
++ context->preferred = preferred;
|
|
|
++ context->valid = valid;
|
|
|
+ }
|
|
|
+-
|
|
|
+- }
|
|
|
+-
|
|
|
+- return 1;
|
|
|
++ else
|
|
|
++ {
|
|
|
++ for (share = daemon->shared_networks; share; share = share->next)
|
|
|
++ {
|
|
|
++ /* IPv4 shared_address - ignore */
|
|
|
++ if (share->shared_addr.s_addr != 0)
|
|
|
++ continue;
|
|
|
++
|
|
|
++ if (share->if_index != 0)
|
|
|
++ {
|
|
|
++ if (share->if_index != if_index)
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++ else
|
|
|
++ {
|
|
|
++ if (!IN6_ARE_ADDR_EQUAL(&share->match_addr6, local))
|
|
|
++ continue;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (is_same_net6(&share->shared_addr6, &context->start6, context->prefix) &&
|
|
|
++ is_same_net6(&share->shared_addr6, &context->end6, context->prefix))
|
|
|
++ {
|
|
|
++ context->current = param->current;
|
|
|
++ param->current = context;
|
|
|
++ context->local6 = *local;
|
|
|
++ context->preferred = context->flags & CONTEXT_DEPRECATE ? 0 :0xffffffff;
|
|
|
++ context->valid = 0xffffffff;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ for (relay = daemon->relay6; relay; relay = relay->next)
|
|
|
++ if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay &&
|
|
|
++ (IN6_IS_ADDR_UNSPECIFIED(¶m->relay_local) || IN6_ARE_ADDR_EQUAL(local, ¶m->relay_local)))
|
|
|
++ {
|
|
|
++ relay->current = param->relay;
|
|
|
++ param->relay = relay;
|
|
|
++ param->relay_local = *local;
|
|
|
++ }
|
|
|
++
|
|
|
++ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
|
|
|
+--- a/src/dnsmasq.h
|
|
|
++++ b/src/dnsmasq.h
|
|
|
+@@ -910,6 +910,16 @@ struct dhcp_context {
|
|
|
+ struct dhcp_context *next, *current;
|
|
|
+ };
|
|
|
+
|
|
|
++struct shared_network {
|
|
|
++ int if_index;
|
|
|
++ struct in_addr match_addr, shared_addr;
|
|
|
++#ifdef HAVE_DHCP6
|
|
|
++ /* shared_addr == 0 for IP6 entries. */
|
|
|
++ struct in6_addr match_addr6, shared_addr6;
|
|
|
++#endif
|
|
|
++ struct shared_network *next;
|
|
|
++};
|
|
|
++
|
|
|
+ #define CONTEXT_STATIC (1u<<0)
|
|
|
+ #define CONTEXT_NETMASK (1u<<1)
|
|
|
+ #define CONTEXT_BRDCAST (1u<<2)
|
|
|
+@@ -1107,6 +1117,7 @@ extern struct daemon {
|
|
|
+ struct ping_result *ping_results;
|
|
|
+ FILE *lease_stream;
|
|
|
+ struct dhcp_bridge *bridges;
|
|
|
++ struct shared_network *shared_networks;
|
|
|
+ #ifdef HAVE_DHCP6
|
|
|
+ int duid_len;
|
|
|
+ unsigned char *duid;
|
|
|
+--- a/src/option.c
|
|
|
++++ b/src/option.c
|
|
|
+@@ -166,6 +166,7 @@ struct myoption {
|
|
|
+ #define LOPT_UBUS 354
|
|
|
+ #define LOPT_NAME_MATCH 355
|
|
|
+ #define LOPT_CAA 356
|
|
|
++#define LOPT_SHARED_NET 357
|
|
|
+
|
|
|
+ #ifdef HAVE_GETOPT_LONG
|
|
|
+ static const struct option opts[] =
|
|
|
+@@ -259,6 +260,7 @@ static const struct myoption opts[] =
|
|
|
+ { "ptr-record", 1, 0, LOPT_PTR },
|
|
|
+ { "naptr-record", 1, 0, LOPT_NAPTR },
|
|
|
+ { "bridge-interface", 1, 0 , LOPT_BRIDGE },
|
|
|
++ { "shared-network", 1, 0, LOPT_SHARED_NET },
|
|
|
+ { "dhcp-option-force", 1, 0, LOPT_FORCE },
|
|
|
+ { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
|
|
|
+ { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
|
|
|
+@@ -431,6 +433,7 @@ static struct {
|
|
|
+ { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
|
|
|
+ { '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
|
|
|
+ { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
|
|
|
++ { LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
|
|
|
+ { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
|
|
|
+ { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
|
|
|
+ { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
|
|
|
+@@ -2873,6 +2876,44 @@ static int one_opt(int option, char *arg
|
|
|
+ }
|
|
|
+
|
|
|
+ #ifdef HAVE_DHCP
|
|
|
++ case LOPT_SHARED_NET: /* --shared-network */
|
|
|
++ {
|
|
|
++ struct shared_network *new = opt_malloc(sizeof(struct shared_network));
|
|
|
++
|
|
|
++#ifdef HAVE_DHCP6
|
|
|
++ new->shared_addr.s_addr = 0;
|
|
|
++#endif
|
|
|
++ new->if_index = 0;
|
|
|
++
|
|
|
++ if (!(comma = split(arg)))
|
|
|
++ {
|
|
|
++ snerr:
|
|
|
++ free(new);
|
|
|
++ ret_err(_("bad shared-network"));
|
|
|
++ }
|
|
|
++
|
|
|
++ if (inet_pton(AF_INET, comma, &new->shared_addr))
|
|
|
++ {
|
|
|
++ if (!inet_pton(AF_INET, arg, &new->match_addr) &&
|
|
|
++ !(new->if_index = if_nametoindex(arg)))
|
|
|
++ goto snerr;
|
|
|
++ }
|
|
|
++#ifdef HAVE_DHCP6
|
|
|
++ else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
|
|
|
++ {
|
|
|
++ if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
|
|
|
++ !(new->if_index = if_nametoindex(arg)))
|
|
|
++ goto snerr;
|
|
|
++ }
|
|
|
++#endif
|
|
|
++ else
|
|
|
++ goto snerr;
|
|
|
++
|
|
|
++ new->next = daemon->shared_networks;
|
|
|
++ daemon->shared_networks = new;
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
+ case 'F': /* --dhcp-range */
|
|
|
+ {
|
|
|
+ int k, leasepos = 2;
|
|
|
+--- a/src/rfc2131.c
|
|
|
++++ b/src/rfc2131.c
|
|
|
+@@ -274,8 +274,9 @@ size_t dhcp_reply(struct dhcp_context *c
|
|
|
+ if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
|
|
|
+ {
|
|
|
+ struct dhcp_context *context_tmp, *context_new = NULL;
|
|
|
++ struct shared_network *share = NULL;
|
|
|
+ struct in_addr addr;
|
|
|
+- int force = 0;
|
|
|
++ int force = 0, via_relay = 0;
|
|
|
+
|
|
|
+ if (subnet_addr.s_addr)
|
|
|
+ {
|
|
|
+@@ -286,6 +287,7 @@ size_t dhcp_reply(struct dhcp_context *c
|
|
|
+ {
|
|
|
+ addr = mess->giaddr;
|
|
|
+ force = 1;
|
|
|
++ via_relay = 1;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+@@ -302,42 +304,65 @@ size_t dhcp_reply(struct dhcp_context *c
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!context_new)
|
|
|
+- for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
|
|
|
+- {
|
|
|
+- struct in_addr netmask = context_tmp->netmask;
|
|
|
++ {
|
|
|
++ for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
|
|
|
++ {
|
|
|
++ struct in_addr netmask = context_tmp->netmask;
|
|
|
++
|
|
|
++ /* guess the netmask for relayed networks */
|
|
|
++ if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
|
|
|
++ {
|
|
|
++ if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
|
|
|
++ netmask.s_addr = htonl(0xff000000);
|
|
|
++ else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
|
|
|
++ netmask.s_addr = htonl(0xffff0000);
|
|
|
++ else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
|
|
|
++ netmask.s_addr = htonl(0xffffff00);
|
|
|
++ }
|
|
|
+
|
|
|
+- /* guess the netmask for relayed networks */
|
|
|
+- if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
|
|
|
+- {
|
|
|
+- if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
|
|
|
+- netmask.s_addr = htonl(0xff000000);
|
|
|
+- else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
|
|
|
+- netmask.s_addr = htonl(0xffff0000);
|
|
|
+- else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
|
|
|
+- netmask.s_addr = htonl(0xffffff00);
|
|
|
+- }
|
|
|
+-
|
|
|
+- /* This section fills in context mainly when a client which is on a remote (relayed)
|
|
|
+- network renews a lease without using the relay, after dnsmasq has restarted. */
|
|
|
+- if (netmask.s_addr != 0 &&
|
|
|
+- is_same_net(addr, context_tmp->start, netmask) &&
|
|
|
+- is_same_net(addr, context_tmp->end, netmask))
|
|
|
+- {
|
|
|
+- context_tmp->netmask = netmask;
|
|
|
+- if (context_tmp->local.s_addr == 0)
|
|
|
+- context_tmp->local = fallback;
|
|
|
+- if (context_tmp->router.s_addr == 0)
|
|
|
+- context_tmp->router = mess->giaddr;
|
|
|
+-
|
|
|
+- /* fill in missing broadcast addresses for relayed ranges */
|
|
|
+- if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
|
|
|
+- context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
|
|
|
+-
|
|
|
+- context_tmp->current = context_new;
|
|
|
+- context_new = context_tmp;
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
++ /* check to see is a context is OK because of a shared address on
|
|
|
++ the relayed subnet. */
|
|
|
++ if (via_relay)
|
|
|
++ for (share = daemon->shared_networks; share; share = share->next)
|
|
|
++ {
|
|
|
++#ifdef HAVE_DHCP6
|
|
|
++ if (share->shared_addr.s_addr == 0)
|
|
|
++ continue;
|
|
|
++#endif
|
|
|
++ if (share->if_index != 0 ||
|
|
|
++ share->match_addr.s_addr != mess->giaddr.s_addr)
|
|
|
++ continue;
|
|
|
++
|
|
|
++ if (netmask.s_addr != 0 &&
|
|
|
++ is_same_net(share->shared_addr, context_tmp->start, netmask) &&
|
|
|
++ is_same_net(share->shared_addr, context_tmp->end, netmask))
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ /* This section fills in context mainly when a client which is on a remote (relayed)
|
|
|
++ network renews a lease without using the relay, after dnsmasq has restarted. */
|
|
|
++ if (share ||
|
|
|
++ (netmask.s_addr != 0 &&
|
|
|
++ is_same_net(addr, context_tmp->start, netmask) &&
|
|
|
++ is_same_net(addr, context_tmp->end, netmask)))
|
|
|
++ {
|
|
|
++ context_tmp->netmask = netmask;
|
|
|
++ if (context_tmp->local.s_addr == 0)
|
|
|
++ context_tmp->local = fallback;
|
|
|
++ if (context_tmp->router.s_addr == 0 && !share)
|
|
|
++ context_tmp->router = mess->giaddr;
|
|
|
++
|
|
|
++ /* fill in missing broadcast addresses for relayed ranges */
|
|
|
++ if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
|
|
|
++ context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
|
|
|
++
|
|
|
++ context_tmp->current = context_new;
|
|
|
++ context_new = context_tmp;
|
|
|
++ }
|
|
|
++
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
+ if (context_new || force)
|
|
|
+ context = context_new;
|
|
|
+ }
|
|
|
+--- a/src/rfc3315.c
|
|
|
++++ b/src/rfc3315.c
|
|
|
+@@ -134,21 +134,41 @@ static int dhcp6_maybe_relay(struct stat
|
|
|
+ else
|
|
|
+ {
|
|
|
+ struct dhcp_context *c;
|
|
|
++ struct shared_network *share = NULL;
|
|
|
+ state->context = NULL;
|
|
|
+-
|
|
|
++
|
|
|
+ if (!IN6_IS_ADDR_LOOPBACK(state->link_address) &&
|
|
|
+ !IN6_IS_ADDR_LINKLOCAL(state->link_address) &&
|
|
|
+ !IN6_IS_ADDR_MULTICAST(state->link_address))
|
|
|
+ for (c = daemon->dhcp6; c; c = c->next)
|
|
|
+- if ((c->flags & CONTEXT_DHCP) &&
|
|
|
+- !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
|
|
|
+- is_same_net6(state->link_address, &c->start6, c->prefix) &&
|
|
|
+- is_same_net6(state->link_address, &c->end6, c->prefix))
|
|
|
+- {
|
|
|
+- c->preferred = c->valid = 0xffffffff;
|
|
|
+- c->current = state->context;
|
|
|
+- state->context = c;
|
|
|
+- }
|
|
|
++ {
|
|
|
++ for (share = daemon->shared_networks; share; share = share->next)
|
|
|
++ {
|
|
|
++ if (share->shared_addr.s_addr != 0)
|
|
|
++ continue;
|
|
|
++
|
|
|
++ if (share->if_index != 0 ||
|
|
|
++ !IN6_ARE_ADDR_EQUAL(state->link_address, &share->match_addr6))
|
|
|
++ continue;
|
|
|
++
|
|
|
++ if ((c->flags & CONTEXT_DHCP) &&
|
|
|
++ !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
|
|
|
++ is_same_net6(&share->shared_addr6, &c->start6, c->prefix) &&
|
|
|
++ is_same_net6(&share->shared_addr6, &c->end6, c->prefix))
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (share ||
|
|
|
++ ((c->flags & CONTEXT_DHCP) &&
|
|
|
++ !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
|
|
|
++ is_same_net6(state->link_address, &c->start6, c->prefix) &&
|
|
|
++ is_same_net6(state->link_address, &c->end6, c->prefix)))
|
|
|
++ {
|
|
|
++ c->preferred = c->valid = 0xffffffff;
|
|
|
++ c->current = state->context;
|
|
|
++ state->context = c;
|
|
|
++ }
|
|
|
++ }
|
|
|
+
|
|
|
+ if (!state->context)
|
|
|
+ {
|