odhcpd: fix extra compiler warning
[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, %zu > %zu", 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)) dhcpv4o6_server {
401 uint16_t type;
402 uint16_t len;
403 struct in6_addr addr;
404 } dhcpv4o6_server = {htons(DHCPV6_OPT_4O6_SERVER), htons(sizeof(struct in6_addr)),
405 IN6ADDR_ANY_INIT};
406
407 struct dhcpv6_cer_id cerid = {
408 #ifdef EXT_CER_ID
409 .type = htons(EXT_CER_ID),
410 #endif
411 .len = htons(36),
412 .addr = iface->dhcpv6_pd_cer,
413 };
414
415
416 uint8_t pdbuf[512];
417 struct iovec iov[IOV_TOTAL] = {
418 [IOV_NESTED] = {NULL, 0},
419 [IOV_DEST] = {&dest, (uint8_t*)&dest.clientid_type - (uint8_t*)&dest},
420 [IOV_MAXRT] = {&maxrt, sizeof(maxrt)},
421 [IOV_RAPID_COMMIT] = {&rapid_commit, 0},
422 [IOV_DNS] = {&dns, (dns_cnt) ? sizeof(dns) : 0},
423 [IOV_DNS_ADDR] = {dns_addr_ptr, dns_cnt * sizeof(*dns_addr_ptr)},
424 [IOV_SEARCH] = {&search, (search_len) ? sizeof(search) : 0},
425 [IOV_SEARCH_DOMAIN] = {search_domain, search_len},
426 [IOV_PDBUF] = {pdbuf, 0},
427 [IOV_CERID] = {&cerid, 0},
428 [IOV_DHCPV6_RAW] = {iface->dhcpv6_raw, iface->dhcpv6_raw_len},
429 [IOV_RELAY_MSG] = {NULL, 0},
430 [IOV_DHCPV4O6_SERVER] = {&dhcpv4o6_server, 0},
431 };
432
433 if (hdr->msg_type == DHCPV6_MSG_RELAY_FORW)
434 handle_nested_message(data, len, &hdr, &opts, &opts_end, iov);
435
436 switch (hdr->msg_type) {
437 case DHCPV6_MSG_SOLICIT:
438 case DHCPV6_MSG_REQUEST:
439 case DHCPV6_MSG_CONFIRM:
440 case DHCPV6_MSG_RENEW:
441 case DHCPV6_MSG_REBIND:
442 case DHCPV6_MSG_RELEASE:
443 case DHCPV6_MSG_DECLINE:
444 case DHCPV6_MSG_INFORMATION_REQUEST:
445 case DHCPV6_MSG_RELAY_FORW:
446 #ifdef DHCPV4_SUPPORT
447 case DHCPV6_MSG_DHCPV4_QUERY:
448 #endif
449 break; /* Valid message types for clients */
450 case DHCPV6_MSG_ADVERTISE:
451 case DHCPV6_MSG_REPLY:
452 case DHCPV6_MSG_RECONFIGURE:
453 case DHCPV6_MSG_RELAY_REPL:
454 case DHCPV6_MSG_DHCPV4_RESPONSE:
455 #ifndef DHCPV4_SUPPORT
456 case DHCPV6_MSG_DHCPV4_QUERY:
457 #endif
458 default:
459 return; /* Invalid message types for clients */
460 }
461
462 if (!IN6_IS_ADDR_MULTICAST((struct in6_addr *)dest_addr) && iov[IOV_NESTED].iov_len == 0 &&
463 (hdr->msg_type == DHCPV6_MSG_SOLICIT || hdr->msg_type == DHCPV6_MSG_CONFIRM ||
464 hdr->msg_type == DHCPV6_MSG_REBIND || hdr->msg_type == DHCPV6_MSG_INFORMATION_REQUEST))
465 return;
466
467 memcpy(dest.tr_id, hdr->transaction_id, sizeof(dest.tr_id));
468
469 /* Go through options and find what we need */
470 uint16_t otype, olen;
471 uint8_t *odata;
472 dhcpv6_for_each_option(opts, opts_end, otype, olen, odata) {
473 if (otype == DHCPV6_OPT_CLIENTID && olen <= 130) {
474 dest.clientid_length = htons(olen);
475 memcpy(dest.clientid_buf, odata, olen);
476 iov[IOV_DEST].iov_len += 4 + olen;
477 } else if (otype == DHCPV6_OPT_SERVERID) {
478 if (olen != ntohs(dest.serverid_length) ||
479 memcmp(odata, &dest.duid_type, olen))
480 return; /* Not for us */
481 } else if (iface->filter_class && otype == DHCPV6_OPT_USER_CLASS) {
482 uint8_t *c = odata, *cend = &odata[olen];
483 for (; &c[2] <= cend && &c[2 + (c[0] << 8) + c[1]] <= cend; c = &c[2 + (c[0] << 8) + c[1]]) {
484 size_t elen = strlen(iface->filter_class);
485 if (((((size_t)c[0]) << 8) | c[1]) == elen && !memcmp(&c[2], iface->filter_class, elen))
486 return; /* Ignore from homenet */
487 }
488 } else if (otype == DHCPV6_OPT_IA_PD) {
489 #ifdef EXT_CER_ID
490 iov[IOV_CERID].iov_len = sizeof(cerid);
491
492 if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)) {
493 struct odhcpd_ipaddr *addrs;
494 ssize_t len = netlink_get_interface_addrs(0, true, &addrs);
495
496 for (ssize_t i = 0; i < len; ++i)
497 if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)
498 || memcmp(&addrs[i].addr, &cerid.addr, sizeof(cerid.addr)) < 0)
499 cerid.addr = addrs[i].addr.in6;
500
501 free(addrs);
502 }
503 #endif
504 } else if (otype == DHCPV6_OPT_RAPID_COMMIT && hdr->msg_type == DHCPV6_MSG_SOLICIT) {
505 iov[IOV_RAPID_COMMIT].iov_len = sizeof(rapid_commit);
506 o_rapid_commit = true;
507 } else if (otype == DHCPV6_OPT_ORO) {
508 for (int i=0; i < olen/2; i++) {
509 uint16_t option = ntohs(((uint16_t *)odata)[i]);
510
511 switch (option) {
512 #ifdef DHCPV4_SUPPORT
513 case DHCPV6_OPT_4O6_SERVER:
514 if (iface->dhcpv4) {
515 /* According to RFC 7341, 7.2. DHCP 4o6 Server Address Option Format:
516 * This option may also carry no IPv6 addresses, which instructs the
517 * client to use the All_DHCP_Relay_Agents_and_Servers multicast address
518 * as the destination address.
519 *
520 * The ISC dhclient logs a missing IPv6 address as an error but seems to
521 * work anyway:
522 * dhcp4-o-dhcp6-server: expecting at least 16 bytes; got 0
523 *
524 * Include the All_DHCP_Relay_Agents_and_Servers multicast address
525 * to make it explicit which address to use. */
526 struct dhcpv4o6_server *server = iov[IOV_DHCPV4O6_SERVER].iov_base;
527
528 inet_pton(AF_INET6, ALL_DHCPV6_RELAYS, &server->addr);
529
530 iov[IOV_DHCPV4O6_SERVER].iov_len = sizeof(dhcpv4o6_server);
531 }
532 break;
533 #endif /* DHCPV4_SUPPORT */
534 default:
535 break;
536 }
537 }
538 }
539 }
540
541 if (!IN6_IS_ADDR_MULTICAST((struct in6_addr *)dest_addr) && iov[IOV_NESTED].iov_len == 0 &&
542 (hdr->msg_type == DHCPV6_MSG_REQUEST || hdr->msg_type == DHCPV6_MSG_RENEW ||
543 hdr->msg_type == DHCPV6_MSG_RELEASE || hdr->msg_type == DHCPV6_MSG_DECLINE)) {
544 iov[IOV_STAT].iov_base = &stat;
545 iov[IOV_STAT].iov_len = sizeof(stat);
546
547 for (ssize_t i = IOV_STAT + 1; i < IOV_TOTAL; ++i)
548 iov[i].iov_len = 0;
549
550 odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
551 return;
552 }
553
554 if (hdr->msg_type == DHCPV6_MSG_SOLICIT && !o_rapid_commit) {
555 dest.msg_type = DHCPV6_MSG_ADVERTISE;
556 } else if (hdr->msg_type == DHCPV6_MSG_INFORMATION_REQUEST) {
557 iov[IOV_REFRESH].iov_base = &refresh;
558 iov[IOV_REFRESH].iov_len = sizeof(refresh);
559
560 /* Return inf max rt option in reply to information request */
561 maxrt.type = htons(DHCPV6_OPT_INF_MAX_RT);
562 }
563
564 #ifdef DHCPV4_SUPPORT
565 if (hdr->msg_type == DHCPV6_MSG_DHCPV4_QUERY) {
566 struct _packed dhcpv4_msg_data {
567 uint16_t type;
568 uint16_t len;
569 uint8_t msg[1];
570 } *msg_opt = (struct dhcpv4_msg_data*)pdbuf;
571 ssize_t msglen;
572
573 memset(pdbuf, 0, sizeof(pdbuf));
574
575 msglen = dhcpv6_4o6_query(msg_opt->msg, sizeof(pdbuf) - sizeof(*msg_opt) + 1,
576 iface, addr, (const void *)hdr, opts_end);
577 if (msglen <= 0) {
578 syslog(LOG_ERR, "4o6: query failed");
579 return;
580 }
581
582 msg_opt->type = htons(DHCPV6_OPT_DHCPV4_MSG);
583 msg_opt->len = htons(msglen);
584 iov[IOV_PDBUF].iov_len = sizeof(*msg_opt) - 1 + msglen;
585 dest.msg_type = DHCPV6_MSG_DHCPV4_RESPONSE;
586 } else
587 #endif /* DHCPV4_SUPPORT */
588 if (hdr->msg_type != DHCPV6_MSG_INFORMATION_REQUEST) {
589 ssize_t ialen = dhcpv6_ia_handle_IAs(pdbuf, sizeof(pdbuf), iface, addr, (const void *)hdr, opts_end);
590
591 iov[IOV_PDBUF].iov_len = ialen;
592 if (ialen < 0 ||
593 (ialen == 0 && (hdr->msg_type == DHCPV6_MSG_REBIND || hdr->msg_type == DHCPV6_MSG_CONFIRM)))
594 return;
595 }
596
597 if (iov[IOV_NESTED].iov_len > 0) /* Update length */
598 update_nested_message(data, len, iov[IOV_DEST].iov_len + iov[IOV_MAXRT].iov_len +
599 iov[IOV_RAPID_COMMIT].iov_len + iov[IOV_DNS].iov_len +
600 iov[IOV_DNS_ADDR].iov_len + iov[IOV_SEARCH].iov_len +
601 iov[IOV_SEARCH_DOMAIN].iov_len + iov[IOV_PDBUF].iov_len +
602 iov[IOV_DHCPV4O6_SERVER].iov_len +
603 iov[IOV_CERID].iov_len + iov[IOV_DHCPV6_RAW].iov_len -
604 (4 + opts_end - opts));
605
606 syslog(LOG_DEBUG, "Sending a DHCPv6-%s on %s", iov[IOV_NESTED].iov_len ? "relay-reply" : "reply", iface->name);
607
608 odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
609 }
610
611
612 /* Central DHCPv6-relay handler */
613 static void handle_dhcpv6(void *addr, void *data, size_t len,
614 struct interface *iface, void *dest_addr)
615 {
616 if (iface->dhcpv6 == MODE_SERVER) {
617 handle_client_request(addr, data, len, iface, dest_addr);
618 } else if (iface->dhcpv6 == MODE_RELAY) {
619 if (iface->master)
620 relay_server_response(data, len);
621 else
622 relay_client_request(addr, data, len, iface);
623 }
624 }
625
626
627 /* Relay server response (regular relay server handling) */
628 static void relay_server_response(uint8_t *data, size_t len)
629 {
630 /* Information we need to gather */
631 uint8_t *payload_data = NULL;
632 size_t payload_len = 0;
633 int32_t ifaceidx = 0;
634 struct sockaddr_in6 target = {AF_INET6, htons(DHCPV6_CLIENT_PORT),
635 0, IN6ADDR_ANY_INIT, 0};
636 int otype, olen;
637 uint8_t *odata, *end = data + len;
638 /* Relay DHCPv6 reply from server to client */
639 struct dhcpv6_relay_header *h = (void*)data;
640
641 syslog(LOG_DEBUG, "Got a DHCPv6-relay-reply");
642
643 if (len < sizeof(*h) || h->msg_type != DHCPV6_MSG_RELAY_REPL)
644 return;
645
646 memcpy(&target.sin6_addr, &h->peer_address, sizeof(struct in6_addr));
647
648 /* Go through options and find what we need */
649 dhcpv6_for_each_option(h->options, end, otype, olen, odata) {
650 if (otype == DHCPV6_OPT_INTERFACE_ID
651 && olen == sizeof(ifaceidx)) {
652 memcpy(&ifaceidx, odata, sizeof(ifaceidx));
653 } else if (otype == DHCPV6_OPT_RELAY_MSG) {
654 payload_data = odata;
655 payload_len = olen;
656 }
657 }
658
659 /* Invalid interface-id or basic payload */
660 struct interface *iface = odhcpd_get_interface_by_index(ifaceidx);
661 if (!iface || iface->master || !payload_data || payload_len < 4)
662 return;
663
664 bool is_authenticated = false;
665 struct in6_addr *dns_ptr = NULL;
666 size_t dns_count = 0;
667
668 /* If the payload is relay-reply we have to send to the server port */
669 if (payload_data[0] == DHCPV6_MSG_RELAY_REPL) {
670 target.sin6_port = htons(DHCPV6_SERVER_PORT);
671 } else { /* Go through the payload data */
672 struct dhcpv6_client_header *h = (void*)payload_data;
673 end = payload_data + payload_len;
674
675 dhcpv6_for_each_option(&h[1], end, otype, olen, odata) {
676 if (otype == DHCPV6_OPT_DNS_SERVERS && olen >= 16) {
677 dns_ptr = (struct in6_addr*)odata;
678 dns_count = olen / 16;
679 } else if (otype == DHCPV6_OPT_AUTH) {
680 is_authenticated = true;
681 }
682 }
683 }
684
685 /* Rewrite DNS servers if requested */
686 if (iface->always_rewrite_dns && dns_ptr && dns_count > 0) {
687 if (is_authenticated)
688 return; /* Impossible to rewrite */
689
690 const struct in6_addr *rewrite = iface->dns;
691 struct in6_addr addr;
692 size_t rewrite_cnt = iface->dns_cnt;
693
694 if (rewrite_cnt == 0) {
695 if (odhcpd_get_interface_dns_addr(iface, &addr))
696 return; /* Unable to get interface address */
697
698 rewrite = &addr;
699 rewrite_cnt = 1;
700 }
701
702 /* Copy over any other addresses */
703 for (size_t i = 0; i < dns_count; ++i) {
704 size_t j = (i < rewrite_cnt) ? i : rewrite_cnt - 1;
705 memcpy(&dns_ptr[i], &rewrite[j], sizeof(*rewrite));
706 }
707 }
708
709 struct iovec iov = {payload_data, payload_len};
710
711 syslog(LOG_DEBUG, "Sending a DHCPv6-reply on %s", iface->name);
712
713 odhcpd_send(iface->dhcpv6_event.uloop.fd, &target, &iov, 1, iface);
714 }
715
716 static struct odhcpd_ipaddr *relay_link_address(struct interface *iface)
717 {
718 struct odhcpd_ipaddr *addr = NULL;
719 time_t now = odhcpd_time();
720
721 for (size_t i = 0; i < iface->addr6_len; i++) {
722 if (iface->addr6[i].valid <= (uint32_t)now)
723 continue;
724
725 if (iface->addr6[i].preferred > (uint32_t)now) {
726 addr = &iface->addr6[i];
727 break;
728 }
729
730 if (!addr || (iface->addr6[i].valid > addr->valid))
731 addr = &iface->addr6[i];
732 }
733
734 return addr;
735 }
736
737 /* Relay client request (regular DHCPv6-relay) */
738 static void relay_client_request(struct sockaddr_in6 *source,
739 const void *data, size_t len, struct interface *iface)
740 {
741 const struct dhcpv6_relay_header *h = data;
742 /* Construct our forwarding envelope */
743 struct dhcpv6_relay_forward_envelope hdr = {
744 .msg_type = DHCPV6_MSG_RELAY_FORW,
745 .hop_count = 0,
746 .interface_id_type = htons(DHCPV6_OPT_INTERFACE_ID),
747 .interface_id_len = htons(sizeof(uint32_t)),
748 .relay_message_type = htons(DHCPV6_OPT_RELAY_MSG),
749 .relay_message_len = htons(len),
750 };
751 struct iovec iov[2] = {{&hdr, sizeof(hdr)}, {(void *)data, len}};
752 struct interface *c;
753 struct odhcpd_ipaddr *ip;
754 struct sockaddr_in6 s;
755
756 if (h->msg_type == DHCPV6_MSG_RELAY_REPL ||
757 h->msg_type == DHCPV6_MSG_RECONFIGURE ||
758 h->msg_type == DHCPV6_MSG_REPLY ||
759 h->msg_type == DHCPV6_MSG_ADVERTISE)
760 return; /* Invalid message types for client */
761
762 syslog(LOG_DEBUG, "Got a DHCPv6-request on %s", iface->name);
763
764 if (h->msg_type == DHCPV6_MSG_RELAY_FORW) { /* handle relay-forward */
765 if (h->hop_count >= DHCPV6_HOP_COUNT_LIMIT)
766 return; /* Invalid hop count */
767
768 hdr.hop_count = h->hop_count + 1;
769 }
770
771 /* use memcpy here as the destination fields are unaligned */
772 memcpy(&hdr.peer_address, &source->sin6_addr, sizeof(struct in6_addr));
773 memcpy(&hdr.interface_id_data, &iface->ifindex, sizeof(iface->ifindex));
774
775 /* Detect public IP of slave interface to use as link-address */
776 ip = relay_link_address(iface);
777 if (ip)
778 memcpy(&hdr.link_address, &ip->addr.in6, sizeof(hdr.link_address));
779
780 memset(&s, 0, sizeof(s));
781 s.sin6_family = AF_INET6;
782 s.sin6_port = htons(DHCPV6_SERVER_PORT);
783 inet_pton(AF_INET6, ALL_DHCPV6_SERVERS, &s.sin6_addr);
784
785 avl_for_each_element(&interfaces, c, avl) {
786 if (!c->master || c->dhcpv6 != MODE_RELAY)
787 continue;
788
789 if (!ip) {
790 /* No suitable address! Is the slave not configured yet?
791 * Detect public IP of master interface and use it instead
792 * This is WRONG and probably violates the RFC. However
793 * otherwise we have a hen and egg problem because the
794 * slave-interface cannot be auto-configured. */
795 ip = relay_link_address(c);
796 if (!ip)
797 continue; /* Could not obtain a suitable address */
798
799 memcpy(&hdr.link_address, &ip->addr.in6, sizeof(hdr.link_address));
800 ip = NULL;
801 }
802
803 syslog(LOG_DEBUG, "Sending a DHCPv6-relay-forward on %s", c->name);
804
805 odhcpd_send(c->dhcpv6_event.uloop.fd, &s, iov, 2, c);
806 }
807 }