]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/dhcp6-option.c
1de4bacd1b434169ccbe59161879bcf61844c4ed
[thirdparty/systemd.git] / src / libsystemd-network / dhcp6-option.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <netinet/in.h>
7
8 #include "sd-dhcp6-client.h"
9 #include "sd-dhcp6-option.h"
10
11 #include "alloc-util.h"
12 #include "dhcp6-internal.h"
13 #include "dhcp6-option.h"
14 #include "dhcp6-protocol.h"
15 #include "dns-def.h"
16 #include "dns-domain.h"
17 #include "escape.h"
18 #include "memory-util.h"
19 #include "network-common.h"
20 #include "ordered-set.h"
21 #include "string-util.h"
22 #include "strv.h"
23 #include "unaligned.h"
24
25 #define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
26 #define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
27 #define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
28
29 bool dhcp6_option_can_request(uint16_t option) {
30 /* See Client ORO field in
31 * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
32
33 switch (option) {
34 case SD_DHCP6_OPTION_CLIENTID:
35 case SD_DHCP6_OPTION_SERVERID:
36 case SD_DHCP6_OPTION_IA_NA:
37 case SD_DHCP6_OPTION_IA_TA:
38 case SD_DHCP6_OPTION_IAADDR:
39 case SD_DHCP6_OPTION_ORO:
40 case SD_DHCP6_OPTION_PREFERENCE:
41 case SD_DHCP6_OPTION_ELAPSED_TIME:
42 case SD_DHCP6_OPTION_RELAY_MSG:
43 case SD_DHCP6_OPTION_AUTH:
44 case SD_DHCP6_OPTION_UNICAST:
45 case SD_DHCP6_OPTION_STATUS_CODE:
46 case SD_DHCP6_OPTION_RAPID_COMMIT:
47 case SD_DHCP6_OPTION_USER_CLASS:
48 case SD_DHCP6_OPTION_VENDOR_CLASS:
49 return false;
50 case SD_DHCP6_OPTION_VENDOR_OPTS:
51 return true;
52 case SD_DHCP6_OPTION_INTERFACE_ID:
53 case SD_DHCP6_OPTION_RECONF_MSG:
54 case SD_DHCP6_OPTION_RECONF_ACCEPT:
55 return false;
56 case SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME:
57 case SD_DHCP6_OPTION_SIP_SERVER_ADDRESS:
58 case SD_DHCP6_OPTION_DNS_SERVER:
59 case SD_DHCP6_OPTION_DOMAIN:
60 return true;
61 case SD_DHCP6_OPTION_IA_PD:
62 case SD_DHCP6_OPTION_IA_PD_PREFIX:
63 return false;
64 case SD_DHCP6_OPTION_NIS_SERVER:
65 case SD_DHCP6_OPTION_NISP_SERVER:
66 case SD_DHCP6_OPTION_NIS_DOMAIN_NAME:
67 case SD_DHCP6_OPTION_NISP_DOMAIN_NAME:
68 case SD_DHCP6_OPTION_SNTP_SERVER:
69 return true;
70 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
71 return false; /* This is automatically set when sending INFORMATION_REQUEST message. */
72 case SD_DHCP6_OPTION_BCMCS_SERVER_D:
73 case SD_DHCP6_OPTION_BCMCS_SERVER_A:
74 case SD_DHCP6_OPTION_GEOCONF_CIVIC:
75 return true;
76 case SD_DHCP6_OPTION_REMOTE_ID:
77 case SD_DHCP6_OPTION_SUBSCRIBER_ID:
78 return false;
79 case SD_DHCP6_OPTION_CLIENT_FQDN:
80 case SD_DHCP6_OPTION_PANA_AGENT:
81 case SD_DHCP6_OPTION_POSIX_TIMEZONE:
82 case SD_DHCP6_OPTION_TZDB_TIMEZONE:
83 return true;
84 case SD_DHCP6_OPTION_ERO:
85 case SD_DHCP6_OPTION_LQ_QUERY:
86 case SD_DHCP6_OPTION_CLIENT_DATA:
87 case SD_DHCP6_OPTION_CLT_TIME:
88 case SD_DHCP6_OPTION_LQ_RELAY_DATA:
89 case SD_DHCP6_OPTION_LQ_CLIENT_LINK:
90 return false;
91 case SD_DHCP6_OPTION_MIP6_HNIDF:
92 case SD_DHCP6_OPTION_MIP6_VDINF:
93 case SD_DHCP6_OPTION_V6_LOST:
94 case SD_DHCP6_OPTION_CAPWAP_AC_V6:
95 return true;
96 case SD_DHCP6_OPTION_RELAY_ID:
97 return false;
98 case SD_DHCP6_OPTION_IPV6_ADDRESS_MOS:
99 case SD_DHCP6_OPTION_IPV6_FQDN_MOS:
100 case SD_DHCP6_OPTION_NTP_SERVER:
101 case SD_DHCP6_OPTION_V6_ACCESS_DOMAIN:
102 case SD_DHCP6_OPTION_SIP_UA_CS_LIST:
103 case SD_DHCP6_OPTION_BOOTFILE_URL:
104 case SD_DHCP6_OPTION_BOOTFILE_PARAM:
105 return true;
106 case SD_DHCP6_OPTION_CLIENT_ARCH_TYPE:
107 return false;
108 case SD_DHCP6_OPTION_NII:
109 case SD_DHCP6_OPTION_GEOLOCATION:
110 case SD_DHCP6_OPTION_AFTR_NAME:
111 case SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME:
112 return true;
113 case SD_DHCP6_OPTION_RSOO:
114 return false;
115 case SD_DHCP6_OPTION_PD_EXCLUDE:
116 return true;
117 case SD_DHCP6_OPTION_VSS:
118 return false;
119 case SD_DHCP6_OPTION_MIP6_IDINF:
120 case SD_DHCP6_OPTION_MIP6_UDINF:
121 case SD_DHCP6_OPTION_MIP6_HNP:
122 case SD_DHCP6_OPTION_MIP6_HAA:
123 case SD_DHCP6_OPTION_MIP6_HAF:
124 case SD_DHCP6_OPTION_RDNSS_SELECTION:
125 case SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME:
126 case SD_DHCP6_OPTION_KRB_REALM_NAME:
127 case SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME:
128 case SD_DHCP6_OPTION_KRB_KDC:
129 return true;
130 case SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR:
131 case SD_DHCP6_OPTION_LINK_ADDRESS:
132 case SD_DHCP6_OPTION_RADIUS:
133 case SD_DHCP6_OPTION_SOL_MAX_RT: /* Automatically set when sending SOLICIT message. */
134 case SD_DHCP6_OPTION_INF_MAX_RT: /* Automatically set when sending INFORMATION_REQUEST message. */
135 return false;
136 case SD_DHCP6_OPTION_ADDRSEL:
137 case SD_DHCP6_OPTION_ADDRSEL_TABLE:
138 case SD_DHCP6_OPTION_V6_PCP_SERVER:
139 return true;
140 case SD_DHCP6_OPTION_DHCPV4_MSG:
141 return false;
142 case SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER:
143 return true;
144 case SD_DHCP6_OPTION_S46_RULE:
145 return false;
146 case SD_DHCP6_OPTION_S46_BR:
147 return true;
148 case SD_DHCP6_OPTION_S46_DMR:
149 case SD_DHCP6_OPTION_S46_V4V6BIND:
150 case SD_DHCP6_OPTION_S46_PORTPARAMS:
151 return false;
152 case SD_DHCP6_OPTION_S46_CONT_MAPE:
153 case SD_DHCP6_OPTION_S46_CONT_MAPT:
154 case SD_DHCP6_OPTION_S46_CONT_LW:
155 case SD_DHCP6_OPTION_4RD:
156 case SD_DHCP6_OPTION_4RD_MAP_RULE:
157 case SD_DHCP6_OPTION_4RD_NON_MAP_RULE:
158 return true;
159 case SD_DHCP6_OPTION_LQ_BASE_TIME:
160 case SD_DHCP6_OPTION_LQ_START_TIME:
161 case SD_DHCP6_OPTION_LQ_END_TIME:
162 return false;
163 case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
164 case SD_DHCP6_OPTION_MPL_PARAMETERS:
165 return true;
166 case SD_DHCP6_OPTION_ANI_ATT:
167 case SD_DHCP6_OPTION_ANI_NETWORK_NAME:
168 case SD_DHCP6_OPTION_ANI_AP_NAME:
169 case SD_DHCP6_OPTION_ANI_AP_BSSID:
170 case SD_DHCP6_OPTION_ANI_OPERATOR_ID:
171 case SD_DHCP6_OPTION_ANI_OPERATOR_REALM:
172 return false;
173 case SD_DHCP6_OPTION_S46_PRIORITY:
174 return true;
175 case SD_DHCP6_OPTION_MUD_URL_V6:
176 return false;
177 case SD_DHCP6_OPTION_V6_PREFIX64:
178 return true;
179 case SD_DHCP6_OPTION_F_BINDING_STATUS:
180 case SD_DHCP6_OPTION_F_CONNECT_FLAGS:
181 case SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO:
182 case SD_DHCP6_OPTION_F_DNS_HOST_NAME:
183 case SD_DHCP6_OPTION_F_DNS_ZONE_NAME:
184 case SD_DHCP6_OPTION_F_DNS_FLAGS:
185 case SD_DHCP6_OPTION_F_EXPIRATION_TIME:
186 case SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD:
187 case SD_DHCP6_OPTION_F_MCLT:
188 case SD_DHCP6_OPTION_F_PARTNER_LIFETIME:
189 case SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT:
190 case SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME:
191 case SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME:
192 case SD_DHCP6_OPTION_F_PROTOCOL_VERSION:
193 case SD_DHCP6_OPTION_F_KEEPALIVE_TIME:
194 case SD_DHCP6_OPTION_F_RECONFIGURE_DATA:
195 case SD_DHCP6_OPTION_F_RELATIONSHIP_NAME:
196 case SD_DHCP6_OPTION_F_SERVER_FLAGS:
197 case SD_DHCP6_OPTION_F_SERVER_STATE:
198 case SD_DHCP6_OPTION_F_START_TIME_OF_STATE:
199 case SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME:
200 case SD_DHCP6_OPTION_RELAY_PORT:
201 return false;
202 case SD_DHCP6_OPTION_V6_SZTP_REDIRECT:
203 case SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX:
204 return true;
205 case SD_DHCP6_OPTION_IA_LL:
206 case SD_DHCP6_OPTION_LLADDR:
207 case SD_DHCP6_OPTION_SLAP_QUAD:
208 return false;
209 case SD_DHCP6_OPTION_V6_DOTS_RI:
210 case SD_DHCP6_OPTION_V6_DOTS_ADDRESS:
211 case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF:
212 case SD_DHCP6_OPTION_V6_DNR:
213 return true;
214 default:
215 return false;
216 }
217 }
218
219 static int option_append_hdr(uint8_t **buf, size_t *offset, uint16_t optcode, size_t optlen) {
220 assert(buf);
221 assert(*buf);
222 assert(offset);
223
224 if (optlen > 0xffff)
225 return -ENOBUFS;
226
227 if (optlen + offsetof(DHCP6Option, data) > SIZE_MAX - *offset)
228 return -ENOBUFS;
229
230 if (!GREEDY_REALLOC(*buf, *offset + optlen + offsetof(DHCP6Option, data)))
231 return -ENOMEM;
232
233 unaligned_write_be16(*buf + *offset + offsetof(DHCP6Option, code), optcode);
234 unaligned_write_be16(*buf + *offset + offsetof(DHCP6Option, len), optlen);
235
236 *offset += offsetof(DHCP6Option, data);
237 return 0;
238 }
239
240 int dhcp6_option_append(
241 uint8_t **buf,
242 size_t *offset,
243 uint16_t code,
244 size_t optlen,
245 const void *optval) {
246
247 int r;
248
249 assert(optval || optlen == 0);
250
251 r = option_append_hdr(buf, offset, code, optlen);
252 if (r < 0)
253 return r;
254
255 memcpy_safe(*buf + *offset, optval, optlen);
256 *offset += optlen;
257
258 return 0;
259 }
260
261 int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *offset, OrderedSet *vendor_options) {
262 sd_dhcp6_option *options;
263 int r;
264
265 assert(buf);
266 assert(*buf);
267 assert(offset);
268
269 ORDERED_SET_FOREACH(options, vendor_options) {
270 _cleanup_free_ uint8_t *p = NULL;
271 size_t total;
272
273 total = 4 + 2 + 2 + options->length;
274
275 p = malloc(total);
276 if (!p)
277 return -ENOMEM;
278
279 unaligned_write_be32(p, options->enterprise_identifier);
280 unaligned_write_be16(p + 4, options->option);
281 unaligned_write_be16(p + 6, options->length);
282 memcpy(p + 8, options->data, options->length);
283
284 r = dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_VENDOR_OPTS, total, p);
285 if (r < 0)
286 return r;
287 }
288
289 return 0;
290 }
291
292 static int option_append_ia_address(uint8_t **buf, size_t *offset, const struct iaaddr *address) {
293 assert(buf);
294 assert(*buf);
295 assert(offset);
296 assert(address);
297
298 /* Do not append T1 and T2. */
299 const struct iaaddr a = {
300 .address = address->address,
301 };
302
303 return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr), &a);
304 }
305
306 static int option_append_pd_prefix(uint8_t **buf, size_t *offset, const struct iapdprefix *prefix) {
307 assert(buf);
308 assert(*buf);
309 assert(offset);
310 assert(prefix);
311
312 if (prefix->prefixlen == 0)
313 return -EINVAL;
314
315 /* Do not append T1 and T2. */
316 const struct iapdprefix p = {
317 .prefixlen = prefix->prefixlen,
318 .address = prefix->address,
319 };
320
321 return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix), &p);
322 }
323
324 int dhcp6_option_append_ia(uint8_t **buf, size_t *offset, const DHCP6IA *ia) {
325 _cleanup_free_ uint8_t *data = NULL;
326 struct ia_header header;
327 size_t len;
328 int r;
329
330 assert(buf);
331 assert(*buf);
332 assert(offset);
333 assert(ia);
334
335 /* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
336
337 switch (ia->type) {
338 case SD_DHCP6_OPTION_IA_NA:
339 case SD_DHCP6_OPTION_IA_PD:
340 len = sizeof(struct ia_header);
341 header = (struct ia_header) {
342 .id = ia->header.id,
343 };
344 break;
345
346 case SD_DHCP6_OPTION_IA_TA:
347 len = sizeof(header.id); /* IA_TA does not have lifetime. */
348 header = (struct ia_header) {
349 .id = ia->header.id,
350 };
351 break;
352
353 default:
354 assert_not_reached();
355 }
356
357 if (!GREEDY_REALLOC(data, len))
358 return -ENOMEM;
359
360 memcpy(data, &header, len);
361
362 LIST_FOREACH(addresses, addr, ia->addresses) {
363 if (ia->type == SD_DHCP6_OPTION_IA_PD)
364 r = option_append_pd_prefix(&data, &len, &addr->iapdprefix);
365 else
366 r = option_append_ia_address(&data, &len, &addr->iaaddr);
367 if (r < 0)
368 return r;
369 }
370
371 return dhcp6_option_append(buf, offset, ia->type, len, data);
372 }
373
374 int dhcp6_option_append_fqdn(uint8_t **buf, size_t *offset, const char *fqdn) {
375 uint8_t buffer[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX];
376 int r;
377
378 assert(buf);
379 assert(*buf);
380 assert(offset);
381
382 if (isempty(fqdn))
383 return 0;
384
385 buffer[0] = DHCP6_FQDN_FLAG_S; /* Request server to perform AAAA RR DNS updates */
386
387 /* Store domain name after flags field */
388 r = dns_name_to_wire_format(fqdn, buffer + 1, sizeof(buffer) - 1, false);
389 if (r <= 0)
390 return r;
391
392 /*
393 * According to RFC 4704, chapter 4.2 only add terminating zero-length
394 * label in case a FQDN is provided. Since dns_name_to_wire_format
395 * always adds terminating zero-length label remove if only a hostname
396 * is provided.
397 */
398 if (dns_name_is_single_label(fqdn))
399 r--;
400
401 return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer);
402 }
403
404 int dhcp6_option_append_user_class(uint8_t **buf, size_t *offset, char * const *user_class) {
405 _cleanup_free_ uint8_t *p = NULL;
406 size_t n = 0;
407
408 assert(buf);
409 assert(*buf);
410 assert(offset);
411
412 if (strv_isempty(user_class))
413 return 0;
414
415 STRV_FOREACH(s, user_class) {
416 size_t len = strlen(*s);
417
418 if (len > UINT16_MAX || len == 0)
419 return -EINVAL;
420
421 if (!GREEDY_REALLOC(p, n + len + 2))
422 return -ENOMEM;
423
424 unaligned_write_be16(p + n, len);
425 memcpy(p + n + 2, *s, len);
426 n += len + 2;
427 }
428
429 return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_USER_CLASS, n, p);
430 }
431
432 int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *offset, char * const *vendor_class) {
433 _cleanup_free_ uint8_t *p = NULL;
434 size_t n = 0;
435
436 assert(buf);
437 assert(*buf);
438 assert(offset);
439
440 if (strv_isempty(vendor_class))
441 return 0;
442
443 if (!GREEDY_REALLOC(p, sizeof(be32_t)))
444 return -ENOMEM;
445
446 /* Enterprise Identifier */
447 unaligned_write_be32(p, SYSTEMD_PEN);
448 n += sizeof(be32_t);
449
450 STRV_FOREACH(s, vendor_class) {
451 size_t len = strlen(*s);
452
453 if (len > UINT16_MAX || len == 0)
454 return -EINVAL;
455
456 if (!GREEDY_REALLOC(p, n + len + 2))
457 return -ENOMEM;
458
459 unaligned_write_be16(p + n, len);
460 memcpy(p + n + 2, *s, len);
461 n += len + 2;
462 }
463
464 return dhcp6_option_append(buf, offset, SD_DHCP6_OPTION_VENDOR_CLASS, n, p);
465 }
466
467 int dhcp6_option_parse(
468 const uint8_t *buf,
469 size_t buflen,
470 size_t *offset,
471 uint16_t *ret_option_code,
472 size_t *ret_option_data_len,
473 const uint8_t **ret_option_data) {
474
475 size_t len;
476
477 assert(buf);
478 assert(offset);
479 assert(ret_option_code);
480 assert(ret_option_data_len);
481 assert(ret_option_data);
482
483 if (buflen < offsetof(DHCP6Option, data))
484 return -EBADMSG;
485
486 if (*offset > buflen - offsetof(DHCP6Option, data))
487 return -EBADMSG;
488
489 len = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, len));
490
491 if (len > buflen - offsetof(DHCP6Option, data) - *offset)
492 return -EBADMSG;
493
494 *ret_option_code = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, code));
495 *ret_option_data_len = len;
496 *ret_option_data = len == 0 ? NULL : buf + *offset + offsetof(DHCP6Option, data);
497 *offset += offsetof(DHCP6Option, data) + len;
498
499 return 0;
500 }
501
502 int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) {
503 DHCP6Status status;
504
505 assert(data || data_len == 0);
506
507 if (data_len < sizeof(uint16_t))
508 return -EBADMSG;
509
510 status = unaligned_read_be16(data);
511
512 if (ret_status_message) {
513 _cleanup_free_ char *msg = NULL;
514 const char *s;
515
516 /* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415.
517 * Let's escape unsafe characters for safety. */
518 msg = cescape_length((const char*) (data + sizeof(uint16_t)), data_len - sizeof(uint16_t));
519 if (!msg)
520 return -ENOMEM;
521
522 s = dhcp6_message_status_to_string(status);
523 if (s && !strextend_with_separator(&msg, ": ", s))
524 return -ENOMEM;
525
526 *ret_status_message = TAKE_PTR(msg);
527 }
528
529 return status;
530 }
531
532 /* parse a string from dhcp option field. *ret must be initialized */
533 int dhcp6_option_parse_string(const uint8_t *data, size_t data_len, char **ret) {
534 _cleanup_free_ char *string = NULL;
535 int r;
536
537 assert(data || data_len == 0);
538 assert(ret);
539
540 if (data_len <= 0) {
541 *ret = mfree(*ret);
542 return 0;
543 }
544
545 r = make_cstring((const char *) data, data_len, MAKE_CSTRING_REFUSE_TRAILING_NUL, &string);
546 if (r < 0)
547 return r;
548
549 return free_and_replace(*ret, string);
550 }
551
552 static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
553 int r;
554
555 assert(buf || buflen == 0);
556
557 for (size_t offset = 0; offset < buflen;) {
558 const uint8_t *data;
559 size_t data_len;
560 uint16_t code;
561
562 r = dhcp6_option_parse(buf, buflen, &offset, &code, &data_len, &data);
563 if (r < 0)
564 return r;
565
566 switch (code) {
567 case SD_DHCP6_OPTION_STATUS_CODE: {
568 _cleanup_free_ char *msg = NULL;
569
570 r = dhcp6_option_parse_status(data, data_len, &msg);
571 if (r == -ENOMEM)
572 return r;
573 if (r > 0)
574 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
575 "Received an IA address or PD prefix option with non-zero status%s%s",
576 isempty(msg) ? "." : ": ", strempty(msg));
577 if (r < 0)
578 /* Let's log but ignore the invalid status option. */
579 log_dhcp6_client_errno(client, r,
580 "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
581 break;
582 }
583 default:
584 log_dhcp6_client(client, "Received an unknown sub option %u in IA address or PD prefix, ignoring.", code);
585 }
586 }
587
588 return 0;
589 }
590
591 static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
592 _cleanup_free_ DHCP6Address *a = NULL;
593 usec_t lt_valid, lt_pref;
594 int r;
595
596 assert(ia);
597 assert(data || len == 0);
598
599 if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA))
600 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
601 "Received an IA address sub-option in an invalid option, ignoring.");
602
603 if (len < sizeof(struct iaaddr))
604 return -EBADMSG;
605
606 a = new(DHCP6Address, 1);
607 if (!a)
608 return -ENOMEM;
609
610 memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
611
612 lt_valid = be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true);
613 lt_pref = be32_sec_to_usec(a->iaaddr.lifetime_preferred, /* max_as_infinity = */ true);
614
615 if (lt_valid == 0)
616 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
617 "Received an IA address with zero valid lifetime, ignoring.");
618 if (lt_pref > lt_valid)
619 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
620 "Received an IA address with preferred lifetime %s "
621 "larger than valid lifetime %s, ignoring.",
622 FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
623 FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
624
625 if (len > sizeof(struct iaaddr)) {
626 r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr));
627 if (r < 0)
628 return r;
629 }
630
631 LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a));
632 return 0;
633 }
634
635 static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) {
636 _cleanup_free_ DHCP6Address *a = NULL;
637 usec_t lt_valid, lt_pref;
638 int r;
639
640 assert(ia);
641 assert(data || len == 0);
642
643 if (ia->type != SD_DHCP6_OPTION_IA_PD)
644 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
645 "Received an PD prefix sub-option in an invalid option, ignoring");
646
647 if (len < sizeof(struct iapdprefix))
648 return -EBADMSG;
649
650 a = new(DHCP6Address, 1);
651 if (!a)
652 return -ENOMEM;
653
654 memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
655
656 lt_valid = be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true);
657 lt_pref = be32_sec_to_usec(a->iapdprefix.lifetime_preferred, /* max_as_infinity = */ true);
658
659 if (lt_valid == 0)
660 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
661 "Received a PD prefix with zero valid lifetime, ignoring.");
662 if (lt_pref > lt_valid)
663 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
664 "Received a PD prefix with preferred lifetime %s "
665 "larger than valid lifetime %s, ignoring.",
666 FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
667 FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
668
669 if (len > sizeof(struct iapdprefix)) {
670 r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
671 if (r < 0)
672 return r;
673 }
674
675 LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a));
676 return 0;
677 }
678
679 int dhcp6_option_parse_ia(
680 sd_dhcp6_client *client,
681 be32_t iaid,
682 uint16_t option_code,
683 size_t option_data_len,
684 const uint8_t *option_data,
685 DHCP6IA **ret) {
686
687 _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
688 usec_t lt_t1, lt_t2;
689 size_t header_len;
690 int r;
691
692 assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD));
693 assert(option_data || option_data_len == 0);
694 assert(ret);
695
696 /* This will return the following:
697 * -ENOMEM: memory allocation error,
698 * -ENOANO: unmatching IAID,
699 * -EINVAL: non-zero status code, or invalid lifetime,
700 * -EBADMSG: invalid message format,
701 * -ENODATA: no valid address or PD prefix,
702 * 0: success. */
703
704 switch (option_code) {
705 case SD_DHCP6_OPTION_IA_NA:
706 case SD_DHCP6_OPTION_IA_PD:
707 header_len = sizeof(struct ia_header);
708 break;
709
710 case SD_DHCP6_OPTION_IA_TA:
711 header_len = sizeof(be32_t); /* IA_TA does not have lifetime. */
712 break;
713
714 default:
715 assert_not_reached();
716 }
717
718 if (option_data_len < header_len)
719 return -EBADMSG;
720
721 ia = new(DHCP6IA, 1);
722 if (!ia)
723 return -ENOMEM;
724
725 *ia = (DHCP6IA) {
726 .type = option_code,
727 };
728 memcpy(&ia->header, option_data, header_len);
729
730 /* According to RFC8415, IAs which do not match the client's IAID should be ignored,
731 * but not necessary to ignore or refuse the whole message. */
732 if (ia->header.id != iaid)
733 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
734 "Received an IA option with a different IAID "
735 "from the one chosen by the client, ignoring.");
736
737 /* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */
738 lt_t1 = be32_sec_to_usec(ia->header.lifetime_t1, /* max_as_infinity = */ true);
739 lt_t2 = be32_sec_to_usec(ia->header.lifetime_t2, /* max_as_infinity = */ true);
740
741 if (lt_t1 > lt_t2)
742 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
743 "Received an IA option with T1 %s > T2 %s, ignoring.",
744 FORMAT_TIMESPAN(lt_t1, USEC_PER_SEC),
745 FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC));
746 if (lt_t1 == 0 && lt_t2 > 0)
747 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
748 "Received an IA option with zero T1 and non-zero T2 (%s), ignoring.",
749 FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC));
750
751 for (size_t offset = header_len; offset < option_data_len;) {
752 const uint8_t *subdata;
753 size_t subdata_len;
754 uint16_t subopt;
755
756 r = dhcp6_option_parse(option_data, option_data_len, &offset, &subopt, &subdata_len, &subdata);
757 if (r < 0)
758 return r;
759
760 switch (subopt) {
761 case SD_DHCP6_OPTION_IAADDR: {
762 r = dhcp6_option_parse_ia_address(client, ia, subdata, subdata_len);
763 if (r == -ENOMEM)
764 return r;
765
766 /* Ignore non-critical errors in the sub-option. */
767 break;
768 }
769 case SD_DHCP6_OPTION_IA_PD_PREFIX: {
770 r = dhcp6_option_parse_ia_pdprefix(client, ia, subdata, subdata_len);
771 if (r == -ENOMEM)
772 return r;
773
774 /* Ignore non-critical errors in the sub-option. */
775 break;
776 }
777 case SD_DHCP6_OPTION_STATUS_CODE: {
778 _cleanup_free_ char *msg = NULL;
779
780 r = dhcp6_option_parse_status(subdata, subdata_len, &msg);
781 if (r == -ENOMEM)
782 return r;
783 if (r > 0)
784 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
785 "Received an IA option with non-zero status%s%s",
786 isempty(msg) ? "." : ": ", strempty(msg));
787 if (r < 0)
788 log_dhcp6_client_errno(client, r,
789 "Received an IA option with an invalid status sub option, ignoring: %m");
790 break;
791 }
792 default:
793 log_dhcp6_client(client, "Received an IA option with an unknown sub-option %u, ignoring", subopt);
794 }
795 }
796
797 if (!ia->addresses)
798 return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA),
799 "Received an IA option without valid IA addresses or PD prefixes, ignoring.");
800
801 *ret = TAKE_PTR(ia);
802 return 0;
803 }
804
805 int dhcp6_option_parse_addresses(
806 const uint8_t *optval,
807 size_t optlen,
808 struct in6_addr **addrs,
809 size_t *count) {
810
811 assert(optval || optlen == 0);
812 assert(addrs);
813 assert(count);
814
815 if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
816 return -EBADMSG;
817
818 if (!GREEDY_REALLOC(*addrs, *count + optlen / sizeof(struct in6_addr)))
819 return -ENOMEM;
820
821 memcpy(*addrs + *count, optval, optlen);
822 *count += optlen / sizeof(struct in6_addr);
823
824 return 0;
825 }
826
827 int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) {
828 _cleanup_free_ char *domain = NULL;
829 int r;
830
831 assert(optval || optlen == 0);
832 assert(ret);
833
834 r = dns_name_from_wire_format(&optval, &optlen, &domain);
835 if (r < 0)
836 return r;
837 if (r == 0)
838 return -ENODATA;
839 if (optlen != 0)
840 return -EINVAL;
841
842 *ret = TAKE_PTR(domain);
843 return 0;
844 }
845
846 int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret) {
847 _cleanup_strv_free_ char **names = NULL;
848 int r;
849
850 assert(optval || optlen == 0);
851 assert(ret);
852
853 if (optlen <= 1)
854 return -ENODATA;
855 if (optval[optlen - 1] != '\0')
856 return -EINVAL;
857
858 while (optlen > 0) {
859 _cleanup_free_ char *name = NULL;
860
861 r = dns_name_from_wire_format(&optval, &optlen, &name);
862 if (r < 0)
863 return r;
864 if (dns_name_is_root(name)) /* root domain */
865 return -EBADMSG;
866
867 r = strv_consume(&names, TAKE_PTR(name));
868 if (r < 0)
869 return r;
870 }
871
872 *ret = TAKE_PTR(names);
873 return 0;
874 }
875
876 static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {
877 if (!i)
878 return NULL;
879
880 free(i->data);
881 return mfree(i);
882 }
883
884 int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret) {
885 assert_return(ret, -EINVAL);
886 assert_return(length == 0 || data, -EINVAL);
887
888 _cleanup_free_ void *q = memdup(data, length);
889 if (!q)
890 return -ENOMEM;
891
892 sd_dhcp6_option *p = new(sd_dhcp6_option, 1);
893 if (!p)
894 return -ENOMEM;
895
896 *p = (sd_dhcp6_option) {
897 .n_ref = 1,
898 .option = option,
899 .enterprise_identifier = enterprise_identifier,
900 .length = length,
901 .data = TAKE_PTR(q),
902 };
903
904 *ret = p;
905 return 0;
906 }
907
908 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_option, sd_dhcp6_option, dhcp6_option_free);
909 DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
910 dhcp6_option_hash_ops,
911 void,
912 trivial_hash_func,
913 trivial_compare_func,
914 sd_dhcp6_option,
915 sd_dhcp6_option_unref);