1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2014-2015 Intel Corporation. All rights reserved.
6 #include <netinet/in.h>
8 #include "sd-dhcp6-client.h"
9 #include "sd-dhcp6-option.h"
11 #include "alloc-util.h"
12 #include "dhcp6-internal.h"
13 #include "dhcp6-option.h"
14 #include "dhcp6-protocol.h"
16 #include "dns-domain.h"
18 #include "memory-util.h"
19 #include "network-common.h"
20 #include "ordered-set.h"
21 #include "string-util.h"
23 #include "unaligned.h"
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))
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 */
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
:
50 case SD_DHCP6_OPTION_VENDOR_OPTS
:
52 case SD_DHCP6_OPTION_INTERFACE_ID
:
53 case SD_DHCP6_OPTION_RECONF_MSG
:
54 case SD_DHCP6_OPTION_RECONF_ACCEPT
:
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
:
61 case SD_DHCP6_OPTION_IA_PD
:
62 case SD_DHCP6_OPTION_IA_PD_PREFIX
:
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
:
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
:
76 case SD_DHCP6_OPTION_REMOTE_ID
:
77 case SD_DHCP6_OPTION_SUBSCRIBER_ID
:
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
:
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
:
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
:
96 case SD_DHCP6_OPTION_RELAY_ID
:
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
:
106 case SD_DHCP6_OPTION_CLIENT_ARCH_TYPE
:
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
:
113 case SD_DHCP6_OPTION_RSOO
:
115 case SD_DHCP6_OPTION_PD_EXCLUDE
:
117 case SD_DHCP6_OPTION_VSS
:
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
:
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. */
136 case SD_DHCP6_OPTION_ADDRSEL
:
137 case SD_DHCP6_OPTION_ADDRSEL_TABLE
:
138 case SD_DHCP6_OPTION_V6_PCP_SERVER
:
140 case SD_DHCP6_OPTION_DHCPV4_MSG
:
142 case SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER
:
144 case SD_DHCP6_OPTION_S46_RULE
:
146 case SD_DHCP6_OPTION_S46_BR
:
148 case SD_DHCP6_OPTION_S46_DMR
:
149 case SD_DHCP6_OPTION_S46_V4V6BIND
:
150 case SD_DHCP6_OPTION_S46_PORTPARAMS
:
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
:
159 case SD_DHCP6_OPTION_LQ_BASE_TIME
:
160 case SD_DHCP6_OPTION_LQ_START_TIME
:
161 case SD_DHCP6_OPTION_LQ_END_TIME
:
163 case SD_DHCP6_OPTION_CAPTIVE_PORTAL
:
164 case SD_DHCP6_OPTION_MPL_PARAMETERS
:
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
:
173 case SD_DHCP6_OPTION_S46_PRIORITY
:
175 case SD_DHCP6_OPTION_MUD_URL_V6
:
177 case SD_DHCP6_OPTION_V6_PREFIX64
:
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
:
202 case SD_DHCP6_OPTION_V6_SZTP_REDIRECT
:
203 case SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX
:
205 case SD_DHCP6_OPTION_IA_LL
:
206 case SD_DHCP6_OPTION_LLADDR
:
207 case SD_DHCP6_OPTION_SLAP_QUAD
:
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
:
219 static int option_append_hdr(uint8_t **buf
, size_t *offset
, uint16_t optcode
, size_t optlen
) {
227 if (optlen
+ offsetof(DHCP6Option
, data
) > SIZE_MAX
- *offset
)
230 if (!GREEDY_REALLOC(*buf
, *offset
+ optlen
+ offsetof(DHCP6Option
, data
)))
233 unaligned_write_be16(*buf
+ *offset
+ offsetof(DHCP6Option
, code
), optcode
);
234 unaligned_write_be16(*buf
+ *offset
+ offsetof(DHCP6Option
, len
), optlen
);
236 *offset
+= offsetof(DHCP6Option
, data
);
240 int dhcp6_option_append(
245 const void *optval
) {
249 assert(optval
|| optlen
== 0);
251 r
= option_append_hdr(buf
, offset
, code
, optlen
);
255 memcpy_safe(*buf
+ *offset
, optval
, optlen
);
261 int dhcp6_option_append_vendor_option(uint8_t **buf
, size_t *offset
, OrderedSet
*vendor_options
) {
262 sd_dhcp6_option
*options
;
269 ORDERED_SET_FOREACH(options
, vendor_options
) {
270 _cleanup_free_
uint8_t *p
= NULL
;
273 total
= 4 + 2 + 2 + options
->length
;
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
);
284 r
= dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_VENDOR_OPTS
, total
, p
);
292 static int option_append_ia_address(uint8_t **buf
, size_t *offset
, const struct iaaddr
*address
) {
298 /* Do not append T1 and T2. */
299 const struct iaaddr a
= {
300 .address
= address
->address
,
303 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_IAADDR
, sizeof(struct iaaddr
), &a
);
306 static int option_append_pd_prefix(uint8_t **buf
, size_t *offset
, const struct iapdprefix
*prefix
) {
312 if (prefix
->prefixlen
== 0)
315 /* Do not append T1 and T2. */
316 const struct iapdprefix p
= {
317 .prefixlen
= prefix
->prefixlen
,
318 .address
= prefix
->address
,
321 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_IA_PD_PREFIX
, sizeof(struct iapdprefix
), &p
);
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
;
335 /* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
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
) {
346 case SD_DHCP6_OPTION_IA_TA
:
347 len
= sizeof(header
.id
); /* IA_TA does not have lifetime. */
348 header
= (struct ia_header
) {
354 assert_not_reached();
357 if (!GREEDY_REALLOC(data
, len
))
360 memcpy(data
, &header
, len
);
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
);
366 r
= option_append_ia_address(&data
, &len
, &addr
->iaaddr
);
371 return dhcp6_option_append(buf
, offset
, ia
->type
, len
, data
);
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
];
385 buffer
[0] = DHCP6_FQDN_FLAG_S
; /* Request server to perform AAAA RR DNS updates */
387 /* Store domain name after flags field */
388 r
= dns_name_to_wire_format(fqdn
, buffer
+ 1, sizeof(buffer
) - 1, false);
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
398 if (dns_name_is_single_label(fqdn
))
401 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_CLIENT_FQDN
, 1 + r
, buffer
);
404 int dhcp6_option_append_user_class(uint8_t **buf
, size_t *offset
, char * const *user_class
) {
405 _cleanup_free_
uint8_t *p
= NULL
;
412 if (strv_isempty(user_class
))
415 STRV_FOREACH(s
, user_class
) {
416 size_t len
= strlen(*s
);
418 if (len
> UINT16_MAX
|| len
== 0)
421 if (!GREEDY_REALLOC(p
, n
+ len
+ 2))
424 unaligned_write_be16(p
+ n
, len
);
425 memcpy(p
+ n
+ 2, *s
, len
);
429 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_USER_CLASS
, n
, p
);
432 int dhcp6_option_append_vendor_class(uint8_t **buf
, size_t *offset
, char * const *vendor_class
) {
433 _cleanup_free_
uint8_t *p
= NULL
;
440 if (strv_isempty(vendor_class
))
443 if (!GREEDY_REALLOC(p
, sizeof(be32_t
)))
446 /* Enterprise Identifier */
447 unaligned_write_be32(p
, SYSTEMD_PEN
);
450 STRV_FOREACH(s
, vendor_class
) {
451 size_t len
= strlen(*s
);
453 if (len
> UINT16_MAX
|| len
== 0)
456 if (!GREEDY_REALLOC(p
, n
+ len
+ 2))
459 unaligned_write_be16(p
+ n
, len
);
460 memcpy(p
+ n
+ 2, *s
, len
);
464 return dhcp6_option_append(buf
, offset
, SD_DHCP6_OPTION_VENDOR_CLASS
, n
, p
);
467 int dhcp6_option_parse(
471 uint16_t *ret_option_code
,
472 size_t *ret_option_data_len
,
473 const uint8_t **ret_option_data
) {
479 assert(ret_option_code
);
480 assert(ret_option_data_len
);
481 assert(ret_option_data
);
483 if (buflen
< offsetof(DHCP6Option
, data
))
486 if (*offset
> buflen
- offsetof(DHCP6Option
, data
))
489 len
= unaligned_read_be16(buf
+ *offset
+ offsetof(DHCP6Option
, len
));
491 if (len
> buflen
- offsetof(DHCP6Option
, data
) - *offset
)
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
;
502 int dhcp6_option_parse_status(const uint8_t *data
, size_t data_len
, char **ret_status_message
) {
505 assert(data
|| data_len
== 0);
507 if (data_len
< sizeof(uint16_t))
510 status
= unaligned_read_be16(data
);
512 if (ret_status_message
) {
513 _cleanup_free_
char *msg
= NULL
;
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));
522 s
= dhcp6_message_status_to_string(status
);
523 if (s
&& !strextend_with_separator(&msg
, ": ", s
))
526 *ret_status_message
= TAKE_PTR(msg
);
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
;
537 assert(data
|| data_len
== 0);
545 r
= make_cstring((const char *) data
, data_len
, MAKE_CSTRING_REFUSE_TRAILING_NUL
, &string
);
549 return free_and_replace(*ret
, string
);
552 static int dhcp6_option_parse_ia_options(sd_dhcp6_client
*client
, const uint8_t *buf
, size_t buflen
) {
555 assert(buf
|| buflen
== 0);
557 for (size_t offset
= 0; offset
< buflen
;) {
562 r
= dhcp6_option_parse(buf
, buflen
, &offset
, &code
, &data_len
, &data
);
567 case SD_DHCP6_OPTION_STATUS_CODE
: {
568 _cleanup_free_
char *msg
= NULL
;
570 r
= dhcp6_option_parse_status(data
, data_len
, &msg
);
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
));
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");
584 log_dhcp6_client(client
, "Received an unknown sub option %u in IA address or PD prefix, ignoring.", code
);
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
;
597 assert(data
|| len
== 0);
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.");
603 if (len
< sizeof(struct iaaddr
))
606 a
= new(DHCP6Address
, 1);
610 memcpy(&a
->iaaddr
, data
, sizeof(struct iaaddr
));
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);
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
));
625 if (len
> sizeof(struct iaaddr
)) {
626 r
= dhcp6_option_parse_ia_options(client
, data
+ sizeof(struct iaaddr
), len
- sizeof(struct iaaddr
));
631 LIST_PREPEND(addresses
, ia
->addresses
, TAKE_PTR(a
));
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
;
641 assert(data
|| len
== 0);
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");
647 if (len
< sizeof(struct iapdprefix
))
650 a
= new(DHCP6Address
, 1);
654 memcpy(&a
->iapdprefix
, data
, sizeof(struct iapdprefix
));
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);
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
));
669 if (len
> sizeof(struct iapdprefix
)) {
670 r
= dhcp6_option_parse_ia_options(client
, data
+ sizeof(struct iapdprefix
), len
- sizeof(struct iapdprefix
));
675 LIST_PREPEND(addresses
, ia
->addresses
, TAKE_PTR(a
));
679 int dhcp6_option_parse_ia(
680 sd_dhcp6_client
*client
,
682 uint16_t option_code
,
683 size_t option_data_len
,
684 const uint8_t *option_data
,
687 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
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);
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,
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
);
710 case SD_DHCP6_OPTION_IA_TA
:
711 header_len
= sizeof(be32_t
); /* IA_TA does not have lifetime. */
715 assert_not_reached();
718 if (option_data_len
< header_len
)
721 ia
= new(DHCP6IA
, 1);
728 memcpy(&ia
->header
, option_data
, header_len
);
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.");
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);
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
));
751 for (size_t offset
= header_len
; offset
< option_data_len
;) {
752 const uint8_t *subdata
;
756 r
= dhcp6_option_parse(option_data
, option_data_len
, &offset
, &subopt
, &subdata_len
, &subdata
);
761 case SD_DHCP6_OPTION_IAADDR
: {
762 r
= dhcp6_option_parse_ia_address(client
, ia
, subdata
, subdata_len
);
766 /* Ignore non-critical errors in the sub-option. */
769 case SD_DHCP6_OPTION_IA_PD_PREFIX
: {
770 r
= dhcp6_option_parse_ia_pdprefix(client
, ia
, subdata
, subdata_len
);
774 /* Ignore non-critical errors in the sub-option. */
777 case SD_DHCP6_OPTION_STATUS_CODE
: {
778 _cleanup_free_
char *msg
= NULL
;
780 r
= dhcp6_option_parse_status(subdata
, subdata_len
, &msg
);
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
));
788 log_dhcp6_client_errno(client
, r
,
789 "Received an IA option with an invalid status sub option, ignoring: %m");
793 log_dhcp6_client(client
, "Received an IA option with an unknown sub-option %u, ignoring", subopt
);
798 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(ENODATA
),
799 "Received an IA option without valid IA addresses or PD prefixes, ignoring.");
805 int dhcp6_option_parse_addresses(
806 const uint8_t *optval
,
808 struct in6_addr
**addrs
,
811 assert(optval
|| optlen
== 0);
815 if (optlen
== 0 || optlen
% sizeof(struct in6_addr
) != 0)
818 if (!GREEDY_REALLOC(*addrs
, *count
+ optlen
/ sizeof(struct in6_addr
)))
821 memcpy(*addrs
+ *count
, optval
, optlen
);
822 *count
+= optlen
/ sizeof(struct in6_addr
);
827 int dhcp6_option_parse_domainname(const uint8_t *optval
, size_t optlen
, char **ret
) {
828 _cleanup_free_
char *domain
= NULL
;
831 assert(optval
|| optlen
== 0);
834 r
= dns_name_from_wire_format(&optval
, &optlen
, &domain
);
842 *ret
= TAKE_PTR(domain
);
846 int dhcp6_option_parse_domainname_list(const uint8_t *optval
, size_t optlen
, char ***ret
) {
847 _cleanup_strv_free_
char **names
= NULL
;
850 assert(optval
|| optlen
== 0);
855 if (optval
[optlen
- 1] != '\0')
859 _cleanup_free_
char *name
= NULL
;
861 r
= dns_name_from_wire_format(&optval
, &optlen
, &name
);
864 if (dns_name_is_root(name
)) /* root domain */
867 r
= strv_consume(&names
, TAKE_PTR(name
));
872 *ret
= TAKE_PTR(names
);
876 static sd_dhcp6_option
* dhcp6_option_free(sd_dhcp6_option
*i
) {
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
);
888 _cleanup_free_
void *q
= memdup(data
, length
);
892 sd_dhcp6_option
*p
= new(sd_dhcp6_option
, 1);
896 *p
= (sd_dhcp6_option
) {
899 .enterprise_identifier
= enterprise_identifier
,
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
,
913 trivial_compare_func
,
915 sd_dhcp6_option_unref
);