odhcpd: fix compilation with GCC10
[oweals/odhcpd.git] / 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
17 #include <errno.h>
18 #include <unistd.h>
19 #include <stddef.h>
20 #include <resolv.h>
21 #include <sys/timerfd.h>
22 #include <arpa/inet.h>
23
24 #include <libubox/utils.h>
25
26 #include "odhcpd.h"
27 #include "dhcpv6.h"
28
29
30 static void relay_client_request(struct sockaddr_in6 *source,
31                 const void *data, size_t len, struct interface *iface);
32 static void relay_server_response(uint8_t *data, size_t len);
33
34 static void handle_dhcpv6(void *addr, void *data, size_t len,
35                 struct interface *iface, void *dest);
36 static void handle_client_request(void *addr, void *data, size_t len,
37                 struct interface *iface, void *dest_addr);
38
39
40 /* Create socket and register events */
41 int dhcpv6_init(void)
42 {
43         return dhcpv6_ia_init();
44 }
45
46 int dhcpv6_setup_interface(struct interface *iface, bool enable)
47 {
48         int ret = 0;
49
50         enable = enable && (iface->dhcpv6 != MODE_DISABLED);
51
52         if (iface->dhcpv6_event.uloop.fd >= 0) {
53                 uloop_fd_delete(&iface->dhcpv6_event.uloop);
54                 close(iface->dhcpv6_event.uloop.fd);
55                 iface->dhcpv6_event.uloop.fd = -1;
56         }
57
58         /* Configure multicast settings */
59         if (enable) {
60                 struct sockaddr_in6 bind_addr = {AF_INET6, htons(DHCPV6_SERVER_PORT),
61                                         0, IN6ADDR_ANY_INIT, 0};
62                 struct ipv6_mreq mreq;
63                 int val = 1;
64
65                 iface->dhcpv6_event.uloop.fd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
66                 if (iface->dhcpv6_event.uloop.fd < 0) {
67                         syslog(LOG_ERR, "socket(AF_INET6): %m");
68                         ret = -1;
69                         goto out;
70                 }
71
72                 /* Basic IPv6 configuration */
73                 if (setsockopt(iface->dhcpv6_event.uloop.fd, SOL_SOCKET, SO_BINDTODEVICE,
74                                         iface->ifname, strlen(iface->ifname)) < 0) {
75                         syslog(LOG_ERR, "setsockopt(SO_BINDTODEVICE): %m");
76                         ret = -1;
77                         goto out;
78                 }
79
80                 if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_V6ONLY,
81                                         &val, sizeof(val)) < 0) {
82                         syslog(LOG_ERR, "setsockopt(IPV6_V6ONLY): %m");
83                         ret = -1;
84                         goto out;
85                 }
86
87                 if (setsockopt(iface->dhcpv6_event.uloop.fd, SOL_SOCKET, SO_REUSEADDR,
88                                         &val, sizeof(val)) < 0) {
89                         syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m");
90                         ret = -1;
91                         goto out;
92                 }
93
94                 if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
95                                         &val, sizeof(val)) < 0) {
96                         syslog(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO): %m");
97                         ret = -1;
98                         goto out;
99                 }
100
101                 val = DHCPV6_HOP_COUNT_LIMIT;
102                 if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
103                                         &val, sizeof(val)) < 0) {
104                         syslog(LOG_ERR, "setsockopt(IPV6_MULTICAST_HOPS): %m");
105                         ret = -1;
106                         goto out;
107                 }
108
109                 val = 0;
110                 if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
111                                         &val, sizeof(val)) < 0) {
112                         syslog(LOG_ERR, "setsockopt(IPV6_MULTICAST_LOOP): %m");
113                         ret = -1;
114                         goto out;
115                 }
116
117                 if (bind(iface->dhcpv6_event.uloop.fd, (struct sockaddr*)&bind_addr,
118                                         sizeof(bind_addr)) < 0) {
119                         syslog(LOG_ERR, "bind(): %m");
120                         ret = -1;
121                         goto out;
122                 }
123
124                 memset(&mreq, 0, sizeof(mreq));
125                 inet_pton(AF_INET6, ALL_DHCPV6_RELAYS, &mreq.ipv6mr_multiaddr);
126                 mreq.ipv6mr_interface = iface->ifindex;
127
128                 if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
129                                         &mreq, sizeof(mreq)) < 0) {
130                         syslog(LOG_ERR, "setsockopt(IPV6_ADD_MEMBERSHIP): %m");
131                         ret = -1;
132                         goto out;
133                 }
134
135                 if (iface->dhcpv6 == MODE_SERVER) {
136                         memset(&mreq, 0, sizeof(mreq));
137                         inet_pton(AF_INET6, ALL_DHCPV6_SERVERS, &mreq.ipv6mr_multiaddr);
138                         mreq.ipv6mr_interface = iface->ifindex;
139
140                         if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
141                                                 &mreq, sizeof(mreq)) < 0) {
142                                 syslog(LOG_ERR, "setsockopt(IPV6_ADD_MEMBERSHIP): %m");
143                                 ret = -1;
144                                 goto out;
145                         }
146                 }
147
148                 iface->dhcpv6_event.handle_dgram = handle_dhcpv6;
149                 odhcpd_register(&iface->dhcpv6_event);
150         }
151
152         ret = dhcpv6_ia_setup_interface(iface, enable);
153
154 out:
155         if (ret < 0 && iface->dhcpv6_event.uloop.fd >= 0) {
156                 close(iface->dhcpv6_event.uloop.fd);
157                 iface->dhcpv6_event.uloop.fd = -1;
158         }
159
160         return ret;
161 }
162
163 enum {
164         IOV_NESTED = 0,
165         IOV_DEST,
166         IOV_MAXRT,
167 #define IOV_STAT IOV_MAXRT
168         IOV_RAPID_COMMIT,
169         IOV_DNS,
170         IOV_DNS_ADDR,
171         IOV_SEARCH,
172         IOV_SEARCH_DOMAIN,
173         IOV_PDBUF,
174 #define IOV_REFRESH IOV_PDBUF
175         IOV_CERID,
176         IOV_DHCPV6_RAW,
177         IOV_RELAY_MSG,
178         IOV_TOTAL
179 };
180
181 static void handle_nested_message(uint8_t *data, size_t len,
182                                   struct dhcpv6_client_header **c_hdr, uint8_t **opts,
183                                   uint8_t **end, struct iovec iov[IOV_TOTAL])
184 {
185         struct dhcpv6_relay_header *r_hdr = (struct dhcpv6_relay_header *)data;
186         uint16_t otype, olen;
187         uint8_t *odata;
188
189         if (iov[IOV_NESTED].iov_base == NULL) {
190                 iov[IOV_NESTED].iov_base = data;
191                 iov[IOV_NESTED].iov_len = len;
192         }
193
194         if (len < sizeof(struct dhcpv6_client_header))
195                 return;
196
197         if (r_hdr->msg_type != DHCPV6_MSG_RELAY_FORW) {
198                 iov[IOV_NESTED].iov_len = data - (uint8_t *)iov[IOV_NESTED].iov_base;
199                 *c_hdr = (void *)data;
200                 *opts = (uint8_t *)&(*c_hdr)[1];
201                 *end = data + len;
202                 return;
203         }
204
205         dhcpv6_for_each_option(r_hdr->options, data + len, otype, olen, odata) {
206                 if (otype == DHCPV6_OPT_RELAY_MSG) {
207                         iov[IOV_RELAY_MSG].iov_base = odata + olen;
208                         iov[IOV_RELAY_MSG].iov_len = (((uint8_t *)iov[IOV_NESTED].iov_base) +
209                                         iov[IOV_NESTED].iov_len) - (odata + olen);
210                         handle_nested_message(odata, olen, c_hdr, opts, end, iov);
211                         return;
212                 }
213         }
214 }
215
216
217 static void update_nested_message(uint8_t *data, size_t len, ssize_t pdiff)
218 {
219         struct dhcpv6_relay_header *hdr = (struct dhcpv6_relay_header*)data;
220         if (hdr->msg_type != DHCPV6_MSG_RELAY_FORW)
221                 return;
222
223         hdr->msg_type = DHCPV6_MSG_RELAY_REPL;
224
225         uint16_t otype, olen;
226         uint8_t *odata;
227         dhcpv6_for_each_option(hdr->options, data + len, otype, olen, odata) {
228                 if (otype == DHCPV6_OPT_RELAY_MSG) {
229                         olen += pdiff;
230                         odata[-2] = (olen >> 8) & 0xff;
231                         odata[-1] = olen & 0xff;
232                         update_nested_message(odata, olen - pdiff, pdiff);
233                         return;
234                 }
235         }
236 }
237
238 /* Simple DHCPv6-server for information requests */
239 static void handle_client_request(void *addr, void *data, size_t len,
240                 struct interface *iface, void *dest_addr)
241 {
242         struct dhcpv6_client_header *hdr = data;
243         uint8_t *opts = (uint8_t *)&hdr[1], *opts_end = (uint8_t *)data + len;
244         bool o_rapid_commit = false;
245
246         if (len < sizeof(*hdr))
247                 return;
248
249         syslog(LOG_DEBUG, "Got a DHCPv6-request on %s", iface->name);
250
251         /* Construct reply message */
252         struct __attribute__((packed)) {
253                 uint8_t msg_type;
254                 uint8_t tr_id[3];
255                 uint16_t serverid_type;
256                 uint16_t serverid_length;
257                 uint16_t duid_type;
258                 uint16_t hardware_type;
259                 uint8_t mac[6];
260                 uint16_t clientid_type;
261                 uint16_t clientid_length;
262                 uint8_t clientid_buf[130];
263         } dest = {
264                 .msg_type = DHCPV6_MSG_REPLY,
265                 .serverid_type = htons(DHCPV6_OPT_SERVERID),
266                 .serverid_length = htons(10),
267                 .duid_type = htons(3),
268                 .hardware_type = htons(1),
269                 .clientid_type = htons(DHCPV6_OPT_CLIENTID),
270                 .clientid_buf = {0}
271         };
272         odhcpd_get_mac(iface, dest.mac);
273
274         struct __attribute__((packed)) {
275                 uint16_t type;
276                 uint16_t len;
277                 uint32_t value;
278         } maxrt = {htons(DHCPV6_OPT_SOL_MAX_RT), htons(sizeof(maxrt) - 4),
279                         htonl(60)};
280
281         struct __attribute__((packed)) {
282                 uint16_t type;
283                 uint16_t len;
284         } rapid_commit = {htons(DHCPV6_OPT_RAPID_COMMIT), 0};
285
286         struct __attribute__((packed)) {
287                 uint16_t type;
288                 uint16_t len;
289                 uint16_t value;
290         } stat = {htons(DHCPV6_OPT_STATUS), htons(sizeof(stat) - 4),
291                         htons(DHCPV6_STATUS_USEMULTICAST)};
292
293         struct __attribute__((packed)) {
294                 uint16_t type;
295                 uint16_t len;
296                 uint32_t value;
297         } refresh = {htons(DHCPV6_OPT_INFO_REFRESH), htons(sizeof(uint32_t)),
298                         htonl(600)};
299
300         struct in6_addr dns_addr, *dns_addr_ptr = iface->dns;
301         size_t dns_cnt = iface->dns_cnt;
302
303         if ((dns_cnt == 0) &&
304                 !odhcpd_get_interface_dns_addr(iface, &dns_addr)) {
305                 dns_addr_ptr = &dns_addr;
306                 dns_cnt = 1;
307         }
308
309         struct {
310                 uint16_t type;
311                 uint16_t len;
312         } dns = {htons(DHCPV6_OPT_DNS_SERVERS), htons(dns_cnt * sizeof(*dns_addr_ptr))};
313
314
315
316         /* DNS Search options */
317         uint8_t search_buf[256], *search_domain = iface->search;
318         size_t search_len = iface->search_len;
319
320         if (!search_domain && !res_init() && _res.dnsrch[0] && _res.dnsrch[0][0]) {
321                 int len = dn_comp(_res.dnsrch[0], search_buf,
322                                 sizeof(search_buf), NULL, NULL);
323                 if (len > 0) {
324                         search_domain = search_buf;
325                         search_len = len;
326                 }
327         }
328
329         struct {
330                 uint16_t type;
331                 uint16_t len;
332         } search = {htons(DHCPV6_OPT_DNS_DOMAIN), htons(search_len)};
333
334
335         struct dhcpv6_cer_id cerid = {
336 #ifdef EXT_CER_ID
337                 .type = htons(EXT_CER_ID),
338 #endif
339                 .len = htons(36),
340                 .addr = iface->dhcpv6_pd_cer,
341         };
342
343
344         uint8_t pdbuf[512];
345         struct iovec iov[IOV_TOTAL] = {
346                 [IOV_NESTED] = {NULL, 0},
347                 [IOV_DEST] = {&dest, (uint8_t*)&dest.clientid_type - (uint8_t*)&dest},
348                 [IOV_MAXRT] = {&maxrt, sizeof(maxrt)},
349                 [IOV_RAPID_COMMIT] = {&rapid_commit, 0},
350                 [IOV_DNS] = {&dns, (dns_cnt) ? sizeof(dns) : 0},
351                 [IOV_DNS_ADDR] = {dns_addr_ptr, dns_cnt * sizeof(*dns_addr_ptr)},
352                 [IOV_SEARCH] = {&search, (search_len) ? sizeof(search) : 0},
353                 [IOV_SEARCH_DOMAIN] = {search_domain, search_len},
354                 [IOV_PDBUF] = {pdbuf, 0},
355                 [IOV_CERID] = {&cerid, 0},
356                 [IOV_DHCPV6_RAW] = {iface->dhcpv6_raw, iface->dhcpv6_raw_len},
357                 [IOV_RELAY_MSG] = {NULL, 0}
358         };
359
360         if (hdr->msg_type == DHCPV6_MSG_RELAY_FORW)
361                 handle_nested_message(data, len, &hdr, &opts, &opts_end, iov);
362
363         if (hdr->msg_type == DHCPV6_MSG_ADVERTISE || hdr->msg_type == DHCPV6_MSG_REPLY ||
364             hdr->msg_type == DHCPV6_MSG_RELAY_REPL)
365                 return;
366
367         if (!IN6_IS_ADDR_MULTICAST((struct in6_addr *)dest_addr) && iov[IOV_NESTED].iov_len == 0 &&
368             (hdr->msg_type == DHCPV6_MSG_SOLICIT || hdr->msg_type == DHCPV6_MSG_CONFIRM ||
369              hdr->msg_type == DHCPV6_MSG_REBIND || hdr->msg_type == DHCPV6_MSG_INFORMATION_REQUEST))
370                 return;
371
372         memcpy(dest.tr_id, hdr->transaction_id, sizeof(dest.tr_id));
373
374         /* Go through options and find what we need */
375         uint16_t otype, olen;
376         uint8_t *odata;
377         dhcpv6_for_each_option(opts, opts_end, otype, olen, odata) {
378                 if (otype == DHCPV6_OPT_CLIENTID && olen <= 130) {
379                         dest.clientid_length = htons(olen);
380                         memcpy(dest.clientid_buf, odata, olen);
381                         iov[IOV_DEST].iov_len += 4 + olen;
382                 } else if (otype == DHCPV6_OPT_SERVERID) {
383                         if (olen != ntohs(dest.serverid_length) ||
384                                         memcmp(odata, &dest.duid_type, olen))
385                                 return; /* Not for us */
386                 } else if (iface->filter_class && otype == DHCPV6_OPT_USER_CLASS) {
387                         uint8_t *c = odata, *cend = &odata[olen];
388                         for (; &c[2] <= cend && &c[2 + (c[0] << 8) + c[1]] <= cend; c = &c[2 + (c[0] << 8) + c[1]]) {
389                                 size_t elen = strlen(iface->filter_class);
390                                 if (((((size_t)c[0]) << 8) | c[1]) == elen && !memcmp(&c[2], iface->filter_class, elen))
391                                         return; /* Ignore from homenet */
392                         }
393                 } else if (otype == DHCPV6_OPT_IA_PD) {
394 #ifdef EXT_CER_ID
395                         iov[IOV_CERID].iov_len = sizeof(cerid);
396
397                         if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)) {
398                                 struct odhcpd_ipaddr *addrs;
399                                 ssize_t len = netlink_get_interface_addrs(0, true, &addrs);
400
401                                 for (ssize_t i = 0; i < len; ++i)
402                                         if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)
403                                                         || memcmp(&addrs[i].addr, &cerid.addr, sizeof(cerid.addr)) < 0)
404                                                 cerid.addr = addrs[i].addr.in6;
405
406                                 free(addrs);
407                         }
408 #endif
409                 } else if (otype == DHCPV6_OPT_RAPID_COMMIT && hdr->msg_type == DHCPV6_MSG_SOLICIT) {
410                         iov[IOV_RAPID_COMMIT].iov_len = sizeof(rapid_commit);
411                         o_rapid_commit = true;
412                 }
413         }
414
415         if (!IN6_IS_ADDR_MULTICAST((struct in6_addr *)dest_addr) && iov[IOV_NESTED].iov_len == 0 &&
416             (hdr->msg_type == DHCPV6_MSG_REQUEST || hdr->msg_type == DHCPV6_MSG_RENEW ||
417              hdr->msg_type == DHCPV6_MSG_RELEASE || hdr->msg_type == DHCPV6_MSG_DECLINE)) {
418                 iov[IOV_STAT].iov_base = &stat;
419                 iov[IOV_STAT].iov_len = sizeof(stat);
420
421                 for (ssize_t i = IOV_STAT + 1; i < IOV_TOTAL; ++i)
422                         iov[i].iov_len = 0;
423
424                 odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
425                 return;
426         }
427
428         if (hdr->msg_type == DHCPV6_MSG_SOLICIT && !o_rapid_commit) {
429                 dest.msg_type = DHCPV6_MSG_ADVERTISE;
430         } else if (hdr->msg_type == DHCPV6_MSG_INFORMATION_REQUEST) {
431                 iov[IOV_REFRESH].iov_base = &refresh;
432                 iov[IOV_REFRESH].iov_len = sizeof(refresh);
433
434                 /* Return inf max rt option in reply to information request */
435                 maxrt.type = htons(DHCPV6_OPT_INF_MAX_RT);
436         }
437
438         if (hdr->msg_type != DHCPV6_MSG_INFORMATION_REQUEST) {
439                 ssize_t ialen = dhcpv6_ia_handle_IAs(pdbuf, sizeof(pdbuf), iface, addr, (const void *)hdr, opts_end);
440
441                 iov[IOV_PDBUF].iov_len = ialen;
442                 if (ialen < 0 ||
443                     (ialen == 0 && (hdr->msg_type == DHCPV6_MSG_REBIND || hdr->msg_type == DHCPV6_MSG_CONFIRM)))
444                         return;
445         }
446
447         if (iov[IOV_NESTED].iov_len > 0) /* Update length */
448                 update_nested_message(data, len, iov[IOV_DEST].iov_len + iov[IOV_MAXRT].iov_len +
449                                       iov[IOV_RAPID_COMMIT].iov_len + iov[IOV_DNS].iov_len +
450                                       iov[IOV_DNS_ADDR].iov_len + iov[IOV_SEARCH].iov_len +
451                                       iov[IOV_SEARCH_DOMAIN].iov_len + iov[IOV_PDBUF].iov_len +
452                                       iov[IOV_CERID].iov_len + iov[IOV_DHCPV6_RAW].iov_len -
453                                       (4 + opts_end - opts));
454
455         syslog(LOG_DEBUG, "Sending a DHCPv6-%s on %s", iov[IOV_NESTED].iov_len ? "relay-reply" : "reply", iface->name);
456
457         odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
458 }
459
460
461 /* Central DHCPv6-relay handler */
462 static void handle_dhcpv6(void *addr, void *data, size_t len,
463                 struct interface *iface, void *dest_addr)
464 {
465         if (iface->dhcpv6 == MODE_SERVER) {
466                 handle_client_request(addr, data, len, iface, dest_addr);
467         } else if (iface->dhcpv6 == MODE_RELAY) {
468                 if (iface->master)
469                         relay_server_response(data, len);
470                 else
471                         relay_client_request(addr, data, len, iface);
472         }
473 }
474
475
476 /* Relay server response (regular relay server handling) */
477 static void relay_server_response(uint8_t *data, size_t len)
478 {
479         /* Information we need to gather */
480         uint8_t *payload_data = NULL;
481         size_t payload_len = 0;
482         int32_t ifaceidx = 0;
483         struct sockaddr_in6 target = {AF_INET6, htons(DHCPV6_CLIENT_PORT),
484                 0, IN6ADDR_ANY_INIT, 0};
485         int otype, olen;
486         uint8_t *odata, *end = data + len;
487         /* Relay DHCPv6 reply from server to client */
488         struct dhcpv6_relay_header *h = (void*)data;
489
490         syslog(LOG_DEBUG, "Got a DHCPv6-relay-reply");
491
492         if (len < sizeof(*h) || h->msg_type != DHCPV6_MSG_RELAY_REPL)
493                 return;
494
495         memcpy(&target.sin6_addr, &h->peer_address, sizeof(struct in6_addr));
496
497         /* Go through options and find what we need */
498         dhcpv6_for_each_option(h->options, end, otype, olen, odata) {
499                 if (otype == DHCPV6_OPT_INTERFACE_ID
500                                 && olen == sizeof(ifaceidx)) {
501                         memcpy(&ifaceidx, odata, sizeof(ifaceidx));
502                 } else if (otype == DHCPV6_OPT_RELAY_MSG) {
503                         payload_data = odata;
504                         payload_len = olen;
505                 }
506         }
507
508         /* Invalid interface-id or basic payload */
509         struct interface *iface = odhcpd_get_interface_by_index(ifaceidx);
510         if (!iface || iface->master || !payload_data || payload_len < 4)
511                 return;
512
513         bool is_authenticated = false;
514         struct in6_addr *dns_ptr = NULL;
515         size_t dns_count = 0;
516
517         /* If the payload is relay-reply we have to send to the server port */
518         if (payload_data[0] == DHCPV6_MSG_RELAY_REPL) {
519                 target.sin6_port = htons(DHCPV6_SERVER_PORT);
520         } else { /* Go through the payload data */
521                 struct dhcpv6_client_header *h = (void*)payload_data;
522                 end = payload_data + payload_len;
523
524                 dhcpv6_for_each_option(&h[1], end, otype, olen, odata) {
525                         if (otype == DHCPV6_OPT_DNS_SERVERS && olen >= 16) {
526                                 dns_ptr = (struct in6_addr*)odata;
527                                 dns_count = olen / 16;
528                         } else if (otype == DHCPV6_OPT_AUTH) {
529                                 is_authenticated = true;
530                         }
531                 }
532         }
533
534         /* Rewrite DNS servers if requested */
535         if (iface->always_rewrite_dns && dns_ptr && dns_count > 0) {
536                 if (is_authenticated)
537                         return; /* Impossible to rewrite */
538
539                 const struct in6_addr *rewrite = iface->dns;
540                 struct in6_addr addr;
541                 size_t rewrite_cnt = iface->dns_cnt;
542
543                 if (rewrite_cnt == 0) {
544                         if (odhcpd_get_interface_dns_addr(iface, &addr))
545                                 return; /* Unable to get interface address */
546
547                         rewrite = &addr;
548                         rewrite_cnt = 1;
549                 }
550
551                 /* Copy over any other addresses */
552                 for (size_t i = 0; i < dns_count; ++i) {
553                         size_t j = (i < rewrite_cnt) ? i : rewrite_cnt - 1;
554                         memcpy(&dns_ptr[i], &rewrite[j], sizeof(*rewrite));
555                 }
556         }
557
558         struct iovec iov = {payload_data, payload_len};
559
560         syslog(LOG_DEBUG, "Sending a DHCPv6-reply on %s", iface->name);
561
562         odhcpd_send(iface->dhcpv6_event.uloop.fd, &target, &iov, 1, iface);
563 }
564
565 static struct odhcpd_ipaddr *relay_link_address(struct interface *iface)
566 {
567         struct odhcpd_ipaddr *addr = NULL;
568         time_t now = odhcpd_time();
569
570         for (size_t i = 0; i < iface->addr6_len; i++) {
571                 if (iface->addr6[i].valid <= (uint32_t)now)
572                         continue;
573
574                 if (iface->addr6[i].preferred > (uint32_t)now) {
575                         addr = &iface->addr6[i];
576                         break;
577                 }
578
579                 if (!addr || (iface->addr6[i].valid > addr->valid))
580                         addr = &iface->addr6[i];
581         }
582
583         return addr;
584 }
585
586 /* Relay client request (regular DHCPv6-relay) */
587 static void relay_client_request(struct sockaddr_in6 *source,
588                 const void *data, size_t len, struct interface *iface)
589 {
590         const struct dhcpv6_relay_header *h = data;
591         /* Construct our forwarding envelope */
592         struct dhcpv6_relay_forward_envelope hdr = {
593                 .msg_type = DHCPV6_MSG_RELAY_FORW,
594                 .hop_count = 0,
595                 .interface_id_type = htons(DHCPV6_OPT_INTERFACE_ID),
596                 .interface_id_len = htons(sizeof(uint32_t)),
597                 .relay_message_type = htons(DHCPV6_OPT_RELAY_MSG),
598                 .relay_message_len = htons(len),
599         };
600         struct iovec iov[2] = {{&hdr, sizeof(hdr)}, {(void *)data, len}};
601         struct interface *c;
602         struct odhcpd_ipaddr *ip;
603         struct sockaddr_in6 s;
604
605         if (h->msg_type == DHCPV6_MSG_RELAY_REPL ||
606             h->msg_type == DHCPV6_MSG_RECONFIGURE ||
607             h->msg_type == DHCPV6_MSG_REPLY ||
608             h->msg_type == DHCPV6_MSG_ADVERTISE)
609                 return; /* Invalid message types for client */
610
611         syslog(LOG_DEBUG, "Got a DHCPv6-request on %s", iface->name);
612
613         if (h->msg_type == DHCPV6_MSG_RELAY_FORW) { /* handle relay-forward */
614                 if (h->hop_count >= DHCPV6_HOP_COUNT_LIMIT)
615                         return; /* Invalid hop count */
616
617                 hdr.hop_count = h->hop_count + 1;
618         }
619
620         /* use memcpy here as the destination fields are unaligned */
621         memcpy(&hdr.peer_address, &source->sin6_addr, sizeof(struct in6_addr));
622         memcpy(&hdr.interface_id_data, &iface->ifindex, sizeof(iface->ifindex));
623
624         /* Detect public IP of slave interface to use as link-address */
625         ip = relay_link_address(iface);
626         if (ip)
627                 memcpy(&hdr.link_address, &ip->addr.in6, sizeof(hdr.link_address));
628
629         memset(&s, 0, sizeof(s));
630         s.sin6_family = AF_INET6;
631         s.sin6_port = htons(DHCPV6_SERVER_PORT);
632         inet_pton(AF_INET6, ALL_DHCPV6_SERVERS, &s.sin6_addr);
633
634         avl_for_each_element(&interfaces, c, avl) {
635                 if (!c->master || c->dhcpv6 != MODE_RELAY)
636                         continue;
637
638                 if (!ip) {
639                         /* No suitable address! Is the slave not configured yet?
640                          * Detect public IP of master interface and use it instead
641                          * This is WRONG and probably violates the RFC. However
642                          * otherwise we have a hen and egg problem because the
643                          * slave-interface cannot be auto-configured. */
644                         ip = relay_link_address(c);
645                         if (!ip)
646                                 continue; /* Could not obtain a suitable address */
647
648                         memcpy(&hdr.link_address, &ip->addr.in6, sizeof(hdr.link_address));
649                         ip = NULL;
650                 }
651
652                 syslog(LOG_DEBUG, "Sending a DHCPv6-relay-forward on %s", c->name);
653
654                 odhcpd_send(c->dhcpv6_event.uloop.fd, &s, iov, 2, c);
655         }
656 }