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