Revert "dnsmasq: improve insecure DS warning"
[openwrt/staging/lynxis.git] / package / network / services / dnsmasq / patches / 0046-Add-shared-network-DHCP-configuration.patch
1 From ae5b7e04a1025167f1b80840e61432a3cea9625c Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Wed, 27 Mar 2019 22:33:28 +0000
4 Subject: [PATCH 46/57] Add --shared-network DHCP configuration.
5
6 Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
7 ---
8 CHANGELOG | 5 ++
9 man/dnsmasq.8 | 21 ++++++
10 src/dhcp.c | 76 ++++++++++++++++++----
11 src/dhcp6.c | 175 ++++++++++++++++++++++++++++----------------------
12 src/dnsmasq.h | 11 ++++
13 src/option.c | 41 ++++++++++++
14 src/rfc2131.c | 97 +++++++++++++++++-----------
15 src/rfc3315.c | 40 +++++++++---
16 8 files changed, 332 insertions(+), 134 deletions(-)
17
18 --- a/CHANGELOG
19 +++ b/CHANGELOG
20 @@ -34,6 +34,11 @@ version 2.81
21 tries not to request capabilities not required by its
22 configuration.
23
24 + Add --shared-network config. This enables allocation of addresses
25 + the DHCP server in subnets where the server (or relay) doesn't
26 + have an interface on the network in that subnet. Many thanks to
27 + plank.de for sponsoring this feature.
28 +
29
30 version 2.80
31 Add support for RFC 4039 DHCP rapid commit. Thanks to Ashram Method
32 --- a/man/dnsmasq.8
33 +++ b/man/dnsmasq.8
34 @@ -1741,6 +1741,27 @@ It is permissible to add more than one a
35 \fB--bridge-interface=int1,alias1,alias2\fP is exactly equivalent to
36 \fB--bridge-interface=int1,alias1 --bridge-interface=int1,alias2\fP
37 .TP
38 +.B --shared-network=<interface>|<addr>,<addr>
39 +The DHCP server determines which dhcp ranges are useable for allocating and
40 +address to a DHCP client based on the network from which the DHCP request arrives,
41 +and the IP configuration of the server's interface on that network. The shared-network
42 +option extends the available subnets (and therefore dhcp ranges) beyond the
43 +subnets configured on the arrival interface. The first argument is either the
44 +name of an interface or an address which is configured on a local interface, and the
45 +second argument is an address which defines another subnet on which addresses can be allocated.
46 +To be useful, there must be suitable dhcp-range which allows address allocation on this subnet
47 +and this dhcp-range MUST include the netmask. Use shared-network also needs extra
48 +consideration of routing. Dnsmasq doesn't have the usual information which it uses to
49 +determine the default route, so the default route option (or other routing) MUST be
50 +manually configured. The client must have a route to the server: if the two-address form
51 +of shared-network is used, this will be to the first specified address. If the interface,address
52 +form is used, there must be a route to all of the addresses configured on the interface.
53 +
54 +The two-address form of shared-network is also usable with a DHCP relay: the first address
55 +is the address of the relay and the second, as before, specifies an extra subnet which
56 +may be allocated.
57 +
58 +.TP
59 .B \-s, --domain=<domain>[,<address range>[,local]]
60 Specifies DNS domains for the DHCP server. Domains may be be given
61 unconditionally (without the IP range) or for limited IP ranges. This has two effects;
62 --- a/src/dhcp.c
63 +++ b/src/dhcp.c
64 @@ -507,33 +507,83 @@ static int check_listen_addrs(struct in_
65
66 Note that the current chain may be superseded later for configured hosts or those coming via gateways. */
67
68 -static int complete_context(struct in_addr local, int if_index, char *label,
69 - struct in_addr netmask, struct in_addr broadcast, void *vparam)
70 +static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
71 {
72 struct dhcp_context *context;
73 - struct dhcp_relay *relay;
74 - struct iface_param *param = vparam;
75
76 - (void)label;
77 -
78 for (context = daemon->dhcp; context; context = context->next)
79 - {
80 - if (!(context->flags & CONTEXT_NETMASK) &&
81 - (is_same_net(local, context->start, netmask) ||
82 - is_same_net(local, context->end, netmask)))
83 + if (!(context->flags & CONTEXT_NETMASK) &&
84 + (is_same_net(addr, context->start, netmask) ||
85 + is_same_net(addr, context->end, netmask)))
86 {
87 if (context->netmask.s_addr != netmask.s_addr &&
88 - !(is_same_net(local, context->start, netmask) &&
89 - is_same_net(local, context->end, netmask)))
90 + !(is_same_net(addr, context->start, netmask) &&
91 + is_same_net(addr, context->end, netmask)))
92 {
93 strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
94 strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
95 my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
96 daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
97 }
98 - context->netmask = netmask;
99 + context->netmask = netmask;
100 }
101 +}
102 +
103 +static int complete_context(struct in_addr local, int if_index, char *label,
104 + struct in_addr netmask, struct in_addr broadcast, void *vparam)
105 +{
106 + struct dhcp_context *context;
107 + struct dhcp_relay *relay;
108 + struct iface_param *param = vparam;
109 + struct shared_network *share;
110 +
111 + (void)label;
112 +
113 + for (share = daemon->shared_networks; share; share = share->next)
114 + {
115
116 +#ifdef HAVE_DHCP6
117 + if (share->shared_addr.s_addr == 0)
118 + continue;
119 +#endif
120 +
121 + if (share->if_index != 0)
122 + {
123 + if (share->if_index != if_index)
124 + continue;
125 + }
126 + else
127 + {
128 + if (share->match_addr.s_addr != local.s_addr)
129 + continue;
130 + }
131 +
132 + for (context = daemon->dhcp; context; context = context->next)
133 + {
134 + if (context->netmask.s_addr != 0 &&
135 + is_same_net(share->shared_addr, context->start, context->netmask) &&
136 + is_same_net(share->shared_addr, context->end, context->netmask))
137 + {
138 + /* link it onto the current chain if we've not seen it before */
139 + if (context->current == context)
140 + {
141 + /* For a shared network, we have no way to guess what the default route should be. */
142 + context->router.s_addr = 0;
143 + context->local = local; /* Use configured address for Server Identifier */
144 + context->current = param->current;
145 + param->current = context;
146 + }
147 +
148 + if (!(context->flags & CONTEXT_BRDCAST))
149 + context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
150 + }
151 + }
152 + }
153 +
154 + guess_range_netmask(local, netmask);
155 +
156 + for (context = daemon->dhcp; context; context = context->next)
157 + {
158 if (context->netmask.s_addr != 0 &&
159 is_same_net(local, context->start, context->netmask) &&
160 is_same_net(local, context->end, context->netmask))
161 --- a/src/dhcp6.c
162 +++ b/src/dhcp6.c
163 @@ -299,89 +299,114 @@ static int complete_context6(struct in6_
164 unsigned int valid, void *vparam)
165 {
166 struct dhcp_context *context;
167 + struct shared_network *share;
168 struct dhcp_relay *relay;
169 struct iface_param *param = vparam;
170 struct iname *tmp;
171
172 (void)scope; /* warning */
173
174 - if (if_index == param->ind)
175 - {
176 - if (IN6_IS_ADDR_LINKLOCAL(local))
177 - param->ll_addr = *local;
178 - else if (IN6_IS_ADDR_ULA(local))
179 - param->ula_addr = *local;
180 -
181 - if (!IN6_IS_ADDR_LOOPBACK(local) &&
182 - !IN6_IS_ADDR_LINKLOCAL(local) &&
183 - !IN6_IS_ADDR_MULTICAST(local))
184 - {
185 - /* if we have --listen-address config, see if the
186 - arrival interface has a matching address. */
187 - for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
188 - if (tmp->addr.sa.sa_family == AF_INET6 &&
189 - IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
190 - param->addr_match = 1;
191 -
192 - /* Determine a globally address on the arrival interface, even
193 - if we have no matching dhcp-context, because we're only
194 - allocating on remote subnets via relays. This
195 - is used as a default for the DNS server option. */
196 - param->fallback = *local;
197 -
198 - for (context = daemon->dhcp6; context; context = context->next)
199 - {
200 - if ((context->flags & CONTEXT_DHCP) &&
201 - !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
202 - prefix <= context->prefix &&
203 - is_same_net6(local, &context->start6, context->prefix) &&
204 - is_same_net6(local, &context->end6, context->prefix))
205 - {
206 -
207 -
208 - /* link it onto the current chain if we've not seen it before */
209 - if (context->current == context)
210 - {
211 - struct dhcp_context *tmp, **up;
212 -
213 - /* use interface values only for constructed contexts */
214 - if (!(context->flags & CONTEXT_CONSTRUCTED))
215 - preferred = valid = 0xffffffff;
216 - else if (flags & IFACE_DEPRECATED)
217 - preferred = 0;
218 -
219 - if (context->flags & CONTEXT_DEPRECATE)
220 - preferred = 0;
221 -
222 - /* order chain, longest preferred time first */
223 - for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
224 - if (tmp->preferred <= preferred)
225 - break;
226 - else
227 - up = &tmp->current;
228 -
229 - context->current = *up;
230 - *up = context;
231 - context->local6 = *local;
232 - context->preferred = preferred;
233 - context->valid = valid;
234 - }
235 - }
236 - }
237 - }
238 -
239 - for (relay = daemon->relay6; relay; relay = relay->next)
240 - if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay &&
241 - (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
242 + if (if_index != param->ind)
243 + return 1;
244 +
245 + if (IN6_IS_ADDR_LINKLOCAL(local))
246 + param->ll_addr = *local;
247 + else if (IN6_IS_ADDR_ULA(local))
248 + param->ula_addr = *local;
249 +
250 + if (IN6_IS_ADDR_LOOPBACK(local) ||
251 + IN6_IS_ADDR_LINKLOCAL(local) ||
252 + IN6_IS_ADDR_MULTICAST(local))
253 + return 1;
254 +
255 + /* if we have --listen-address config, see if the
256 + arrival interface has a matching address. */
257 + for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
258 + if (tmp->addr.sa.sa_family == AF_INET6 &&
259 + IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
260 + param->addr_match = 1;
261 +
262 + /* Determine a globally address on the arrival interface, even
263 + if we have no matching dhcp-context, because we're only
264 + allocating on remote subnets via relays. This
265 + is used as a default for the DNS server option. */
266 + param->fallback = *local;
267 +
268 + for (context = daemon->dhcp6; context; context = context->next)
269 + if ((context->flags & CONTEXT_DHCP) &&
270 + !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
271 + prefix <= context->prefix &&
272 + context->current == context)
273 + {
274 + if (is_same_net6(local, &context->start6, context->prefix) &&
275 + is_same_net6(local, &context->end6, context->prefix))
276 {
277 - relay->current = param->relay;
278 - param->relay = relay;
279 - param->relay_local = *local;
280 + struct dhcp_context *tmp, **up;
281 +
282 + /* use interface values only for constructed contexts */
283 + if (!(context->flags & CONTEXT_CONSTRUCTED))
284 + preferred = valid = 0xffffffff;
285 + else if (flags & IFACE_DEPRECATED)
286 + preferred = 0;
287 +
288 + if (context->flags & CONTEXT_DEPRECATE)
289 + preferred = 0;
290 +
291 + /* order chain, longest preferred time first */
292 + for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
293 + if (tmp->preferred <= preferred)
294 + break;
295 + else
296 + up = &tmp->current;
297 +
298 + context->current = *up;
299 + *up = context;
300 + context->local6 = *local;
301 + context->preferred = preferred;
302 + context->valid = valid;
303 }
304 -
305 - }
306 -
307 - return 1;
308 + else
309 + {
310 + for (share = daemon->shared_networks; share; share = share->next)
311 + {
312 + /* IPv4 shared_address - ignore */
313 + if (share->shared_addr.s_addr != 0)
314 + continue;
315 +
316 + if (share->if_index != 0)
317 + {
318 + if (share->if_index != if_index)
319 + continue;
320 + }
321 + else
322 + {
323 + if (!IN6_ARE_ADDR_EQUAL(&share->match_addr6, local))
324 + continue;
325 + }
326 +
327 + if (is_same_net6(&share->shared_addr6, &context->start6, context->prefix) &&
328 + is_same_net6(&share->shared_addr6, &context->end6, context->prefix))
329 + {
330 + context->current = param->current;
331 + param->current = context;
332 + context->local6 = *local;
333 + context->preferred = context->flags & CONTEXT_DEPRECATE ? 0 :0xffffffff;
334 + context->valid = 0xffffffff;
335 + }
336 + }
337 + }
338 + }
339 +
340 + for (relay = daemon->relay6; relay; relay = relay->next)
341 + if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay &&
342 + (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
343 + {
344 + relay->current = param->relay;
345 + param->relay = relay;
346 + param->relay_local = *local;
347 + }
348 +
349 + return 1;
350 }
351
352 struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
353 --- a/src/dnsmasq.h
354 +++ b/src/dnsmasq.h
355 @@ -910,6 +910,16 @@ struct dhcp_context {
356 struct dhcp_context *next, *current;
357 };
358
359 +struct shared_network {
360 + int if_index;
361 + struct in_addr match_addr, shared_addr;
362 +#ifdef HAVE_DHCP6
363 + /* shared_addr == 0 for IP6 entries. */
364 + struct in6_addr match_addr6, shared_addr6;
365 +#endif
366 + struct shared_network *next;
367 +};
368 +
369 #define CONTEXT_STATIC (1u<<0)
370 #define CONTEXT_NETMASK (1u<<1)
371 #define CONTEXT_BRDCAST (1u<<2)
372 @@ -1107,6 +1117,7 @@ extern struct daemon {
373 struct ping_result *ping_results;
374 FILE *lease_stream;
375 struct dhcp_bridge *bridges;
376 + struct shared_network *shared_networks;
377 #ifdef HAVE_DHCP6
378 int duid_len;
379 unsigned char *duid;
380 --- a/src/option.c
381 +++ b/src/option.c
382 @@ -166,6 +166,7 @@ struct myoption {
383 #define LOPT_UBUS 354
384 #define LOPT_NAME_MATCH 355
385 #define LOPT_CAA 356
386 +#define LOPT_SHARED_NET 357
387
388 #ifdef HAVE_GETOPT_LONG
389 static const struct option opts[] =
390 @@ -259,6 +260,7 @@ static const struct myoption opts[] =
391 { "ptr-record", 1, 0, LOPT_PTR },
392 { "naptr-record", 1, 0, LOPT_NAPTR },
393 { "bridge-interface", 1, 0 , LOPT_BRIDGE },
394 + { "shared-network", 1, 0, LOPT_SHARED_NET },
395 { "dhcp-option-force", 1, 0, LOPT_FORCE },
396 { "tftp-no-blocksize", 0, 0, LOPT_NOBLOCK },
397 { "log-dhcp", 0, 0, LOPT_LOG_OPTS },
398 @@ -431,6 +433,7 @@ static struct {
399 { '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
400 { '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
401 { LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
402 + { LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
403 { '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
404 { '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
405 { LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
406 @@ -2873,6 +2876,44 @@ static int one_opt(int option, char *arg
407 }
408
409 #ifdef HAVE_DHCP
410 + case LOPT_SHARED_NET: /* --shared-network */
411 + {
412 + struct shared_network *new = opt_malloc(sizeof(struct shared_network));
413 +
414 +#ifdef HAVE_DHCP6
415 + new->shared_addr.s_addr = 0;
416 +#endif
417 + new->if_index = 0;
418 +
419 + if (!(comma = split(arg)))
420 + {
421 + snerr:
422 + free(new);
423 + ret_err(_("bad shared-network"));
424 + }
425 +
426 + if (inet_pton(AF_INET, comma, &new->shared_addr))
427 + {
428 + if (!inet_pton(AF_INET, arg, &new->match_addr) &&
429 + !(new->if_index = if_nametoindex(arg)))
430 + goto snerr;
431 + }
432 +#ifdef HAVE_DHCP6
433 + else if (inet_pton(AF_INET6, comma, &new->shared_addr6))
434 + {
435 + if (!inet_pton(AF_INET6, arg, &new->match_addr6) &&
436 + !(new->if_index = if_nametoindex(arg)))
437 + goto snerr;
438 + }
439 +#endif
440 + else
441 + goto snerr;
442 +
443 + new->next = daemon->shared_networks;
444 + daemon->shared_networks = new;
445 + break;
446 + }
447 +
448 case 'F': /* --dhcp-range */
449 {
450 int k, leasepos = 2;
451 --- a/src/rfc2131.c
452 +++ b/src/rfc2131.c
453 @@ -274,8 +274,9 @@ size_t dhcp_reply(struct dhcp_context *c
454 if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
455 {
456 struct dhcp_context *context_tmp, *context_new = NULL;
457 + struct shared_network *share = NULL;
458 struct in_addr addr;
459 - int force = 0;
460 + int force = 0, via_relay = 0;
461
462 if (subnet_addr.s_addr)
463 {
464 @@ -286,6 +287,7 @@ size_t dhcp_reply(struct dhcp_context *c
465 {
466 addr = mess->giaddr;
467 force = 1;
468 + via_relay = 1;
469 }
470 else
471 {
472 @@ -302,42 +304,65 @@ size_t dhcp_reply(struct dhcp_context *c
473 }
474
475 if (!context_new)
476 - for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
477 - {
478 - struct in_addr netmask = context_tmp->netmask;
479 + {
480 + for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
481 + {
482 + struct in_addr netmask = context_tmp->netmask;
483 +
484 + /* guess the netmask for relayed networks */
485 + if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
486 + {
487 + if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
488 + netmask.s_addr = htonl(0xff000000);
489 + else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
490 + netmask.s_addr = htonl(0xffff0000);
491 + else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
492 + netmask.s_addr = htonl(0xffffff00);
493 + }
494
495 - /* guess the netmask for relayed networks */
496 - if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
497 - {
498 - if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
499 - netmask.s_addr = htonl(0xff000000);
500 - else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
501 - netmask.s_addr = htonl(0xffff0000);
502 - else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
503 - netmask.s_addr = htonl(0xffffff00);
504 - }
505 -
506 - /* This section fills in context mainly when a client which is on a remote (relayed)
507 - network renews a lease without using the relay, after dnsmasq has restarted. */
508 - if (netmask.s_addr != 0 &&
509 - is_same_net(addr, context_tmp->start, netmask) &&
510 - is_same_net(addr, context_tmp->end, netmask))
511 - {
512 - context_tmp->netmask = netmask;
513 - if (context_tmp->local.s_addr == 0)
514 - context_tmp->local = fallback;
515 - if (context_tmp->router.s_addr == 0)
516 - context_tmp->router = mess->giaddr;
517 -
518 - /* fill in missing broadcast addresses for relayed ranges */
519 - if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
520 - context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
521 -
522 - context_tmp->current = context_new;
523 - context_new = context_tmp;
524 - }
525 - }
526 -
527 + /* check to see is a context is OK because of a shared address on
528 + the relayed subnet. */
529 + if (via_relay)
530 + for (share = daemon->shared_networks; share; share = share->next)
531 + {
532 +#ifdef HAVE_DHCP6
533 + if (share->shared_addr.s_addr == 0)
534 + continue;
535 +#endif
536 + if (share->if_index != 0 ||
537 + share->match_addr.s_addr != mess->giaddr.s_addr)
538 + continue;
539 +
540 + if (netmask.s_addr != 0 &&
541 + is_same_net(share->shared_addr, context_tmp->start, netmask) &&
542 + is_same_net(share->shared_addr, context_tmp->end, netmask))
543 + break;
544 + }
545 +
546 + /* This section fills in context mainly when a client which is on a remote (relayed)
547 + network renews a lease without using the relay, after dnsmasq has restarted. */
548 + if (share ||
549 + (netmask.s_addr != 0 &&
550 + is_same_net(addr, context_tmp->start, netmask) &&
551 + is_same_net(addr, context_tmp->end, netmask)))
552 + {
553 + context_tmp->netmask = netmask;
554 + if (context_tmp->local.s_addr == 0)
555 + context_tmp->local = fallback;
556 + if (context_tmp->router.s_addr == 0 && !share)
557 + context_tmp->router = mess->giaddr;
558 +
559 + /* fill in missing broadcast addresses for relayed ranges */
560 + if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
561 + context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
562 +
563 + context_tmp->current = context_new;
564 + context_new = context_tmp;
565 + }
566 +
567 + }
568 + }
569 +
570 if (context_new || force)
571 context = context_new;
572 }
573 --- a/src/rfc3315.c
574 +++ b/src/rfc3315.c
575 @@ -134,21 +134,41 @@ static int dhcp6_maybe_relay(struct stat
576 else
577 {
578 struct dhcp_context *c;
579 + struct shared_network *share = NULL;
580 state->context = NULL;
581 -
582 +
583 if (!IN6_IS_ADDR_LOOPBACK(state->link_address) &&
584 !IN6_IS_ADDR_LINKLOCAL(state->link_address) &&
585 !IN6_IS_ADDR_MULTICAST(state->link_address))
586 for (c = daemon->dhcp6; c; c = c->next)
587 - if ((c->flags & CONTEXT_DHCP) &&
588 - !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
589 - is_same_net6(state->link_address, &c->start6, c->prefix) &&
590 - is_same_net6(state->link_address, &c->end6, c->prefix))
591 - {
592 - c->preferred = c->valid = 0xffffffff;
593 - c->current = state->context;
594 - state->context = c;
595 - }
596 + {
597 + for (share = daemon->shared_networks; share; share = share->next)
598 + {
599 + if (share->shared_addr.s_addr != 0)
600 + continue;
601 +
602 + if (share->if_index != 0 ||
603 + !IN6_ARE_ADDR_EQUAL(state->link_address, &share->match_addr6))
604 + continue;
605 +
606 + if ((c->flags & CONTEXT_DHCP) &&
607 + !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
608 + is_same_net6(&share->shared_addr6, &c->start6, c->prefix) &&
609 + is_same_net6(&share->shared_addr6, &c->end6, c->prefix))
610 + break;
611 + }
612 +
613 + if (share ||
614 + ((c->flags & CONTEXT_DHCP) &&
615 + !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
616 + is_same_net6(state->link_address, &c->start6, c->prefix) &&
617 + is_same_net6(state->link_address, &c->end6, c->prefix)))
618 + {
619 + c->preferred = c->valid = 0xffffffff;
620 + c->current = state->context;
621 + state->context = c;
622 + }
623 + }
624
625 if (!state->context)
626 {