1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
7 #include <netinet/in.h>
9 #include "sd-dhcp6-client.h"
11 #include "alloc-util.h"
12 #include "dhcp6-internal.h"
13 #include "dhcp6-option.h"
14 #include "dhcp6-protocol.h"
15 #include "dns-domain.h"
17 #include "memory-util.h"
19 #include "unaligned.h"
21 #define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
22 #define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
23 #define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
25 bool dhcp6_option_can_request(uint16_t option
) {
26 /* See Client ORO field in
27 * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
30 case SD_DHCP6_OPTION_CLIENTID
:
31 case SD_DHCP6_OPTION_SERVERID
:
32 case SD_DHCP6_OPTION_IA_NA
:
33 case SD_DHCP6_OPTION_IA_TA
:
34 case SD_DHCP6_OPTION_IAADDR
:
35 case SD_DHCP6_OPTION_ORO
:
36 case SD_DHCP6_OPTION_PREFERENCE
:
37 case SD_DHCP6_OPTION_ELAPSED_TIME
:
38 case SD_DHCP6_OPTION_RELAY_MSG
:
39 case SD_DHCP6_OPTION_AUTH
:
40 case SD_DHCP6_OPTION_UNICAST
:
41 case SD_DHCP6_OPTION_STATUS_CODE
:
42 case SD_DHCP6_OPTION_RAPID_COMMIT
:
43 case SD_DHCP6_OPTION_USER_CLASS
:
44 case SD_DHCP6_OPTION_VENDOR_CLASS
:
46 case SD_DHCP6_OPTION_VENDOR_OPTS
:
48 case SD_DHCP6_OPTION_INTERFACE_ID
:
49 case SD_DHCP6_OPTION_RECONF_MSG
:
50 case SD_DHCP6_OPTION_RECONF_ACCEPT
:
52 case SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME
:
53 case SD_DHCP6_OPTION_SIP_SERVER_ADDRESS
:
54 case SD_DHCP6_OPTION_DNS_SERVERS
:
55 case SD_DHCP6_OPTION_DOMAIN_LIST
:
57 case SD_DHCP6_OPTION_IA_PD
:
58 case SD_DHCP6_OPTION_IA_PD_PREFIX
:
60 case SD_DHCP6_OPTION_NIS_SERVERS
:
61 case SD_DHCP6_OPTION_NISP_SERVERS
:
62 case SD_DHCP6_OPTION_NIS_DOMAIN_NAME
:
63 case SD_DHCP6_OPTION_NISP_DOMAIN_NAME
:
64 case SD_DHCP6_OPTION_SNTP_SERVERS
:
65 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
66 case SD_DHCP6_OPTION_BCMCS_SERVER_D
:
67 case SD_DHCP6_OPTION_BCMCS_SERVER_A
:
68 case SD_DHCP6_OPTION_GEOCONF_CIVIC
:
70 case SD_DHCP6_OPTION_REMOTE_ID
:
71 case SD_DHCP6_OPTION_SUBSCRIBER_ID
:
73 case SD_DHCP6_OPTION_CLIENT_FQDN
:
74 case SD_DHCP6_OPTION_PANA_AGENT
:
75 case SD_DHCP6_OPTION_POSIX_TIMEZONE
:
76 case SD_DHCP6_OPTION_TZDB_TIMEZONE
:
78 case SD_DHCP6_OPTION_ERO
:
79 case SD_DHCP6_OPTION_LQ_QUERY
:
80 case SD_DHCP6_OPTION_CLIENT_DATA
:
81 case SD_DHCP6_OPTION_CLT_TIME
:
82 case SD_DHCP6_OPTION_LQ_RELAY_DATA
:
83 case SD_DHCP6_OPTION_LQ_CLIENT_LINK
:
85 case SD_DHCP6_OPTION_MIP6_HNIDF
:
86 case SD_DHCP6_OPTION_MIP6_VDINF
:
87 case SD_DHCP6_OPTION_V6_LOST
:
88 case SD_DHCP6_OPTION_CAPWAP_AC_V6
:
90 case SD_DHCP6_OPTION_RELAY_ID
:
92 case SD_DHCP6_OPTION_IPV6_ADDRESS_MOS
:
93 case SD_DHCP6_OPTION_IPV6_FQDN_MOS
:
94 case SD_DHCP6_OPTION_NTP_SERVER
:
95 case SD_DHCP6_OPTION_V6_ACCESS_DOMAIN
:
96 case SD_DHCP6_OPTION_SIP_UA_CS_LIST
:
97 case SD_DHCP6_OPTION_BOOTFILE_URL
:
98 case SD_DHCP6_OPTION_BOOTFILE_PARAM
:
100 case SD_DHCP6_OPTION_CLIENT_ARCH_TYPE
:
102 case SD_DHCP6_OPTION_NII
:
103 case SD_DHCP6_OPTION_GEOLOCATION
:
104 case SD_DHCP6_OPTION_AFTR_NAME
:
105 case SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME
:
107 case SD_DHCP6_OPTION_RSOO
:
109 case SD_DHCP6_OPTION_PD_EXCLUDE
:
111 case SD_DHCP6_OPTION_VSS
:
113 case SD_DHCP6_OPTION_MIP6_IDINF
:
114 case SD_DHCP6_OPTION_MIP6_UDINF
:
115 case SD_DHCP6_OPTION_MIP6_HNP
:
116 case SD_DHCP6_OPTION_MIP6_HAA
:
117 case SD_DHCP6_OPTION_MIP6_HAF
:
118 case SD_DHCP6_OPTION_RDNSS_SELECTION
:
119 case SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME
:
120 case SD_DHCP6_OPTION_KRB_REALM_NAME
:
121 case SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME
:
122 case SD_DHCP6_OPTION_KRB_KDC
:
124 case SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR
:
125 case SD_DHCP6_OPTION_LINK_ADDRESS
:
126 case SD_DHCP6_OPTION_RADIUS
:
128 case SD_DHCP6_OPTION_SOL_MAX_RT
:
129 case SD_DHCP6_OPTION_INF_MAX_RT
:
130 case SD_DHCP6_OPTION_ADDRSEL
:
131 case SD_DHCP6_OPTION_ADDRSEL_TABLE
:
132 case SD_DHCP6_OPTION_V6_PCP_SERVER
:
134 case SD_DHCP6_OPTION_DHCPV4_MSG
:
136 case SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER
:
138 case SD_DHCP6_OPTION_S46_RULE
:
140 case SD_DHCP6_OPTION_S46_BR
:
142 case SD_DHCP6_OPTION_S46_DMR
:
143 case SD_DHCP6_OPTION_S46_V4V6BIND
:
144 case SD_DHCP6_OPTION_S46_PORTPARAMS
:
146 case SD_DHCP6_OPTION_S46_CONT_MAPE
:
147 case SD_DHCP6_OPTION_S46_CONT_MAPT
:
148 case SD_DHCP6_OPTION_S46_CONT_LW
:
149 case SD_DHCP6_OPTION_4RD
:
150 case SD_DHCP6_OPTION_4RD_MAP_RULE
:
151 case SD_DHCP6_OPTION_4RD_NON_MAP_RULE
:
153 case SD_DHCP6_OPTION_LQ_BASE_TIME
:
154 case SD_DHCP6_OPTION_LQ_START_TIME
:
155 case SD_DHCP6_OPTION_LQ_END_TIME
:
157 case SD_DHCP6_OPTION_CAPTIVE_PORTAL
:
158 case SD_DHCP6_OPTION_MPL_PARAMETERS
:
160 case SD_DHCP6_OPTION_ANI_ATT
:
161 case SD_DHCP6_OPTION_ANI_NETWORK_NAME
:
162 case SD_DHCP6_OPTION_ANI_AP_NAME
:
163 case SD_DHCP6_OPTION_ANI_AP_BSSID
:
164 case SD_DHCP6_OPTION_ANI_OPERATOR_ID
:
165 case SD_DHCP6_OPTION_ANI_OPERATOR_REALM
:
167 case SD_DHCP6_OPTION_S46_PRIORITY
:
169 case SD_DHCP6_OPTION_MUD_URL_V6
:
171 case SD_DHCP6_OPTION_V6_PREFIX64
:
173 case SD_DHCP6_OPTION_F_BINDING_STATUS
:
174 case SD_DHCP6_OPTION_F_CONNECT_FLAGS
:
175 case SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO
:
176 case SD_DHCP6_OPTION_F_DNS_HOST_NAME
:
177 case SD_DHCP6_OPTION_F_DNS_ZONE_NAME
:
178 case SD_DHCP6_OPTION_F_DNS_FLAGS
:
179 case SD_DHCP6_OPTION_F_EXPIRATION_TIME
:
180 case SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD
:
181 case SD_DHCP6_OPTION_F_MCLT
:
182 case SD_DHCP6_OPTION_F_PARTNER_LIFETIME
:
183 case SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT
:
184 case SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME
:
185 case SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME
:
186 case SD_DHCP6_OPTION_F_PROTOCOL_VERSION
:
187 case SD_DHCP6_OPTION_F_KEEPALIVE_TIME
:
188 case SD_DHCP6_OPTION_F_RECONFIGURE_DATA
:
189 case SD_DHCP6_OPTION_F_RELATIONSHIP_NAME
:
190 case SD_DHCP6_OPTION_F_SERVER_FLAGS
:
191 case SD_DHCP6_OPTION_F_SERVER_STATE
:
192 case SD_DHCP6_OPTION_F_START_TIME_OF_STATE
:
193 case SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME
:
194 case SD_DHCP6_OPTION_RELAY_PORT
:
196 case SD_DHCP6_OPTION_V6_SZTP_REDIRECT
:
197 case SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX
:
199 case SD_DHCP6_OPTION_IA_LL
:
200 case SD_DHCP6_OPTION_LLADDR
:
201 case SD_DHCP6_OPTION_SLAP_QUAD
:
203 case SD_DHCP6_OPTION_V6_DOTS_RI
:
204 case SD_DHCP6_OPTION_V6_DOTS_ADDRESS
:
205 case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF
:
212 static int option_append_hdr(uint8_t **buf
, size_t *buflen
, uint16_t optcode
, size_t optlen
) {
213 assert_return(buf
, -EINVAL
);
214 assert_return(*buf
, -EINVAL
);
215 assert_return(buflen
, -EINVAL
);
217 if (optlen
> 0xffff || *buflen
< optlen
+ offsetof(DHCP6Option
, data
))
220 unaligned_write_be16(*buf
+ offsetof(DHCP6Option
, code
), optcode
);
221 unaligned_write_be16(*buf
+ offsetof(DHCP6Option
, len
), optlen
);
223 *buf
+= offsetof(DHCP6Option
, data
);
224 *buflen
-= offsetof(DHCP6Option
, data
);
229 int dhcp6_option_append(uint8_t **buf
, size_t *buflen
, uint16_t code
,
230 size_t optlen
, const void *optval
) {
233 assert_return(optval
|| optlen
== 0, -EINVAL
);
235 r
= option_append_hdr(buf
, buflen
, code
, optlen
);
239 *buf
= mempcpy_safe(*buf
, optval
, optlen
);
245 int dhcp6_option_append_vendor_option(uint8_t **buf
, size_t *buflen
, OrderedSet
*vendor_options
) {
246 sd_dhcp6_option
*options
;
252 assert(vendor_options
);
254 ORDERED_SET_FOREACH(options
, vendor_options
) {
255 _cleanup_free_
uint8_t *p
= NULL
;
258 total
= 4 + 2 + 2 + options
->length
;
264 unaligned_write_be32(p
, options
->enterprise_identifier
);
265 unaligned_write_be16(p
+ 4, options
->option
);
266 unaligned_write_be16(p
+ 6, options
->length
);
267 memcpy(p
+ 8, options
->data
, options
->length
);
269 r
= dhcp6_option_append(buf
, buflen
, SD_DHCP6_OPTION_VENDOR_OPTS
, total
, p
);
277 static int option_append_ia_address(uint8_t **buf
, size_t *buflen
, const struct iaaddr
*address
) {
286 /* Do not append T1 and T2. */
287 a
= (struct iaaddr
) {
288 .address
= address
->address
,
291 r
= option_append_hdr(buf
, buflen
, SD_DHCP6_OPTION_IAADDR
, sizeof(struct iaaddr
));
295 *buf
= mempcpy(*buf
, &a
, sizeof(struct iaaddr
));
296 *buflen
-= sizeof(struct iaaddr
);
298 return offsetof(DHCP6Option
, data
) + sizeof(struct iaaddr
);
301 static int option_append_pd_prefix(uint8_t **buf
, size_t *buflen
, const struct iapdprefix
*prefix
) {
310 if (prefix
->prefixlen
== 0)
313 /* Do not append T1 and T2. */
314 p
= (struct iapdprefix
) {
315 .prefixlen
= prefix
->prefixlen
,
316 .address
= prefix
->address
,
319 r
= option_append_hdr(buf
, buflen
, SD_DHCP6_OPTION_IA_PD_PREFIX
, sizeof(struct iapdprefix
));
323 *buf
= mempcpy(*buf
, &p
, sizeof(struct iapdprefix
));
324 *buflen
-= sizeof(struct iapdprefix
);
326 return offsetof(DHCP6Option
, data
) + sizeof(struct iapdprefix
);
329 int dhcp6_option_append_ia(uint8_t **buf
, size_t *buflen
, const DHCP6IA
*ia
) {
330 struct ia_header header
;
336 assert_return(buf
, -EINVAL
);
337 assert_return(*buf
, -EINVAL
);
338 assert_return(buflen
, -EINVAL
);
339 assert_return(ia
, -EINVAL
);
341 /* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
344 case SD_DHCP6_OPTION_IA_NA
:
345 case SD_DHCP6_OPTION_IA_PD
:
346 len
= sizeof(struct ia_header
);
347 header
= (struct ia_header
) {
352 case SD_DHCP6_OPTION_IA_TA
:
353 len
= sizeof(header
.id
); /* IA_TA does not have lifetime. */
354 header
= (struct ia_header
) {
360 assert_not_reached();
363 if (*buflen
< offsetof(DHCP6Option
, data
) + len
)
369 /* The header will be written at the end of this function. */
370 *buf
+= offsetof(DHCP6Option
, data
);
371 *buflen
-= offsetof(DHCP6Option
, data
);
373 *buf
= mempcpy(*buf
, &header
, len
);
376 LIST_FOREACH(addresses
, addr
, ia
->addresses
) {
377 if (ia
->type
== SD_DHCP6_OPTION_IA_PD
)
378 r
= option_append_pd_prefix(buf
, buflen
, &addr
->iapdprefix
);
380 r
= option_append_ia_address(buf
, buflen
, &addr
->iaaddr
);
387 return option_append_hdr(&ia_hdr
, &ia_buflen
, ia
->type
, len
);
390 int dhcp6_option_append_fqdn(uint8_t **buf
, size_t *buflen
, const char *fqdn
) {
391 uint8_t buffer
[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX
];
394 assert_return(buf
&& *buf
&& buflen
&& fqdn
, -EINVAL
);
396 buffer
[0] = DHCP6_FQDN_FLAG_S
; /* Request server to perform AAAA RR DNS updates */
398 /* Store domain name after flags field */
399 r
= dns_name_to_wire_format(fqdn
, buffer
+ 1, sizeof(buffer
) - 1, false);
404 * According to RFC 4704, chapter 4.2 only add terminating zero-length
405 * label in case a FQDN is provided. Since dns_name_to_wire_format
406 * always adds terminating zero-length label remove if only a hostname
409 if (dns_name_is_single_label(fqdn
))
412 r
= dhcp6_option_append(buf
, buflen
, SD_DHCP6_OPTION_CLIENT_FQDN
, 1 + r
, buffer
);
417 int dhcp6_option_append_user_class(uint8_t **buf
, size_t *buflen
, char * const *user_class
) {
418 _cleanup_free_
uint8_t *p
= NULL
;
419 size_t total
= 0, offset
= 0;
424 assert(!strv_isempty(user_class
));
426 STRV_FOREACH(s
, user_class
) {
427 size_t len
= strlen(*s
);
430 if (len
> 0xffff || len
== 0)
432 q
= realloc(p
, total
+ len
+ 2);
438 unaligned_write_be16(&p
[offset
], len
);
439 memcpy(&p
[offset
+ 2], *s
, len
);
445 return dhcp6_option_append(buf
, buflen
, SD_DHCP6_OPTION_USER_CLASS
, total
, p
);
448 int dhcp6_option_append_vendor_class(uint8_t **buf
, size_t *buflen
, char * const *vendor_class
) {
449 _cleanup_free_
uint8_t *p
= NULL
;
450 uint32_t enterprise_identifier
;
451 size_t total
, offset
;
456 assert(!strv_isempty(vendor_class
));
458 enterprise_identifier
= htobe32(SYSTEMD_PEN
);
460 p
= memdup(&enterprise_identifier
, sizeof(enterprise_identifier
));
464 total
= sizeof(enterprise_identifier
);
467 STRV_FOREACH(s
, vendor_class
) {
468 size_t len
= strlen(*s
);
471 if (len
> UINT16_MAX
|| len
== 0)
474 q
= realloc(p
, total
+ len
+ 2);
480 unaligned_write_be16(&p
[offset
], len
);
481 memcpy(&p
[offset
+ 2], *s
, len
);
487 return dhcp6_option_append(buf
, buflen
, SD_DHCP6_OPTION_VENDOR_CLASS
, total
, p
);
490 int dhcp6_option_parse(
494 uint16_t *ret_option_code
,
495 size_t *ret_option_data_len
,
496 const uint8_t **ret_option_data
) {
502 assert(ret_option_code
);
503 assert(ret_option_data_len
);
504 assert(ret_option_data
);
506 if (buflen
< offsetof(DHCP6Option
, data
))
509 if (*offset
>= buflen
- offsetof(DHCP6Option
, data
))
512 len
= unaligned_read_be16(buf
+ *offset
+ offsetof(DHCP6Option
, len
));
514 if (len
> buflen
- offsetof(DHCP6Option
, data
) - *offset
)
517 *ret_option_code
= unaligned_read_be16(buf
+ *offset
+ offsetof(DHCP6Option
, code
));
518 *ret_option_data_len
= len
;
519 *ret_option_data
= buf
+ *offset
+ offsetof(DHCP6Option
, data
);
520 *offset
+= offsetof(DHCP6Option
, data
) + len
;
525 int dhcp6_option_parse_status(const uint8_t *data
, size_t data_len
, char **ret_status_message
) {
528 if (data_len
< sizeof(uint16_t))
531 if (ret_status_message
) {
534 /* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415.
535 * Let's escape unsafe characters for safety. */
536 msg
= cescape_length((const char*) (data
+ sizeof(uint16_t)), data_len
- sizeof(uint16_t));
540 *ret_status_message
= msg
;
543 return unaligned_read_be16(data
);
546 static int dhcp6_option_parse_ia_options(sd_dhcp6_client
*client
, const uint8_t *buf
, size_t buflen
) {
549 assert(buf
|| buflen
== 0);
551 for(size_t offset
= 0; offset
< buflen
;) {
556 r
= dhcp6_option_parse(buf
, buflen
, &offset
, &code
, &data_len
, &data
);
561 case SD_DHCP6_OPTION_STATUS_CODE
: {
562 _cleanup_free_
char *msg
= NULL
;
564 r
= dhcp6_option_parse_status(data
, data_len
, &msg
);
568 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
569 "Received an IA address or PD prefix option with non-zero status: %s%s%s",
570 strempty(msg
), isempty(msg
) ? "" : ": ",
571 dhcp6_message_status_to_string(r
));
573 /* Let's log but ignore the invalid status option. */
574 log_dhcp6_client_errno(client
, r
,
575 "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
579 log_dhcp6_client(client
, "Received an unknown sub option %u in IA address or PD prefix, ignoring.", code
);
586 static int dhcp6_option_parse_ia_address(sd_dhcp6_client
*client
, DHCP6IA
*ia
, const uint8_t *data
, size_t len
) {
587 _cleanup_free_ DHCP6Address
*a
= NULL
;
588 uint32_t lt_valid
, lt_pref
;
592 assert(data
|| len
== 0);
594 if (!IN_SET(ia
->type
, SD_DHCP6_OPTION_IA_NA
, SD_DHCP6_OPTION_IA_TA
))
595 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
596 "Received an IA address sub-option in an invalid option, ignoring.");
598 if (len
< sizeof(struct iaaddr
))
601 a
= new(DHCP6Address
, 1);
605 memcpy(&a
->iaaddr
, data
, sizeof(struct iaaddr
));
607 lt_valid
= be32toh(a
->iaaddr
.lifetime_valid
);
608 lt_pref
= be32toh(a
->iaaddr
.lifetime_preferred
);
611 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
612 "Received an IA address with zero valid lifetime, ignoring.");
613 if (lt_pref
> lt_valid
)
614 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
615 "Received an IA address with preferred lifetime %"PRIu32
616 " larger than valid lifetime %"PRIu32
", ignoring.",
619 if (len
> sizeof(struct iaaddr
)) {
620 r
= dhcp6_option_parse_ia_options(client
, data
+ sizeof(struct iaaddr
), len
- sizeof(struct iaaddr
));
625 LIST_PREPEND(addresses
, ia
->addresses
, TAKE_PTR(a
));
629 static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client
*client
, DHCP6IA
*ia
, const uint8_t *data
, size_t len
) {
630 _cleanup_free_ DHCP6Address
*a
= NULL
;
631 uint32_t lt_valid
, lt_pref
;
635 assert(data
|| len
== 0);
637 if (ia
->type
!= SD_DHCP6_OPTION_IA_PD
)
638 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
639 "Received an PD prefix sub-option in an invalid option, ignoring");
641 if (len
< sizeof(struct iapdprefix
))
644 a
= new(DHCP6Address
, 1);
648 memcpy(&a
->iapdprefix
, data
, sizeof(struct iapdprefix
));
650 lt_valid
= be32toh(a
->iapdprefix
.lifetime_valid
);
651 lt_pref
= be32toh(a
->iapdprefix
.lifetime_preferred
);
654 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
655 "Received a PD prefix with zero valid lifetime, ignoring.");
656 if (lt_pref
> lt_valid
)
657 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
658 "Received a PD prefix with preferred lifetime %"PRIu32
659 " larger than valid lifetime %"PRIu32
", ignoring.",
662 if (len
> sizeof(struct iapdprefix
)) {
663 r
= dhcp6_option_parse_ia_options(client
, data
+ sizeof(struct iapdprefix
), len
- sizeof(struct iapdprefix
));
668 LIST_PREPEND(addresses
, ia
->addresses
, TAKE_PTR(a
));
672 int dhcp6_option_parse_ia(
673 sd_dhcp6_client
*client
,
675 uint16_t option_code
,
676 size_t option_data_len
,
677 const uint8_t *option_data
,
680 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
681 uint32_t lt_t1
, lt_t2
;
685 assert(IN_SET(option_code
, SD_DHCP6_OPTION_IA_NA
, SD_DHCP6_OPTION_IA_TA
, SD_DHCP6_OPTION_IA_PD
));
686 assert(option_data
|| option_data_len
== 0);
689 /* This will return the following:
690 * -ENOMEM: memory allocation error,
691 * -ENOANO: unmatching IAID,
692 * -EINVAL: non-zero status code, or invalid lifetime,
693 * -EBADMSG: invalid message format,
694 * -ENODATA: no valid address or PD prefix,
697 switch (option_code
) {
698 case SD_DHCP6_OPTION_IA_NA
:
699 case SD_DHCP6_OPTION_IA_PD
:
700 header_len
= sizeof(struct ia_header
);
703 case SD_DHCP6_OPTION_IA_TA
:
704 header_len
= sizeof(be32_t
); /* IA_TA does not have lifetime. */
708 assert_not_reached();
711 if (option_data_len
< header_len
)
714 ia
= new(DHCP6IA
, 1);
721 memcpy(&ia
->header
, option_data
, header_len
);
723 /* According to RFC8415, IAs which do not match the client's IAID should be ignored,
724 * but not necessary to ignore or refuse the whole message. */
725 if (ia
->header
.id
!= iaid
)
726 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(ENOANO
),
727 "Received an IA option with a different IAID "
728 "from the one chosen by the client, ignoring.");
730 /* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */
731 lt_t1
= be32toh(ia
->header
.lifetime_t1
);
732 lt_t2
= be32toh(ia
->header
.lifetime_t2
);
735 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
736 "Received an IA option with T1 %"PRIu32
"sec > T2 %"PRIu32
"sec, ignoring.",
738 if (lt_t1
== 0 && lt_t2
> 0)
739 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
740 "Received an IA option with zero T1 and non-zero T2 (%"PRIu32
"sec), ignoring.",
743 for (size_t offset
= header_len
; offset
< option_data_len
;) {
744 const uint8_t *subdata
;
748 r
= dhcp6_option_parse(option_data
, option_data_len
, &offset
, &subopt
, &subdata_len
, &subdata
);
753 case SD_DHCP6_OPTION_IAADDR
: {
754 r
= dhcp6_option_parse_ia_address(client
, ia
, subdata
, subdata_len
);
758 /* Ignore non-critical errors in the sub-option. */
761 case SD_DHCP6_OPTION_IA_PD_PREFIX
: {
762 r
= dhcp6_option_parse_ia_pdprefix(client
, ia
, subdata
, subdata_len
);
766 /* Ignore non-critical errors in the sub-option. */
769 case SD_DHCP6_OPTION_STATUS_CODE
: {
770 _cleanup_free_
char *msg
= NULL
;
772 r
= dhcp6_option_parse_status(subdata
, subdata_len
, &msg
);
776 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
777 "Received an IA option with non-zero status: %s%s%s",
778 strempty(msg
), isempty(msg
) ? "" : ": ",
779 dhcp6_message_status_to_string(r
));
781 log_dhcp6_client_errno(client
, r
,
782 "Received an IA option with an invalid status sub option, ignoring: %m");
786 log_dhcp6_client(client
, "Received an IA option with an unknown sub-option %u, ignoring", subopt
);
791 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(ENODATA
),
792 "Received an IA option without valid IA addresses or PD prefixes, ignoring.");
798 int dhcp6_option_parse_addresses(
799 const uint8_t *optval
,
801 struct in6_addr
**addrs
,
808 if (optlen
== 0 || optlen
% sizeof(struct in6_addr
) != 0)
811 if (!GREEDY_REALLOC(*addrs
, *count
+ optlen
/ sizeof(struct in6_addr
)))
814 memcpy(*addrs
+ *count
, optval
, optlen
);
815 *count
+= optlen
/ sizeof(struct in6_addr
);
820 static int parse_domain(const uint8_t **data
, size_t *len
, char **ret
) {
821 _cleanup_free_
char *domain
= NULL
;
822 const uint8_t *optval
;
823 size_t optlen
, n
= 0;
857 label
= (const char*) optval
;
861 if (!GREEDY_REALLOC(domain
, n
+ (n
!= 0) + DNS_LABEL_ESCAPED_MAX
))
867 r
= dns_label_escape(label
, c
, domain
+ n
, DNS_LABEL_ESCAPED_MAX
);
875 if (!GREEDY_REALLOC(domain
, n
+ 1))
881 *ret
= TAKE_PTR(domain
);
888 int dhcp6_option_parse_domainname(const uint8_t *optval
, size_t optlen
, char **ret
) {
889 _cleanup_free_
char *domain
= NULL
;
895 r
= parse_domain(&optval
, &optlen
, &domain
);
903 *ret
= TAKE_PTR(domain
);
907 int dhcp6_option_parse_domainname_list(const uint8_t *optval
, size_t optlen
, char ***ret
) {
908 _cleanup_strv_free_
char **names
= NULL
;
916 if (optval
[optlen
- 1] != '\0')
920 _cleanup_free_
char *name
= NULL
;
922 r
= parse_domain(&optval
, &optlen
, &name
);
928 r
= strv_consume(&names
, TAKE_PTR(name
));
933 *ret
= TAKE_PTR(names
);
937 static sd_dhcp6_option
* dhcp6_option_free(sd_dhcp6_option
*i
) {
945 int sd_dhcp6_option_new(uint16_t option
, const void *data
, size_t length
, uint32_t enterprise_identifier
, sd_dhcp6_option
**ret
) {
946 assert_return(ret
, -EINVAL
);
947 assert_return(length
== 0 || data
, -EINVAL
);
949 _cleanup_free_
void *q
= memdup(data
, length
);
953 sd_dhcp6_option
*p
= new(sd_dhcp6_option
, 1);
957 *p
= (sd_dhcp6_option
) {
960 .enterprise_identifier
= enterprise_identifier
,
969 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_option
, sd_dhcp6_option
, dhcp6_option_free
);
970 DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
971 dhcp6_option_hash_ops
,
974 trivial_compare_func
,
976 sd_dhcp6_option_unref
);