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 "dhcp-identifier.h"
13 #include "dhcp6-internal.h"
14 #include "dhcp6-lease-internal.h"
15 #include "dhcp6-protocol.h"
16 #include "dns-domain.h"
18 #include "memory-util.h"
19 #include "sparse-endian.h"
21 #include "unaligned.h"
23 #define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
24 #define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
25 #define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
27 bool dhcp6_option_can_request(uint16_t option
) {
28 /* See Client ORO field in
29 * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
32 case SD_DHCP6_OPTION_CLIENTID
:
33 case SD_DHCP6_OPTION_SERVERID
:
34 case SD_DHCP6_OPTION_IA_NA
:
35 case SD_DHCP6_OPTION_IA_TA
:
36 case SD_DHCP6_OPTION_IAADDR
:
37 case SD_DHCP6_OPTION_ORO
:
38 case SD_DHCP6_OPTION_PREFERENCE
:
39 case SD_DHCP6_OPTION_ELAPSED_TIME
:
40 case SD_DHCP6_OPTION_RELAY_MSG
:
41 case SD_DHCP6_OPTION_AUTH
:
42 case SD_DHCP6_OPTION_UNICAST
:
43 case SD_DHCP6_OPTION_STATUS_CODE
:
44 case SD_DHCP6_OPTION_RAPID_COMMIT
:
45 case SD_DHCP6_OPTION_USER_CLASS
:
46 case SD_DHCP6_OPTION_VENDOR_CLASS
:
48 case SD_DHCP6_OPTION_VENDOR_OPTS
:
50 case SD_DHCP6_OPTION_INTERFACE_ID
:
51 case SD_DHCP6_OPTION_RECONF_MSG
:
52 case SD_DHCP6_OPTION_RECONF_ACCEPT
:
54 case SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME
:
55 case SD_DHCP6_OPTION_SIP_SERVER_ADDRESS
:
56 case SD_DHCP6_OPTION_DNS_SERVERS
:
57 case SD_DHCP6_OPTION_DOMAIN_LIST
:
59 case SD_DHCP6_OPTION_IA_PD
:
60 case SD_DHCP6_OPTION_IA_PD_PREFIX
:
62 case SD_DHCP6_OPTION_NIS_SERVERS
:
63 case SD_DHCP6_OPTION_NISP_SERVERS
:
64 case SD_DHCP6_OPTION_NIS_DOMAIN_NAME
:
65 case SD_DHCP6_OPTION_NISP_DOMAIN_NAME
:
66 case SD_DHCP6_OPTION_SNTP_SERVERS
:
67 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME
:
68 case SD_DHCP6_OPTION_BCMCS_SERVER_D
:
69 case SD_DHCP6_OPTION_BCMCS_SERVER_A
:
70 case SD_DHCP6_OPTION_GEOCONF_CIVIC
:
72 case SD_DHCP6_OPTION_REMOTE_ID
:
73 case SD_DHCP6_OPTION_SUBSCRIBER_ID
:
75 case SD_DHCP6_OPTION_CLIENT_FQDN
:
76 case SD_DHCP6_OPTION_PANA_AGENT
:
77 case SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE
:
78 case SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE
:
80 case SD_DHCP6_OPTION_ERO
:
81 case SD_DHCP6_OPTION_LQ_QUERY
:
82 case SD_DHCP6_OPTION_CLIENT_DATA
:
83 case SD_DHCP6_OPTION_CLT_TIME
:
84 case SD_DHCP6_OPTION_LQ_RELAY_DATA
:
85 case SD_DHCP6_OPTION_LQ_CLIENT_LINK
:
87 case SD_DHCP6_OPTION_MIP6_HNIDF
:
88 case SD_DHCP6_OPTION_MIP6_VDINF
:
89 case SD_DHCP6_OPTION_V6_LOST
:
90 case SD_DHCP6_OPTION_CAPWAP_AC_V6
:
92 case SD_DHCP6_OPTION_RELAY_ID
:
94 case SD_DHCP6_OPTION_IPV6_ADDRESS_MOS
:
95 case SD_DHCP6_OPTION_IPV6_FQDN_MOS
:
96 case SD_DHCP6_OPTION_NTP_SERVER
:
97 case SD_DHCP6_OPTION_V6_ACCESS_DOMAIN
:
98 case SD_DHCP6_OPTION_SIP_UA_CS_LIST
:
99 case SD_DHCP6_OPTION_BOOTFILE_URL
:
100 case SD_DHCP6_OPTION_BOOTFILE_PARAM
:
102 case SD_DHCP6_OPTION_CLIENT_ARCH_TYPE
:
104 case SD_DHCP6_OPTION_NII
:
105 case SD_DHCP6_OPTION_GEOLOCATION
:
106 case SD_DHCP6_OPTION_AFTR_NAME
:
107 case SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME
:
109 case SD_DHCP6_OPTION_RSOO
:
111 case SD_DHCP6_OPTION_PD_EXCLUDE
:
113 case SD_DHCP6_OPTION_VSS
:
115 case SD_DHCP6_OPTION_MIP6_IDINF
:
116 case SD_DHCP6_OPTION_MIP6_UDINF
:
117 case SD_DHCP6_OPTION_MIP6_HNP
:
118 case SD_DHCP6_OPTION_MIP6_HAA
:
119 case SD_DHCP6_OPTION_MIP6_HAF
:
120 case SD_DHCP6_OPTION_RDNSS_SELECTION
:
121 case SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME
:
122 case SD_DHCP6_OPTION_KRB_REALM_NAME
:
123 case SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME
:
124 case SD_DHCP6_OPTION_KRB_KDC
:
126 case SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR
:
127 case SD_DHCP6_OPTION_LINK_ADDRESS
:
128 case SD_DHCP6_OPTION_RADIUS
:
130 case SD_DHCP6_OPTION_SOL_MAX_RT
:
131 case SD_DHCP6_OPTION_INF_MAX_RT
:
132 case SD_DHCP6_OPTION_ADDRSEL
:
133 case SD_DHCP6_OPTION_ADDRSEL_TABLE
:
134 case SD_DHCP6_OPTION_V6_PCP_SERVER
:
136 case SD_DHCP6_OPTION_DHCPV4_MSG
:
138 case SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER
:
140 case SD_DHCP6_OPTION_S46_RULE
:
142 case SD_DHCP6_OPTION_S46_BR
:
144 case SD_DHCP6_OPTION_S46_DMR
:
145 case SD_DHCP6_OPTION_S46_V4V6BIND
:
146 case SD_DHCP6_OPTION_S46_PORTPARAMS
:
148 case SD_DHCP6_OPTION_S46_CONT_MAPE
:
149 case SD_DHCP6_OPTION_S46_CONT_MAPT
:
150 case SD_DHCP6_OPTION_S46_CONT_LW
:
151 case SD_DHCP6_OPTION_4RD
:
152 case SD_DHCP6_OPTION_4RD_MAP_RULE
:
153 case SD_DHCP6_OPTION_4RD_NON_MAP_RULE
:
155 case SD_DHCP6_OPTION_LQ_BASE_TIME
:
156 case SD_DHCP6_OPTION_LQ_START_TIME
:
157 case SD_DHCP6_OPTION_LQ_END_TIME
:
159 case SD_DHCP6_OPTION_CAPTIVE_PORTAL
:
160 case SD_DHCP6_OPTION_MPL_PARAMETERS
:
162 case SD_DHCP6_OPTION_ANI_ATT
:
163 case SD_DHCP6_OPTION_ANI_NETWORK_NAME
:
164 case SD_DHCP6_OPTION_ANI_AP_NAME
:
165 case SD_DHCP6_OPTION_ANI_AP_BSSID
:
166 case SD_DHCP6_OPTION_ANI_OPERATOR_ID
:
167 case SD_DHCP6_OPTION_ANI_OPERATOR_REALM
:
169 case SD_DHCP6_OPTION_S46_PRIORITY
:
171 case SD_DHCP6_OPTION_MUD_URL_V6
:
173 case SD_DHCP6_OPTION_V6_PREFIX64
:
175 case SD_DHCP6_OPTION_F_BINDING_STATUS
:
176 case SD_DHCP6_OPTION_F_CONNECT_FLAGS
:
177 case SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO
:
178 case SD_DHCP6_OPTION_F_DNS_HOST_NAME
:
179 case SD_DHCP6_OPTION_F_DNS_ZONE_NAME
:
180 case SD_DHCP6_OPTION_F_DNS_FLAGS
:
181 case SD_DHCP6_OPTION_F_EXPIRATION_TIME
:
182 case SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD
:
183 case SD_DHCP6_OPTION_F_MCLT
:
184 case SD_DHCP6_OPTION_F_PARTNER_LIFETIME
:
185 case SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT
:
186 case SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME
:
187 case SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME
:
188 case SD_DHCP6_OPTION_F_PROTOCOL_VERSION
:
189 case SD_DHCP6_OPTION_F_KEEPALIVE_TIME
:
190 case SD_DHCP6_OPTION_F_RECONFIGURE_DATA
:
191 case SD_DHCP6_OPTION_F_RELATIONSHIP_NAME
:
192 case SD_DHCP6_OPTION_F_SERVER_FLAGS
:
193 case SD_DHCP6_OPTION_F_SERVER_STATE
:
194 case SD_DHCP6_OPTION_F_START_TIME_OF_STATE
:
195 case SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME
:
196 case SD_DHCP6_OPTION_RELAY_PORT
:
198 case SD_DHCP6_OPTION_V6_SZTP_REDIRECT
:
199 case SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX
:
201 case SD_DHCP6_OPTION_IA_LL
:
202 case SD_DHCP6_OPTION_LLADDR
:
203 case SD_DHCP6_OPTION_SLAP_QUAD
:
205 case SD_DHCP6_OPTION_V6_DOTS_RI
:
206 case SD_DHCP6_OPTION_V6_DOTS_ADDRESS
:
207 case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF
:
214 static int option_append_hdr(uint8_t **buf
, size_t *buflen
, uint16_t optcode
, size_t optlen
) {
215 assert_return(buf
, -EINVAL
);
216 assert_return(*buf
, -EINVAL
);
217 assert_return(buflen
, -EINVAL
);
219 if (optlen
> 0xffff || *buflen
< optlen
+ offsetof(DHCP6Option
, data
))
222 unaligned_write_be16(*buf
+ offsetof(DHCP6Option
, code
), optcode
);
223 unaligned_write_be16(*buf
+ offsetof(DHCP6Option
, len
), optlen
);
225 *buf
+= offsetof(DHCP6Option
, data
);
226 *buflen
-= offsetof(DHCP6Option
, data
);
231 int dhcp6_option_append(uint8_t **buf
, size_t *buflen
, uint16_t code
,
232 size_t optlen
, const void *optval
) {
235 assert_return(optval
|| optlen
== 0, -EINVAL
);
237 r
= option_append_hdr(buf
, buflen
, code
, optlen
);
241 memcpy_safe(*buf
, optval
, optlen
);
249 int dhcp6_option_append_vendor_option(uint8_t **buf
, size_t *buflen
, OrderedHashmap
*vendor_options
) {
250 sd_dhcp6_option
*options
;
256 assert(vendor_options
);
258 ORDERED_HASHMAP_FOREACH(options
, vendor_options
) {
259 _cleanup_free_
uint8_t *p
= NULL
;
262 total
= 4 + 2 + 2 + options
->length
;
268 unaligned_write_be32(p
, options
->enterprise_identifier
);
269 unaligned_write_be16(p
+ 4, options
->option
);
270 unaligned_write_be16(p
+ 6, options
->length
);
271 memcpy(p
+ 8, options
->data
, options
->length
);
273 r
= dhcp6_option_append(buf
, buflen
, SD_DHCP6_OPTION_VENDOR_OPTS
, total
, p
);
281 static int option_append_ia_address(uint8_t **buf
, size_t *buflen
, const struct iaaddr
*address
) {
290 /* Do not append T1 and T2. */
291 a
= (struct iaaddr
) {
292 .address
= address
->address
,
295 r
= option_append_hdr(buf
, buflen
, SD_DHCP6_OPTION_IAADDR
, sizeof(struct iaaddr
));
299 memcpy(*buf
, &a
, sizeof(struct iaaddr
));
301 *buf
+= sizeof(struct iaaddr
);
302 *buflen
-= sizeof(struct iaaddr
);
304 return offsetof(DHCP6Option
, data
) + sizeof(struct iaaddr
);
307 static int option_append_pd_prefix(uint8_t **buf
, size_t *buflen
, const struct iapdprefix
*prefix
) {
316 if (prefix
->prefixlen
== 0)
319 /* Do not append T1 and T2. */
320 p
= (struct iapdprefix
) {
321 .prefixlen
= prefix
->prefixlen
,
322 .address
= prefix
->address
,
325 r
= option_append_hdr(buf
, buflen
, SD_DHCP6_OPTION_IA_PD_PREFIX
, sizeof(struct iapdprefix
));
329 memcpy(*buf
, &p
, sizeof(struct iapdprefix
));
331 *buf
+= sizeof(struct iapdprefix
);
332 *buflen
-= sizeof(struct iapdprefix
);
334 return offsetof(DHCP6Option
, data
) + sizeof(struct iapdprefix
);
337 int dhcp6_option_append_ia(uint8_t **buf
, size_t *buflen
, const DHCP6IA
*ia
) {
338 struct ia_header header
;
339 const DHCP6Address
*addr
;
345 assert_return(buf
, -EINVAL
);
346 assert_return(*buf
, -EINVAL
);
347 assert_return(buflen
, -EINVAL
);
348 assert_return(ia
, -EINVAL
);
350 /* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
353 case SD_DHCP6_OPTION_IA_NA
:
354 case SD_DHCP6_OPTION_IA_PD
:
355 len
= sizeof(struct ia_header
);
356 header
= (struct ia_header
) {
361 case SD_DHCP6_OPTION_IA_TA
:
362 len
= sizeof(be32_t
); /* IA_TA does not have lifetime. */
363 header
= (struct ia_header
) {
369 assert_not_reached();
372 if (*buflen
< offsetof(DHCP6Option
, data
) + len
)
378 /* The header will be written at the end of this function. */
379 *buf
+= offsetof(DHCP6Option
, data
);
380 *buflen
-= offsetof(DHCP6Option
, data
);
382 memcpy(*buf
, &header
, len
);
386 LIST_FOREACH(addresses
, addr
, ia
->addresses
) {
387 if (ia
->type
== SD_DHCP6_OPTION_IA_PD
)
388 r
= option_append_pd_prefix(buf
, buflen
, &addr
->iapdprefix
);
390 r
= option_append_ia_address(buf
, buflen
, &addr
->iaaddr
);
397 return option_append_hdr(&ia_hdr
, &ia_buflen
, ia
->type
, len
);
400 int dhcp6_option_append_fqdn(uint8_t **buf
, size_t *buflen
, const char *fqdn
) {
401 uint8_t buffer
[1 + DNS_WIRE_FORMAT_HOSTNAME_MAX
];
404 assert_return(buf
&& *buf
&& buflen
&& fqdn
, -EINVAL
);
406 buffer
[0] = DHCP6_FQDN_FLAG_S
; /* Request server to perform AAAA RR DNS updates */
408 /* Store domain name after flags field */
409 r
= dns_name_to_wire_format(fqdn
, buffer
+ 1, sizeof(buffer
) - 1, false);
414 * According to RFC 4704, chapter 4.2 only add terminating zero-length
415 * label in case a FQDN is provided. Since dns_name_to_wire_format
416 * always adds terminating zero-length label remove if only a hostname
419 if (dns_name_is_single_label(fqdn
))
422 r
= dhcp6_option_append(buf
, buflen
, SD_DHCP6_OPTION_CLIENT_FQDN
, 1 + r
, buffer
);
427 int dhcp6_option_append_user_class(uint8_t **buf
, size_t *buflen
, char * const *user_class
) {
428 _cleanup_free_
uint8_t *p
= NULL
;
429 size_t total
= 0, offset
= 0;
435 assert(!strv_isempty(user_class
));
437 STRV_FOREACH(s
, user_class
) {
438 size_t len
= strlen(*s
);
441 if (len
> 0xffff || len
== 0)
443 q
= realloc(p
, total
+ len
+ 2);
449 unaligned_write_be16(&p
[offset
], len
);
450 memcpy(&p
[offset
+ 2], *s
, len
);
456 return dhcp6_option_append(buf
, buflen
, SD_DHCP6_OPTION_USER_CLASS
, total
, p
);
459 int dhcp6_option_append_vendor_class(uint8_t **buf
, size_t *buflen
, char * const *vendor_class
) {
460 _cleanup_free_
uint8_t *p
= NULL
;
461 uint32_t enterprise_identifier
;
462 size_t total
, offset
;
468 assert(!strv_isempty(vendor_class
));
470 enterprise_identifier
= htobe32(SYSTEMD_PEN
);
472 p
= memdup(&enterprise_identifier
, sizeof(enterprise_identifier
));
476 total
= sizeof(enterprise_identifier
);
479 STRV_FOREACH(s
, vendor_class
) {
480 size_t len
= strlen(*s
);
483 if (len
> UINT16_MAX
|| len
== 0)
486 q
= realloc(p
, total
+ len
+ 2);
492 unaligned_write_be16(&p
[offset
], len
);
493 memcpy(&p
[offset
+ 2], *s
, len
);
499 return dhcp6_option_append(buf
, buflen
, SD_DHCP6_OPTION_VENDOR_CLASS
, total
, p
);
502 int dhcp6_option_parse(
506 uint16_t *ret_option_code
,
507 size_t *ret_option_data_len
,
508 const uint8_t **ret_option_data
) {
514 assert(ret_option_code
);
515 assert(ret_option_data_len
);
516 assert(ret_option_data
);
518 if (buflen
< offsetof(DHCP6Option
, data
))
521 if (*offset
>= buflen
- offsetof(DHCP6Option
, data
))
524 len
= unaligned_read_be16(buf
+ *offset
+ offsetof(DHCP6Option
, len
));
526 if (len
> buflen
- offsetof(DHCP6Option
, data
) - *offset
)
529 *ret_option_code
= unaligned_read_be16(buf
+ *offset
+ offsetof(DHCP6Option
, code
));
530 *ret_option_data_len
= len
;
531 *ret_option_data
= buf
+ *offset
+ offsetof(DHCP6Option
, data
);
532 *offset
+= offsetof(DHCP6Option
, data
) + len
;
537 int dhcp6_option_parse_status(const uint8_t *data
, size_t data_len
, char **ret_status_message
) {
540 if (data_len
< sizeof(uint16_t))
543 if (ret_status_message
) {
546 /* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415.
547 * Let's escape unsafe characters for safety. */
548 msg
= cescape_length((const char*) (data
+ sizeof(uint16_t)), data_len
- sizeof(uint16_t));
552 *ret_status_message
= msg
;
555 return unaligned_read_be16(data
);
558 static int dhcp6_option_parse_ia_options(sd_dhcp6_client
*client
, const uint8_t *buf
, size_t buflen
) {
561 assert(buf
|| buflen
== 0);
563 for(size_t offset
= 0; offset
< buflen
;) {
568 r
= dhcp6_option_parse(buf
, buflen
, &offset
, &code
, &data_len
, &data
);
573 case SD_DHCP6_OPTION_STATUS_CODE
: {
574 _cleanup_free_
char *msg
= NULL
;
576 r
= dhcp6_option_parse_status(data
, data_len
, &msg
);
580 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
581 "Received an IA address or PD prefix option with non-zero status: %s%s%s",
582 strempty(msg
), isempty(msg
) ? "" : ": ",
583 dhcp6_message_status_to_string(r
));
585 /* Let's log but ignore the invalid status option. */
586 log_dhcp6_client_errno(client
, r
,
587 "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
591 log_dhcp6_client(client
, "Received an unknown sub option %u in IA address or PD prefix, ignoring.", code
);
598 static int dhcp6_option_parse_ia_address(sd_dhcp6_client
*client
, DHCP6IA
*ia
, const uint8_t *data
, size_t len
) {
599 _cleanup_free_ DHCP6Address
*a
= NULL
;
600 uint32_t lt_valid
, lt_pref
;
604 assert(data
|| len
== 0);
606 if (!IN_SET(ia
->type
, SD_DHCP6_OPTION_IA_NA
, SD_DHCP6_OPTION_IA_TA
))
607 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
608 "Received an IA address sub-option in an invalid option, ignoring.");
610 if (len
< sizeof(struct iaaddr
))
613 a
= new(DHCP6Address
, 1);
617 memcpy(&a
->iaaddr
, data
, sizeof(struct iaaddr
));
619 lt_valid
= be32toh(a
->iaaddr
.lifetime_valid
);
620 lt_pref
= be32toh(a
->iaaddr
.lifetime_preferred
);
623 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
624 "Received an IA address with zero valid lifetime, ignoring.");
625 if (lt_pref
> lt_valid
)
626 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
627 "Received an IA address with preferred lifetime %"PRIu32
628 " larger than valid lifetime %"PRIu32
", ignoring.",
631 if (len
> sizeof(struct iaaddr
)) {
632 r
= dhcp6_option_parse_ia_options(client
, data
+ sizeof(struct iaaddr
), len
- sizeof(struct iaaddr
));
637 LIST_PREPEND(addresses
, ia
->addresses
, TAKE_PTR(a
));
641 static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client
*client
, DHCP6IA
*ia
, const uint8_t *data
, size_t len
) {
642 _cleanup_free_ DHCP6Address
*a
= NULL
;
643 uint32_t lt_valid
, lt_pref
;
647 assert(data
|| len
== 0);
649 if (ia
->type
!= SD_DHCP6_OPTION_IA_PD
)
650 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
651 "Received an PD prefix sub-option in an invalid option, ignoring");
653 if (len
< sizeof(struct iapdprefix
))
656 a
= new(DHCP6Address
, 1);
660 memcpy(&a
->iapdprefix
, data
, sizeof(struct iapdprefix
));
662 lt_valid
= be32toh(a
->iapdprefix
.lifetime_valid
);
663 lt_pref
= be32toh(a
->iapdprefix
.lifetime_preferred
);
666 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
667 "Received a PD prefix with zero valid lifetime, ignoring.");
668 if (lt_pref
> lt_valid
)
669 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
670 "Received a PD prefix with preferred lifetime %"PRIu32
671 " larger than valid lifetime %"PRIu32
", ignoring.",
674 if (len
> sizeof(struct iapdprefix
)) {
675 r
= dhcp6_option_parse_ia_options(client
, data
+ sizeof(struct iapdprefix
), len
- sizeof(struct iapdprefix
));
680 LIST_PREPEND(addresses
, ia
->addresses
, TAKE_PTR(a
));
684 int dhcp6_option_parse_ia(
685 sd_dhcp6_client
*client
,
687 uint16_t option_code
,
688 size_t option_data_len
,
689 const uint8_t *option_data
,
692 _cleanup_(dhcp6_ia_freep
) DHCP6IA
*ia
= NULL
;
693 uint32_t lt_t1
, lt_t2
;
697 assert(IN_SET(option_code
, SD_DHCP6_OPTION_IA_NA
, SD_DHCP6_OPTION_IA_TA
, SD_DHCP6_OPTION_IA_PD
));
698 assert(option_data
|| option_data_len
== 0);
701 /* This will return the following:
702 * -ENOMEM: memory allocation error,
703 * -ENOANO: unmatching IAID,
704 * -EINVAL: non-zero status code, or invalid lifetime,
705 * -EBADMSG: invalid message format,
706 * -ENODATA: no valid address or PD prefix,
709 switch (option_code
) {
710 case SD_DHCP6_OPTION_IA_NA
:
711 case SD_DHCP6_OPTION_IA_PD
:
712 header_len
= sizeof(struct ia_header
);
715 case SD_DHCP6_OPTION_IA_TA
:
716 header_len
= sizeof(be32_t
); /* IA_TA does not have lifetime. */
720 assert_not_reached();
723 if (option_data_len
< header_len
)
726 ia
= new(DHCP6IA
, 1);
733 memcpy(&ia
->header
, option_data
, header_len
);
735 /* According to RFC8415, IAs which do not match the client's IAID should be ignored,
736 * but not necessary to ignore or refuse the whole message. */
737 if (ia
->header
.id
!= iaid
)
738 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(ENOANO
),
739 "Received an IA option with a different IAID "
740 "from the one chosen by the client, ignoring.");
742 /* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */
743 lt_t1
= be32toh(ia
->header
.lifetime_t1
);
744 lt_t2
= be32toh(ia
->header
.lifetime_t2
);
747 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
748 "Received an IA option with T1 %"PRIu32
"sec > T2 %"PRIu32
"sec, ignoring.",
750 if (lt_t1
== 0 && lt_t2
> 0)
751 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
752 "Received an IA option with zero T1 and non-zero T2 (%"PRIu32
"sec), ignoring.",
755 for (size_t offset
= header_len
; offset
< option_data_len
;) {
756 const uint8_t *subdata
;
760 r
= dhcp6_option_parse(option_data
, option_data_len
, &offset
, &subopt
, &subdata_len
, &subdata
);
765 case SD_DHCP6_OPTION_IAADDR
: {
766 r
= dhcp6_option_parse_ia_address(client
, ia
, subdata
, subdata_len
);
770 /* Ignore non-critical errors in the sub-option. */
773 case SD_DHCP6_OPTION_IA_PD_PREFIX
: {
774 r
= dhcp6_option_parse_ia_pdprefix(client
, ia
, subdata
, subdata_len
);
778 /* Ignore non-critical errors in the sub-option. */
781 case SD_DHCP6_OPTION_STATUS_CODE
: {
782 _cleanup_free_
char *msg
= NULL
;
784 r
= dhcp6_option_parse_status(subdata
, subdata_len
, &msg
);
788 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(EINVAL
),
789 "Received an IA option with non-zero status: %s%s%s",
790 strempty(msg
), isempty(msg
) ? "" : ": ",
791 dhcp6_message_status_to_string(r
));
793 log_dhcp6_client_errno(client
, r
,
794 "Received an IA option with an invalid status sub option, ignoring: %m");
798 log_dhcp6_client(client
, "Received an IA option with an unknown sub-option %u, ignoring", subopt
);
803 return log_dhcp6_client_errno(client
, SYNTHETIC_ERRNO(ENODATA
),
804 "Received an IA option without valid IA addresses or PD prefixes, ignoring.");
810 int dhcp6_option_parse_addresses(
811 const uint8_t *optval
,
813 struct in6_addr
**addrs
,
820 if (optlen
== 0 || optlen
% sizeof(struct in6_addr
) != 0)
823 if (!GREEDY_REALLOC(*addrs
, *count
+ optlen
/ sizeof(struct in6_addr
)))
826 memcpy(*addrs
+ *count
, optval
, optlen
);
827 *count
+= optlen
/ sizeof(struct in6_addr
);
832 static int parse_domain(const uint8_t **data
, size_t *len
, char **ret
) {
833 _cleanup_free_
char *domain
= NULL
;
834 const uint8_t *optval
;
835 size_t optlen
, n
= 0;
869 label
= (const char*) optval
;
873 if (!GREEDY_REALLOC(domain
, n
+ (n
!= 0) + DNS_LABEL_ESCAPED_MAX
))
879 r
= dns_label_escape(label
, c
, domain
+ n
, DNS_LABEL_ESCAPED_MAX
);
887 if (!GREEDY_REALLOC(domain
, n
+ 1))
893 *ret
= TAKE_PTR(domain
);
900 int dhcp6_option_parse_domainname(const uint8_t *optval
, size_t optlen
, char **ret
) {
901 _cleanup_free_
char *domain
= NULL
;
907 r
= parse_domain(&optval
, &optlen
, &domain
);
915 *ret
= TAKE_PTR(domain
);
919 int dhcp6_option_parse_domainname_list(const uint8_t *optval
, size_t optlen
, char ***ret
) {
920 _cleanup_strv_free_
char **names
= NULL
;
928 if (optval
[optlen
- 1] != '\0')
932 _cleanup_free_
char *name
= NULL
;
934 r
= parse_domain(&optval
, &optlen
, &name
);
940 r
= strv_consume(&names
, TAKE_PTR(name
));
945 *ret
= TAKE_PTR(names
);
949 static sd_dhcp6_option
* dhcp6_option_free(sd_dhcp6_option
*i
) {
957 int sd_dhcp6_option_new(uint16_t option
, const void *data
, size_t length
, uint32_t enterprise_identifier
, sd_dhcp6_option
**ret
) {
958 assert_return(ret
, -EINVAL
);
959 assert_return(length
== 0 || data
, -EINVAL
);
961 _cleanup_free_
void *q
= memdup(data
, length
);
965 sd_dhcp6_option
*p
= new(sd_dhcp6_option
, 1);
969 *p
= (sd_dhcp6_option
) {
972 .enterprise_identifier
= enterprise_identifier
,
981 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_option
, sd_dhcp6_option
, dhcp6_option_free
);
982 DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
983 dhcp6_option_hash_ops
,
986 trivial_compare_func
,
988 sd_dhcp6_option_unref
);