• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/odhcpd/src/dhcpv6.c

  1 /**
  2  * Copyright (C) 2012-2013 Steven Barth <steven@midlink.org>
  3  * Copyright (C) 2018 Hans Dedecker <dedeckeh@gmail.com>
  4  *
  5  * This program is free software; you can redistribute it and/or modify
  6  * it under the terms of the GNU General Public License v2 as published by
  7  * the Free Software Foundation.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 12  * GNU General Public License for more details.
 13  *
 14  */
 15 
 16 #include <errno.h>
 17 #include <unistd.h>
 18 #include <stddef.h>
 19 #include <arpa/inet.h>
 20 
 21 #include <libubox/utils.h>
 22 
 23 #include "odhcpd.h"
 24 #include "dhcpv6.h"
 25 #include "dhcpv6-pxe.h"
 26 #ifdef DHCPV4_SUPPORT
 27 #include "dhcpv4.h"
 28 #endif
 29 
 30 static void relay_client_request(struct sockaddr_in6 *source,
 31                 const void *data, size_t len, struct interface *iface,
 32                 struct in6_addr *dest);
 33 static void relay_server_response(uint8_t *data, size_t len);
 34 
 35 static void handle_dhcpv6(void *addr, void *data, size_t len,
 36                 struct interface *iface, void *dest);
 37 static void handle_client_request(void *addr, void *data, size_t len,
 38                 struct interface *iface, void *dest_addr);
 39 
 40 
 41 /* Create socket and register events */
 42 int dhcpv6_init(void)
 43 {
 44         return dhcpv6_ia_init();
 45 }
 46 
 47 int dhcpv6_setup_interface(struct interface *iface, bool enable)
 48 {
 49         int ret = 0;
 50 
 51         enable = enable && (iface->dhcpv6 != MODE_DISABLED);
 52 
 53         if (iface->dhcpv6_event.uloop.fd >= 0) {
 54                 uloop_fd_delete(&iface->dhcpv6_event.uloop);
 55                 close(iface->dhcpv6_event.uloop.fd);
 56                 iface->dhcpv6_event.uloop.fd = -1;
 57         }
 58 
 59         /* Configure multicast settings */
 60         if (enable) {
 61                 struct sockaddr_in6 bind_addr = {AF_INET6, htons(DHCPV6_SERVER_PORT),
 62                                         0, IN6ADDR_ANY_INIT, 0};
 63                 struct ipv6_mreq mreq;
 64                 int val = 1;
 65 
 66                 iface->dhcpv6_event.uloop.fd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
 67                 if (iface->dhcpv6_event.uloop.fd < 0) {
 68                         error("socket(AF_INET6): %m");
 69                         ret = -1;
 70                         goto out;
 71                 }
 72 
 73                 /* Basic IPv6 configuration */
 74                 if (setsockopt(iface->dhcpv6_event.uloop.fd, SOL_SOCKET, SO_BINDTODEVICE,
 75                                         iface->ifname, strlen(iface->ifname)) < 0) {
 76                         error("setsockopt(SO_BINDTODEVICE): %m");
 77                         ret = -1;
 78                         goto out;
 79                 }
 80 
 81                 if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_V6ONLY,
 82                                         &val, sizeof(val)) < 0) {
 83                         error("setsockopt(IPV6_V6ONLY): %m");
 84                         ret = -1;
 85                         goto out;
 86                 }
 87 
 88                 if (setsockopt(iface->dhcpv6_event.uloop.fd, SOL_SOCKET, SO_REUSEADDR,
 89                                         &val, sizeof(val)) < 0) {
 90                         error("setsockopt(SO_REUSEADDR): %m");
 91                         ret = -1;
 92                         goto out;
 93                 }
 94 
 95                 if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
 96                                         &val, sizeof(val)) < 0) {
 97                         error("setsockopt(IPV6_RECVPKTINFO): %m");
 98                         ret = -1;
 99                         goto out;
100                 }
101 
102                 val = DHCPV6_HOP_COUNT_LIMIT;
103                 if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
104                                         &val, sizeof(val)) < 0) {
105                         error("setsockopt(IPV6_MULTICAST_HOPS): %m");
106                         ret = -1;
107                         goto out;
108                 }
109 
110                 val = 0;
111                 if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
112                                         &val, sizeof(val)) < 0) {
113                         error("setsockopt(IPV6_MULTICAST_LOOP): %m");
114                         ret = -1;
115                         goto out;
116                 }
117 
118                 if (bind(iface->dhcpv6_event.uloop.fd, (struct sockaddr*)&bind_addr,
119                                         sizeof(bind_addr)) < 0) {
120                         error("bind(): %m");
121                         ret = -1;
122                         goto out;
123                 }
124 
125                 memset(&mreq, 0, sizeof(mreq));
126                 inet_pton(AF_INET6, ALL_DHCPV6_RELAYS, &mreq.ipv6mr_multiaddr);
127                 mreq.ipv6mr_interface = iface->ifindex;
128 
129                 if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
130                                         &mreq, sizeof(mreq)) < 0) {
131                         error("setsockopt(IPV6_ADD_MEMBERSHIP): %m");
132                         ret = -1;
133                         goto out;
134                 }
135 
136                 if (iface->dhcpv6 == MODE_SERVER) {
137                         memset(&mreq, 0, sizeof(mreq));
138                         inet_pton(AF_INET6, ALL_DHCPV6_SERVERS, &mreq.ipv6mr_multiaddr);
139                         mreq.ipv6mr_interface = iface->ifindex;
140 
141                         if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
142                                                 &mreq, sizeof(mreq)) < 0) {
143                                 error("setsockopt(IPV6_ADD_MEMBERSHIP): %m");
144                                 ret = -1;
145                                 goto out;
146                         }
147                 }
148 
149                 iface->dhcpv6_event.handle_dgram = handle_dhcpv6;
150                 odhcpd_register(&iface->dhcpv6_event);
151         }
152 
153         ret = dhcpv6_ia_setup_interface(iface, enable);
154 
155 out:
156         if (ret < 0 && iface->dhcpv6_event.uloop.fd >= 0) {
157                 close(iface->dhcpv6_event.uloop.fd);
158                 iface->dhcpv6_event.uloop.fd = -1;
159         }
160 
161         return ret;
162 }
163 
164 enum {
165         IOV_NESTED = 0,
166         IOV_DEST,
167         IOV_CLIENTID,
168         IOV_MAXRT,
169 #define IOV_STAT IOV_MAXRT
170         IOV_RAPID_COMMIT,
171         IOV_DNS,
172         IOV_DNS_ADDR,
173         IOV_SEARCH,
174         IOV_SEARCH_DOMAIN,
175         IOV_PDBUF,
176 #define IOV_REFRESH IOV_PDBUF
177         IOV_DHCPV6_RAW,
178         IOV_NTP,
179         IOV_NTP_ADDR,
180         IOV_SNTP,
181         IOV_SNTP_ADDR,
182         IOV_RELAY_MSG,
183         IOV_DHCPV4O6_SERVER,
184         IOV_DNR,
185         IOV_BOOTFILE_URL,
186         IOV_POSIX_TZ,
187         IOV_POSIX_TZ_STR,
188         IOV_TZDB_TZ,
189         IOV_TZDB_TZ_STR,
190         IOV_CAPT_PORTAL,
191         IOV_CAPT_PORTAL_URI,
192         IOV_TOTAL
193 };
194 
195 static void handle_nested_message(uint8_t *data, size_t len, unsigned depth,
196                                   struct dhcpv6_client_header **c_hdr, uint8_t **opts,
197                                   uint8_t **end, struct iovec iov[IOV_TOTAL])
198 {
199         struct dhcpv6_relay_header *r_hdr = (struct dhcpv6_relay_header *)data;
200         uint16_t otype, olen;
201         uint8_t *odata;
202 
203         if (iov[IOV_NESTED].iov_base == NULL) {
204                 iov[IOV_NESTED].iov_base = data;
205                 iov[IOV_NESTED].iov_len = len;
206         }
207 
208         if (len < sizeof(struct dhcpv6_client_header))
209                 return;
210 
211         if (r_hdr->msg_type != DHCPV6_MSG_RELAY_FORW) {
212                 iov[IOV_NESTED].iov_len = data - (uint8_t *)iov[IOV_NESTED].iov_base;
213                 *c_hdr = (void *)data;
214                 *opts = (uint8_t *)&(*c_hdr)[1];
215                 *end = data + len;
216                 return;
217         }
218 
219         /* Each nested Relay-Forward is one relay hop; RFC8415 bounds a relay
220          * chain via HOP_COUNT_LIMIT (defined in §7.6, enforced in §19.1.2),
221          * so refuse to recurse deeper and avoid stack exhaustion on crafted
222          * relay loops. */
223         if (depth >= DHCPV6_HOP_COUNT_LIMIT)
224                 return;
225 
226         dhcpv6_for_each_option(r_hdr->options, data + len, otype, olen, odata) {
227                 if (otype == DHCPV6_OPT_RELAY_MSG) {
228                         iov[IOV_RELAY_MSG].iov_base = odata + olen;
229                         iov[IOV_RELAY_MSG].iov_len = (((uint8_t *)iov[IOV_NESTED].iov_base) +
230                                         iov[IOV_NESTED].iov_len) - (odata + olen);
231                         handle_nested_message(odata, olen, depth + 1, c_hdr, opts, end, iov);
232                         return;
233                 }
234         }
235 }
236 
237 
238 static void update_nested_message(uint8_t *data, size_t len, unsigned depth, ssize_t pdiff)
239 {
240         struct dhcpv6_relay_header *hdr = (struct dhcpv6_relay_header*)data;
241         if (hdr->msg_type != DHCPV6_MSG_RELAY_FORW)
242                 return;
243 
244         hdr->msg_type = DHCPV6_MSG_RELAY_REPL;
245 
246         /* Bound recursion to mirror handle_nested_message(). */
247         if (depth >= DHCPV6_HOP_COUNT_LIMIT)
248                 return;
249 
250         uint16_t otype, olen;
251         uint8_t *odata;
252         dhcpv6_for_each_option(hdr->options, data + len, otype, olen, odata) {
253                 if (otype == DHCPV6_OPT_RELAY_MSG) {
254                         ssize_t newlen = (ssize_t)olen + pdiff;
255 
256                         /* olen was validated to lie within the buffer by the option
257                          * iterator. Reject a pdiff that would make the rewritten
258                          * RELAY_MSG length wrap the 16-bit field, which would also feed
259                          * a bogus length into the recursion below and walk options past
260                          * the end of the (untrusted) packet buffer. */
261                         if (newlen < 0 || newlen > UINT16_MAX)
262                                 return;
263 
264                         odata[-2] = (newlen >> 8) & 0xff;
265                         odata[-1] = newlen & 0xff;
266                         update_nested_message(odata, olen, depth + 1, pdiff);
267                         return;
268                 }
269         }
270 }
271 
272 #ifdef DHCPV4_SUPPORT
273 
274 struct dhcpv4_msg_data {
275         uint8_t *msg;
276         size_t maxsize;
277         ssize_t len;
278 };
279 
280 static ssize_t dhcpv6_4o6_send_reply(struct iovec *iov, size_t iov_len,
281                                      _o_unused struct sockaddr *dest,
282                                      _o_unused socklen_t dest_len,
283                                      void *opaque)
284 {
285         struct dhcpv4_msg_data *reply = opaque;
286         size_t len = 0;
287 
288         for (size_t i = 0; i < iov_len; i++)
289                 len += iov[i].iov_len;
290 
291         if (len > reply->maxsize) {
292                 error("4o6: reply too large, %zu > %zu", len, reply->maxsize);
293                 reply->len = -1;
294                 return -1;
295         }
296 
297         for (size_t i = 0, off = 0; i < iov_len; i++) {
298                 memcpy(reply->msg + off, iov[i].iov_base, iov[i].iov_len);
299                 off += iov[i].iov_len;
300         }
301         reply->len = len;
302 
303         return len;
304 }
305 
306 static ssize_t dhcpv6_4o6_query(uint8_t *buf, size_t buflen,
307                                 struct interface *iface,
308                                 const struct sockaddr_in6 *addr,
309                                 const void *data, const uint8_t *end)
310 {
311         const struct dhcpv6_client_header *hdr = data;
312         uint16_t otype, olen, msgv4_len = 0;
313         uint8_t *msgv4_data = NULL;
314         uint8_t *start = (uint8_t *)&hdr[1], *odata;
315         struct sockaddr_in addrv4;
316         struct dhcpv4_msg_data reply = { .msg = buf, .maxsize = buflen, .len = -1 };
317 
318         dhcpv6_for_each_option(start, end, otype, olen, odata) {
319                 if (otype == DHCPV6_OPT_DHCPV4_MSG) {
320                         msgv4_data = odata;
321                         msgv4_len = olen;
322                 }
323         }
324 
325         if (!msgv4_data || msgv4_len == 0) {
326                 error("4o6: missing DHCPv4 message option (%d)", DHCPV6_OPT_DHCPV4_MSG);
327                 return -1;
328         }
329 
330         /* dhcpv4_handle_msg() reads the full BOOTP fixed header (op, htype,
331          * hlen, hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr,
332          * chaddr, sname, file, cookie = offsetof(options) bytes) before
333          * parsing options. The normal DHCPv4 socket path enforces this via a
334          * BPF filter, but the DHCPv4-over-DHCPv6 path bypasses that filter,
335          * so validate the length here to prevent an out-of-bounds read on a
336          * truncated DHCPV4_MSG option. RFC7341 §7.1 defines the DHCPv4 Message
337          * option but mandates no minimum length, so this floor is ours. */
338         if (msgv4_len < offsetof(struct dhcpv4_message, options)) {
339                 error("4o6: encapsulated DHCPv4 message too short (%u)", msgv4_len);
340                 return -1;
341         }
342 
343         // Dummy IPv4 address
344         memset(&addrv4, 0, sizeof(addrv4));
345         addrv4.sin_family = AF_INET;
346         addrv4.sin_addr.s_addr = INADDR_ANY;
347         addrv4.sin_port = htons(DHCPV4_CLIENT_PORT);
348 
349         dhcpv4_handle_msg(&addrv4, msgv4_data, msgv4_len,
350                           iface, NULL, dhcpv6_4o6_send_reply, &reply);
351 
352         return reply.len;
353 }
354 #endif  /* DHCPV4_SUPPORT */
355 
356 /* Simple DHCPv6-server for information requests */
357 static void handle_client_request(void *addr, void *data, size_t len,
358                 struct interface *iface, void *dest_addr)
359 {
360         struct dhcpv6_client_header *hdr = data;
361         uint8_t *opts = (uint8_t *)&hdr[1], *opts_end = (uint8_t *)data + len;
362         bool o_rapid_commit = false;
363 
364         if (len < sizeof(*hdr))
365                 return;
366 
367         switch (hdr->msg_type) {
368         /* Valid message types for clients */
369         case DHCPV6_MSG_SOLICIT:
370         case DHCPV6_MSG_REQUEST:
371         case DHCPV6_MSG_CONFIRM:
372         case DHCPV6_MSG_RENEW:
373         case DHCPV6_MSG_REBIND:
374         case DHCPV6_MSG_RELEASE:
375         case DHCPV6_MSG_DECLINE:
376         case DHCPV6_MSG_INFORMATION_REQUEST:
377         case DHCPV6_MSG_RELAY_FORW:
378 #ifdef DHCPV4_SUPPORT
379         /* if we include DHCPV4 support, handle this message type */
380         case DHCPV6_MSG_DHCPV4_QUERY:
381 #endif
382                 break;
383         /* Invalid message types for clients i.e. server messages */
384         case DHCPV6_MSG_ADVERTISE:
385         case DHCPV6_MSG_REPLY:
386         case DHCPV6_MSG_RECONFIGURE:
387         case DHCPV6_MSG_RELAY_REPL:
388 #ifndef DHCPV4_SUPPORT
389         /* if we omit DHCPV4 support, ignore this client message type */
390         case DHCPV6_MSG_DHCPV4_QUERY:
391 #endif
392         case DHCPV6_MSG_DHCPV4_RESPONSE:
393         default:
394                 return;
395         }
396 
397         debug("Got a DHCPv6-request on %s", iface->name);
398 
399         /* Construct reply message */
400         struct _o_packed {
401                 uint8_t msg_type;
402                 uint8_t tr_id[3];
403                 uint16_t serverid_type;
404                 uint16_t serverid_length;
405                 uint8_t serverid_buf[DUID_MAX_LEN];
406         } dest = {
407                 .msg_type = DHCPV6_MSG_REPLY,
408                 .serverid_type = htons(DHCPV6_OPT_SERVERID),
409                 .serverid_length = 0,
410                 .serverid_buf = { 0 },
411         };
412 
413         if (config.default_duid_len > 0) {
414                 memcpy(dest.serverid_buf, config.default_duid, config.default_duid_len);
415                 dest.serverid_length = htons(config.default_duid_len);
416         } else {
417                 uint16_t duid_ll_hdr[] = { htons(DUID_TYPE_LL), htons(ARPHRD_ETHER) };
418                 memcpy(dest.serverid_buf, duid_ll_hdr, sizeof(duid_ll_hdr));
419                 odhcpd_get_mac(iface, &dest.serverid_buf[sizeof(duid_ll_hdr)]);
420                 dest.serverid_length = htons(sizeof(duid_ll_hdr) + ETH_ALEN);
421         }
422 
423         struct _o_packed {
424                 uint16_t type;
425                 uint16_t len;
426                 uint8_t buf[DUID_MAX_LEN];
427         } clientid = {
428                 .type = htons(DHCPV6_OPT_CLIENTID),
429                 .len = 0,
430                 .buf = { 0 },
431         };
432 
433         struct _o_packed {
434                 uint16_t type;
435                 uint16_t len;
436                 uint32_t value;
437         } maxrt = {htons(DHCPV6_OPT_SOL_MAX_RT), htons(sizeof(maxrt) - DHCPV6_OPT_HDR_SIZE),
438                         htonl(60)};
439 
440         struct _o_packed {
441                 uint16_t type;
442                 uint16_t len;
443         } rapid_commit = {htons(DHCPV6_OPT_RAPID_COMMIT), 0};
444 
445         struct _o_packed {
446                 uint16_t type;
447                 uint16_t len;
448                 uint16_t value;
449         } stat = {htons(DHCPV6_OPT_STATUS), htons(sizeof(stat) - DHCPV6_OPT_HDR_SIZE),
450                         htons(DHCPV6_STATUS_USEMULTICAST)};
451 
452         struct _o_packed {
453                 uint16_t type;
454                 uint16_t len;
455                 uint32_t value;
456         } refresh = {htons(DHCPV6_OPT_INFO_REFRESH), htons(sizeof(uint32_t)),
457                         htonl(600)};
458 
459         struct in6_addr *dns_addrs6 = NULL, dns_addr6;
460         size_t dns_addrs6_cnt = 0;
461 
462         if (iface->dns_addrs6_cnt > 0) {
463                 dns_addrs6 = iface->dns_addrs6;
464                 dns_addrs6_cnt = iface->dns_addrs6_cnt;
465         } else if (!odhcpd_get_interface_dns_addr6(iface, &dns_addr6)) {
466                 dns_addrs6 = &dns_addr6;
467                 dns_addrs6_cnt = 1;
468         }
469 
470         struct {
471                 uint16_t type;
472                 uint16_t len;
473         } dns_hdr = { htons(DHCPV6_OPT_DNS_SERVERS), htons(dns_addrs6_cnt * sizeof(*dns_addrs6)) };
474 
475         /* SNTP */
476         struct in6_addr *sntp_addr_ptr = iface->dhcpv6_sntp;
477         size_t sntp_cnt = 0;
478         struct {
479                 uint16_t type;
480                 uint16_t len;
481         } dhcpv6_sntp;
482 
483         /* RFC 4833 - Timezones */
484         bool posix_want = false;
485         uint8_t *posix_ptr = sys_conf.posix_tz;
486         uint16_t posix_len = sys_conf.posix_tz_len;
487         /* RFC 4833 - OPTION_NEW_POSIX_TIMEZONE (41)
488          * e.g. EST5EDT4,M3.2.0/02:00,M11.1.0/02:00
489          * Variable-length opaque tz_string blob.
490          */
491         struct {
492                 uint16_t type;
493                 uint16_t len;
494         } posix_tz;
495 
496         bool tzdb_want = false;
497         uint8_t *tzdb_ptr = sys_conf.tzdb_tz;
498         uint16_t tzdb_len = sys_conf.tzdb_tz_len;
499         /* RFC 4833 - OPTION_NEW_TZDB_TIMEZONE (42)
500          * e.g. Europe/Zurich
501          * Variable-length opaque tz_name blob.
502          */
503         struct {
504                 uint16_t type;
505                 uint16_t len;
506         } tzdb_tz;
507 
508         /* NTP */
509         uint8_t *ntp_ptr = iface->dhcpv6_ntp;
510         uint16_t ntp_len = iface->dhcpv6_ntp_len;
511         size_t ntp_cnt = 0;
512         struct {
513                 uint16_t type;
514                 uint16_t len;
515         } ntp;
516 
517         /* DNR */
518         struct dhcpv6_dnr {
519                 uint16_t type;
520                 uint16_t len;
521                 uint16_t priority;
522                 uint16_t adn_len;
523                 uint8_t body[];
524         };
525         struct dhcpv6_dnr *dnrs = NULL;
526         size_t dnrs_len = 0;
527 
528         /* RFC8910 Captive-Portal URI */
529         uint8_t *capt_portal_ptr = (uint8_t *)iface->captive_portal_uri;
530         size_t capt_portal_len = iface->captive_portal_uri_len;
531         struct {
532                 uint16_t type;
533                 uint16_t len;
534         } capt_portal;
535 
536         /* RFC8910 §2:
537          * DHCP servers MAY send the Captive Portal option without any explicit request
538          * If it is configured, send it.
539          */
540         capt_portal.type = htons(DHCPV6_OPT_CAPTIVE_PORTAL);
541         capt_portal.len = htons(capt_portal_len);
542 
543         uint16_t otype, olen;
544         uint8_t *odata;
545         /* OPTION_ORO payload is an array of uint16_t but the underlying buffer
546          * isn't guaranteed to be 2-byte aligned (it's at an arbitrary offset
547          * inside the packed wire packet), so keep it as a byte pointer and
548          * memcpy each value out rather than casting to uint16_t *. */
549         uint8_t *reqopts = NULL;
550         size_t reqopts_cnt = 0;
551 
552         /* FIXME: this should be merged with the second loop further down */
553         dhcpv6_for_each_option(opts, opts_end, otype, olen, odata) {
554                 /* Requested options, array of uint16_t, RFC 8415 §21.7 */
555                 if (otype == DHCPV6_OPT_ORO) {
556                         reqopts_cnt = olen / sizeof(uint16_t);
557                         reqopts = odata;
558                         break;
559                 }
560         }
561 
562         /* Requested options */
563         for (size_t i = 0; i < reqopts_cnt; i++) {
564                 uint16_t opt;
565 
566                 memcpy(&opt, &reqopts[i * sizeof(uint16_t)], sizeof(opt));
567                 opt = ntohs(opt);
568 
569                 switch (opt) {
570                 case DHCPV6_OPT_SNTP_SERVERS:
571                         sntp_cnt = iface->dhcpv6_sntp_cnt;
572                         dhcpv6_sntp.type = htons(DHCPV6_OPT_SNTP_SERVERS);
573                         dhcpv6_sntp.len = htons(sntp_cnt * sizeof(*sntp_addr_ptr));
574                         break;
575 
576                 case DHCPV6_OPT_NTP_SERVERS:
577                         ntp_cnt = iface->dhcpv6_ntp_cnt;
578                         ntp.type = htons(DHCPV6_OPT_NTP_SERVERS);
579                         ntp.len = htons(ntp_len);
580                         break;
581 
582                 case DHCPV6_OPT_NEW_POSIX_TIMEZONE:
583                         posix_want = true;
584                         posix_tz.type = htons(DHCPV6_OPT_NEW_POSIX_TIMEZONE);
585                         posix_tz.len = htons(posix_len);
586                         break;
587 
588                 case DHCPV6_OPT_NEW_TZDB_TIMEZONE:
589                         tzdb_want = true;
590                         tzdb_tz.type = htons(DHCPV6_OPT_NEW_TZDB_TIMEZONE);
591                         tzdb_tz.len = htons(tzdb_len);
592                         break;
593 
594                 case DHCPV6_OPT_DNR:
595                         for (size_t j = 0; j < iface->dnr_cnt; j++) {
596                                 struct dnr_options *dnr = &iface->dnr[j];
597 
598                                 if (dnr->addr6_cnt == 0 && dnr->addr4_cnt > 0)
599                                         continue;
600 
601                                 dnrs_len += sizeof(struct dhcpv6_dnr);
602                                 dnrs_len += dnr->adn_len;
603 
604                                 if (dnr->addr6_cnt > 0 || dnr->svc_len > 0) {
605                                         dnrs_len += sizeof(uint16_t);
606                                         dnrs_len += dnr->addr6_cnt * sizeof(*dnr->addr6);
607                                         dnrs_len += dnr->svc_len;
608                                 }
609                         }
610 
611                         dnrs = alloca(dnrs_len);
612                         uint8_t *pos = (uint8_t *)dnrs;
613 
614                         for (size_t j = 0; j < iface->dnr_cnt; j++) {
615                                 struct dnr_options *dnr = &iface->dnr[j];
616                                 struct dhcpv6_dnr *d6dnr = (struct dhcpv6_dnr *)pos;
617                                 uint16_t d6dnr_type_be = htons(DHCPV6_OPT_DNR);
618                                 uint16_t d6dnr_len = 2 * sizeof(uint16_t) + dnr->adn_len;
619                                 uint16_t d6dnr_len_be;
620                                 uint16_t d6dnr_priority_be = htons(dnr->priority);
621                                 uint16_t d6dnr_adn_len_be = htons(dnr->adn_len);
622 
623                                 if (dnr->addr6_cnt == 0 && dnr->addr4_cnt > 0)
624                                         continue;
625 
626                                 /* memcpy as the struct is unaligned */
627                                 memcpy(&d6dnr->type, &d6dnr_type_be, sizeof(d6dnr_type_be));
628                                 memcpy(&d6dnr->priority, &d6dnr_priority_be, sizeof(d6dnr_priority_be));
629                                 memcpy(&d6dnr->adn_len, &d6dnr_adn_len_be, sizeof(d6dnr_adn_len_be));
630 
631                                 pos = d6dnr->body;
632                                 memcpy(pos, dnr->adn, dnr->adn_len);
633                                 pos += dnr->adn_len;
634 
635                                 if (dnr->addr6_cnt > 0 || dnr->svc_len > 0) {
636                                         uint16_t addr6_len = dnr->addr6_cnt * sizeof(*dnr->addr6);
637                                         uint16_t addr6_len_be = htons(addr6_len);
638 
639                                         memcpy(pos, &addr6_len_be, sizeof(addr6_len_be));
640                                         pos += sizeof(addr6_len_be);
641                                         memcpy(pos, dnr->addr6, addr6_len);
642                                         pos += addr6_len;
643                                         memcpy(pos, dnr->svc, dnr->svc_len);
644                                         pos += dnr->svc_len;
645 
646                                         d6dnr_len += sizeof(addr6_len_be) + addr6_len + dnr->svc_len;
647                                 }
648 
649                                 d6dnr_len_be = htons(d6dnr_len);
650                                 memcpy(&d6dnr->len, &d6dnr_len_be, sizeof(d6dnr_len_be));
651                         }
652                         break;
653                 }
654         }
655 
656         /* DNS Search options */
657         struct {
658                 uint16_t type;
659                 uint16_t len;
660         } dns_search_hdr = { htons(DHCPV6_OPT_DNS_DOMAIN), htons(iface->dns_search_len) };
661 
662 
663         struct _o_packed dhcpv4o6_server {
664                 uint16_t type;
665                 uint16_t len;
666                 struct in6_addr addr;
667         } dhcpv4o6_server = {htons(DHCPV6_OPT_4O6_SERVER), htons(sizeof(struct in6_addr)),
668                         IN6ADDR_ANY_INIT};
669 
670         uint8_t pdbuf[512];
671         struct iovec iov[IOV_TOTAL] = {
672                 [IOV_NESTED] = {NULL, 0},
673                 [IOV_DEST] = {&dest, offsetof(typeof(dest), serverid_buf) + ntohs(dest.serverid_length) },
674                 [IOV_CLIENTID] = {&clientid, 0},
675                 [IOV_MAXRT] = {&maxrt, sizeof(maxrt)},
676                 [IOV_RAPID_COMMIT] = {&rapid_commit, 0},
677                 [IOV_DNS] = { &dns_hdr, (dns_addrs6_cnt) ? sizeof(dns_hdr) : 0},
678                 [IOV_DNS_ADDR] = { dns_addrs6, dns_addrs6_cnt * sizeof(*dns_addrs6) },
679                 [IOV_SEARCH] = { &dns_search_hdr, iface->dns_search_len ? sizeof(dns_search_hdr) : 0 },
680                 [IOV_SEARCH_DOMAIN] = { iface->dns_search, iface->dns_search_len },
681                 [IOV_PDBUF] = {pdbuf, 0},
682                 [IOV_DHCPV6_RAW] = {iface->dhcpv6_raw, iface->dhcpv6_raw_len},
683                 [IOV_NTP] = {&ntp, (ntp_cnt) ? sizeof(ntp) : 0},
684                 [IOV_NTP_ADDR] = {ntp_ptr, (ntp_cnt) ? ntp_len : 0},
685                 [IOV_SNTP] = {&dhcpv6_sntp, (sntp_cnt) ? sizeof(dhcpv6_sntp) : 0},
686                 [IOV_SNTP_ADDR] = {sntp_addr_ptr, sntp_cnt * sizeof(*sntp_addr_ptr)},
687                 [IOV_POSIX_TZ] = {&posix_tz, (posix_want) ? sizeof(posix_tz) : 0},
688                 [IOV_POSIX_TZ_STR] = {posix_ptr, (posix_want) ? posix_len : 0 },
689                 [IOV_TZDB_TZ] = {&tzdb_tz, (tzdb_want) ? sizeof(tzdb_tz) : 0},
690                 [IOV_TZDB_TZ_STR] = {tzdb_ptr, (tzdb_want) ? tzdb_len : 0 },
691                 [IOV_DNR] = {dnrs, dnrs_len},
692                 [IOV_RELAY_MSG] = {NULL, 0},
693                 [IOV_DHCPV4O6_SERVER] = {&dhcpv4o6_server, 0},
694                 [IOV_CAPT_PORTAL] = {&capt_portal, capt_portal_len ? sizeof(capt_portal) : 0},
695                 [IOV_CAPT_PORTAL_URI] = {capt_portal_ptr, capt_portal_len ? capt_portal_len : 0},
696                 [IOV_BOOTFILE_URL] = {NULL, 0}
697         };
698 
699         if (hdr->msg_type == DHCPV6_MSG_RELAY_FORW)
700                 handle_nested_message(data, len, 0, &hdr, &opts, &opts_end, iov);
701 
702         if (!IN6_IS_ADDR_MULTICAST((struct in6_addr *)dest_addr) && iov[IOV_NESTED].iov_len == 0 &&
703             (hdr->msg_type == DHCPV6_MSG_SOLICIT || hdr->msg_type == DHCPV6_MSG_CONFIRM ||
704              hdr->msg_type == DHCPV6_MSG_REBIND || hdr->msg_type == DHCPV6_MSG_INFORMATION_REQUEST))
705                 return;
706 
707         memcpy(dest.tr_id, hdr->transaction_id, sizeof(dest.tr_id));
708 
709         /* Go through options and find what we need */
710         dhcpv6_for_each_option(opts, opts_end, otype, olen, odata) {
711                 if (otype == DHCPV6_OPT_CLIENTID && olen <= DUID_MAX_LEN) {
712                         clientid.len = htons(olen);
713                         memcpy(clientid.buf, odata, olen);
714                         iov[IOV_CLIENTID].iov_len = offsetof(typeof(clientid), buf) + olen;
715                 } else if (otype == DHCPV6_OPT_SERVERID) {
716                         if (olen != ntohs(dest.serverid_length) ||
717                             memcmp(odata, &dest.serverid_buf, olen))
718                                 return; /* Not for us */
719                 } else if (otype == DHCPV6_OPT_RAPID_COMMIT && hdr->msg_type == DHCPV6_MSG_SOLICIT) {
720                         iov[IOV_RAPID_COMMIT].iov_len = sizeof(rapid_commit);
721                         o_rapid_commit = true;
722                 } else if (otype == DHCPV6_OPT_ORO) {
723                         for (int i = 0; i < olen / 2; i++) {
724                                 uint16_t option;
725 
726                                 /* odata is not guaranteed to be uint16-aligned. */
727                                 memcpy(&option, &odata[i * 2], sizeof(option));
728                                 option = ntohs(option);
729 
730                                 switch (option) {
731 #ifdef DHCPV4_SUPPORT
732                                 case DHCPV6_OPT_4O6_SERVER:
733                                         if (iface->dhcpv4) {
734                                                 /* According to RFC 7341, 7.2. DHCP 4o6 Server Address Option Format:
735                                                  * This option may also carry no IPv6 addresses, which instructs the
736                                                  * client to use the All_DHCP_Relay_Agents_and_Servers multicast address
737                                                  * as the destination address.
738                                                  *
739                                                  * The ISC dhclient logs a missing IPv6 address as an error but seems to
740                                                  * work anyway:
741                                                  * dhcp4-o-dhcp6-server: expecting at least 16 bytes; got 0
742                                                  *
743                                                  * Include the All_DHCP_Relay_Agents_and_Servers multicast address
744                                                  * to make it explicit which address to use. */
745                                                 struct dhcpv4o6_server *server = iov[IOV_DHCPV4O6_SERVER].iov_base;
746 
747                                                 inet_pton(AF_INET6, ALL_DHCPV6_RELAYS, &server->addr);
748 
749                                                 iov[IOV_DHCPV4O6_SERVER].iov_len = sizeof(dhcpv4o6_server);
750                                         }
751                                         break;
752 #endif /* DHCPV4_SUPPORT */
753                                 default:
754                                         break;
755                                 }
756                         }
757                 } else if (otype == DHCPV6_OPT_CLIENT_ARCH && olen >= sizeof(uint16_t)) {
758                         uint16_t arch_code;
759 
760                         /* odata is not guaranteed to be uint16-aligned, and
761                          * RFC5970 §3.3 mandates olen be a multiple of 2 with
762                          * at least one architecture entry — read defensively. */
763                         memcpy(&arch_code, odata, sizeof(arch_code));
764                         arch_code = ntohs(arch_code);
765                         ipv6_pxe_serve_boot_url(arch_code, &iov[IOV_BOOTFILE_URL]);
766                 }
767         }
768 
769         if (dest.serverid_length == clientid.len &&
770             !memcmp(clientid.buf, dest.serverid_buf, ntohs(dest.serverid_length))) {
771                 /* Bail if we are in a network loop where we talk with ourself */
772                 return;
773         }
774 
775         if (!IN6_IS_ADDR_MULTICAST((struct in6_addr *)dest_addr) && iov[IOV_NESTED].iov_len == 0 &&
776             (hdr->msg_type == DHCPV6_MSG_REQUEST || hdr->msg_type == DHCPV6_MSG_RENEW ||
777              hdr->msg_type == DHCPV6_MSG_RELEASE || hdr->msg_type == DHCPV6_MSG_DECLINE)) {
778                 iov[IOV_STAT].iov_base = &stat;
779                 iov[IOV_STAT].iov_len = sizeof(stat);
780 
781                 for (ssize_t i = IOV_STAT + 1; i < IOV_TOTAL; ++i)
782                         iov[i].iov_len = 0;
783 
784                 odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
785                 return;
786         }
787 
788         if (hdr->msg_type == DHCPV6_MSG_SOLICIT && !o_rapid_commit) {
789                 dest.msg_type = DHCPV6_MSG_ADVERTISE;
790         } else if (hdr->msg_type == DHCPV6_MSG_INFORMATION_REQUEST) {
791                 iov[IOV_REFRESH].iov_base = &refresh;
792                 iov[IOV_REFRESH].iov_len = sizeof(refresh);
793 
794                 /* Return inf max rt option in reply to information request */
795                 maxrt.type = htons(DHCPV6_OPT_INF_MAX_RT);
796         }
797 
798 #ifdef DHCPV4_SUPPORT
799         if (hdr->msg_type == DHCPV6_MSG_DHCPV4_QUERY) {
800                 struct _o_packed dhcpv4_msg_data {
801                         uint16_t type;
802                         uint16_t len;
803                         uint8_t msg[1];
804                 } *msg_opt = (struct dhcpv4_msg_data*)pdbuf;
805                 ssize_t msglen;
806 
807                 memset(pdbuf, 0, sizeof(pdbuf));
808 
809                 msglen = dhcpv6_4o6_query(msg_opt->msg, sizeof(pdbuf) - sizeof(*msg_opt) + 1,
810                                                 iface, addr, (const void *)hdr, opts_end);
811                 if (msglen <= 0) {
812                         error("4o6: query failed");
813                         return;
814                 }
815 
816                 msg_opt->type = htons(DHCPV6_OPT_DHCPV4_MSG);
817                 msg_opt->len = htons(msglen);
818                 iov[IOV_PDBUF].iov_len = sizeof(*msg_opt) - 1 + msglen;
819                 dest.msg_type = DHCPV6_MSG_DHCPV4_RESPONSE;
820         } else
821 #endif  /* DHCPV4_SUPPORT */
822 
823         if (hdr->msg_type != DHCPV6_MSG_INFORMATION_REQUEST) {
824                 ssize_t ialen = dhcpv6_ia_handle_IAs(pdbuf, sizeof(pdbuf), iface, addr, (const void *)hdr, opts_end);
825 
826                 iov[IOV_PDBUF].iov_len = ialen;
827                 if (ialen < 0 ||
828                     (ialen == 0 && (hdr->msg_type == DHCPV6_MSG_REBIND || hdr->msg_type == DHCPV6_MSG_CONFIRM)))
829                         return;
830         }
831 
832         if (iov[IOV_NESTED].iov_len > 0) /* Update length */
833                 update_nested_message(data, len, 0, iov[IOV_DEST].iov_len + iov[IOV_MAXRT].iov_len +
834                                       iov[IOV_RAPID_COMMIT].iov_len + iov[IOV_DNS].iov_len +
835                                       iov[IOV_DNS_ADDR].iov_len + iov[IOV_SEARCH].iov_len +
836                                       iov[IOV_SEARCH_DOMAIN].iov_len + iov[IOV_PDBUF].iov_len +
837                                       iov[IOV_DHCPV4O6_SERVER].iov_len +
838                                       iov[IOV_DHCPV6_RAW].iov_len +
839                                       iov[IOV_NTP].iov_len + iov[IOV_NTP_ADDR].iov_len +
840                                       iov[IOV_SNTP].iov_len + iov[IOV_SNTP_ADDR].iov_len +
841                                       iov[IOV_POSIX_TZ].iov_len + iov[IOV_POSIX_TZ_STR].iov_len +
842                                       iov[IOV_TZDB_TZ].iov_len + iov[IOV_TZDB_TZ_STR].iov_len +
843                                       iov[IOV_CAPT_PORTAL].iov_len + iov[IOV_CAPT_PORTAL_URI].iov_len +
844                                       iov[IOV_DNR].iov_len + iov[IOV_BOOTFILE_URL].iov_len -
845                                       (4 + opts_end - opts));
846 
847         debug("Sending a DHCPv6-%s on %s", iov[IOV_NESTED].iov_len ? "relay-reply" : "reply", iface->name);
848 
849         odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
850 }
851 
852 
853 /* Central DHCPv6-relay handler */
854 static void handle_dhcpv6(void *addr, void *data, size_t len,
855                 struct interface *iface, void *dest_addr)
856 {
857         if (iface->dhcpv6 == MODE_SERVER) {
858                 handle_client_request(addr, data, len, iface, dest_addr);
859         } else if (iface->dhcpv6 == MODE_RELAY) {
860                 if (iface->master) {
861                         relay_server_response(data, len);
862                 } else if (iface->dhcpv6_relay_server_addrs6_cnt > 0) {
863                         for (size_t i = 0; i < iface->dhcpv6_relay_server_addrs6_cnt; i++) {
864                                 relay_client_request(addr, data, len, iface, &iface->dhcpv6_relay_server_addrs6[i]);
865                         }
866                 } else {
867                         relay_client_request(addr, data, len, iface, NULL);
868                 }
869         }
870 }
871 
872 
873 /* Relay server response (regular relay server handling) */
874 static void relay_server_response(uint8_t *data, size_t len)
875 {
876         /* Information we need to gather */
877         uint8_t *payload_data = NULL;
878         size_t payload_len = 0;
879         int32_t ifaceidx = 0;
880         struct sockaddr_in6 target = {AF_INET6, htons(DHCPV6_CLIENT_PORT),
881                 0, IN6ADDR_ANY_INIT, 0};
882         uint16_t otype, olen;
883         uint8_t *odata, *end = data + len;
884         /* Relay DHCPv6 reply from server to client */
885         struct dhcpv6_relay_header *h = (void*)data;
886 
887         debug("Got a DHCPv6-relay-reply");
888 
889         if (len < sizeof(*h) || h->msg_type != DHCPV6_MSG_RELAY_REPL)
890                 return;
891 
892         memcpy(&target.sin6_addr, &h->peer_address, sizeof(struct in6_addr));
893 
894         /* Go through options and find what we need */
895         dhcpv6_for_each_option(h->options, end, otype, olen, odata) {
896                 if (otype == DHCPV6_OPT_INTERFACE_ID
897                                 && olen == sizeof(ifaceidx)) {
898                         memcpy(&ifaceidx, odata, sizeof(ifaceidx));
899                 } else if (otype == DHCPV6_OPT_RELAY_MSG) {
900                         payload_data = odata;
901                         payload_len = olen;
902                 }
903         }
904 
905         /* Invalid interface-id or basic payload */
906         struct interface *iface = odhcpd_get_interface_by_index(ifaceidx);
907         if (!iface || iface->master || !payload_data || payload_len < 4)
908                 return;
909 
910         bool is_authenticated = false;
911         struct in6_addr *dns_addrs6 = NULL;
912         size_t dns_addrs6_cnt = 0;
913 
914         /* If the payload is relay-reply we have to send to the server port */
915         if (payload_data[0] == DHCPV6_MSG_RELAY_REPL) {
916                 target.sin6_port = htons(DHCPV6_SERVER_PORT);
917         } else { /* Go through the payload data */
918                 struct dhcpv6_client_header *dch = (void*)payload_data;
919                 end = payload_data + payload_len;
920 
921                 dhcpv6_for_each_option(&dch[1], end, otype, olen, odata) {
922                         if (otype == DHCPV6_OPT_DNS_SERVERS && olen >= sizeof(struct in6_addr)) {
923                                 dns_addrs6 = (struct in6_addr*)odata;
924                                 dns_addrs6_cnt = olen / sizeof(struct in6_addr);
925                         } else if (otype == DHCPV6_OPT_AUTH) {
926                                 is_authenticated = true;
927                         }
928                 }
929         }
930 
931         /* Rewrite DNS servers if requested */
932         if (iface->always_rewrite_dns && dns_addrs6 && dns_addrs6_cnt > 0) {
933                 if (is_authenticated)
934                         return; /* Impossible to rewrite */
935 
936                 const struct in6_addr *rewrite = iface->dns_addrs6;
937                 struct in6_addr addr;
938                 size_t rewrite_cnt = iface->dns_addrs6_cnt;
939 
940                 if (rewrite_cnt == 0) {
941                         if (odhcpd_get_interface_dns_addr6(iface, &addr))
942                                 return; /* Unable to get interface address */
943 
944                         rewrite = &addr;
945                         rewrite_cnt = 1;
946                 }
947 
948                 /* Copy over any other addresses */
949                 for (size_t i = 0; i < dns_addrs6_cnt; ++i) {
950                         size_t j = (i < rewrite_cnt) ? i : rewrite_cnt - 1;
951                         memcpy(&dns_addrs6[i], &rewrite[j], sizeof(*rewrite));
952                 }
953         }
954 
955         struct iovec iov = {payload_data, payload_len};
956 
957         debug("Sending a DHCPv6-reply on %s", iface->name);
958 
959         odhcpd_send(iface->dhcpv6_event.uloop.fd, &target, &iov, 1, iface);
960 }
961 
962 static struct odhcpd_ipaddr *relay_link_address(struct interface *iface)
963 {
964         struct odhcpd_ipaddr *addr = NULL;
965         time_t now = odhcpd_time();
966 
967         for (size_t i = 0; i < iface->addr6_len; i++) {
968                 if (iface->addr6[i].valid_lt <= (uint32_t)now)
969                         continue;
970 
971                 if (iface->addr6[i].preferred_lt > (uint32_t)now) {
972                         addr = &iface->addr6[i];
973                         break;
974                 }
975 
976                 if (!addr || (iface->addr6[i].valid_lt > addr->valid_lt))
977                         addr = &iface->addr6[i];
978         }
979 
980         return addr;
981 }
982 
983 /* Relay client request (regular DHCPv6-relay) */
984 static void relay_client_request(struct sockaddr_in6 *source,
985                 const void *data, size_t len, struct interface *iface,
986                 struct in6_addr *dest)
987 {
988         const struct dhcpv6_relay_header *h = data;
989         /* Construct our forwarding envelope */
990         struct dhcpv6_relay_forward_envelope hdr = {
991                 .msg_type = DHCPV6_MSG_RELAY_FORW,
992                 .hop_count = 0,
993                 .interface_id_type = htons(DHCPV6_OPT_INTERFACE_ID),
994                 .interface_id_len = htons(sizeof(uint32_t)),
995                 .relay_message_type = htons(DHCPV6_OPT_RELAY_MSG),
996                 .relay_message_len = htons(len),
997         };
998         struct iovec iov[2] = {{&hdr, sizeof(hdr)}, {(void *)data, len}};
999         struct interface *c;
1000         struct odhcpd_ipaddr *ip;
1001         struct sockaddr_in6 s;
1002 
1003         /* A bare UDP socket can deliver a zero/short payload; the relay-reply
1004          * path (relay_server_response) checks this but the client-side relay
1005          * did not. relay_client_request() reads h->msg_type, plus h->hop_count
1006          * for a RELAY_FORW, out of the relay header and forwards the rest
1007          * verbatim, so require at least those two leading header bytes. */
1008         if (len < offsetof(struct dhcpv6_relay_header, link_address))
1009                 return;
1010 
1011         switch (h->msg_type) {
1012         /* Valid message types from clients */
1013         case DHCPV6_MSG_SOLICIT:
1014         case DHCPV6_MSG_REQUEST:
1015         case DHCPV6_MSG_CONFIRM:
1016         case DHCPV6_MSG_RENEW:
1017         case DHCPV6_MSG_REBIND:
1018         case DHCPV6_MSG_RELEASE:
1019         case DHCPV6_MSG_DECLINE:
1020         case DHCPV6_MSG_INFORMATION_REQUEST:
1021         case DHCPV6_MSG_RELAY_FORW:
1022         case DHCPV6_MSG_DHCPV4_QUERY:
1023                 break;
1024         /* Invalid message types from clients i.e. server messages */
1025         case DHCPV6_MSG_ADVERTISE:
1026         case DHCPV6_MSG_REPLY:
1027         case DHCPV6_MSG_RECONFIGURE:
1028         case DHCPV6_MSG_RELAY_REPL:
1029         case DHCPV6_MSG_DHCPV4_RESPONSE:
1030                 return;
1031         default:
1032                 break;
1033         }
1034 
1035         debug("Got a DHCPv6-request on %s", iface->name);
1036 
1037         if (h->msg_type == DHCPV6_MSG_RELAY_FORW) { /* handle relay-forward */
1038                 if (h->hop_count >= DHCPV6_HOP_COUNT_LIMIT)
1039                         return; /* Invalid hop count */
1040 
1041                 hdr.hop_count = h->hop_count + 1;
1042         }
1043 
1044         /* use memcpy here as the destination fields are unaligned */
1045         memcpy(&hdr.peer_address, &source->sin6_addr, sizeof(struct in6_addr));
1046         memcpy(&hdr.interface_id_data, &iface->ifindex, sizeof(iface->ifindex));
1047 
1048         /* Detect public IP of slave interface to use as link-address */
1049         ip = relay_link_address(iface);
1050         if (ip)
1051                 memcpy(&hdr.link_address, &ip->addr.in6, sizeof(hdr.link_address));
1052 
1053         memset(&s, 0, sizeof(s));
1054         s.sin6_family = AF_INET6;
1055         s.sin6_port = htons(DHCPV6_SERVER_PORT);
1056 
1057         if (dest)
1058                 s.sin6_addr = *dest;
1059         else
1060                 inet_pton(AF_INET6, ALL_DHCPV6_SERVERS, &s.sin6_addr);
1061 
1062         avl_for_each_element(&interfaces, c, avl) {
1063                 if (!c->master || c->dhcpv6 != MODE_RELAY)
1064                         continue;
1065 
1066                 if (!ip) {
1067                         /* No suitable address! Is the slave not configured yet?
1068                          * Detect public IP of master interface and use it instead
1069                          * This is WRONG and probably violates the RFC. However
1070                          * otherwise we have a hen and egg problem because the
1071                          * slave-interface cannot be auto-configured. */
1072                         ip = relay_link_address(c);
1073                         if (!ip)
1074                                 continue; /* Could not obtain a suitable address */
1075 
1076                         memcpy(&hdr.link_address, &ip->addr.in6, sizeof(hdr.link_address));
1077                         ip = NULL;
1078                 }
1079 
1080                 debug("Sending a DHCPv6-relay-forward on %s", c->name);
1081 
1082                 odhcpd_send(c->dhcpv6_event.uloop.fd, &s, iov, 2, c);
1083         }
1084 }
1085 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt