2 * Copyright (C) 2013 Steven Barth <steven@midlink.org>
3 * Copyright (C) 2016 Hans Dedecker <dedeckeh@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License v2 as published by
7 * the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
19 #include "dhcpv6-ia.h"
20 #include "statefiles.h"
31 #include <arpa/inet.h>
33 #include <libubox/md5.h>
35 static void dhcpv6_netevent_cb(unsigned long event
, struct netevent_handler_info
*info
);
36 static void apply_lease(struct dhcpv6_lease
*a
, bool add
);
37 static void set_border_assignment_size(struct interface
*iface
, struct dhcpv6_lease
*b
);
38 static void handle_addrlist_change(struct netevent_handler_info
*info
);
39 static void start_reconf(struct dhcpv6_lease
*a
);
40 static void stop_reconf(struct dhcpv6_lease
*a
);
41 static void valid_until_cb(struct uloop_timeout
*event
);
43 static struct netevent_handler dhcpv6_netevent_handler
= { .cb
= dhcpv6_netevent_cb
, };
44 static struct uloop_timeout valid_until_timeout
= {.cb
= valid_until_cb
};
45 static uint32_t serial
= 0;
47 static struct dhcpv6_lease
*
48 dhcpv6_alloc_lease(size_t extra_len
)
50 struct dhcpv6_lease
*a
= calloc(1, sizeof(*a
) + extra_len
);
55 INIT_LIST_HEAD(&a
->head
);
56 INIT_LIST_HEAD(&a
->lease_cfg_list
);
61 void dhcpv6_free_lease(struct dhcpv6_lease
*a
)
67 list_del(&a
->lease_cfg_list
);
69 if (a
->bound
&& (a
->flags
& OAF_DHCPV6_PD
))
70 apply_lease(a
, false);
79 int dhcpv6_ia_init(void)
81 uloop_timeout_set(&valid_until_timeout
, 1000);
83 netlink_add_netevent_handler(&dhcpv6_netevent_handler
);
88 int dhcpv6_ia_setup_interface(struct interface
*iface
, bool enable
)
90 enable
= enable
&& (iface
->dhcpv6
== MODE_SERVER
);
93 struct dhcpv6_lease
*border
;
95 if (list_empty(&iface
->ia_assignments
)) {
96 border
= dhcpv6_alloc_lease(0);
99 warn("Failed to alloc border on %s", iface
->name
);
104 list_add(&border
->head
, &iface
->ia_assignments
);
106 border
= list_last_entry(&iface
->ia_assignments
, struct dhcpv6_lease
, head
);
108 set_border_assignment_size(iface
, border
);
110 struct dhcpv6_lease
*c
;
112 while (!list_empty(&iface
->ia_assignments
)) {
113 c
= list_first_entry(&iface
->ia_assignments
, struct dhcpv6_lease
, head
);
114 dhcpv6_free_lease(c
);
122 static void dhcpv6_netevent_cb(unsigned long event
, struct netevent_handler_info
*info
)
124 struct interface
*iface
= info
->iface
;
126 if (!iface
|| iface
->dhcpv6
!= MODE_SERVER
)
130 case NETEV_ADDR6LIST_CHANGE
:
131 handle_addrlist_change(info
);
138 size_t get_preferred_addr(const struct odhcpd_ipaddr
*addrs
, const size_t addrlen
)
142 for (i
= 0, m
= 0; i
< addrlen
; ++i
) {
143 if (addrs
[i
].preferred_lt
> addrs
[m
].preferred_lt
||
144 (addrs
[i
].preferred_lt
== addrs
[m
].preferred_lt
&&
145 memcmp(&addrs
[i
].addr
, &addrs
[m
].addr
, 16) > 0))
161 static int send_reconf(struct dhcpv6_lease
*assign
)
163 struct interface
*iface
= assign
->iface
;
164 struct dhcpv6_client_header hdr
= {
165 .msg_type
= DHCPV6_MSG_RECONFIGURE
,
166 .transaction_id
= { 0, 0, 0 },
171 uint8_t data
[DUID_MAX_LEN
];
172 } _o_packed serverid
= {
173 .code
= htons(DHCPV6_OPT_SERVERID
),
180 uint8_t data
[DUID_MAX_LEN
];
181 } _o_packed clientid
= {
182 .code
= htons(DHCPV6_OPT_CLIENTID
),
183 .len
= htons(assign
->duid_len
),
190 } _o_packed message
= {
191 .code
= htons(DHCPV6_OPT_RECONF_MSG
),
193 .id
= DHCPV6_MSG_RENEW
,
195 struct dhcpv6_auth_reconfigure auth
= {
196 .type
= htons(DHCPV6_OPT_AUTH
),
197 .len
= htons(sizeof(struct dhcpv6_auth_reconfigure
)),
201 .replay
= { htonl(time(NULL
)), htonl(++serial
) },
206 if (config
.default_duid_len
> 0) {
207 memcpy(serverid
.data
, config
.default_duid
, config
.default_duid_len
);
208 serverid
.len
= htons(config
.default_duid_len
);
210 uint16_t duid_ll_hdr
[] = { htons(DUID_TYPE_LL
), htons(ARPHRD_ETHER
) };
211 memcpy(serverid
.data
, duid_ll_hdr
, sizeof(duid_ll_hdr
));
212 odhcpd_get_mac(iface
, &serverid
.data
[sizeof(duid_ll_hdr
)]);
213 serverid
.len
= htons(sizeof(duid_ll_hdr
) + ETH_ALEN
);
216 memcpy(clientid
.data
, assign
->duid
, assign
->duid_len
);
218 struct iovec iov
[IOV_TOTAL
] = {
219 [IOV_HDR
] = { &hdr
, sizeof(hdr
) },
220 [IOV_SERVERID
] = { &serverid
, sizeof(serverid
) },
221 [IOV_CLIENTID
] = { &clientid
, sizeof(clientid
) },
222 [IOV_MESSAGE
] = { &message
, sizeof(message
) },
223 [IOV_AUTH
] = { &auth
, sizeof(auth
) },
227 uint8_t secretbytes
[64];
228 memset(secretbytes
, 0, sizeof(secretbytes
));
229 memcpy(secretbytes
, assign
->key
, sizeof(assign
->key
));
231 for (size_t i
= 0; i
< sizeof(secretbytes
); ++i
)
232 secretbytes
[i
] ^= 0x36;
235 md5_hash(secretbytes
, sizeof(secretbytes
), &md5
);
236 for (size_t i
= 0; i
< ARRAY_SIZE(iov
); i
++)
237 md5_hash(iov
[i
].iov_base
, iov
[i
].iov_len
, &md5
);
238 md5_end(auth
.key
, &md5
);
240 for (size_t i
= 0; i
< sizeof(secretbytes
); ++i
) {
241 secretbytes
[i
] ^= 0x36;
242 secretbytes
[i
] ^= 0x5c;
246 md5_hash(secretbytes
, sizeof(secretbytes
), &md5
);
247 md5_hash(auth
.key
, 16, &md5
);
248 md5_end(auth
.key
, &md5
);
250 return odhcpd_send(iface
->dhcpv6_event
.uloop
.fd
, &assign
->peer
, iov
, ARRAY_SIZE(iov
), iface
);
253 static void in6_copy_iid(struct in6_addr
*dest
, uint64_t iid
, unsigned n
)
255 uint64_t iid_be
= htobe64(iid
);
256 uint8_t *iid_bytes
= (uint8_t *)&iid_be
;
257 unsigned bytes
= n
/ 8;
258 unsigned bits
= n
% 8;
260 if (n
== 0 || n
> 64)
263 memcpy(&dest
->s6_addr
[16 - bytes
], &iid_bytes
[8 - bytes
], bytes
);
266 unsigned dest_idx
= 16 - bytes
- 1;
267 unsigned src_idx
= 8 - bytes
- 1;
268 uint8_t mask
= (1 << bits
) - 1;
269 dest
->s6_addr
[dest_idx
] = (dest
->s6_addr
[dest_idx
] & ~mask
) |
270 (iid_bytes
[src_idx
] & mask
);
274 struct in6_addr
in6_from_prefix_and_iid(const struct odhcpd_ipaddr
*prefix
, uint64_t iid
)
276 struct in6_addr addr
;
277 uint8_t iid_len
= min(128 - prefix
->prefix_len
, 64);
279 addr
= prefix
->addr
.in6
;
280 in6_copy_iid(&addr
, iid
, iid_len
);
285 static void __apply_lease(struct dhcpv6_lease
*a
,
286 struct odhcpd_ipaddr
*addrs
, ssize_t addr_len
, bool add
)
288 if (a
->flags
& OAF_DHCPV6_NA
)
291 for (ssize_t i
= 0; i
< addr_len
; ++i
) {
292 struct in6_addr prefix
;
294 if (ADDR_MATCH_PIO_FILTER(&addrs
[i
], a
->iface
))
297 prefix
= addrs
[i
].addr
.in6
;
298 prefix
.s6_addr32
[1] |= htonl(a
->assigned_subnet_id
);
299 prefix
.s6_addr32
[2] = prefix
.s6_addr32
[3] = 0;
300 netlink_setup_route(&prefix
, a
->length
, a
->iface
->ifindex
,
301 &a
->peer
.sin6_addr
, 1024, add
);
305 static void apply_lease(struct dhcpv6_lease
*a
, bool add
)
307 struct interface
*iface
= a
->iface
;
308 struct odhcpd_ipaddr
*addrs
= iface
->addr6
;
309 ssize_t addrlen
= (ssize_t
)iface
->addr6_len
;
311 __apply_lease(a
, addrs
, addrlen
, add
);
314 /* Set border assignment size based on the IPv6 address prefixes */
315 static void set_border_assignment_size(struct interface
*iface
, struct dhcpv6_lease
*b
)
317 time_t now
= odhcpd_time();
320 for (size_t i
= 0; i
< iface
->addr6_len
; ++i
) {
321 struct odhcpd_ipaddr
*addr
= &iface
->addr6
[i
];
323 if (ADDR_MATCH_PIO_FILTER(addr
, iface
))
326 if (addr
->preferred_lt
> (uint32_t)now
&&
327 addr
->prefix_len
< 64 &&
328 addr
->prefix_len
> minprefix
)
329 minprefix
= addr
->prefix_len
;
332 if (minprefix
> 32 && minprefix
<= 64)
333 b
->assigned_subnet_id
= 1U << (64 - minprefix
);
335 b
->assigned_subnet_id
= 0;
338 static bool assign_pd(struct interface
*iface
, struct dhcpv6_lease
*assign
)
340 struct dhcpv6_lease
*c
;
342 if (iface
->addr6_len
< 1)
345 /* Try honoring the hint first */
346 uint32_t current
= 1, asize
= (1 << (64 - assign
->length
)) - 1;
347 if (assign
->assigned_subnet_id
) {
348 list_for_each_entry(c
, &iface
->ia_assignments
, head
) {
349 if (c
->flags
& OAF_DHCPV6_NA
)
352 if (assign
->assigned_subnet_id
>= current
&& assign
->assigned_subnet_id
+ asize
< c
->assigned_subnet_id
) {
353 list_add_tail(&assign
->head
, &c
->head
);
356 apply_lease(assign
, true);
361 current
= (c
->assigned_subnet_id
+ (1 << (64 - c
->length
)));
365 /* Fallback to a variable assignment */
367 list_for_each_entry(c
, &iface
->ia_assignments
, head
) {
368 if (c
->flags
& OAF_DHCPV6_NA
)
371 current
= (current
+ asize
) & (~asize
);
373 if (current
+ asize
< c
->assigned_subnet_id
) {
374 assign
->assigned_subnet_id
= current
;
375 list_add_tail(&assign
->head
, &c
->head
);
378 apply_lease(assign
, true);
383 current
= (c
->assigned_subnet_id
+ (1 << (64 - c
->length
)));
389 /* Check iid against reserved IPv6 interface identifiers.
390 * Refer to: http://www.iana.org/assignments/ipv6-interface-ids
392 static bool is_reserved_ipv6_iid(uint64_t iid
)
394 if (iid
== 0x0000000000000000)
395 /* Subnet-Router Anycast [RFC4291] */
398 if ((iid
& 0xFFFFFFFFFF000000) == 0x02005EFFFE000000)
399 /* Reserved IPv6 Interface Identifiers corresponding
400 * to the IANA Ethernet Block [RFC4291]
404 if ((iid
& 0xFFFFFFFFFFFFFF80) == 0xFDFFFFFFFFFFFF80)
405 /* Reserved Subnet Anycast Addresses [RFC2526] */
411 static bool assign_na(struct interface
*iface
, struct dhcpv6_lease
*a
)
413 struct dhcpv6_lease
*c
;
414 uint64_t pool_start
= 0x100;
415 uint64_t pool_end
= (iface
->dhcpv6_hostid_len
>= 64) ? UINT64_MAX
: ((1ULL << iface
->dhcpv6_hostid_len
) - 1);
416 uint64_t pool_size
= pool_end
- pool_start
+ 1;
418 unsigned short xsubi
[3] = { 0 };
420 /* Preconfigured assignment by static lease */
421 if (a
->assigned_host_id
) {
422 list_for_each_entry(c
, &iface
->ia_assignments
, head
) {
423 if (!(c
->flags
& OAF_DHCPV6_NA
) || c
->assigned_host_id
> a
->assigned_host_id
) {
424 list_add_tail(&a
->head
, &c
->head
);
426 } else if (c
->assigned_host_id
== a
->assigned_host_id
)
431 /* Pick a starting point, using the last bytes of the DUID as seed... */
433 a
->duid
+ (a
->duid_len
> sizeof(xsubi
) ? a
->duid_len
- sizeof(xsubi
) : 0),
434 min(a
->duid_len
, sizeof(xsubi
)));
435 try = ((uint64_t)jrand48(xsubi
) << 32) | (jrand48(xsubi
) & UINT32_MAX
);
436 try = pool_start
+ try % pool_size
;
438 /* ...then try to assign sequentially from that starting point... */
439 for (size_t i
= 0; i
< 100; i
++, try++) {
443 if (is_reserved_ipv6_iid(try))
446 if (config_find_lease_cfg_by_hostid(try))
449 list_for_each_entry(c
, &iface
->ia_assignments
, head
) {
450 if (!(c
->flags
& OAF_DHCPV6_NA
) || c
->assigned_host_id
> try) {
451 a
->assigned_host_id
= try;
452 list_add_tail(&a
->head
, &c
->head
);
454 } else if (c
->assigned_host_id
== try)
462 static void handle_addrlist_change(struct netevent_handler_info
*info
)
464 struct interface
*iface
= info
->iface
;
465 struct dhcpv6_lease
*c
, *d
, *border
= list_last_entry(
466 &iface
->ia_assignments
, struct dhcpv6_lease
, head
);
467 struct list_head reassign
= LIST_HEAD_INIT(reassign
);
468 time_t now
= odhcpd_time();
470 list_for_each_entry(c
, &iface
->ia_assignments
, head
) {
471 if ((c
->flags
& OAF_DHCPV6_PD
) && !(iface
->ra_flags
& ND_RA_FLAG_MANAGED
)
473 __apply_lease(c
, info
->addrs_old
.addrs
,
474 info
->addrs_old
.len
, false);
477 set_border_assignment_size(iface
, border
);
479 list_for_each_entry_safe(c
, d
, &iface
->ia_assignments
, head
) {
480 if (c
->duid_len
== 0 ||
481 !(c
->flags
& OAF_DHCPV6_PD
) ||
482 (!INFINITE_VALID(c
->valid_until
) && c
->valid_until
< now
))
485 if (c
->assigned_subnet_id
>= border
->assigned_subnet_id
)
486 list_move(&c
->head
, &reassign
);
488 apply_lease(c
, true);
490 if (c
->accept_fr_nonce
&& c
->fr_cnt
== 0) {
491 struct dhcpv6_lease
*a
;
495 /* Leave all other assignments of that client alone */
496 list_for_each_entry(a
, &iface
->ia_assignments
, head
)
497 if (a
!= c
&& a
->duid_len
== c
->duid_len
&&
498 !memcmp(a
->duid
, c
->duid
, a
->duid_len
))
503 while (!list_empty(&reassign
)) {
504 c
= list_first_entry(&reassign
, struct dhcpv6_lease
, head
);
505 list_del_init(&c
->head
);
506 if (!assign_pd(iface
, c
))
507 dhcpv6_free_lease(c
);
513 static void reconf_timeout_cb(struct uloop_timeout
*event
)
515 struct dhcpv6_lease
*a
= container_of(event
, struct dhcpv6_lease
, fr_timer
);
517 if (a
->fr_cnt
> 0 && a
->fr_cnt
< DHCPV6_REC_MAX_RC
) {
519 uloop_timeout_set(&a
->fr_timer
,
520 DHCPV6_REC_TIMEOUT
<< a
->fr_cnt
);
526 static void start_reconf(struct dhcpv6_lease
*a
)
528 uloop_timeout_set(&a
->fr_timer
,
529 DHCPV6_REC_TIMEOUT
<< a
->fr_cnt
);
530 a
->fr_timer
.cb
= reconf_timeout_cb
;
536 static void stop_reconf(struct dhcpv6_lease
*a
)
538 uloop_timeout_cancel(&a
->fr_timer
);
540 a
->fr_timer
.cb
= NULL
;
543 static void valid_until_cb(struct uloop_timeout
*event
)
545 struct interface
*iface
;
546 time_t now
= odhcpd_time();
548 avl_for_each_element(&interfaces
, iface
, avl
) {
549 struct dhcpv6_lease
*a
, *n
;
551 if (iface
->dhcpv6
!= MODE_SERVER
)
554 list_for_each_entry_safe(a
, n
, &iface
->ia_assignments
, head
) {
555 if (a
->duid_len
> 0 && !INFINITE_VALID(a
->valid_until
) && a
->valid_until
< now
)
556 dhcpv6_free_lease(a
);
559 uloop_timeout_set(event
, 1000);
562 static size_t build_ia(uint8_t *buf
, size_t buflen
, uint16_t status
,
563 const struct dhcpv6_ia_hdr
*ia
, struct dhcpv6_lease
*a
,
564 struct interface
*iface
, bool request
)
566 struct dhcpv6_ia_hdr o_ia
= {
573 size_t ia_len
= sizeof(o_ia
);
574 time_t now
= odhcpd_time();
585 .type
= htons(DHCPV6_OPT_STATUS
),
586 .len
= htons(sizeof(o_status
) - 4),
587 .val
= htons(status
),
590 memcpy(buf
+ ia_len
, &o_status
, sizeof(o_status
));
591 ia_len
+= sizeof(o_status
);
593 o_ia
.len
= htons(ia_len
- 4);
594 memcpy(buf
, &o_ia
, sizeof(o_ia
));
603 leasetime
= a
->leasetime
;
605 leasetime
= iface
->dhcp_leasetime
;
608 uint32_t floor_preferred_lifetime
, floor_valid_lifetime
; /* For calculating T1 / T2 */
610 if (iface
->max_preferred_lifetime
&& iface
->max_preferred_lifetime
< leasetime
) {
611 floor_preferred_lifetime
= iface
->max_preferred_lifetime
;
613 floor_preferred_lifetime
= leasetime
;
616 if (iface
->max_valid_lifetime
&& iface
->max_valid_lifetime
< leasetime
) {
617 floor_valid_lifetime
= iface
->max_valid_lifetime
;
619 floor_valid_lifetime
= leasetime
;
622 struct odhcpd_ipaddr
*addrs
= iface
->addr6
;
623 size_t addrlen
= iface
->addr6_len
;
624 size_t m
= get_preferred_addr(addrs
, addrlen
);
626 for (size_t i
= 0; i
< addrlen
; ++i
) {
627 uint32_t prefix_preferred_lt
, prefix_valid_lt
;
629 if (!valid_addr(&addrs
[i
], now
))
632 /* Filter Out Prefixes */
633 if (ADDR_MATCH_PIO_FILTER(&addrs
[i
], iface
)) {
634 char addrbuf
[INET6_ADDRSTRLEN
];
635 info("Address %s filtered out on %s",
636 inet_ntop(AF_INET6
, &addrs
[i
].addr
.in6
, addrbuf
, sizeof(addrbuf
)),
641 prefix_preferred_lt
= addrs
[i
].preferred_lt
;
642 prefix_valid_lt
= addrs
[i
].valid_lt
;
644 if (prefix_preferred_lt
!= UINT32_MAX
) {
645 prefix_preferred_lt
-= now
;
647 if (iface
->max_preferred_lifetime
&& prefix_preferred_lt
> iface
->max_preferred_lifetime
)
648 prefix_preferred_lt
= iface
->max_preferred_lifetime
;
651 if (prefix_valid_lt
!= UINT32_MAX
) {
652 prefix_valid_lt
-= now
;
654 if (iface
->max_valid_lifetime
&& prefix_valid_lt
> iface
->max_valid_lifetime
)
655 prefix_valid_lt
= iface
->max_valid_lifetime
;
658 if (prefix_valid_lt
> leasetime
)
659 prefix_valid_lt
= leasetime
;
661 if (prefix_preferred_lt
> prefix_valid_lt
)
662 prefix_preferred_lt
= prefix_valid_lt
;
664 if (a
->flags
& OAF_DHCPV6_PD
) {
665 struct dhcpv6_ia_prefix o_ia_p
= {
666 .type
= htons(DHCPV6_OPT_IA_PREFIX
),
667 .len
= htons(sizeof(o_ia_p
) - 4),
668 .preferred_lt
= htonl(prefix_preferred_lt
),
669 .valid_lt
= htonl(prefix_valid_lt
),
670 .prefix_len
= a
->length
,
671 .addr
= addrs
[i
].addr
.in6
,
674 o_ia_p
.addr
.s6_addr32
[1] |= htonl(a
->assigned_subnet_id
);
675 o_ia_p
.addr
.s6_addr32
[2] = o_ia_p
.addr
.s6_addr32
[3] = 0;
677 if (!valid_prefix_length(a
, addrs
[i
].prefix_len
))
680 if (buflen
< ia_len
+ sizeof(o_ia_p
))
683 memcpy(buf
+ ia_len
, &o_ia_p
, sizeof(o_ia_p
));
684 ia_len
+= sizeof(o_ia_p
);
687 if (a
->flags
& OAF_DHCPV6_NA
) {
688 struct dhcpv6_ia_addr o_ia_a
= {
689 .type
= htons(DHCPV6_OPT_IA_ADDR
),
690 .len
= htons(sizeof(o_ia_a
) - 4),
691 .addr
= in6_from_prefix_and_iid(&addrs
[i
], a
->assigned_host_id
),
692 .preferred_lt
= htonl(prefix_preferred_lt
),
693 .valid_lt
= htonl(prefix_valid_lt
)
696 if (!ADDR_ENTRY_VALID_IA_ADDR(iface
, i
, m
, addrs
))
699 if (buflen
< ia_len
+ sizeof(o_ia_a
))
702 memcpy(buf
+ ia_len
, &o_ia_a
, sizeof(o_ia_a
));
703 ia_len
+= sizeof(o_ia_a
);
706 /* Calculate T1 / T2 based on non-deprecated addresses */
707 if (prefix_preferred_lt
> 0) {
708 if (floor_preferred_lifetime
> prefix_preferred_lt
)
709 floor_preferred_lifetime
= prefix_preferred_lt
;
711 if (floor_valid_lifetime
> prefix_valid_lt
)
712 floor_valid_lifetime
= prefix_valid_lt
;
716 if (!INFINITE_VALID(a
->valid_until
))
717 /* UINT32_MAX is RFC defined as infinite lease-time */
718 a
->valid_until
= (floor_valid_lifetime
== UINT32_MAX
) ? 0 : floor_valid_lifetime
+ now
;
720 if (!INFINITE_VALID(a
->preferred_until
))
721 /* UINT32_MAX is RFC defined as infinite lease-time */
722 a
->preferred_until
= (floor_preferred_lifetime
== UINT32_MAX
) ? 0 : floor_preferred_lifetime
+ now
;
724 o_ia
.t1
= htonl((floor_preferred_lifetime
== UINT32_MAX
) ? floor_preferred_lifetime
: floor_preferred_lifetime
* 5 / 10);
725 o_ia
.t2
= htonl((floor_preferred_lifetime
== UINT32_MAX
) ? floor_preferred_lifetime
: floor_preferred_lifetime
* 8 / 10);
735 uint8_t *odata
, *end
= ((uint8_t*)ia
) + htons(ia
->len
) + 4;
736 uint16_t otype
, olen
;
738 dhcpv6_for_each_option((uint8_t*)&ia
[1], end
, otype
, olen
, odata
) {
739 struct dhcpv6_ia_prefix
*ia_p
= (struct dhcpv6_ia_prefix
*)&odata
[-4];
740 struct dhcpv6_ia_addr
*ia_a
= (struct dhcpv6_ia_addr
*)&odata
[-4];
743 if ((otype
!= DHCPV6_OPT_IA_PREFIX
|| olen
< sizeof(*ia_p
) - 4) &&
744 (otype
!= DHCPV6_OPT_IA_ADDR
|| olen
< sizeof(*ia_a
) - 4))
748 struct odhcpd_ipaddr
*addrs
= iface
->addr6
;
749 size_t addrlen
= iface
->addr6_len
;
751 for (size_t i
= 0; i
< addrlen
; ++i
) {
752 struct in6_addr addr
;
754 if (!valid_addr(&addrs
[i
], now
))
757 if (!valid_prefix_length(a
, addrs
[i
].prefix_len
))
760 if (ADDR_MATCH_PIO_FILTER(&addrs
[i
], iface
))
763 if (ia
->type
== htons(DHCPV6_OPT_IA_PD
)) {
764 addr
= addrs
[i
].addr
.in6
;
765 addr
.s6_addr32
[1] |= htonl(a
->assigned_subnet_id
);
766 addr
.s6_addr32
[2] = addr
.s6_addr32
[3] = 0;
768 if (!memcmp(&ia_p
->addr
, &addr
, sizeof(addr
)) &&
769 ia_p
->prefix_len
== a
->length
)
772 addr
= in6_from_prefix_and_iid(&addrs
[i
], a
->assigned_host_id
);
774 if (!memcmp(&ia_a
->addr
, &addr
, sizeof(addr
)))
781 if (otype
== DHCPV6_OPT_IA_PREFIX
) {
782 struct dhcpv6_ia_prefix o_ia_p
= {
783 .type
= htons(DHCPV6_OPT_IA_PREFIX
),
784 .len
= htons(sizeof(o_ia_p
) - 4),
787 .prefix_len
= ia_p
->prefix_len
,
791 if (buflen
< ia_len
+ sizeof(o_ia_p
))
794 memcpy(buf
+ ia_len
, &o_ia_p
, sizeof(o_ia_p
));
795 ia_len
+= sizeof(o_ia_p
);
797 struct dhcpv6_ia_addr o_ia_a
= {
798 .type
= htons(DHCPV6_OPT_IA_ADDR
),
799 .len
= htons(sizeof(o_ia_a
) - 4),
805 if (buflen
< ia_len
+ sizeof(o_ia_a
))
808 memcpy(buf
+ ia_len
, &o_ia_a
, sizeof(o_ia_a
));
809 ia_len
+= sizeof(o_ia_a
);
815 o_ia
.len
= htons(ia_len
- 4);
816 memcpy(buf
, &o_ia
, sizeof(o_ia
));
826 static void dhcpv6_log_ia_addr(_o_unused
struct dhcpv6_lease
*lease
, struct in6_addr
*addr
, uint8_t prefix_len
,
827 _o_unused
uint32_t pref_lt
, _o_unused
uint32_t valid_lt
, void *arg
)
829 struct log_ctxt
*ctxt
= (struct log_ctxt
*)arg
;
830 char addrbuf
[INET6_ADDRSTRLEN
];
832 inet_ntop(AF_INET6
, addr
, addrbuf
, sizeof(addrbuf
));
833 ctxt
->buf_idx
+= snprintf(ctxt
->buf
+ ctxt
->buf_idx
, ctxt
->buf_len
- ctxt
->buf_idx
,
834 " %s/%" PRIu8
, addrbuf
, prefix_len
);
837 static void dhcpv6_log(uint8_t msgtype
, struct interface
*iface
, time_t now
,
838 const char *duidbuf
, bool is_pd
, struct dhcpv6_lease
*a
, int code
)
840 const char *type
= "UNKNOWN";
841 const char *status
= "UNKNOWN";
844 case DHCPV6_MSG_SOLICIT
:
847 case DHCPV6_MSG_REQUEST
:
850 case DHCPV6_MSG_CONFIRM
:
853 case DHCPV6_MSG_RENEW
:
856 case DHCPV6_MSG_REBIND
:
859 case DHCPV6_MSG_RELEASE
:
862 case DHCPV6_MSG_DECLINE
:
868 case DHCPV6_STATUS_OK
:
871 case DHCPV6_STATUS_NOADDRSAVAIL
:
872 status
= "no addresses available";
874 case DHCPV6_STATUS_NOBINDING
:
875 status
= "no binding";
877 case DHCPV6_STATUS_NOTONLINK
:
878 status
= "not on-link";
880 case DHCPV6_STATUS_NOPREFIXAVAIL
:
881 status
= "no prefix available";
885 char leasebuf
[256] = "";
888 struct log_ctxt ctxt
= {.buf
= leasebuf
,
889 .buf_len
= sizeof(leasebuf
),
892 odhcpd_enum_addr6(iface
, a
, now
, dhcpv6_log_ia_addr
, &ctxt
);
895 info("DHCPV6 %s %s from %s on %s: %s%s", type
, (is_pd
) ? "IA_PD" : "IA_NA",
896 duidbuf
, iface
->name
, status
, leasebuf
);
899 static bool dhcpv6_ia_on_link(const struct dhcpv6_ia_hdr
*ia
, struct dhcpv6_lease
*a
,
900 struct interface
*iface
)
902 struct odhcpd_ipaddr
*addrs
= iface
->addr6
;
903 size_t addrlen
= iface
->addr6_len
;
904 time_t now
= odhcpd_time();
905 uint8_t *odata
, *end
= ((uint8_t*)ia
) + htons(ia
->len
) + 4;
906 uint16_t otype
, olen
;
909 dhcpv6_for_each_option((uint8_t*)&ia
[1], end
, otype
, olen
, odata
) {
910 struct dhcpv6_ia_prefix
*p
= (struct dhcpv6_ia_prefix
*)&odata
[-4];
911 struct dhcpv6_ia_addr
*n
= (struct dhcpv6_ia_addr
*)&odata
[-4];
913 if ((otype
!= DHCPV6_OPT_IA_PREFIX
|| olen
< sizeof(*p
) - 4) &&
914 (otype
!= DHCPV6_OPT_IA_ADDR
|| olen
< sizeof(*n
) - 4))
918 for (size_t i
= 0; i
< addrlen
; ++i
) {
919 if (!valid_addr(&addrs
[i
], now
))
922 if (ADDR_MATCH_PIO_FILTER(&addrs
[i
], iface
))
925 if (ia
->type
== htons(DHCPV6_OPT_IA_PD
)) {
926 if (p
->prefix_len
< addrs
[i
].prefix_len
||
927 odhcpd_bmemcmp(&p
->addr
, &addrs
[i
].addr
.in6
, addrs
[i
].prefix_len
))
930 } else if (odhcpd_bmemcmp(&n
->addr
, &addrs
[i
].addr
.in6
, addrs
[i
].prefix_len
))
943 ssize_t
dhcpv6_ia_handle_IAs(uint8_t *buf
, size_t buflen
, struct interface
*iface
,
944 const struct sockaddr_in6
*addr
, const void *data
, const uint8_t *end
)
946 struct dhcpv6_lease
*first
= NULL
;
947 const struct dhcpv6_client_header
*hdr
= data
;
948 time_t now
= odhcpd_time();
949 uint16_t otype
, olen
, duid_len
= 0;
950 uint8_t *start
= (uint8_t *)&hdr
[1], *odata
;
951 uint8_t *duid
= NULL
, mac
[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
952 size_t hostname_len
= 0, response_len
= 0;
953 bool notonlink
= false, rapid_commit
= false, accept_reconf
= false;
954 char duidbuf
[DUID_HEXSTRLEN
], hostname
[256];
956 dhcpv6_for_each_option(start
, end
, otype
, olen
, odata
) {
957 if (otype
== DHCPV6_OPT_CLIENTID
) {
961 if (olen
== 14 && odata
[0] == 0 && odata
[1] == 1)
962 memcpy(mac
, &odata
[8], sizeof(mac
));
963 else if (olen
== 10 && odata
[0] == 0 && odata
[1] == 3)
964 memcpy(mac
, &odata
[4], sizeof(mac
));
966 if (olen
<= DUID_MAX_LEN
)
967 odhcpd_hexlify(duidbuf
, odata
, olen
);
968 } else if (otype
== DHCPV6_OPT_FQDN
&& olen
>= 2 && olen
<= 255) {
969 uint8_t fqdn_buf
[256];
970 memcpy(fqdn_buf
, odata
, olen
);
971 fqdn_buf
[olen
++] = 0;
973 if (dn_expand(&fqdn_buf
[1], &fqdn_buf
[olen
], &fqdn_buf
[1], hostname
, sizeof(hostname
)) > 0)
974 hostname_len
= strcspn(hostname
, ".");
975 } else if (otype
== DHCPV6_OPT_RECONF_ACCEPT
)
976 accept_reconf
= true;
977 else if (otype
== DHCPV6_OPT_RAPID_COMMIT
&& hdr
->msg_type
== DHCPV6_MSG_SOLICIT
)
981 if (!duid
|| duid_len
< DUID_MIN_LEN
|| duid_len
> DUID_MAX_LEN
)
984 dhcpv6_for_each_option(start
, end
, otype
, olen
, odata
) {
985 bool is_pd
= (otype
== DHCPV6_OPT_IA_PD
);
986 bool is_na
= (otype
== DHCPV6_OPT_IA_NA
);
987 bool ia_addr_present
= false;
988 if (!is_pd
&& !is_na
)
991 struct dhcpv6_ia_hdr
*ia
= (struct dhcpv6_ia_hdr
*)&odata
[-4];
992 size_t ia_response_len
= 0;
993 uint8_t reqlen
= (is_pd
) ? 62 : 128;
994 uint32_t reqhint
= 0;
995 struct lease_cfg
*lease_cfg
;
997 lease_cfg
= config_find_lease_cfg_by_duid_and_iaid(duid
, duid_len
, ntohl(ia
->iaid
));
999 lease_cfg
= config_find_lease_cfg_by_mac(mac
);
1001 if (lease_cfg
&& lease_cfg
->ignore6
)
1004 /* Parse request hint for IA-PD */
1007 uint16_t stype
, slen
;
1008 dhcpv6_for_each_sub_option(&ia
[1], odata
+ olen
, stype
, slen
, sdata
) {
1009 if (stype
!= DHCPV6_OPT_IA_PREFIX
|| slen
< sizeof(struct dhcpv6_ia_prefix
) - 4)
1012 struct dhcpv6_ia_prefix
*p
= (struct dhcpv6_ia_prefix
*)&sdata
[-4];
1013 if (p
->prefix_len
) {
1014 reqlen
= p
->prefix_len
;
1015 reqhint
= ntohl(p
->addr
.s6_addr32
[1]);
1016 if (reqlen
> 32 && reqlen
<= 64)
1017 reqhint
&= (1U << (64 - reqlen
)) - 1;
1025 * A requesting router can include a desired prefix length for its
1026 * delegation. The delegating router (us) is not required to honor
1027 * the hint (RFC3633, section 11.2, we MAY choose to use the
1028 * information in the option; RFC8168, section 3.2 has several SHOULDs
1029 * about desired choices for selecting a prefix to delegate).
1031 * We support a policy setting to conserve prefix space, which purposely
1032 * assigns prefixes that might not match the requesting router's hint.
1034 * If the minimum prefix length is set in this interface's
1035 * configuration, we use it as a floor for the requested (hinted)
1036 * prefix length. This allows us to conserve prefix space so that
1037 * any single router can't grab too much of it. Consider if we have
1038 * an interface with a /56 prefix. A requesting router could ask for
1039 * a /58 and take 1/4 of our total address space. But if we set a
1040 * minimum of /60, we can limit each requesting router to get only
1041 * 1/16 of our total address space.
1043 if (iface
->dhcpv6_pd_min_len
&& reqlen
< iface
->dhcpv6_pd_min_len
) {
1044 info("clamping requested PD from %d to %d", reqlen
,
1045 iface
->dhcpv6_pd_min_len
);
1046 reqlen
= iface
->dhcpv6_pd_min_len
;
1050 uint16_t stype
, slen
;
1051 dhcpv6_for_each_sub_option(&ia
[1], odata
+ olen
, stype
, slen
, sdata
) {
1052 if (stype
!= DHCPV6_OPT_IA_ADDR
|| slen
< sizeof(struct dhcpv6_ia_addr
) - 4)
1055 ia_addr_present
= true;
1059 /* Find an existing assignment */
1060 struct dhcpv6_lease
*c
, *a
= NULL
;
1061 list_for_each_entry(c
, &iface
->ia_assignments
, head
) {
1062 /* If we're looking for a PD, is this a PD? */
1063 if (is_pd
&& !(c
->flags
& OAF_DHCPV6_PD
))
1066 /* If we're looking for a NA, is this a NA? */
1067 if (is_na
&& !(c
->flags
& OAF_DHCPV6_NA
))
1070 /* Is this assignment still valid? */
1071 if (!INFINITE_VALID(c
->valid_until
) && now
>= c
->valid_until
)
1074 /* Does the DUID match? */
1075 if (c
->duid_len
!= duid_len
|| memcmp(c
->duid
, duid
, duid_len
))
1078 /* Does the IAID match? */
1079 if (c
->iaid
!= ia
->iaid
) {
1086 /* Does the existing assignment stem from the same static lease cfg? */
1087 if (c
->lease_cfg
!= lease_cfg
)
1091 * If there's a DUID configured for this static lease, but without
1092 * an IAID, we will proceed under the assumption that a request
1093 * with the right DUID but with *any* IAID should be able to take
1094 * over the assignment. E.g. when switching from WiFi to ethernet
1095 * on the same client. This is similar to how multiple MAC adresses
1096 * are handled for DHCPv4.
1098 for (size_t i
= 0; i
< lease_cfg
->duid_count
; i
++) {
1099 if (lease_cfg
->duids
[i
].iaid_set
&& lease_cfg
->duids
[i
].iaid
!= htonl(ia
->iaid
))
1102 if (lease_cfg
->duids
[i
].len
!= duid_len
)
1105 if (memcmp(lease_cfg
->duids
[i
].id
, duid
, duid_len
))
1109 * Reconf doesn't specify the IAID, so we have to assume the client
1110 * already knows or doesn't care about the old assignment.
1113 dhcpv6_free_lease(c
);
1119 /* We have a match */
1124 apply_lease(a
, false);
1130 if (lease_cfg
&& a
&& a
->lease_cfg
!= lease_cfg
) {
1131 dhcpv6_free_lease(a
);
1136 /* Generic message handling */
1137 uint16_t status
= DHCPV6_STATUS_OK
;
1139 if (hdr
->msg_type
== DHCPV6_MSG_SOLICIT
||
1140 hdr
->msg_type
== DHCPV6_MSG_REQUEST
||
1141 (hdr
->msg_type
== DHCPV6_MSG_REBIND
&& !a
)) {
1142 bool assigned
= !!a
;
1145 if ((!iface
->no_dynamic_dhcp
|| (lease_cfg
&& is_na
)) &&
1146 (iface
->dhcpv6_pd
|| iface
->dhcpv6_na
)) {
1147 /* Create new binding */
1148 a
= dhcpv6_alloc_lease(duid_len
);
1151 a
->duid_len
= duid_len
;
1152 memcpy(a
->duid
, duid
, duid_len
);
1157 a
->assigned_host_id
= lease_cfg
? lease_cfg
->hostid
: 0;
1159 a
->assigned_subnet_id
= reqhint
;
1160 a
->valid_until
= now
;
1161 a
->preferred_until
= now
;
1163 a
->flags
= (is_pd
? OAF_DHCPV6_PD
: OAF_DHCPV6_NA
);
1166 memcpy(a
->key
, first
->key
, sizeof(a
->key
));
1168 odhcpd_urandom(a
->key
, sizeof(a
->key
));
1170 if (is_pd
&& iface
->dhcpv6_pd
)
1171 while (!(assigned
= assign_pd(iface
, a
)) &&
1173 else if (is_na
&& iface
->dhcpv6_na
)
1174 assigned
= assign_na(iface
, a
);
1176 if (lease_cfg
&& assigned
) {
1177 if (lease_cfg
->hostname
) {
1178 a
->hostname
= strdup(lease_cfg
->hostname
);
1179 a
->hostname_valid
= true;
1182 if (lease_cfg
->leasetime
)
1183 a
->leasetime
= lease_cfg
->leasetime
;
1185 list_add(&a
->lease_cfg_list
, &lease_cfg
->dhcpv6_leases
);
1186 a
->lease_cfg
= lease_cfg
;
1192 if (!assigned
|| iface
->addr6_len
== 0)
1193 /* Set error status */
1194 status
= (is_pd
) ? DHCPV6_STATUS_NOPREFIXAVAIL
: DHCPV6_STATUS_NOADDRSAVAIL
;
1195 else if (hdr
->msg_type
== DHCPV6_MSG_REQUEST
&& !dhcpv6_ia_on_link(ia
, a
, iface
)) {
1196 /* Send NOTONLINK status for the IA */
1197 status
= DHCPV6_STATUS_NOTONLINK
;
1199 } else if (accept_reconf
&& assigned
&& !first
&&
1200 hdr
->msg_type
!= DHCPV6_MSG_REBIND
) {
1201 size_t handshake_len
= 4;
1203 buf
[1] = DHCPV6_OPT_RECONF_ACCEPT
;
1207 if (hdr
->msg_type
== DHCPV6_MSG_REQUEST
) {
1208 struct dhcpv6_auth_reconfigure auth
= {
1209 htons(DHCPV6_OPT_AUTH
),
1210 htons(sizeof(auth
) - 4),
1212 {htonl(time(NULL
)), htonl(++serial
)},
1216 memcpy(auth
.key
, a
->key
, sizeof(a
->key
));
1217 memcpy(buf
+ handshake_len
, &auth
, sizeof(auth
));
1218 handshake_len
+= sizeof(auth
);
1222 buf
+= handshake_len
;
1223 buflen
-= handshake_len
;
1224 response_len
+= handshake_len
;
1229 ia_response_len
= build_ia(buf
, buflen
, status
, ia
, a
, iface
,
1230 hdr
->msg_type
== DHCPV6_MSG_REBIND
? false : true);
1232 /* Was only a solicitation: mark binding for removal in 60 seconds */
1233 if (assigned
&& hdr
->msg_type
== DHCPV6_MSG_SOLICIT
&& !rapid_commit
) {
1235 a
->valid_until
= now
+ 60;
1237 } else if (assigned
&&
1238 ((hdr
->msg_type
== DHCPV6_MSG_SOLICIT
&& rapid_commit
) ||
1239 hdr
->msg_type
== DHCPV6_MSG_REQUEST
||
1240 hdr
->msg_type
== DHCPV6_MSG_REBIND
)) {
1241 if (hostname_len
> 0 && (!a
->lease_cfg
|| !a
->lease_cfg
->hostname
)) {
1242 char *tmp
= realloc(a
->hostname
, hostname_len
+ 1);
1245 memcpy(a
->hostname
, hostname
, hostname_len
);
1246 a
->hostname
[hostname_len
] = 0;
1247 a
->hostname_valid
= odhcpd_hostname_valid(a
->hostname
);
1250 a
->accept_fr_nonce
= accept_reconf
;
1252 apply_lease(a
, true);
1253 } else if (!assigned
) {
1254 /* Cleanup failed assignment */
1255 dhcpv6_free_lease(a
);
1258 } else if (hdr
->msg_type
== DHCPV6_MSG_RENEW
||
1259 hdr
->msg_type
== DHCPV6_MSG_RELEASE
||
1260 hdr
->msg_type
== DHCPV6_MSG_REBIND
||
1261 hdr
->msg_type
== DHCPV6_MSG_DECLINE
) {
1262 if (!a
&& hdr
->msg_type
!= DHCPV6_MSG_REBIND
) {
1263 status
= DHCPV6_STATUS_NOBINDING
;
1264 ia_response_len
= build_ia(buf
, buflen
, status
, ia
, a
, iface
, false);
1265 } else if (hdr
->msg_type
== DHCPV6_MSG_RENEW
||
1266 hdr
->msg_type
== DHCPV6_MSG_REBIND
) {
1267 ia_response_len
= build_ia(buf
, buflen
, status
, ia
, a
, iface
, false);
1270 apply_lease(a
, true);
1272 } else if (hdr
->msg_type
== DHCPV6_MSG_RELEASE
) {
1273 a
->valid_until
= now
- 1;
1274 } else if ((a
->flags
& OAF_DHCPV6_NA
) && hdr
->msg_type
== DHCPV6_MSG_DECLINE
) {
1277 if (!a
->lease_cfg
|| a
->lease_cfg
->hostid
!= a
->assigned_host_id
) {
1278 memset(a
->duid
, 0, a
->duid_len
);
1279 a
->valid_until
= now
+ 3600; /* Block address for 1h */
1281 a
->valid_until
= now
- 1;
1283 } else if (hdr
->msg_type
== DHCPV6_MSG_CONFIRM
) {
1284 if (ia_addr_present
&& !dhcpv6_ia_on_link(ia
, a
, iface
)) {
1289 if (!ia_addr_present
|| !a
|| !a
->bound
) {
1295 buf
+= ia_response_len
;
1296 buflen
-= ia_response_len
;
1297 response_len
+= ia_response_len
;
1298 dhcpv6_log(hdr
->msg_type
, iface
, now
, duidbuf
, is_pd
, a
, status
);
1301 switch (hdr
->msg_type
) {
1302 case DHCPV6_MSG_RELEASE
:
1303 case DHCPV6_MSG_DECLINE
:
1304 case DHCPV6_MSG_CONFIRM
:
1305 if (response_len
+ 6 < buflen
) {
1307 buf
[1] = DHCPV6_OPT_STATUS
;
1311 buf
[5] = (notonlink
) ? DHCPV6_STATUS_NOTONLINK
: DHCPV6_STATUS_OK
;
1323 return response_len
;