1 /* $KAME: dhcp6s.c,v 1.162 2005/10/04 11:53:32 suz Exp $ */
3 * Copyright (C) 1998 and 1999 WIDE Project.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the project nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/ioctl.h>
34 #include <sys/queue.h>
36 #if TIME_WITH_SYS_TIME
37 # include <sys/time.h>
41 # include <sys/time.h>
50 #include <net/if_var.h>
53 #include <netinet/in.h>
55 #include <netinet6/in6_var.h>
58 #include <arpa/inet.h>
76 #include <dhcp6_ctl.h>
80 #define DUID_FILE LOCALDBDIR "/dhcp6s_duid"
81 #define DHCP6S_CONF SYSCONFDIR "/dhcp6s.conf"
82 #define DEFAULT_KEYFILE SYSCONFDIR "/dhcp6sctlkey"
83 #define DHCP6S_PIDFILE "/var/run/dhcp6s.pid"
87 typedef enum { DHCP6_BINDING_IA
} dhcp6_bindingtype_t
;
89 struct dhcp6_binding
{
90 TAILQ_ENTRY(dhcp6_binding
) link
;
92 dhcp6_bindingtype_t type
;
94 /* identifier of the binding */
96 /* additional identifiers for IA-based bindings */
101 * configuration information of this binding,
102 * which is type-dependent.
105 struct dhcp6_list uv_list
;
107 #define val_list val.uv_list
111 struct dhcp6_timer
*timer
;
113 static TAILQ_HEAD(, dhcp6_binding
) dhcp6_binding_head
;
116 TAILQ_ENTRY(relayinfo
) link
;
118 u_int hcnt
; /* hop count */
119 struct in6_addr linkaddr
; /* link address */
120 struct in6_addr peeraddr
; /* peer address */
121 struct dhcp6_vbuf relay_ifid
; /* Interface ID (if provided) */
122 struct dhcp6_vbuf relay_msg
; /* relay message */
124 TAILQ_HEAD(relayinfolist
, relayinfo
);
126 static int debug
= 0;
127 static sig_atomic_t sig_flags
= 0;
128 #define SIGF_TERM 0x1
130 const dhcp6_mode_t dhcp6_mode
= DHCP6_MODE_SERVER
;
133 int insock
; /* inbound UDP port */
134 int outsock
; /* outbound UDP port */
135 int ctlsock
= -1; /* control TCP port */
136 char *ctladdr
= DEFAULT_SERVER_CONTROL_ADDR
;
137 char *ctlport
= DEFAULT_SERVER_CONTROL_PORT
;
139 static const struct sockaddr_in6
*sa6_any_downstream
, *sa6_any_relay
;
140 static struct msghdr rmh
;
141 static char rdatabuf
[BUFSIZ
];
142 static int rmsgctllen
;
143 static char *conffile
= DHCP6S_CONF
;
144 static char *rmsgctlbuf
;
145 static struct duid server_duid
;
146 static struct dhcp6_list arg_dnslist
;
147 static char *ctlkeyfile
= DEFAULT_KEYFILE
;
148 static struct keyinfo
*ctlkey
= NULL
;
149 static int ctldigestlen
;
150 static char *pid_file
= DHCP6S_PIDFILE
;
152 static inline int get_val32
__P((char **, int *, u_int32_t
*));
153 static inline int get_val
__P((char **, int *, void *, size_t));
155 static void usage
__P((void));
156 static void server6_init
__P((void));
157 static void server6_mainloop
__P((void));
158 static int server6_do_ctlcommand
__P((char *, ssize_t
));
159 static void server6_reload
__P((void));
160 static void server6_stop
__P((void));
161 static void server6_recv
__P((int));
162 static void process_signals
__P((void));
163 static void server6_signal
__P((int));
164 static void free_relayinfo
__P((struct relayinfo
*));
165 static int process_relayforw
__P((struct dhcp6
**, struct dhcp6opt
**,
166 struct relayinfolist
*, struct sockaddr
*));
167 static int set_statelessinfo
__P((int, struct dhcp6_optinfo
*));
168 static int react_solicit
__P((struct dhcp6_if
*, struct dhcp6
*, ssize_t
,
169 struct dhcp6_optinfo
*, struct sockaddr
*, int, struct relayinfolist
*));
170 static int react_request
__P((struct dhcp6_if
*, struct in6_pktinfo
*,
171 struct dhcp6
*, ssize_t
, struct dhcp6_optinfo
*, struct sockaddr
*, int,
172 struct relayinfolist
*));
173 static int react_renew
__P((struct dhcp6_if
*, struct in6_pktinfo
*,
174 struct dhcp6
*, ssize_t
, struct dhcp6_optinfo
*, struct sockaddr
*, int,
175 struct relayinfolist
*));
176 static int react_rebind
__P((struct dhcp6_if
*, struct dhcp6
*, ssize_t
,
177 struct dhcp6_optinfo
*, struct sockaddr
*, int, struct relayinfolist
*));
178 static int react_release
__P((struct dhcp6_if
*, struct in6_pktinfo
*,
179 struct dhcp6
*, ssize_t
, struct dhcp6_optinfo
*, struct sockaddr
*, int,
180 struct relayinfolist
*));
181 static int react_decline
__P((struct dhcp6_if
*, struct in6_pktinfo
*,
182 struct dhcp6
*, ssize_t
, struct dhcp6_optinfo
*, struct sockaddr
*, int,
183 struct relayinfolist
*));
184 static int react_confirm
__P((struct dhcp6_if
*, struct in6_pktinfo
*,
185 struct dhcp6
*, ssize_t
,
186 struct dhcp6_optinfo
*, struct sockaddr
*, int, struct relayinfolist
*));
187 static int react_informreq
__P((struct dhcp6_if
*, struct dhcp6
*, ssize_t
,
188 struct dhcp6_optinfo
*, struct sockaddr
*, int, struct relayinfolist
*));
189 static int server6_send
__P((int, struct dhcp6_if
*, struct dhcp6
*,
190 struct dhcp6_optinfo
*, struct sockaddr
*, int, struct dhcp6_optinfo
*,
191 struct relayinfolist
*, struct host_conf
*));
192 static int make_ia_stcode
__P((int, u_int32_t
, u_int16_t
,
193 struct dhcp6_list
*));
194 static int update_ia
__P((int, struct dhcp6_listval
*,
195 struct dhcp6_list
*, struct dhcp6_optinfo
*));
196 static int release_binding_ia
__P((struct dhcp6_listval
*, struct dhcp6_list
*,
197 struct dhcp6_optinfo
*));
198 static int decline_binding_ia
__P((struct dhcp6_listval
*, struct dhcp6_list
*,
199 struct dhcp6_optinfo
*));
200 static int make_ia
__P((struct dhcp6_listval
*, struct dhcp6_list
*,
201 struct dhcp6_list
*, struct host_conf
*, int));
202 static int make_match_ia
__P((struct dhcp6_listval
*, struct dhcp6_list
*,
203 struct dhcp6_list
*));
204 static int make_iana_from_pool
__P((struct dhcp6_poolspec
*,
205 struct dhcp6_listval
*, struct dhcp6_list
*));
206 static void calc_ia_timo
__P((struct dhcp6_ia
*, struct dhcp6_list
*,
207 struct host_conf
*));
208 static void update_binding_duration
__P((struct dhcp6_binding
*));
209 static struct dhcp6_binding
*add_binding
__P((struct duid
*,
210 dhcp6_bindingtype_t
, int, u_int32_t
, void *));
211 static struct dhcp6_binding
*find_binding
__P((struct duid
*,
212 dhcp6_bindingtype_t
, int, u_int32_t
));
213 static void update_binding
__P((struct dhcp6_binding
*));
214 static void remove_binding
__P((struct dhcp6_binding
*));
215 static void free_binding
__P((struct dhcp6_binding
*));
216 static struct dhcp6_timer
*binding_timo
__P((void *));
217 static struct dhcp6_listval
*find_binding_ia
__P((struct dhcp6_listval
*,
218 struct dhcp6_binding
*));
219 static char *bindingstr
__P((struct dhcp6_binding
*));
220 static int process_auth
__P((struct dhcp6
*, ssize_t
, struct host_conf
*,
221 struct dhcp6_optinfo
*, struct dhcp6_optinfo
*));
222 static inline char *clientstr
__P((struct host_conf
*, struct duid
*));
231 struct dhcp6_listval
*dlv
;
235 if ((progname
= strrchr(*argv
, '/')) == NULL
)
240 TAILQ_INIT(&arg_dnslist
);
241 TAILQ_INIT(&dnslist
);
242 TAILQ_INIT(&dnsnamelist
);
243 TAILQ_INIT(&siplist
);
244 TAILQ_INIT(&sipnamelist
);
245 TAILQ_INIT(&ntplist
);
246 TAILQ_INIT(&nislist
);
247 TAILQ_INIT(&nisnamelist
);
248 TAILQ_INIT(&nisplist
);
249 TAILQ_INIT(&nispnamelist
);
250 TAILQ_INIT(&bcmcslist
);
251 TAILQ_INIT(&bcmcsnamelist
);
253 srandom(time(NULL
) & getpid());
254 while ((ch
= getopt(argc
, argv
, "c:dDfk:n:p:P:")) != -1) {
272 warnx("-n dnsserv option was obsoleted. "
273 "use configuration file.");
274 if (inet_pton(AF_INET6
, optarg
, &a
) != 1) {
275 errx(1, "invalid DNS server %s", optarg
);
278 if ((dlv
= malloc(sizeof *dlv
)) == NULL
) {
279 errx(1, "malloc failed for a DNS server");
283 TAILQ_INSERT_TAIL(&arg_dnslist
, dlv
, link
);
306 openlog(progname
, LOG_NDELAY
|LOG_PID
, LOG_DAEMON
);
310 if (ifinit(device
) == NULL
)
313 if ((cfparse(conffile
)) != 0) {
314 dprintf(LOG_ERR
, FNAME
, "failed to parse configuration file");
318 if (foreground
== 0) {
321 if (daemon(0, 0) < 0)
324 for (fd
= 3; fd
< 1024; fd
++)
328 /* dump current PID */
330 if ((pidfp
= fopen(pid_file
, "w")) != NULL
) {
331 fprintf(pidfp
, "%d\n", pid
);
335 /* prohibit a mixture of old and new style of DNS server config */
336 if (!TAILQ_EMPTY(&arg_dnslist
)) {
337 if (!TAILQ_EMPTY(&dnslist
)) {
338 dprintf(LOG_INFO
, FNAME
, "do not specify DNS servers "
339 "both by command line and by configuration file.");
342 dhcp6_move_list(&dnslist
, &arg_dnslist
);
343 TAILQ_INIT(&arg_dnslist
);
356 "usage: dhcp6s [-c configfile] [-dDf] [-k ctlkeyfile] "
357 "[-p ctlport] [-P pidfile] intface\n");
361 /*------------------------------------------------------------*/
366 struct addrinfo hints
;
367 struct addrinfo
*res
, *res2
;
370 struct ipv6_mreq mreq6
;
371 static struct iovec iov
;
372 static struct sockaddr_in6 sa6_any_downstream_storage
;
373 static struct sockaddr_in6 sa6_any_relay_storage
;
375 TAILQ_INIT(&dhcp6_binding_head
);
376 if (lease_init() != 0) {
377 dprintf(LOG_ERR
, FNAME
, "failed to initialize the lease table");
381 ifidx
= if_nametoindex(device
);
383 dprintf(LOG_ERR
, FNAME
, "invalid interface %s", device
);
388 if (get_duid(DUID_FILE
, &server_duid
)) {
389 dprintf(LOG_ERR
, FNAME
, "failed to get a DUID");
393 if (dhcp6_ctl_authinit(ctlkeyfile
, &ctlkey
, &ctldigestlen
) != 0) {
394 dprintf(LOG_NOTICE
, FNAME
,
395 "failed to initialize control message authentication");
396 /* run the server anyway */
399 /* initialize send/receive buffer */
400 iov
.iov_base
= (caddr_t
)rdatabuf
;
401 iov
.iov_len
= sizeof(rdatabuf
);
404 rmsgctllen
= CMSG_SPACE(sizeof(struct in6_pktinfo
));
405 if ((rmsgctlbuf
= (char *)malloc(rmsgctllen
)) == NULL
) {
406 dprintf(LOG_ERR
, FNAME
, "memory allocation failed");
410 /* initialize socket */
411 memset(&hints
, 0, sizeof(hints
));
412 hints
.ai_family
= AF_INET6
;
413 hints
.ai_socktype
= SOCK_DGRAM
;
414 hints
.ai_protocol
= IPPROTO_UDP
;
415 hints
.ai_flags
= AI_PASSIVE
;
416 error
= getaddrinfo(NULL
, DH6PORT_UPSTREAM
, &hints
, &res
);
418 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
419 gai_strerror(error
));
422 insock
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
424 dprintf(LOG_ERR
, FNAME
, "socket(insock): %s",
428 if (setsockopt(insock
, SOL_SOCKET
, SO_REUSEPORT
, &on
,
430 dprintf(LOG_ERR
, FNAME
, "setsockopt(insock, SO_REUSEPORT): %s",
434 if (setsockopt(insock
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
436 dprintf(LOG_ERR
, FNAME
, "setsockopt(insock, SO_REUSEADDR): %s",
440 #ifdef IPV6_RECVPKTINFO
441 if (setsockopt(insock
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &on
,
443 dprintf(LOG_ERR
, FNAME
,
444 "setsockopt(inbound, IPV6_RECVPKTINFO): %s",
449 if (setsockopt(insock
, IPPROTO_IPV6
, IPV6_PKTINFO
, &on
,
451 dprintf(LOG_ERR
, FNAME
,
452 "setsockopt(inbound, IPV6_PKTINFO): %s",
458 if (setsockopt(insock
, IPPROTO_IPV6
, IPV6_V6ONLY
,
459 &on
, sizeof(on
)) < 0) {
460 dprintf(LOG_ERR
, FNAME
,
461 "setsockopt(inbound, IPV6_V6ONLY): %s", strerror(errno
));
465 if (bind(insock
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
466 dprintf(LOG_ERR
, FNAME
, "bind(insock): %s", strerror(errno
));
472 error
= getaddrinfo(DH6ADDR_ALLAGENT
, DH6PORT_UPSTREAM
, &hints
, &res2
);
474 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
475 gai_strerror(error
));
478 memset(&mreq6
, 0, sizeof(mreq6
));
479 mreq6
.ipv6mr_interface
= ifidx
;
480 memcpy(&mreq6
.ipv6mr_multiaddr
,
481 &((struct sockaddr_in6
*)res2
->ai_addr
)->sin6_addr
,
482 sizeof(mreq6
.ipv6mr_multiaddr
));
483 if (setsockopt(insock
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
,
484 &mreq6
, sizeof(mreq6
))) {
485 dprintf(LOG_ERR
, FNAME
,
486 "setsockopt(insock, IPV6_JOIN_GROUP): %s",
493 error
= getaddrinfo(DH6ADDR_ALLSERVER
, DH6PORT_UPSTREAM
,
496 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
497 gai_strerror(error
));
500 memset(&mreq6
, 0, sizeof(mreq6
));
501 mreq6
.ipv6mr_interface
= ifidx
;
502 memcpy(&mreq6
.ipv6mr_multiaddr
,
503 &((struct sockaddr_in6
*)res2
->ai_addr
)->sin6_addr
,
504 sizeof(mreq6
.ipv6mr_multiaddr
));
505 if (setsockopt(insock
, IPPROTO_IPV6
, IPV6_JOIN_GROUP
,
506 &mreq6
, sizeof(mreq6
))) {
507 dprintf(LOG_ERR
, FNAME
,
508 "setsockopt(insock, IPV6_JOIN_GROUP): %s",
515 error
= getaddrinfo(NULL
, DH6PORT_DOWNSTREAM
, &hints
, &res
);
517 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
518 gai_strerror(error
));
521 outsock
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
523 dprintf(LOG_ERR
, FNAME
, "socket(outsock): %s",
527 /* set outgoing interface of multicast packets for DHCP reconfig */
528 if (setsockopt(outsock
, IPPROTO_IPV6
, IPV6_MULTICAST_IF
,
529 &ifidx
, sizeof(ifidx
)) < 0) {
530 dprintf(LOG_ERR
, FNAME
,
531 "setsockopt(outsock, IPV6_MULTICAST_IF): %s",
535 #if !defined(__linux__) && !defined(__sun__)
536 /* make the socket write-only */
537 if (shutdown(outsock
, 0)) {
538 dprintf(LOG_ERR
, FNAME
, "shutdown(outbound, 0): %s",
545 memset(&hints
, 0, sizeof(hints
));
546 hints
.ai_family
= AF_INET6
;
547 hints
.ai_socktype
= SOCK_DGRAM
;
548 hints
.ai_protocol
= IPPROTO_UDP
;
549 error
= getaddrinfo("::", DH6PORT_DOWNSTREAM
, &hints
, &res
);
551 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
552 gai_strerror(error
));
555 memcpy(&sa6_any_downstream_storage
, res
->ai_addr
, res
->ai_addrlen
);
557 (const struct sockaddr_in6
*)&sa6_any_downstream_storage
;
560 memset(&hints
, 0, sizeof(hints
));
561 hints
.ai_family
= AF_INET6
;
562 hints
.ai_socktype
= SOCK_DGRAM
;
563 hints
.ai_protocol
= IPPROTO_UDP
;
564 error
= getaddrinfo("::", DH6PORT_UPSTREAM
, &hints
, &res
);
566 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
567 gai_strerror(error
));
570 memcpy(&sa6_any_relay_storage
, res
->ai_addr
, res
->ai_addrlen
);
572 (const struct sockaddr_in6
*)&sa6_any_relay_storage
;
575 /* set up control socket */
577 dprintf(LOG_NOTICE
, FNAME
, "skip opening control port");
578 else if (dhcp6_ctl_init(ctladdr
, ctlport
,
579 DHCP6CTL_DEF_COMMANDQUEUELEN
, &ctlsock
)) {
580 dprintf(LOG_ERR
, FNAME
,
581 "failed to initialize control channel");
585 if (signal(SIGTERM
, server6_signal
) == SIG_ERR
) {
586 dprintf(LOG_WARNING
, FNAME
, "failed to set signal: %s",
596 if ((sig_flags
& SIGF_TERM
)) {
615 w
= dhcp6_check_timer();
622 maxsock
= (insock
> ctlsock
) ? insock
: ctlsock
;
623 (void)dhcp6_ctl_setreadfds(&r
, &maxsock
);
626 ret
= select(maxsock
+ 1, &r
, NULL
, NULL
, w
);
629 if (errno
!= EINTR
) {
630 dprintf(LOG_ERR
, FNAME
, "select: %s",
635 case 0: /* timeout */
641 if (FD_ISSET(insock
, &r
))
642 server6_recv(insock
);
644 if (FD_ISSET(ctlsock
, &r
)) {
645 (void)dhcp6_ctl_acceptcommand(ctlsock
,
646 server6_do_ctlcommand
);
648 (void)dhcp6_ctl_readcommand(&r
);
654 get_val32(bpp
, lenp
, valp
)
663 if (len
< sizeof(*valp
))
666 memcpy(&i32
, bp
, sizeof(i32
));
669 *bpp
= bp
+ sizeof(*valp
);
670 *lenp
= len
- sizeof(*valp
);
676 get_val(bpp
, lenp
, valp
, vallen
)
688 memcpy(valp
, bp
, vallen
);
691 *lenp
= len
- vallen
;
697 server6_do_ctlcommand(buf
, len
)
701 struct dhcp6ctl
*ctlhead
;
702 struct dhcp6ctl_iaspec iaspec
;
703 u_int16_t command
, version
;
704 u_int32_t p32
, iaid
, duidlen
, ts
, ts0
;
706 struct dhcp6_binding
*binding
;
711 ctlhead
= (struct dhcp6ctl
*)buf
;
713 command
= ntohs(ctlhead
->command
);
714 commandlen
= (int)(ntohs(ctlhead
->len
));
715 version
= ntohs(ctlhead
->version
);
716 if (len
!= sizeof(struct dhcp6ctl
) + commandlen
) {
717 dprintf(LOG_ERR
, FNAME
,
718 "assumption failure: command length mismatch");
719 return (DHCP6CTL_R_FAILURE
);
722 /* replay protection and message authentication */
723 if ((now
= time(NULL
)) < 0) {
724 dprintf(LOG_ERR
, FNAME
, "failed to get current time: %s",
726 return (DHCP6CTL_R_FAILURE
);
728 ts0
= (u_int32_t
)now
;
729 ts
= ntohl(ctlhead
->timestamp
);
730 if (ts
+ CTLSKEW
< ts0
|| (ts
- CTLSKEW
) > ts0
) {
731 dprintf(LOG_INFO
, FNAME
, "timestamp is out of range");
732 return (DHCP6CTL_R_FAILURE
);
735 if (ctlkey
== NULL
) { /* should not happen!! */
736 dprintf(LOG_ERR
, FNAME
, "no secret key for control channel");
737 return (DHCP6CTL_R_FAILURE
);
739 if (dhcp6_verify_mac(buf
, len
, DHCP6CTL_AUTHPROTO_UNDEF
,
740 DHCP6CTL_AUTHALG_HMACMD5
, sizeof(*ctlhead
), ctlkey
) != 0) {
741 dprintf(LOG_INFO
, FNAME
, "authentication failure");
742 return (DHCP6CTL_R_FAILURE
);
745 bp
= buf
+ sizeof(*ctlhead
) + ctldigestlen
;
746 commandlen
-= ctldigestlen
;
748 if (version
> DHCP6CTL_VERSION
) {
749 dprintf(LOG_INFO
, FNAME
, "unsupported version: %d", version
);
750 return (DHCP6CTL_R_FAILURE
);
754 case DHCP6CTL_COMMAND_RELOAD
:
755 if (commandlen
!= 0) {
756 dprintf(LOG_INFO
, FNAME
, "invalid command length "
757 "for reload: %d", commandlen
);
758 return (DHCP6CTL_R_DONE
);
762 case DHCP6CTL_COMMAND_STOP
:
763 if (commandlen
!= 0) {
764 dprintf(LOG_INFO
, FNAME
, "invalid command length "
765 "for stop: %d", commandlen
);
766 return (DHCP6CTL_R_DONE
);
770 case DHCP6CTL_COMMAND_REMOVE
:
771 if (get_val32(&bp
, &commandlen
, &p32
))
772 return (DHCP6CTL_R_FAILURE
);
773 if (p32
!= DHCP6CTL_BINDING
) {
774 dprintf(LOG_INFO
, FNAME
,
775 "unknown remove target: %ul", p32
);
776 return (DHCP6CTL_R_FAILURE
);
779 if (get_val32(&bp
, &commandlen
, &p32
))
780 return (DHCP6CTL_R_FAILURE
);
781 if (p32
!= DHCP6CTL_BINDING_IA
) {
782 dprintf(LOG_INFO
, FNAME
, "unknown binding type: %ul",
784 return (DHCP6CTL_R_FAILURE
);
787 if (get_val(&bp
, &commandlen
, &iaspec
, sizeof(iaspec
)))
788 return (DHCP6CTL_R_FAILURE
);
789 if (ntohl(iaspec
.type
) != DHCP6CTL_IA_PD
&&
790 ntohl(iaspec
.type
) != DHCP6CTL_IA_NA
) {
791 dprintf(LOG_INFO
, FNAME
, "unknown IA type: %ul",
793 return (DHCP6CTL_R_FAILURE
);
795 iaid
= ntohl(iaspec
.id
);
796 duidlen
= ntohl(iaspec
.duidlen
);
798 if (duidlen
> commandlen
) {
799 dprintf(LOG_INFO
, FNAME
, "DUID length mismatch");
800 return (DHCP6CTL_R_FAILURE
);
803 duid
.duid_len
= (size_t)duidlen
;
806 binding
= find_binding(&duid
, DHCP6_BINDING_IA
,
807 DHCP6_LISTVAL_IAPD
, iaid
);
808 if (binding
== NULL
) {
809 binding
= find_binding(&duid
, DHCP6_BINDING_IA
,
810 DHCP6_LISTVAL_IANA
, iaid
);
811 if (binding
== NULL
) {
812 dprintf(LOG_INFO
, FNAME
, "no such binding");
813 return (DHCP6CTL_R_FAILURE
);
816 remove_binding(binding
);
820 dprintf(LOG_INFO
, FNAME
,
821 "unknown control command: %d (len=%d)",
822 (int)command
, commandlen
);
823 return (DHCP6CTL_R_FAILURE
);
826 return (DHCP6CTL_R_DONE
);
832 /* reload the configuration file */
833 if (cfparse(conffile
) != 0) {
834 dprintf(LOG_WARNING
, FNAME
,
835 "failed to reload configuration file");
839 dprintf(LOG_NOTICE
, FNAME
, "server reloaded");
847 /* Right now, we simply stop running */
849 dprintf(LOG_NOTICE
, FNAME
, "exiting");
859 struct sockaddr_storage from
;
863 char cmsgbuf
[BUFSIZ
];
865 struct in6_pktinfo
*pi
= NULL
;
866 struct dhcp6_if
*ifp
;
868 struct dhcp6_optinfo optinfo
;
869 struct dhcp6opt
*optend
;
870 struct relayinfolist relayinfohead
;
871 struct relayinfo
*relayinfo
;
873 TAILQ_INIT(&relayinfohead
);
875 memset(&iov
, 0, sizeof(iov
));
876 memset(&mhdr
, 0, sizeof(mhdr
));
878 iov
.iov_base
= rdatabuf
;
879 iov
.iov_len
= sizeof(rdatabuf
);
880 mhdr
.msg_name
= &from
;
881 mhdr
.msg_namelen
= sizeof(from
);
884 mhdr
.msg_control
= (caddr_t
)cmsgbuf
;
885 mhdr
.msg_controllen
= sizeof(cmsgbuf
);
887 if ((len
= recvmsg(insock
, &mhdr
, 0)) < 0) {
888 dprintf(LOG_ERR
, FNAME
, "recvmsg: %s", strerror(errno
));
891 fromlen
= mhdr
.msg_namelen
;
893 for (cm
= (struct cmsghdr
*)CMSG_FIRSTHDR(&mhdr
); cm
;
894 cm
= (struct cmsghdr
*)CMSG_NXTHDR(&mhdr
, cm
)) {
895 if (cm
->cmsg_level
== IPPROTO_IPV6
&&
896 cm
->cmsg_type
== IPV6_PKTINFO
&&
897 cm
->cmsg_len
== CMSG_LEN(sizeof(struct in6_pktinfo
))) {
898 pi
= (struct in6_pktinfo
*)(CMSG_DATA(cm
));
902 dprintf(LOG_NOTICE
, FNAME
, "failed to get packet info");
906 * DHCPv6 server may receive a DHCPv6 packet from a non-listening
907 * interface, when a DHCPv6 relay agent is running on that interface.
908 * This check prevents such reception.
910 if (pi
->ipi6_ifindex
!= ifidx
)
912 if ((ifp
= find_ifconfbyid((unsigned int)pi
->ipi6_ifindex
)) == NULL
) {
913 dprintf(LOG_INFO
, FNAME
, "unexpected interface (%d)",
914 (unsigned int)pi
->ipi6_ifindex
);
918 dh6
= (struct dhcp6
*)rdatabuf
;
920 if (len
< sizeof(*dh6
)) {
921 dprintf(LOG_INFO
, FNAME
, "short packet (%d bytes)", len
);
925 dprintf(LOG_DEBUG
, FNAME
, "received %s from %s",
926 dhcp6msgstr(dh6
->dh6_msgtype
),
927 addr2str((struct sockaddr
*)&from
));
930 * A server MUST discard any Solicit, Confirm, Rebind or
931 * Information-request messages it receives with a unicast
932 * destination address.
933 * [RFC3315 Section 15.]
935 if (!IN6_IS_ADDR_MULTICAST(&pi
->ipi6_addr
) &&
936 (dh6
->dh6_msgtype
== DH6_SOLICIT
||
937 dh6
->dh6_msgtype
== DH6_CONFIRM
||
938 dh6
->dh6_msgtype
== DH6_REBIND
||
939 dh6
->dh6_msgtype
== DH6_INFORM_REQ
)) {
940 dprintf(LOG_INFO
, FNAME
, "invalid unicast message");
945 * A server never receives a relay reply message. Since relay
946 * replay messages will annoy option parser below, we explicitly
949 if (dh6
->dh6_msgtype
== DH6_RELAY_REPLY
) {
950 dprintf(LOG_INFO
, FNAME
, "relay reply message from %s",
951 addr2str((struct sockaddr
*)&from
));
956 optend
= (struct dhcp6opt
*)(rdatabuf
+ len
);
957 if (dh6
->dh6_msgtype
== DH6_RELAY_FORW
) {
958 if (process_relayforw(&dh6
, &optend
, &relayinfohead
,
959 (struct sockaddr
*)&from
)) {
962 /* dh6 and optend should have been updated. */
963 len
= (ssize_t
)((char *)optend
- (char *)dh6
);
967 * parse and validate options in the message
969 dhcp6_init_options(&optinfo
);
970 if (dhcp6_get_options((struct dhcp6opt
*)(dh6
+ 1),
971 optend
, &optinfo
) < 0) {
972 dprintf(LOG_INFO
, FNAME
, "failed to parse options");
976 switch (dh6
->dh6_msgtype
) {
978 (void)react_solicit(ifp
, dh6
, len
, &optinfo
,
979 (struct sockaddr
*)&from
, fromlen
, &relayinfohead
);
982 (void)react_request(ifp
, pi
, dh6
, len
, &optinfo
,
983 (struct sockaddr
*)&from
, fromlen
, &relayinfohead
);
986 (void)react_renew(ifp
, pi
, dh6
, len
, &optinfo
,
987 (struct sockaddr
*)&from
, fromlen
, &relayinfohead
);
990 (void)react_rebind(ifp
, dh6
, len
, &optinfo
,
991 (struct sockaddr
*)&from
, fromlen
, &relayinfohead
);
994 (void)react_release(ifp
, pi
, dh6
, len
, &optinfo
,
995 (struct sockaddr
*)&from
, fromlen
, &relayinfohead
);
998 (void)react_decline(ifp
, pi
, dh6
, len
, &optinfo
,
999 (struct sockaddr
*)&from
, fromlen
, &relayinfohead
);
1002 (void)react_confirm(ifp
, pi
, dh6
, len
, &optinfo
,
1003 (struct sockaddr
*)&from
, fromlen
, &relayinfohead
);
1005 case DH6_INFORM_REQ
:
1006 (void)react_informreq(ifp
, dh6
, len
, &optinfo
,
1007 (struct sockaddr
*)&from
, fromlen
, &relayinfohead
);
1010 dprintf(LOG_INFO
, FNAME
, "unknown or unsupported msgtype (%s)",
1011 dhcp6msgstr(dh6
->dh6_msgtype
));
1015 dhcp6_clear_options(&optinfo
);
1018 while ((relayinfo
= TAILQ_FIRST(&relayinfohead
)) != NULL
) {
1019 TAILQ_REMOVE(&relayinfohead
, relayinfo
, link
);
1020 free_relayinfo(relayinfo
);
1027 free_relayinfo(relayinfo
)
1028 struct relayinfo
*relayinfo
;
1030 if (relayinfo
->relay_ifid
.dv_buf
)
1031 dhcp6_vbuf_free(&relayinfo
->relay_ifid
);
1033 if (relayinfo
->relay_msg
.dv_buf
)
1034 dhcp6_vbuf_free(&relayinfo
->relay_msg
);
1040 process_relayforw(dh6p
, optendp
, relayinfohead
, from
)
1041 struct dhcp6
**dh6p
;
1042 struct dhcp6opt
**optendp
;
1043 struct relayinfolist
*relayinfohead
;
1044 struct sockaddr
*from
;
1046 struct dhcp6_relay
*dh6relay
= (struct dhcp6_relay
*)*dh6p
;
1047 struct dhcp6opt
*optend
= *optendp
;
1048 struct relayinfo
*relayinfo
;
1049 struct dhcp6_optinfo optinfo
;
1053 len
= (void *)optend
- (void *)dh6relay
;
1054 if (len
< sizeof (*dh6relay
)) {
1055 dprintf(LOG_INFO
, FNAME
, "short relay message from %s",
1059 dprintf(LOG_DEBUG
, FNAME
,
1060 "dhcp6 relay: hop=%d, linkaddr=%s, peeraddr=%s",
1061 dh6relay
->dh6relay_hcnt
,
1062 in6addr2str(&dh6relay
->dh6relay_linkaddr
, 0),
1063 in6addr2str(&dh6relay
->dh6relay_peeraddr
, 0));
1066 * parse and validate options in the relay forward message.
1068 dhcp6_init_options(&optinfo
);
1069 if (dhcp6_get_options((struct dhcp6opt
*)(dh6relay
+ 1),
1070 optend
, &optinfo
) < 0) {
1071 dprintf(LOG_INFO
, FNAME
, "failed to parse options");
1075 /* A relay forward message must include a relay message option */
1076 if (optinfo
.relaymsg_msg
== NULL
) {
1077 dprintf(LOG_INFO
, FNAME
, "relay forward from %s "
1078 "without a relay message", addr2str(from
));
1082 /* relay message must contain a DHCPv6 message. */
1083 len
= optinfo
.relaymsg_len
;
1084 if (len
< sizeof (struct dhcp6
)) {
1085 dprintf(LOG_INFO
, FNAME
,
1086 "short packet (%d bytes) in relay message", len
);
1090 if ((relayinfo
= malloc(sizeof (*relayinfo
))) == NULL
) {
1091 dprintf(LOG_ERR
, FNAME
, "failed to allocate relay info");
1094 memset(relayinfo
, 0, sizeof (*relayinfo
));
1096 relayinfo
->hcnt
= dh6relay
->dh6relay_hcnt
;
1097 memcpy(&relayinfo
->linkaddr
, &dh6relay
->dh6relay_linkaddr
,
1098 sizeof (relayinfo
->linkaddr
));
1099 memcpy(&relayinfo
->peeraddr
, &dh6relay
->dh6relay_peeraddr
,
1100 sizeof (relayinfo
->peeraddr
));
1102 if (dhcp6_vbuf_copy(&relayinfo
->relay_msg
, &optinfo
.relay_msg
))
1104 if (optinfo
.ifidopt_id
&&
1105 dhcp6_vbuf_copy(&relayinfo
->relay_ifid
, &optinfo
.ifidopt
)) {
1109 TAILQ_INSERT_HEAD(relayinfohead
, relayinfo
, link
);
1111 dhcp6_clear_options(&optinfo
);
1113 optend
= (struct dhcp6opt
*)(relayinfo
->relay_msg
.dv_buf
+ len
);
1114 dh6relay
= (struct dhcp6_relay
*)relayinfo
->relay_msg
.dv_buf
;
1116 if (dh6relay
->dh6relay_msgtype
!= DH6_RELAY_FORW
) {
1117 *dh6p
= (struct dhcp6
*)dh6relay
;
1125 free_relayinfo(relayinfo
);
1126 dhcp6_clear_options(&optinfo
);
1132 * Set stateless configuration information to a option structure.
1133 * It is the caller's responsibility to deal with error cases.
1136 set_statelessinfo(type
, optinfo
)
1138 struct dhcp6_optinfo
*optinfo
;
1140 /* SIP domain name */
1141 if (dhcp6_copy_list(&optinfo
->sipname_list
, &sipnamelist
)) {
1142 dprintf(LOG_ERR
, FNAME
,
1143 "failed to copy SIP domain list");
1148 if (dhcp6_copy_list(&optinfo
->sip_list
, &siplist
)) {
1149 dprintf(LOG_ERR
, FNAME
, "failed to copy SIP servers");
1154 if (dhcp6_copy_list(&optinfo
->dns_list
, &dnslist
)) {
1155 dprintf(LOG_ERR
, FNAME
, "failed to copy DNS servers");
1159 /* DNS search list */
1160 if (dhcp6_copy_list(&optinfo
->dnsname_list
, &dnsnamelist
)) {
1161 dprintf(LOG_ERR
, FNAME
, "failed to copy DNS search list");
1166 if (dhcp6_copy_list(&optinfo
->ntp_list
, &ntplist
)) {
1167 dprintf(LOG_ERR
, FNAME
, "failed to copy NTP servers");
1171 /* NIS domain name */
1172 if (dhcp6_copy_list(&optinfo
->nisname_list
, &nisnamelist
)) {
1173 dprintf(LOG_ERR
, FNAME
,
1174 "failed to copy NIS domain list");
1179 if (dhcp6_copy_list(&optinfo
->nis_list
, &nislist
)) {
1180 dprintf(LOG_ERR
, FNAME
, "failed to copy NIS servers");
1184 /* NIS+ domain name */
1185 if (dhcp6_copy_list(&optinfo
->nispname_list
, &nispnamelist
)) {
1186 dprintf(LOG_ERR
, FNAME
,
1187 "failed to copy NIS+ domain list");
1192 if (dhcp6_copy_list(&optinfo
->nisp_list
, &nisplist
)) {
1193 dprintf(LOG_ERR
, FNAME
, "failed to copy NIS+ servers");
1197 /* BCMCS domain name */
1198 if (dhcp6_copy_list(&optinfo
->bcmcsname_list
, &bcmcsnamelist
)) {
1199 dprintf(LOG_ERR
, FNAME
,
1200 "failed to copy BCMCS domain list");
1205 if (dhcp6_copy_list(&optinfo
->bcmcs_list
, &bcmcslist
)) {
1206 dprintf(LOG_ERR
, FNAME
, "failed to copy BCMCS servers");
1211 * Information refresh time. Only include in a response to
1212 * an Information-request message.
1214 if (type
== DH6_INFORM_REQ
&&
1215 optrefreshtime
!= DH6OPT_REFRESHTIME_UNDEF
) {
1216 optinfo
->refreshtime
= (int64_t)optrefreshtime
;
1223 react_solicit(ifp
, dh6
, len
, optinfo
, from
, fromlen
, relayinfohead
)
1224 struct dhcp6_if
*ifp
;
1227 struct dhcp6_optinfo
*optinfo
;
1228 struct sockaddr
*from
;
1230 struct relayinfolist
*relayinfohead
;
1232 struct dhcp6_optinfo roptinfo
;
1233 struct host_conf
*client_conf
;
1234 int resptype
, do_binding
= 0, error
;
1237 * Servers MUST discard any Solicit messages that do not include a
1238 * Client Identifier option.
1239 * [RFC3315 Section 15.2]
1241 if (optinfo
->clientID
.duid_len
== 0) {
1242 dprintf(LOG_INFO
, FNAME
, "no client ID option");
1245 dprintf(LOG_DEBUG
, FNAME
, "client ID %s",
1246 duidstr(&optinfo
->clientID
));
1250 * Servers MUST discard any Solicit messages that do include a
1251 * Server Identifier option.
1252 * [RFC3315 Section 15.2]
1254 if (optinfo
->serverID
.duid_len
) {
1255 dprintf(LOG_INFO
, FNAME
, "server ID option found");
1259 /* get per-host configuration for the client, if any. */
1260 if ((client_conf
= find_hostconf(&optinfo
->clientID
))) {
1261 dprintf(LOG_DEBUG
, FNAME
, "found a host configuration for %s",
1266 * configure necessary options based on the options in solicit.
1268 dhcp6_init_options(&roptinfo
);
1270 /* process authentication */
1271 if (process_auth(dh6
, len
, client_conf
, optinfo
, &roptinfo
)) {
1272 dprintf(LOG_INFO
, FNAME
, "failed to process authentication "
1273 "information for %s",
1274 clientstr(client_conf
, &optinfo
->clientID
));
1278 /* server identifier option */
1279 if (duidcpy(&roptinfo
.serverID
, &server_duid
)) {
1280 dprintf(LOG_ERR
, FNAME
, "failed to copy server ID");
1284 /* copy client information back */
1285 if (duidcpy(&roptinfo
.clientID
, &optinfo
->clientID
)) {
1286 dprintf(LOG_ERR
, FNAME
, "failed to copy client ID");
1290 /* preference (if configured) */
1291 if (ifp
->server_pref
!= DH6OPT_PREF_UNDEF
)
1292 roptinfo
.pref
= ifp
->server_pref
;
1294 /* add other configuration information */
1295 if (set_statelessinfo(DH6_SOLICIT
, &roptinfo
)) {
1296 dprintf(LOG_ERR
, FNAME
,
1297 "failed to set other stateless information");
1302 * see if we have information for requested options, and if so,
1303 * configure corresponding options.
1305 if (optinfo
->rapidcommit
&& (ifp
->allow_flags
& DHCIFF_RAPID_COMMIT
))
1309 * The delegating router MUST include an IA_PD option, identifying any
1310 * prefix(es) that the delegating router will delegate to the
1311 * requesting router. [RFC3633 Section 11.2]
1313 if (!TAILQ_EMPTY(&optinfo
->iapd_list
)) {
1315 struct dhcp6_list conflist
;
1316 struct dhcp6_listval
*iapd
;
1318 TAILQ_INIT(&conflist
);
1320 /* make a local copy of the configured prefixes */
1322 dhcp6_copy_list(&conflist
, &client_conf
->prefix_list
)) {
1323 dprintf(LOG_NOTICE
, FNAME
,
1324 "failed to make local data");
1328 for (iapd
= TAILQ_FIRST(&optinfo
->iapd_list
); iapd
;
1329 iapd
= TAILQ_NEXT(iapd
, link
)) {
1331 * find an appropriate prefix for each IA_PD,
1332 * removing the adopted prefixes from the list.
1333 * (dhcp6s cannot create IAs without client config)
1336 make_ia(iapd
, &conflist
, &roptinfo
.iapd_list
,
1337 client_conf
, do_binding
) > 0)
1341 dhcp6_clear_list(&conflist
);
1345 * If the delegating router will not assign any
1346 * prefixes to any IA_PDs in a subsequent Request from
1347 * the requesting router, the delegating router MUST
1348 * send an Advertise message to the requesting router
1349 * that includes a Status Code option with code
1351 * [dhcpv6-opt-prefix-delegation-01 Section 10.2]
1353 u_int16_t stcode
= DH6OPT_STCODE_NOPREFIXAVAIL
;
1355 if (dhcp6_add_listval(&roptinfo
.stcode_list
,
1356 DHCP6_LISTVAL_STCODE
, &stcode
, NULL
) == NULL
)
1361 if (!TAILQ_EMPTY(&optinfo
->iana_list
)) {
1363 struct dhcp6_list conflist
;
1364 struct dhcp6_listval
*iana
;
1366 if (client_conf
== NULL
&& ifp
->pool
.name
) {
1367 if ((client_conf
= create_dynamic_hostconf(&optinfo
->clientID
,
1368 &ifp
->pool
)) == NULL
)
1369 dprintf(LOG_NOTICE
, FNAME
,
1370 "failed to make host configuration");
1372 TAILQ_INIT(&conflist
);
1374 /* make a local copy of the configured addresses */
1376 dhcp6_copy_list(&conflist
, &client_conf
->addr_list
)) {
1377 dprintf(LOG_NOTICE
, FNAME
,
1378 "failed to make local data");
1382 for (iana
= TAILQ_FIRST(&optinfo
->iana_list
); iana
;
1383 iana
= TAILQ_NEXT(iana
, link
)) {
1385 * find an appropriate address for each IA_NA,
1386 * removing the adopted addresses from the list.
1387 * (dhcp6s cannot create IAs without client config)
1390 make_ia(iana
, &conflist
, &roptinfo
.iana_list
,
1391 client_conf
, do_binding
) > 0)
1395 dhcp6_clear_list(&conflist
);
1398 u_int16_t stcode
= DH6OPT_STCODE_NOADDRSAVAIL
;
1400 if (dhcp6_add_listval(&roptinfo
.stcode_list
,
1401 DHCP6_LISTVAL_STCODE
, &stcode
, NULL
) == NULL
)
1406 if (optinfo
->rapidcommit
&& (ifp
->allow_flags
& DHCIFF_RAPID_COMMIT
)) {
1408 * If the client has included a Rapid Commit option and the
1409 * server has been configured to respond with committed address
1410 * assignments and other resources, responds to the Solicit
1411 * with a Reply message.
1412 * [RFC3315 Section 17.2.1]
1414 roptinfo
.rapidcommit
= 1;
1415 resptype
= DH6_REPLY
;
1417 resptype
= DH6_ADVERTISE
;
1419 error
= server6_send(resptype
, ifp
, dh6
, optinfo
, from
, fromlen
,
1420 &roptinfo
, relayinfohead
, client_conf
);
1421 dhcp6_clear_options(&roptinfo
);
1425 dhcp6_clear_options(&roptinfo
);
1430 react_request(ifp
, pi
, dh6
, len
, optinfo
, from
, fromlen
, relayinfohead
)
1431 struct dhcp6_if
*ifp
;
1432 struct in6_pktinfo
*pi
;
1435 struct dhcp6_optinfo
*optinfo
;
1436 struct sockaddr
*from
;
1438 struct relayinfolist
*relayinfohead
;
1440 struct dhcp6_optinfo roptinfo
;
1441 struct host_conf
*client_conf
;
1443 /* message validation according to Section 15.4 of RFC3315 */
1445 /* the message must include a Server Identifier option */
1446 if (optinfo
->serverID
.duid_len
== 0) {
1447 dprintf(LOG_INFO
, FNAME
, "no server ID option");
1450 /* the contents of the Server Identifier option must match ours */
1451 if (duidcmp(&optinfo
->serverID
, &server_duid
)) {
1452 dprintf(LOG_INFO
, FNAME
, "server ID mismatch");
1455 /* the message must include a Client Identifier option */
1456 if (optinfo
->clientID
.duid_len
== 0) {
1457 dprintf(LOG_INFO
, FNAME
, "no client ID option");
1462 * configure necessary options based on the options in request.
1464 dhcp6_init_options(&roptinfo
);
1466 /* server identifier option */
1467 if (duidcpy(&roptinfo
.serverID
, &server_duid
)) {
1468 dprintf(LOG_ERR
, FNAME
, "failed to copy server ID");
1471 /* copy client information back */
1472 if (duidcpy(&roptinfo
.clientID
, &optinfo
->clientID
)) {
1473 dprintf(LOG_ERR
, FNAME
, "failed to copy client ID");
1477 /* get per-host configuration for the client, if any. */
1478 if ((client_conf
= find_hostconf(&optinfo
->clientID
))) {
1479 dprintf(LOG_DEBUG
, FNAME
,
1480 "found a host configuration named %s", client_conf
->name
);
1483 /* process authentication */
1484 if (process_auth(dh6
, len
, client_conf
, optinfo
, &roptinfo
)) {
1485 dprintf(LOG_INFO
, FNAME
, "failed to process authentication "
1486 "information for %s",
1487 clientstr(client_conf
, &optinfo
->clientID
));
1492 * When the server receives a Request message via unicast from a
1493 * client to which the server has not sent a unicast option, the server
1494 * discards the Request message and responds with a Reply message
1495 * containing a Status Code option with value UseMulticast, a Server
1496 * Identifier option containing the server's DUID, the Client
1497 * Identifier option from the client message and no other options.
1499 * (Our current implementation never sends a unicast option.)
1500 * Note: a request message encapsulated in a relay server option can be
1503 if (!IN6_IS_ADDR_MULTICAST(&pi
->ipi6_addr
) &&
1504 TAILQ_EMPTY(relayinfohead
)) {
1505 u_int16_t stcode
= DH6OPT_STCODE_USEMULTICAST
;
1507 dprintf(LOG_INFO
, FNAME
, "unexpected unicast message from %s",
1509 if (dhcp6_add_listval(&roptinfo
.stcode_list
,
1510 DHCP6_LISTVAL_STCODE
, &stcode
, NULL
) == NULL
) {
1511 dprintf(LOG_ERR
, FNAME
, "failed to add a status code");
1514 server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
,
1515 fromlen
, &roptinfo
, relayinfohead
, client_conf
);
1520 * See if we have to make a binding of some configuration information
1525 * When a delegating router receives a Request message from a
1526 * requesting router that contains an IA_PD option, and the delegating
1527 * router is authorized to delegate prefix(es) to the requesting
1528 * router, the delegating router selects the prefix(es) to be delegated
1529 * to the requesting router.
1530 * [RFC3633 Section 12.2]
1532 if (!TAILQ_EMPTY(&optinfo
->iapd_list
)) {
1533 struct dhcp6_list conflist
;
1534 struct dhcp6_listval
*iapd
;
1536 TAILQ_INIT(&conflist
);
1538 /* make a local copy of the configured prefixes */
1540 dhcp6_copy_list(&conflist
, &client_conf
->prefix_list
)) {
1541 dprintf(LOG_NOTICE
, FNAME
,
1542 "failed to make local data");
1546 for (iapd
= TAILQ_FIRST(&optinfo
->iapd_list
); iapd
;
1547 iapd
= TAILQ_NEXT(iapd
, link
)) {
1549 * Find an appropriate prefix for each IA_PD,
1550 * removing the adopted prefixes from the list.
1551 * The prefixes will be bound to the client.
1553 if (make_ia(iapd
, &conflist
, &roptinfo
.iapd_list
,
1554 client_conf
, 1) == 0) {
1556 * We could not find any prefixes for the IA.
1557 * RFC3315 specifies to include NoAddrsAvail
1558 * for the IA in the address configuration
1559 * case (Section 18.2.1). We follow the same
1560 * logic for prefix delegation as well.
1562 if (make_ia_stcode(DHCP6_LISTVAL_IAPD
,
1564 DH6OPT_STCODE_NOPREFIXAVAIL
,
1565 &roptinfo
.iapd_list
)) {
1566 dprintf(LOG_NOTICE
, FNAME
,
1567 "failed to make an option list");
1568 dhcp6_clear_list(&conflist
);
1574 dhcp6_clear_list(&conflist
);
1577 if (!TAILQ_EMPTY(&optinfo
->iana_list
)) {
1578 struct dhcp6_list conflist
;
1579 struct dhcp6_listval
*iana
;
1581 if (client_conf
== NULL
&& ifp
->pool
.name
) {
1582 if ((client_conf
= create_dynamic_hostconf(&optinfo
->clientID
,
1583 &ifp
->pool
)) == NULL
)
1584 dprintf(LOG_NOTICE
, FNAME
,
1585 "failed to make host configuration");
1587 TAILQ_INIT(&conflist
);
1589 /* make a local copy of the configured prefixes */
1591 dhcp6_copy_list(&conflist
, &client_conf
->addr_list
)) {
1592 dprintf(LOG_NOTICE
, FNAME
,
1593 "failed to make local data");
1597 for (iana
= TAILQ_FIRST(&optinfo
->iana_list
); iana
;
1598 iana
= TAILQ_NEXT(iana
, link
)) {
1600 * Find an appropriate address for each IA_NA,
1601 * removing the adopted addresses from the list.
1602 * The addresses will be bound to the client.
1604 if (make_ia(iana
, &conflist
, &roptinfo
.iana_list
,
1605 client_conf
, 1) == 0) {
1606 if (make_ia_stcode(DHCP6_LISTVAL_IANA
,
1608 DH6OPT_STCODE_NOADDRSAVAIL
,
1609 &roptinfo
.iana_list
)) {
1610 dprintf(LOG_NOTICE
, FNAME
,
1611 "failed to make an option list");
1612 dhcp6_clear_list(&conflist
);
1618 dhcp6_clear_list(&conflist
);
1622 * If the Request message contained an Option Request option, the
1623 * server MUST include options in the Reply message for any options in
1624 * the Option Request option the server is configured to return to the
1627 * Note: our current implementation always includes all information
1628 * that we can provide. So we do not have to check the option request
1632 for (opt
= TAILQ_FIRST(&optinfo
->reqopt_list
); opt
;
1633 opt
= TAILQ_NEXT(opt
, link
)) {
1639 * Add options to the Reply message for any other configuration
1640 * information to be assigned to the client.
1642 if (set_statelessinfo(DH6_REQUEST
, &roptinfo
)) {
1643 dprintf(LOG_ERR
, FNAME
,
1644 "failed to set other stateless information");
1648 /* send a reply message. */
1649 (void)server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
, fromlen
,
1650 &roptinfo
, relayinfohead
, client_conf
);
1653 dhcp6_clear_options(&roptinfo
);
1657 dhcp6_clear_options(&roptinfo
);
1662 react_renew(ifp
, pi
, dh6
, len
, optinfo
, from
, fromlen
, relayinfohead
)
1663 struct dhcp6_if
*ifp
;
1664 struct in6_pktinfo
*pi
;
1667 struct dhcp6_optinfo
*optinfo
;
1668 struct sockaddr
*from
;
1670 struct relayinfolist
*relayinfohead
;
1672 struct dhcp6_optinfo roptinfo
;
1673 struct dhcp6_listval
*ia
;
1674 struct host_conf
*client_conf
;
1676 /* message validation according to Section 15.6 of RFC3315 */
1678 /* the message must include a Server Identifier option */
1679 if (optinfo
->serverID
.duid_len
== 0) {
1680 dprintf(LOG_INFO
, FNAME
, "no server ID option");
1683 /* the contents of the Server Identifier option must match ours */
1684 if (duidcmp(&optinfo
->serverID
, &server_duid
)) {
1685 dprintf(LOG_INFO
, FNAME
, "server ID mismatch");
1688 /* the message must include a Client Identifier option */
1689 if (optinfo
->clientID
.duid_len
== 0) {
1690 dprintf(LOG_INFO
, FNAME
, "no client ID option");
1695 * configure necessary options based on the options in request.
1697 dhcp6_init_options(&roptinfo
);
1699 /* server identifier option */
1700 if (duidcpy(&roptinfo
.serverID
, &server_duid
)) {
1701 dprintf(LOG_ERR
, FNAME
, "failed to copy server ID");
1704 /* copy client information back */
1705 if (duidcpy(&roptinfo
.clientID
, &optinfo
->clientID
)) {
1706 dprintf(LOG_ERR
, FNAME
, "failed to copy client ID");
1710 /* get per-host configuration for the client, if any. */
1711 if ((client_conf
= find_hostconf(&optinfo
->clientID
))) {
1712 dprintf(LOG_DEBUG
, FNAME
,
1713 "found a host configuration named %s", client_conf
->name
);
1716 /* process authentication */
1717 if (process_auth(dh6
, len
, client_conf
, optinfo
, &roptinfo
)) {
1718 dprintf(LOG_INFO
, FNAME
, "failed to process authentication "
1719 "information for %s",
1720 clientstr(client_conf
, &optinfo
->clientID
));
1725 * When the server receives a Renew message via unicast from a
1726 * client to which the server has not sent a unicast option, the server
1727 * discards the Request message and responds with a Reply message
1728 * containing a status code option with value UseMulticast, a Server
1729 * Identifier option containing the server's DUID, the Client
1730 * Identifier option from the client message and no other options.
1732 * (Our current implementation never sends a unicast option.)
1734 if (!IN6_IS_ADDR_MULTICAST(&pi
->ipi6_addr
) &&
1735 TAILQ_EMPTY(relayinfohead
)) {
1736 u_int16_t stcode
= DH6OPT_STCODE_USEMULTICAST
;
1738 dprintf(LOG_INFO
, FNAME
, "unexpected unicast message from %s",
1740 if (dhcp6_add_listval(&roptinfo
.stcode_list
,
1741 DHCP6_LISTVAL_STCODE
, &stcode
, NULL
) == NULL
) {
1742 dprintf(LOG_ERR
, FNAME
, "failed to add a status code");
1745 server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
,
1746 fromlen
, &roptinfo
, relayinfohead
, client_conf
);
1751 * Locates the client's binding and verifies that the information
1752 * from the client matches the information stored for that client.
1754 for (ia
= TAILQ_FIRST(&optinfo
->iapd_list
); ia
;
1755 ia
= TAILQ_NEXT(ia
, link
)) {
1756 if (update_ia(DH6_RENEW
, ia
, &roptinfo
.iapd_list
, optinfo
))
1759 for (ia
= TAILQ_FIRST(&optinfo
->iana_list
); ia
;
1760 ia
= TAILQ_NEXT(ia
, link
)) {
1761 if (update_ia(DH6_RENEW
, ia
, &roptinfo
.iana_list
, optinfo
))
1765 /* add other configuration information */
1766 if (set_statelessinfo(DH6_RENEW
, &roptinfo
)) {
1767 dprintf(LOG_ERR
, FNAME
,
1768 "failed to set other stateless information");
1772 (void)server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
, fromlen
,
1773 &roptinfo
, relayinfohead
, client_conf
);
1776 dhcp6_clear_options(&roptinfo
);
1780 dhcp6_clear_options(&roptinfo
);
1785 react_rebind(ifp
, dh6
, len
, optinfo
, from
, fromlen
, relayinfohead
)
1786 struct dhcp6_if
*ifp
;
1789 struct dhcp6_optinfo
*optinfo
;
1790 struct sockaddr
*from
;
1792 struct relayinfolist
*relayinfohead
;
1794 struct dhcp6_optinfo roptinfo
;
1795 struct dhcp6_listval
*ia
;
1796 struct host_conf
*client_conf
;
1798 /* message validation according to Section 15.7 of RFC3315 */
1800 /* the message must include a Client Identifier option */
1801 if (optinfo
->clientID
.duid_len
== 0) {
1802 dprintf(LOG_INFO
, FNAME
, "no client ID option");
1806 /* the message must not include a server Identifier option */
1807 if (optinfo
->serverID
.duid_len
) {
1808 dprintf(LOG_INFO
, FNAME
, "server ID option is included in "
1809 "a rebind message");
1814 * configure necessary options based on the options in request.
1816 dhcp6_init_options(&roptinfo
);
1818 /* server identifier option */
1819 if (duidcpy(&roptinfo
.serverID
, &server_duid
)) {
1820 dprintf(LOG_ERR
, FNAME
, "failed to copy server ID");
1823 /* copy client information back */
1824 if (duidcpy(&roptinfo
.clientID
, &optinfo
->clientID
)) {
1825 dprintf(LOG_ERR
, FNAME
, "failed to copy client ID");
1829 /* get per-host configuration for the client, if any. */
1830 if ((client_conf
= find_hostconf(&optinfo
->clientID
))) {
1831 dprintf(LOG_DEBUG
, FNAME
,
1832 "found a host configuration named %s", client_conf
->name
);
1835 /* process authentication */
1836 if (process_auth(dh6
, len
, client_conf
, optinfo
, &roptinfo
)) {
1837 dprintf(LOG_INFO
, FNAME
, "failed to process authentication "
1838 "information for %s",
1839 clientstr(client_conf
, &optinfo
->clientID
));
1844 * Locates the client's binding and verifies that the information
1845 * from the client matches the information stored for that client.
1847 for (ia
= TAILQ_FIRST(&optinfo
->iapd_list
); ia
;
1848 ia
= TAILQ_NEXT(ia
, link
)) {
1849 if (update_ia(DH6_REBIND
, ia
, &roptinfo
.iapd_list
, optinfo
))
1852 for (ia
= TAILQ_FIRST(&optinfo
->iana_list
); ia
;
1853 ia
= TAILQ_NEXT(ia
, link
)) {
1854 if (update_ia(DH6_REBIND
, ia
, &roptinfo
.iana_list
, optinfo
))
1859 * If the returned iana/pd_list is empty, we do not have an explicit
1860 * knowledge about validity nor invalidity for any IA_NA/PD information
1861 * in the Rebind message. In this case, we should rather ignore the
1862 * message than to send a Reply with empty information back to the
1863 * client, which may annoy the recipient. However, if we have at least
1864 * one useful information, either positive or negative, based on some
1865 * explicit knowledge, we should reply with the responsible part.
1867 if (TAILQ_EMPTY(&roptinfo
.iapd_list
) &&
1868 TAILQ_EMPTY(&roptinfo
.iana_list
)) {
1869 dprintf(LOG_INFO
, FNAME
, "no useful information for a rebind");
1870 goto fail
; /* discard the rebind */
1873 /* add other configuration information */
1874 if (set_statelessinfo(DH6_REBIND
, &roptinfo
)) {
1875 dprintf(LOG_ERR
, FNAME
,
1876 "failed to set other stateless information");
1880 (void)server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
, fromlen
,
1881 &roptinfo
, relayinfohead
, client_conf
);
1883 dhcp6_clear_options(&roptinfo
);
1887 dhcp6_clear_options(&roptinfo
);
1892 react_release(ifp
, pi
, dh6
, len
, optinfo
, from
, fromlen
, relayinfohead
)
1893 struct dhcp6_if
*ifp
;
1894 struct in6_pktinfo
*pi
;
1897 struct dhcp6_optinfo
*optinfo
;
1898 struct sockaddr
*from
;
1900 struct relayinfolist
*relayinfohead
;
1902 struct dhcp6_optinfo roptinfo
;
1903 struct dhcp6_listval
*ia
;
1904 struct host_conf
*client_conf
;
1907 /* message validation according to Section 15.9 of RFC3315 */
1909 /* the message must include a Server Identifier option */
1910 if (optinfo
->serverID
.duid_len
== 0) {
1911 dprintf(LOG_INFO
, FNAME
, "no server ID option");
1914 /* the contents of the Server Identifier option must match ours */
1915 if (duidcmp(&optinfo
->serverID
, &server_duid
)) {
1916 dprintf(LOG_INFO
, FNAME
, "server ID mismatch");
1919 /* the message must include a Client Identifier option */
1920 if (optinfo
->clientID
.duid_len
== 0) {
1921 dprintf(LOG_INFO
, FNAME
, "no client ID option");
1926 * configure necessary options based on the options in request.
1928 dhcp6_init_options(&roptinfo
);
1930 /* server identifier option */
1931 if (duidcpy(&roptinfo
.serverID
, &server_duid
)) {
1932 dprintf(LOG_ERR
, FNAME
, "failed to copy server ID");
1935 /* copy client information back */
1936 if (duidcpy(&roptinfo
.clientID
, &optinfo
->clientID
)) {
1937 dprintf(LOG_ERR
, FNAME
, "failed to copy client ID");
1941 /* get per-host configuration for the client, if any. */
1942 if ((client_conf
= find_hostconf(&optinfo
->clientID
))) {
1943 dprintf(LOG_DEBUG
, FNAME
,
1944 "found a host configuration named %s", client_conf
->name
);
1947 /* process authentication */
1948 if (process_auth(dh6
, len
, client_conf
, optinfo
, &roptinfo
)) {
1949 dprintf(LOG_INFO
, FNAME
, "failed to process authentication "
1950 "information for %s",
1951 clientstr(client_conf
, &optinfo
->clientID
));
1956 * When the server receives a Release message via unicast from a
1957 * client to which the server has not sent a unicast option, the server
1958 * discards the Release message and responds with a Reply message
1959 * containing a Status Code option with value UseMulticast, a Server
1960 * Identifier option containing the server's DUID, the Client
1961 * Identifier option from the client message and no other options.
1963 * (Our current implementation never sends a unicast option.)
1965 if (!IN6_IS_ADDR_MULTICAST(&pi
->ipi6_addr
) &&
1966 TAILQ_EMPTY(relayinfohead
)) {
1967 u_int16_t stcode
= DH6OPT_STCODE_USEMULTICAST
;
1969 dprintf(LOG_INFO
, FNAME
, "unexpected unicast message from %s",
1971 if (dhcp6_add_listval(&roptinfo
.stcode_list
,
1972 DHCP6_LISTVAL_STCODE
, &stcode
, NULL
) == NULL
) {
1973 dprintf(LOG_ERR
, FNAME
, "failed to add a status code");
1976 server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
,
1977 fromlen
, &roptinfo
, relayinfohead
, client_conf
);
1982 * Locates the client's binding and verifies that the information
1983 * from the client matches the information stored for that client.
1985 for (ia
= TAILQ_FIRST(&optinfo
->iapd_list
); ia
;
1986 ia
= TAILQ_NEXT(ia
, link
)) {
1987 if (release_binding_ia(ia
, &roptinfo
.iapd_list
, optinfo
))
1990 for (ia
= TAILQ_FIRST(&optinfo
->iana_list
); ia
;
1991 ia
= TAILQ_NEXT(ia
, link
)) {
1992 if (release_binding_ia(ia
, &roptinfo
.iana_list
, optinfo
))
1997 * After all the addresses have been processed, the server generates a
1998 * Reply message and includes a Status Code option with value Success.
1999 * [RFC3315 Section 18.2.6]
2001 stcode
= DH6OPT_STCODE_SUCCESS
;
2002 if (dhcp6_add_listval(&roptinfo
.stcode_list
,
2003 DHCP6_LISTVAL_STCODE
, &stcode
, NULL
) == NULL
) {
2004 dprintf(LOG_NOTICE
, FNAME
, "failed to add a status code");
2008 (void)server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
, fromlen
,
2009 &roptinfo
, relayinfohead
, client_conf
);
2012 dhcp6_clear_options(&roptinfo
);
2016 dhcp6_clear_options(&roptinfo
);
2021 react_decline(ifp
, pi
, dh6
, len
, optinfo
, from
, fromlen
, relayinfohead
)
2022 struct dhcp6_if
*ifp
;
2023 struct in6_pktinfo
*pi
;
2026 struct dhcp6_optinfo
*optinfo
;
2027 struct sockaddr
*from
;
2029 struct relayinfolist
*relayinfohead
;
2031 struct dhcp6_optinfo roptinfo
;
2032 struct dhcp6_listval
*ia
;
2033 struct host_conf
*client_conf
;
2036 /* message validation according to Section 15.8 of RFC3315 */
2038 /* the message must include a Server Identifier option */
2039 if (optinfo
->serverID
.duid_len
== 0) {
2040 dprintf(LOG_INFO
, FNAME
, "no server ID option");
2043 /* the contents of the Server Identifier option must match ours */
2044 if (duidcmp(&optinfo
->serverID
, &server_duid
)) {
2045 dprintf(LOG_INFO
, FNAME
, "server ID mismatch");
2048 /* the message must include a Client Identifier option */
2049 if (optinfo
->clientID
.duid_len
== 0) {
2050 dprintf(LOG_INFO
, FNAME
, "no client ID option");
2055 * configure necessary options based on the options in request.
2057 dhcp6_init_options(&roptinfo
);
2059 /* server identifier option */
2060 if (duidcpy(&roptinfo
.serverID
, &server_duid
)) {
2061 dprintf(LOG_ERR
, FNAME
, "failed to copy server ID");
2064 /* copy client information back */
2065 if (duidcpy(&roptinfo
.clientID
, &optinfo
->clientID
)) {
2066 dprintf(LOG_ERR
, FNAME
, "failed to copy client ID");
2070 /* get per-host configuration for the client, if any. */
2071 if ((client_conf
= find_hostconf(&optinfo
->clientID
))) {
2072 dprintf(LOG_DEBUG
, FNAME
,
2073 "found a host configuration named %s", client_conf
->name
);
2076 /* process authentication */
2077 if (process_auth(dh6
, len
, client_conf
, optinfo
, &roptinfo
)) {
2078 dprintf(LOG_INFO
, FNAME
, "failed to process authentication "
2079 "information for %s",
2080 clientstr(client_conf
, &optinfo
->clientID
));
2085 * When the server receives a Decline message via unicast from a
2086 * client to which the server has not sent a unicast option, the server
2087 * discards the Decline message and responds with a Reply message
2088 * containing a Status Code option with value UseMulticast, a Server
2089 * Identifier option containing the server's DUID, the Client
2090 * Identifier option from the client message and no other options.
2092 * (Our current implementation never sends a unicast option.)
2094 if (!IN6_IS_ADDR_MULTICAST(&pi
->ipi6_addr
) &&
2095 TAILQ_EMPTY(relayinfohead
)) {
2096 stcode
= DH6OPT_STCODE_USEMULTICAST
;
2098 dprintf(LOG_INFO
, FNAME
, "unexpected unicast message from %s",
2100 if (dhcp6_add_listval(&roptinfo
.stcode_list
,
2101 DHCP6_LISTVAL_STCODE
, &stcode
, NULL
) == NULL
) {
2102 dprintf(LOG_ERR
, FNAME
, "failed to add a status code");
2105 server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
,
2106 fromlen
, &roptinfo
, relayinfohead
, client_conf
);
2111 * Locates the client's binding on IA-NA and verifies that the
2112 * information from the client matches the information stored
2113 * for that client. (IA-PD is just ignored [RFC3633 12.1])
2115 for (ia
= TAILQ_FIRST(&optinfo
->iana_list
); ia
;
2116 ia
= TAILQ_NEXT(ia
, link
)) {
2117 if (decline_binding_ia(ia
, &roptinfo
.iana_list
, optinfo
))
2122 * After all the addresses have been processed, the server generates a
2123 * Reply message and includes a Status Code option with value Success.
2124 * [RFC3315 Section 18.2.7]
2126 stcode
= DH6OPT_STCODE_SUCCESS
;
2127 if (dhcp6_add_listval(&roptinfo
.stcode_list
,
2128 DHCP6_LISTVAL_STCODE
, &stcode
, NULL
) == NULL
) {
2129 dprintf(LOG_NOTICE
, FNAME
, "failed to add a status code");
2133 (void)server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
, fromlen
,
2134 &roptinfo
, relayinfohead
, client_conf
);
2137 dhcp6_clear_options(&roptinfo
);
2141 dhcp6_clear_options(&roptinfo
);
2146 react_confirm(ifp
, pi
, dh6
, len
, optinfo
, from
, fromlen
, relayinfohead
)
2147 struct dhcp6_if
*ifp
;
2148 struct in6_pktinfo
*pi
;
2151 struct dhcp6_optinfo
*optinfo
;
2152 struct sockaddr
*from
;
2154 struct relayinfolist
*relayinfohead
;
2156 struct dhcp6_optinfo roptinfo
;
2157 struct dhcp6_list conflist
;
2158 struct dhcp6_listval
*iana
, *iaaddr
;
2159 struct host_conf
*client_conf
;
2160 u_int16_t stcode
= DH6OPT_STCODE_SUCCESS
;
2163 /* message validation according to Section 15.5 of RFC3315 */
2165 /* the message may not include a Server Identifier option */
2166 if (optinfo
->serverID
.duid_len
) {
2167 dprintf(LOG_INFO
, FNAME
, "server ID option found");
2170 /* the message must include a Client Identifier option */
2171 if (optinfo
->clientID
.duid_len
== 0) {
2172 dprintf(LOG_INFO
, FNAME
, "no client ID option");
2176 dhcp6_init_options(&roptinfo
);
2178 /* server identifier option */
2179 if (duidcpy(&roptinfo
.serverID
, &server_duid
)) {
2180 dprintf(LOG_ERR
, FNAME
, "failed to copy server ID");
2183 /* copy client information back */
2184 if (duidcpy(&roptinfo
.clientID
, &optinfo
->clientID
)) {
2185 dprintf(LOG_ERR
, FNAME
, "failed to copy client ID");
2189 /* get per-host configuration for the client, if any. */
2190 if ((client_conf
= find_hostconf(&optinfo
->clientID
))) {
2191 dprintf(LOG_DEBUG
, FNAME
,
2192 "found a host configuration named %s", client_conf
->name
);
2195 /* process authentication */
2196 if (process_auth(dh6
, len
, client_conf
, optinfo
, &roptinfo
)) {
2197 dprintf(LOG_INFO
, FNAME
, "failed to process authentication "
2198 "information for %s",
2199 clientstr(client_conf
, &optinfo
->clientID
));
2203 if (client_conf
== NULL
&& ifp
->pool
.name
) {
2204 if ((client_conf
= create_dynamic_hostconf(&optinfo
->clientID
,
2205 &ifp
->pool
)) == NULL
) {
2206 dprintf(LOG_NOTICE
, FNAME
,
2207 "failed to make host configuration");
2211 TAILQ_INIT(&conflist
);
2212 /* make a local copy of the configured addresses */
2213 if (dhcp6_copy_list(&conflist
, &client_conf
->addr_list
)) {
2214 dprintf(LOG_NOTICE
, FNAME
,
2215 "failed to make local data");
2220 * the message must include an IPv6 address to be confirmed
2221 * [RFC3315 18.2]. (IA-PD is just ignored [RFC3633 12.1])
2223 if (TAILQ_EMPTY(&optinfo
->iana_list
)) {
2224 dprintf(LOG_INFO
, FNAME
, "no IA-NA option found");
2227 for (iana
= TAILQ_FIRST(&optinfo
->iana_list
); iana
;
2228 iana
= TAILQ_NEXT(iana
, link
)) {
2229 if (TAILQ_EMPTY(&iana
->sublist
)) {
2230 dprintf(LOG_INFO
, FNAME
,
2231 "no IA-ADDR option found in IA-NA %d",
2237 * check whether the confirmed prefix matches
2238 * the prefix from where the message originates.
2239 * XXX: prefix length is assumed to be 64
2241 for (iaaddr
= TAILQ_FIRST(&iana
->sublist
); iaaddr
;
2242 iaaddr
= TAILQ_NEXT(iaaddr
, link
)) {
2244 struct in6_addr
*confaddr
= &iaaddr
->val_statefuladdr6
.addr
;
2245 struct in6_addr
*linkaddr
;
2246 struct sockaddr_in6
*src
= (struct sockaddr_in6
*)from
;
2248 if (!IN6_IS_ADDR_LINKLOCAL(&src
->sin6_addr
)) {
2249 /* CONFIRM is relayed via a DHCP-relay */
2250 struct relayinfo
*relayinfo
;
2252 if (relayinfohead
== NULL
) {
2253 dprintf(LOG_INFO
, FNAME
,
2254 "no link-addr found");
2257 relayinfo
= TAILQ_LAST(relayinfohead
, relayinfolist
);
2259 /* XXX: link-addr is supposed to be a global address */
2260 linkaddr
= &relayinfo
->linkaddr
;
2262 /* CONFIRM is directly arrived */
2263 linkaddr
= &ifp
->addr
;
2266 if (memcmp(linkaddr
, confaddr
, 8) != 0) {
2267 dprintf(LOG_INFO
, FNAME
,
2268 "%s does not seem to belong to %s's link",
2269 in6addr2str(confaddr
, 0),
2270 in6addr2str(linkaddr
, 0));
2271 stcode
= DH6OPT_STCODE_NOTONLINK
;
2278 * even when the given address seems to be on the appropriate link,
2279 * the confirm should be ignore if there's no corrensponding IA-NA
2282 for (iana
= TAILQ_FIRST(&optinfo
->iana_list
); iana
;
2283 iana
= TAILQ_NEXT(iana
, link
)) {
2284 if (make_ia(iana
, &conflist
, &roptinfo
.iana_list
,
2285 client_conf
, 1) == 0) {
2286 dprintf(LOG_DEBUG
, FNAME
,
2287 "IA-NA configuration not found");
2293 if (dhcp6_add_listval(&roptinfo
.stcode_list
,
2294 DHCP6_LISTVAL_STCODE
, &stcode
, NULL
) == NULL
)
2296 error
= server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
, fromlen
,
2297 &roptinfo
, relayinfohead
, client_conf
);
2299 dhcp6_clear_options(&roptinfo
);
2300 dhcp6_clear_list(&conflist
);
2305 dhcp6_clear_options(&roptinfo
);
2306 dhcp6_clear_list(&conflist
);
2311 react_informreq(ifp
, dh6
, len
, optinfo
, from
, fromlen
, relayinfohead
)
2312 struct dhcp6_if
*ifp
;
2315 struct dhcp6_optinfo
*optinfo
;
2316 struct sockaddr
*from
;
2318 struct relayinfolist
*relayinfohead
;
2320 struct dhcp6_optinfo roptinfo
;
2324 * An IA option is not allowed to appear in an Information-request
2325 * message. Such a message SHOULD be discarded.
2326 * [RFC3315 Section 15]
2328 if (!TAILQ_EMPTY(&optinfo
->iapd_list
)) {
2329 dprintf(LOG_INFO
, FNAME
,
2330 "information request contains an IA_PD option");
2333 if (!TAILQ_EMPTY(&optinfo
->iana_list
)) {
2334 dprintf(LOG_INFO
, FNAME
,
2335 "information request contains an IA_NA option");
2339 /* if a server identifier is included, it must match ours. */
2340 if (optinfo
->serverID
.duid_len
&&
2341 duidcmp(&optinfo
->serverID
, &server_duid
)) {
2342 dprintf(LOG_INFO
, FNAME
, "server DUID mismatch");
2347 * configure necessary options based on the options in request.
2349 dhcp6_init_options(&roptinfo
);
2351 /* server identifier option */
2352 if (duidcpy(&roptinfo
.serverID
, &server_duid
)) {
2353 dprintf(LOG_ERR
, FNAME
, "failed to copy server ID");
2357 /* copy client information back (if provided) */
2358 if (optinfo
->clientID
.duid_id
&&
2359 duidcpy(&roptinfo
.clientID
, &optinfo
->clientID
)) {
2360 dprintf(LOG_ERR
, FNAME
, "failed to copy client ID");
2364 /* set stateless information */
2365 if (set_statelessinfo(DH6_INFORM_REQ
, &roptinfo
)) {
2366 dprintf(LOG_ERR
, FNAME
,
2367 "failed to set other stateless information");
2371 error
= server6_send(DH6_REPLY
, ifp
, dh6
, optinfo
, from
, fromlen
,
2372 &roptinfo
, relayinfohead
, NULL
);
2374 dhcp6_clear_options(&roptinfo
);
2378 dhcp6_clear_options(&roptinfo
);
2383 update_ia(msgtype
, iap
, retlist
, optinfo
)
2385 struct dhcp6_listval
*iap
;
2386 struct dhcp6_list
*retlist
;
2387 struct dhcp6_optinfo
*optinfo
;
2389 struct dhcp6_binding
*binding
;
2390 struct host_conf
*client_conf
;
2392 /* get per-host configuration for the client, if any. */
2393 if ((client_conf
= find_hostconf(&optinfo
->clientID
))) {
2394 dprintf(LOG_DEBUG
, FNAME
,
2395 "found a host configuration named %s", client_conf
->name
);
2398 if ((binding
= find_binding(&optinfo
->clientID
, DHCP6_BINDING_IA
,
2399 iap
->type
, iap
->val_ia
.iaid
)) == NULL
) {
2401 * Behavior in the case where the delegating router cannot
2402 * find a binding for the requesting router's IA_PD as
2403 * described in RFC3633 Section 12.2. It is derived from
2404 * Sections 18.2.3 and 18.2.4 of RFC3315, and the two sets
2405 * of behavior are identical.
2407 dprintf(LOG_INFO
, FNAME
, "no binding found for %s",
2408 duidstr(&optinfo
->clientID
));
2413 * If the delegating router cannot find a binding for
2414 * the requesting router's IA_PD the delegating router
2415 * returns the IA_PD containing no prefixes with a
2416 * Status Code option set to NoBinding in the Reply
2419 if (make_ia_stcode(iap
->type
, iap
->val_ia
.iaid
,
2420 DH6OPT_STCODE_NOBINDING
, retlist
)) {
2421 dprintf(LOG_NOTICE
, FNAME
,
2422 "failed to make an option list");
2428 * If it can be determined the prefixes are not
2429 * appropriate from the delegating router's explicit
2430 * configuration, it MAY send a Reply message to
2431 * the requesting router containing the IA_PD with the
2432 * lifetimes of the prefixes in the IA_PD set to zero.
2434 * If unable to determine, the Rebind message is
2437 * XXX: it is not very clear what the explicit
2438 * configuration means. Thus, we always discard the
2442 default: /* XXX: should be a bug */
2443 dprintf(LOG_ERR
, FNAME
, "impossible message type %s",
2444 dhcp6msgstr(msgtype
));
2447 } else { /* we found a binding */
2448 struct dhcp6_list ialist
;
2449 struct dhcp6_listval
*lv
;
2450 struct dhcp6_prefix prefix
;
2451 struct dhcp6_statefuladdr saddr
;
2454 TAILQ_INIT(&ialist
);
2455 update_binding(binding
);
2457 /* see if each information to be renewed is still valid. */
2458 for (lv
= TAILQ_FIRST(&iap
->sublist
); lv
;
2459 lv
= TAILQ_NEXT(lv
, link
)) {
2460 struct dhcp6_listval
*blv
;
2462 switch (iap
->type
) {
2463 case DHCP6_LISTVAL_IAPD
:
2464 if (lv
->type
!= DHCP6_LISTVAL_PREFIX6
)
2467 prefix
= lv
->val_prefix6
;
2468 blv
= dhcp6_find_listval(&binding
->val_list
,
2469 DHCP6_LISTVAL_PREFIX6
, &prefix
, 0);
2471 dprintf(LOG_DEBUG
, FNAME
,
2472 "%s/%d is not found in %s",
2473 in6addr2str(&prefix
.addr
, 0),
2474 prefix
.plen
, bindingstr(binding
));
2479 blv
->val_prefix6
.pltime
;
2481 blv
->val_prefix6
.vltime
;
2484 if (dhcp6_add_listval(&ialist
,
2485 DHCP6_LISTVAL_PREFIX6
, &prefix
, NULL
)
2487 dprintf(LOG_NOTICE
, FNAME
,
2488 "failed to copy binding info");
2489 dhcp6_clear_list(&ialist
);
2493 case DHCP6_LISTVAL_IANA
:
2494 if (lv
->type
!= DHCP6_LISTVAL_STATEFULADDR6
)
2497 saddr
= lv
->val_statefuladdr6
;
2498 blv
= dhcp6_find_listval(&binding
->val_list
,
2499 DHCP6_LISTVAL_STATEFULADDR6
, &saddr
, 0);
2501 dprintf(LOG_DEBUG
, FNAME
,
2502 "%s is not found in %s",
2503 in6addr2str(&saddr
.addr
, 0),
2504 bindingstr(binding
));
2509 blv
->val_statefuladdr6
.pltime
;
2511 blv
->val_statefuladdr6
.vltime
;
2514 if (dhcp6_add_listval(&ialist
,
2515 DHCP6_LISTVAL_STATEFULADDR6
, &saddr
, NULL
)
2517 dprintf(LOG_NOTICE
, FNAME
,
2518 "failed to copy binding info");
2519 dhcp6_clear_list(&ialist
);
2524 dprintf(LOG_ERR
, FNAME
, "unsupported IA type");
2525 return (-1); /* XXX */
2529 memset(&ia
, 0, sizeof(ia
));
2530 ia
.iaid
= binding
->iaid
;
2531 /* determine appropriate T1 and T2 */
2532 calc_ia_timo(&ia
, &ialist
, client_conf
);
2534 if (dhcp6_add_listval(retlist
, iap
->type
,
2535 &ia
, &ialist
) == NULL
) {
2536 dhcp6_clear_list(&ialist
);
2539 dhcp6_clear_list(&ialist
);
2546 release_binding_ia(iap
, retlist
, optinfo
)
2547 struct dhcp6_listval
*iap
;
2548 struct dhcp6_list
*retlist
;
2549 struct dhcp6_optinfo
*optinfo
;
2551 struct dhcp6_binding
*binding
;
2553 if ((binding
= find_binding(&optinfo
->clientID
, DHCP6_BINDING_IA
,
2554 iap
->type
, iap
->val_ia
.iaid
)) == NULL
) {
2556 * For each IA in the Release message for which the server has
2557 * no binding information, the server adds an IA option using
2558 * the IAID from the Release message and includes a Status Code
2559 * option with the value NoBinding in the IA option.
2561 if (make_ia_stcode(iap
->type
, iap
->val_ia
.iaid
,
2562 DH6OPT_STCODE_NOBINDING
, retlist
)) {
2563 dprintf(LOG_NOTICE
, FNAME
,
2564 "failed to make an option list");
2568 struct dhcp6_listval
*lv
, *lvia
;
2571 * If the IAs in the message are in a binding for the client
2572 * and the addresses in the IAs have been assigned by the
2573 * server to those IAs, the server deletes the addresses from
2574 * the IAs and makes the addresses available for assignment to
2576 * [RFC3315 Section 18.2.6]
2577 * RFC3633 is not very clear about the similar case for IA_PD,
2578 * but we apply the same logic.
2580 for (lv
= TAILQ_FIRST(&iap
->sublist
); lv
;
2581 lv
= TAILQ_NEXT(lv
, link
)) {
2582 if ((lvia
= find_binding_ia(lv
, binding
)) != NULL
) {
2583 switch (binding
->iatype
) {
2584 case DHCP6_LISTVAL_IAPD
:
2585 dprintf(LOG_DEBUG
, FNAME
,
2586 "bound prefix %s/%d "
2587 "has been released",
2588 in6addr2str(&lvia
->val_prefix6
.addr
,
2590 lvia
->val_prefix6
.plen
);
2592 case DHCP6_LISTVAL_IANA
:
2593 release_address(&lvia
->val_prefix6
.addr
);
2594 dprintf(LOG_DEBUG
, FNAME
,
2596 "has been released",
2597 in6addr2str(&lvia
->val_prefix6
.addr
,
2602 TAILQ_REMOVE(&binding
->val_list
, lvia
, link
);
2603 dhcp6_clear_listval(lvia
);
2604 if (TAILQ_EMPTY(&binding
->val_list
)) {
2606 * if the binding has become empty,
2609 remove_binding(binding
);
2620 decline_binding_ia(iap
, retlist
, optinfo
)
2621 struct dhcp6_listval
*iap
;
2622 struct dhcp6_list
*retlist
;
2623 struct dhcp6_optinfo
*optinfo
;
2625 struct dhcp6_binding
*binding
;
2626 struct dhcp6_listval
*lv
, *lvia
;
2628 if ((binding
= find_binding(&optinfo
->clientID
, DHCP6_BINDING_IA
,
2629 iap
->type
, iap
->val_ia
.iaid
)) == NULL
) {
2631 * For each IA in the Decline message for which the server has
2632 * no binding information, the server adds an IA option using
2633 * the IAID from the Release message and includes a Status Code
2634 * option with the value NoBinding in the IA option.
2636 if (make_ia_stcode(iap
->type
, iap
->val_ia
.iaid
,
2637 DH6OPT_STCODE_NOBINDING
, retlist
)) {
2638 dprintf(LOG_NOTICE
, FNAME
,
2639 "failed to make an option list");
2647 * If the IAs in the message are in a binding for the client and the
2648 * addresses in the IAs have been assigned by the server to those IAs,
2649 * the server deletes the addresses from the IAs and makes the addresses
2650 * available for assignment to other clients. [RFC3315 Section 18.2.7]
2652 for (lv
= TAILQ_FIRST(&iap
->sublist
); lv
;
2653 lv
= TAILQ_NEXT(lv
, link
)) {
2654 if (binding
->iatype
!= DHCP6_LISTVAL_IANA
) {
2655 /* should never reach here */
2659 if ((lvia
= find_binding_ia(lv
, binding
)) == NULL
) {
2660 dprintf(LOG_DEBUG
, FNAME
, "no binding found "
2662 in6addr2str(&lv
->val_statefuladdr6
.addr
, 0));
2666 dprintf(LOG_DEBUG
, FNAME
,
2667 "bound address %s has been marked as declined",
2668 in6addr2str(&lvia
->val_statefuladdr6
.addr
, 0));
2669 decline_address(&lvia
->val_statefuladdr6
.addr
);
2671 TAILQ_REMOVE(&binding
->val_list
, lvia
, link
);
2672 dhcp6_clear_listval(lvia
);
2673 if (TAILQ_EMPTY(&binding
->val_list
)) {
2675 * if the binding has become empty,
2678 remove_binding(binding
);
2691 dprintf(LOG_INFO
, FNAME
, "received a signal (%d)", sig
);
2695 sig_flags
|= SIGF_TERM
;
2701 server6_send(type
, ifp
, origmsg
, optinfo
, from
, fromlen
,
2702 roptinfo
, relayinfohead
, client_conf
)
2704 struct dhcp6_if
*ifp
;
2705 struct dhcp6
*origmsg
;
2706 struct dhcp6_optinfo
*optinfo
, *roptinfo
;
2707 struct sockaddr
*from
;
2709 struct relayinfolist
*relayinfohead
;
2710 struct host_conf
*client_conf
;
2712 char replybuf
[BUFSIZ
];
2713 struct sockaddr_in6 dst
;
2717 struct relayinfo
*relayinfo
;
2719 if (sizeof(struct dhcp6
) > sizeof(replybuf
)) {
2720 dprintf(LOG_ERR
, FNAME
, "buffer size assumption failed");
2724 dh6
= (struct dhcp6
*)replybuf
;
2726 memset(dh6
, 0, sizeof(*dh6
));
2727 dh6
->dh6_msgtypexid
= origmsg
->dh6_msgtypexid
;
2728 dh6
->dh6_msgtype
= (u_int8_t
)type
;
2730 /* set options in the reply message */
2731 if ((optlen
= dhcp6_set_options(type
, (struct dhcp6opt
*)(dh6
+ 1),
2732 (struct dhcp6opt
*)(replybuf
+ sizeof(replybuf
)), roptinfo
)) < 0) {
2733 dprintf(LOG_INFO
, FNAME
, "failed to construct reply options");
2738 /* calculate MAC if necessary, and put it to the message */
2739 switch (roptinfo
->authproto
) {
2740 case DHCP6_AUTHPROTO_DELAYED
:
2741 if (client_conf
== NULL
|| client_conf
->delayedkey
== NULL
) {
2742 /* This case should have been caught earlier */
2743 dprintf(LOG_ERR
, FNAME
, "authentication required "
2744 "but not key provided");
2747 if (dhcp6_calc_mac((char *)dh6
, len
, roptinfo
->authproto
,
2748 roptinfo
->authalgorithm
,
2749 roptinfo
->delayedauth_offset
+ sizeof(*dh6
),
2750 client_conf
->delayedkey
)) {
2751 dprintf(LOG_WARNING
, FNAME
, "failed to calculate MAC");
2756 break; /* do nothing */
2759 /* construct a relay chain, if necessary */
2760 for (relayinfo
= TAILQ_FIRST(relayinfohead
); relayinfo
;
2761 relayinfo
= TAILQ_NEXT(relayinfo
, link
)) {
2762 struct dhcp6_optinfo relayopt
;
2763 struct dhcp6_vbuf relaymsgbuf
;
2764 struct dhcp6_relay
*dh6relay
;
2767 dhcp6_init_options(&relayopt
);
2769 relaymsgbuf
.dv_len
= len
;
2770 relaymsgbuf
.dv_buf
= replybuf
;
2771 if (dhcp6_vbuf_copy(&relayopt
.relay_msg
, &relaymsgbuf
))
2773 if (relayinfo
->relay_ifid
.dv_buf
&&
2774 dhcp6_vbuf_copy(&relayopt
.ifidopt
,
2775 &relayinfo
->relay_ifid
)) {
2776 dhcp6_vbuf_free(&relayopt
.relay_msg
);
2780 /* we can safely reuse replybuf here */
2781 dh6relay
= (struct dhcp6_relay
*)replybuf
;
2782 memset(dh6relay
, 0, sizeof (*dh6relay
));
2783 dh6relay
->dh6relay_msgtype
= DH6_RELAY_REPLY
;
2784 dh6relay
->dh6relay_hcnt
= relayinfo
->hcnt
;
2785 memcpy(&dh6relay
->dh6relay_linkaddr
, &relayinfo
->linkaddr
,
2786 sizeof (dh6relay
->dh6relay_linkaddr
));
2787 memcpy(&dh6relay
->dh6relay_peeraddr
, &relayinfo
->peeraddr
,
2788 sizeof (dh6relay
->dh6relay_peeraddr
));
2790 len
= sizeof(*dh6relay
);
2791 if ((optlen
= dhcp6_set_options(DH6_RELAY_REPLY
,
2792 (struct dhcp6opt
*)(dh6relay
+ 1),
2793 (struct dhcp6opt
*)(replybuf
+ sizeof(replybuf
)),
2795 dprintf(LOG_INFO
, FNAME
,
2796 "failed to construct relay message");
2797 dhcp6_clear_options(&relayopt
);
2802 dhcp6_clear_options(&relayopt
);
2805 /* specify the destination and send the reply */
2806 dst
= relayed
? *sa6_any_relay
: *sa6_any_downstream
;
2807 dst
.sin6_addr
= ((struct sockaddr_in6
*)from
)->sin6_addr
;
2808 dst
.sin6_scope_id
= ((struct sockaddr_in6
*)from
)->sin6_scope_id
;
2809 if (transmit_sa(outsock
, (struct sockaddr
*)&dst
,
2810 replybuf
, len
) != 0) {
2811 dprintf(LOG_ERR
, FNAME
, "transmit %s to %s failed",
2812 dhcp6msgstr(type
), addr2str((struct sockaddr
*)&dst
));
2816 dprintf(LOG_DEBUG
, FNAME
, "transmit %s to %s",
2817 dhcp6msgstr(type
), addr2str((struct sockaddr
*)&dst
));
2823 make_ia_stcode(iatype
, iaid
, stcode
, retlist
)
2827 struct dhcp6_list
*retlist
;
2829 struct dhcp6_list stcode_list
;
2830 struct dhcp6_ia ia_empty
;
2832 memset(&ia_empty
, 0, sizeof(ia_empty
));
2833 ia_empty
.iaid
= iaid
;
2835 TAILQ_INIT(&stcode_list
);
2836 if (dhcp6_add_listval(&stcode_list
, DHCP6_LISTVAL_STCODE
,
2837 &stcode
, NULL
) == NULL
) {
2838 dprintf(LOG_NOTICE
, FNAME
, "failed to make an option list");
2842 if (dhcp6_add_listval(retlist
, iatype
,
2843 &ia_empty
, &stcode_list
) == NULL
) {
2844 dprintf(LOG_NOTICE
, FNAME
, "failed to make an option list");
2845 dhcp6_clear_list(&stcode_list
);
2848 dhcp6_clear_list(&stcode_list
);
2854 make_ia(spec
, conflist
, retlist
, client_conf
, do_binding
)
2855 struct dhcp6_listval
*spec
;
2856 struct dhcp6_list
*conflist
, *retlist
;
2857 struct host_conf
*client_conf
;
2860 struct dhcp6_binding
*binding
;
2861 struct dhcp6_list ialist
;
2862 struct dhcp6_listval
*specia
;
2867 * If we happen to have a binding already, update the binding and
2868 * return it. Perhaps the request is being retransmitted.
2870 if ((binding
= find_binding(&client_conf
->duid
, DHCP6_BINDING_IA
,
2871 spec
->type
, spec
->val_ia
.iaid
)) != NULL
) {
2872 struct dhcp6_list
*blist
= &binding
->val_list
;
2873 struct dhcp6_listval
*bia
, *v
;
2875 dprintf(LOG_DEBUG
, FNAME
, "we have a binding already: %s",
2876 bindingstr(binding
));
2878 update_binding(binding
);
2880 memset(&ia
, 0, sizeof(ia
));
2881 ia
.iaid
= spec
->val_ia
.iaid
;
2882 /* determine appropriate T1 and T2 */
2883 calc_ia_timo(&ia
, blist
, client_conf
);
2884 if (dhcp6_add_listval(retlist
, spec
->type
, &ia
, blist
)
2886 dprintf(LOG_NOTICE
, FNAME
,
2887 "failed to copy binding info");
2891 /* remove bound values from the configuration */
2892 for (bia
= TAILQ_FIRST(blist
); bia
;
2893 bia
= TAILQ_NEXT(bia
, link
)) {
2894 if ((v
= dhcp6_find_listval(conflist
,
2895 bia
->type
, &bia
->uv
, 0)) != NULL
) {
2896 TAILQ_REMOVE(conflist
, v
, link
);
2897 dhcp6_clear_listval(v
);
2906 * if the configuration is empty, we cannot make any IA.
2908 if (TAILQ_EMPTY(conflist
)) {
2909 if (spec
->type
!= DHCP6_LISTVAL_IANA
||
2910 client_conf
->pool
.name
== NULL
) {
2915 TAILQ_INIT(&ialist
);
2917 /* First, check if we can meet the client's requirement */
2918 for (specia
= TAILQ_FIRST(&spec
->sublist
); specia
;
2919 specia
= TAILQ_NEXT(specia
, link
)) {
2920 /* try to find an IA that matches the spec best. */
2921 if (!TAILQ_EMPTY(conflist
)) {
2922 if (make_match_ia(specia
, conflist
, &ialist
))
2924 } else if (spec
->type
== DHCP6_LISTVAL_IANA
&&
2925 client_conf
->pool
.name
!= NULL
) {
2926 if (make_iana_from_pool(&client_conf
->pool
, specia
, &ialist
))
2931 if (!TAILQ_EMPTY(conflist
)) {
2932 struct dhcp6_listval
*v
;
2934 /* use the first IA in the configuration list */
2935 for (v
= TAILQ_FIRST(conflist
); v
; v
= TAILQ_NEXT(v
, link
)) {
2936 if (spec
->type
!= DHCP6_LISTVAL_IANA
)
2937 break; /* always use the first IA for non-IANA */
2938 if (!is_leased(&v
->val_statefuladdr6
.addr
))
2941 if (v
&& dhcp6_add_listval(&ialist
, v
->type
, &v
->uv
, NULL
)) {
2943 TAILQ_REMOVE(conflist
, v
, link
);
2944 dhcp6_clear_listval(v
);
2946 } else if (spec
->type
== DHCP6_LISTVAL_IANA
&&
2947 client_conf
->pool
.name
!= NULL
) {
2948 if (make_iana_from_pool(&client_conf
->pool
, NULL
, &ialist
))
2953 memset(&ia
, 0, sizeof(ia
));
2954 ia
.iaid
= spec
->val_ia
.iaid
;
2955 /* determine appropriate T1 and T2 */
2956 calc_ia_timo(&ia
, &ialist
, client_conf
);
2958 /* make a binding for the set if necessary */
2960 if (add_binding(&client_conf
->duid
, DHCP6_BINDING_IA
,
2961 spec
->type
, spec
->val_ia
.iaid
, &ialist
) == NULL
) {
2962 dprintf(LOG_NOTICE
, FNAME
,
2963 "failed to make a binding");
2968 /* make an IA for the set */
2969 if (dhcp6_add_listval(retlist
, spec
->type
,
2970 &ia
, &ialist
) == NULL
)
2973 dhcp6_clear_list(&ialist
);
2980 make_match_ia(spec
, conflist
, retlist
)
2981 struct dhcp6_listval
*spec
;
2982 struct dhcp6_list
*conflist
, *retlist
;
2984 struct dhcp6_listval
*match
;
2987 /* do we have the exact value specified? */
2988 match
= dhcp6_find_listval(conflist
, spec
->type
, &spec
->uv
, 0);
2990 /* if not, make further search specific to the IA type. */
2992 switch (spec
->type
) {
2993 case DHCP6_LISTVAL_PREFIX6
:
2994 match
= dhcp6_find_listval(conflist
, spec
->type
,
2995 &spec
->uv
, MATCHLIST_PREFIXLEN
);
2997 case DHCP6_LISTVAL_STATEFULADDR6
:
2998 /* No "partial match" for addresses */
2999 if (is_leased(&spec
->val_statefuladdr6
.addr
))
3003 dprintf(LOG_ERR
, FNAME
, "unsupported IA type");
3004 return (0); /* XXX */
3009 * if found, remove the matched entry from the configuration list
3010 * and copy the value in the returned list.
3013 if (dhcp6_add_listval(retlist
, match
->type
,
3014 &match
->uv
, NULL
)) {
3016 TAILQ_REMOVE(conflist
, match
, link
);
3017 dhcp6_clear_listval(match
);
3024 /* making sublist of iana */
3026 make_iana_from_pool(poolspec
, spec
, retlist
)
3027 struct dhcp6_poolspec
*poolspec
;
3028 struct dhcp6_listval
*spec
;
3029 struct dhcp6_list
*retlist
;
3031 struct dhcp6_statefuladdr saddr
;
3032 struct pool_conf
*pool
;
3035 dprintf(LOG_DEBUG
, FNAME
, "called");
3037 if ((pool
= find_pool(poolspec
->name
)) == NULL
) {
3038 dprintf(LOG_ERR
, FNAME
, "pool '%s' not found", poolspec
->name
);
3043 memcpy(&saddr
.addr
, &spec
->val_statefuladdr6
.addr
, sizeof(saddr
.addr
));
3044 if (is_available_in_pool(pool
, &saddr
.addr
)) {
3048 if (get_free_address_from_pool(pool
, &saddr
.addr
)) {
3054 saddr
.pltime
= poolspec
->pltime
;
3055 saddr
.vltime
= poolspec
->vltime
;
3057 if (!dhcp6_add_listval(retlist
, DHCP6_LISTVAL_STATEFULADDR6
,
3063 dprintf(LOG_DEBUG
, FNAME
, "returns (found=%d)", found
);
3069 calc_ia_timo(ia
, ialist
, client_conf
)
3070 struct dhcp6_ia
*ia
;
3071 struct dhcp6_list
*ialist
; /* this should not be empty */
3072 struct host_conf
*client_conf
; /* unused yet */
3074 struct dhcp6_listval
*iav
;
3075 u_int32_t base
= DHCP6_DURATION_INFINITE
;
3078 iatype
= TAILQ_FIRST(ialist
)->type
;
3079 for (iav
= TAILQ_FIRST(ialist
); iav
; iav
= TAILQ_NEXT(iav
, link
)) {
3080 if (iav
->type
!= iatype
) {
3081 dprintf(LOG_ERR
, FNAME
,
3082 "assumption failure: IA list is not consistent");
3086 case DHCP6_LISTVAL_PREFIX6
:
3087 case DHCP6_LISTVAL_STATEFULADDR6
:
3088 if (base
== DHCP6_DURATION_INFINITE
||
3089 iav
->val_prefix6
.pltime
< base
)
3090 base
= iav
->val_prefix6
.pltime
;
3096 case DHCP6_LISTVAL_PREFIX6
:
3097 case DHCP6_LISTVAL_STATEFULADDR6
:
3099 * Configure the timeout parameters as recommended in
3100 * Section 22.4 of RFC3315 and Section 9 of RFC3633.
3101 * We could also set the parameters to 0 if we let the client
3102 * decide the renew timing (not implemented yet).
3104 if (base
== DHCP6_DURATION_INFINITE
) {
3105 ia
->t1
= DHCP6_DURATION_INFINITE
;
3106 ia
->t2
= DHCP6_DURATION_INFINITE
;
3109 ia
->t2
= (base
* 4) / 5;
3116 update_binding_duration(binding
)
3117 struct dhcp6_binding
*binding
;
3119 struct dhcp6_list
*ia_list
= &binding
->val_list
;
3120 struct dhcp6_listval
*iav
;
3121 int duration
= DHCP6_DURATION_INFINITE
;
3122 u_int32_t past
, min_lifetime
;
3123 time_t now
= time(NULL
);
3126 past
= (u_int32_t
)(now
>= binding
->updatetime
?
3127 now
- binding
->updatetime
: 0);
3129 switch (binding
->type
) {
3130 case DHCP6_BINDING_IA
:
3132 * Binding configuration is a list of IA parameters.
3133 * Determine the minimum valid lifetime.
3135 for (iav
= TAILQ_FIRST(ia_list
); iav
;
3136 iav
= TAILQ_NEXT(iav
, link
)) {
3139 switch (binding
->iatype
) {
3140 case DHCP6_LISTVAL_IAPD
:
3141 lifetime
= iav
->val_prefix6
.vltime
;
3143 case DHCP6_LISTVAL_IANA
:
3144 lifetime
= iav
->val_statefuladdr6
.vltime
;
3147 dprintf(LOG_ERR
, FNAME
, "unsupported IA type");
3151 if (min_lifetime
== 0 ||
3152 (lifetime
!= DHCP6_DURATION_INFINITE
&&
3153 lifetime
< min_lifetime
))
3154 min_lifetime
= lifetime
;
3157 if (past
< min_lifetime
)
3158 duration
= min_lifetime
- past
;
3164 /* should be internal error. */
3165 dprintf(LOG_ERR
, FNAME
, "unknown binding type (%d)",
3170 binding
->duration
= duration
;
3173 static struct dhcp6_binding
*
3174 add_binding(clientid
, btype
, iatype
, iaid
, val0
)
3175 struct duid
*clientid
;
3176 dhcp6_bindingtype_t btype
;
3181 struct dhcp6_binding
*binding
= NULL
;
3182 u_int32_t duration
= DHCP6_DURATION_INFINITE
;
3184 if ((binding
= malloc(sizeof(*binding
))) == NULL
) {
3185 dprintf(LOG_NOTICE
, FNAME
, "failed to allocate memory");
3188 memset(binding
, 0, sizeof(*binding
));
3189 binding
->type
= btype
;
3190 if (duidcpy(&binding
->clientid
, clientid
)) {
3191 dprintf(LOG_NOTICE
, FNAME
, "failed to copy DUID");
3194 binding
->iatype
= iatype
;
3195 binding
->iaid
= iaid
;
3197 /* construct configuration information for this binding */
3199 case DHCP6_BINDING_IA
:
3200 TAILQ_INIT(&binding
->val_list
);
3201 if (dhcp6_copy_list(&binding
->val_list
,
3202 (struct dhcp6_list
*)val0
)) {
3203 dprintf(LOG_NOTICE
, FNAME
,
3204 "failed to copy binding data");
3208 if (iatype
== DHCP6_LISTVAL_IANA
) {
3209 struct dhcp6_list
*ia_list
= &binding
->val_list
;
3210 struct dhcp6_listval
*lv
, *lv_next
;
3212 for (lv
= TAILQ_FIRST(ia_list
); lv
; lv
= lv_next
) {
3213 lv_next
= TAILQ_NEXT(lv
, link
);
3215 if (lv
->type
!= DHCP6_LISTVAL_STATEFULADDR6
) {
3216 dprintf(LOG_ERR
, FNAME
,
3217 "unexpected binding value type(%d)", lv
->type
);
3221 if (!lease_address(&lv
->val_statefuladdr6
.addr
)) {
3222 dprintf(LOG_NOTICE
, FNAME
,
3223 "cannot lease address %s",
3224 in6addr2str(&lv
->val_statefuladdr6
.addr
, 0));
3225 TAILQ_REMOVE(ia_list
, lv
, link
);
3226 dhcp6_clear_listval(lv
);
3229 if (TAILQ_EMPTY(ia_list
)) {
3230 dprintf(LOG_NOTICE
, FNAME
, "cannot lease any address");
3236 dprintf(LOG_ERR
, FNAME
, "unexpected binding type(%d)", btype
);
3240 /* calculate duration and start timer accordingly */
3241 binding
->updatetime
= time(NULL
);
3242 update_binding_duration(binding
);
3243 if (binding
->duration
!= DHCP6_DURATION_INFINITE
) {
3244 struct timeval timo
;
3246 binding
->timer
= dhcp6_add_timer(binding_timo
, binding
);
3247 if (binding
->timer
== NULL
) {
3248 dprintf(LOG_NOTICE
, FNAME
, "failed to add timer");
3251 timo
.tv_sec
= (long)duration
;
3253 dhcp6_set_timer(&timo
, binding
->timer
);
3256 TAILQ_INSERT_TAIL(&dhcp6_binding_head
, binding
, link
);
3258 dprintf(LOG_DEBUG
, FNAME
, "add a new binding %s", bindingstr(binding
));
3264 free_binding(binding
);
3268 static struct dhcp6_binding
*
3269 find_binding(clientid
, btype
, iatype
, iaid
)
3270 struct duid
*clientid
;
3271 dhcp6_bindingtype_t btype
;
3275 struct dhcp6_binding
*bp
;
3277 for (bp
= TAILQ_FIRST(&dhcp6_binding_head
); bp
;
3278 bp
= TAILQ_NEXT(bp
, link
)) {
3279 if (bp
->type
!= btype
|| duidcmp(&bp
->clientid
, clientid
))
3282 if (btype
== DHCP6_BINDING_IA
&&
3283 (bp
->iatype
!= iatype
|| bp
->iaid
!= iaid
))
3293 update_binding(binding
)
3294 struct dhcp6_binding
*binding
;
3296 struct timeval timo
;
3298 dprintf(LOG_DEBUG
, FNAME
, "update binding %s for %s",
3299 bindingstr(binding
), duidstr(&binding
->clientid
));
3301 /* update timestamp and calculate new duration */
3302 binding
->updatetime
= time(NULL
);
3303 update_binding_duration(binding
);
3305 /* if the lease duration is infinite, there's nothing to do. */
3306 if (binding
->duration
== DHCP6_DURATION_INFINITE
)
3309 /* reset the timer with the duration */
3310 timo
.tv_sec
= (long)binding
->duration
;
3312 dhcp6_set_timer(&timo
, binding
->timer
);
3316 remove_binding(binding
)
3317 struct dhcp6_binding
*binding
;
3319 dprintf(LOG_DEBUG
, FNAME
, "remove a binding %s",
3320 bindingstr(binding
));
3323 dhcp6_remove_timer(&binding
->timer
);
3325 TAILQ_REMOVE(&dhcp6_binding_head
, binding
, link
);
3327 free_binding(binding
);
3331 free_binding(binding
)
3332 struct dhcp6_binding
*binding
;
3334 duidfree(&binding
->clientid
);
3336 /* free configuration info in a type dependent manner. */
3337 switch (binding
->type
) {
3338 case DHCP6_BINDING_IA
:
3339 /* releaes address */
3340 if (binding
->iatype
== DHCP6_LISTVAL_IANA
) {
3341 struct dhcp6_list
*ia_list
= &binding
->val_list
;
3342 struct dhcp6_listval
*lv
;
3344 for (lv
= TAILQ_FIRST(ia_list
); lv
; lv
= TAILQ_NEXT(lv
, link
)) {
3345 if (lv
->type
!= DHCP6_LISTVAL_STATEFULADDR6
) {
3346 dprintf(LOG_ERR
, FNAME
,
3347 "unexpected binding value type(%d)", lv
->type
);
3350 release_address(&lv
->val_statefuladdr6
.addr
);
3353 dhcp6_clear_list(&binding
->val_list
);
3356 dprintf(LOG_ERR
, FNAME
, "unknown binding type %d",
3364 static struct dhcp6_timer
*
3368 struct dhcp6_binding
*binding
= (struct dhcp6_binding
*)arg
;
3369 struct dhcp6_list
*ia_list
= &binding
->val_list
;
3370 struct dhcp6_listval
*iav
, *iav_next
;
3371 time_t now
= time(NULL
);
3372 u_int32_t past
, lifetime
;
3373 struct timeval timo
;
3375 past
= (u_int32_t
)(now
>= binding
->updatetime
?
3376 now
- binding
->updatetime
: 0);
3378 switch (binding
->type
) {
3379 case DHCP6_BINDING_IA
:
3380 for (iav
= TAILQ_FIRST(ia_list
); iav
; iav
= iav_next
) {
3381 iav_next
= TAILQ_NEXT(iav
, link
);
3383 switch (binding
->iatype
) {
3384 case DHCP6_LISTVAL_IAPD
:
3385 case DHCP6_LISTVAL_IANA
:
3386 lifetime
= iav
->val_prefix6
.vltime
;
3389 dprintf(LOG_ERR
, FNAME
, "internal error: "
3390 "unknown binding type (%d)",
3392 return (NULL
); /* XXX */
3395 if (lifetime
!= DHCP6_DURATION_INFINITE
&&
3397 dprintf(LOG_DEBUG
, FNAME
, "bound prefix %s/%d"
3398 " in %s has expired",
3399 in6addr2str(&iav
->val_prefix6
.addr
, 0),
3400 iav
->val_prefix6
.plen
,
3401 bindingstr(binding
));
3402 if (binding
->iatype
== DHCP6_LISTVAL_IANA
)
3403 release_address(&iav
->val_prefix6
.addr
);
3404 TAILQ_REMOVE(ia_list
, iav
, link
);
3405 dhcp6_clear_listval(iav
);
3409 /* If all IA parameters have expired, remove the binding. */
3410 if (TAILQ_EMPTY(ia_list
)) {
3411 remove_binding(binding
);
3417 dprintf(LOG_ERR
, FNAME
, "unknown binding type %d",
3419 return (NULL
); /* XXX */
3422 update_binding_duration(binding
);
3424 /* if the lease duration is infinite, there's nothing to do. */
3425 if (binding
->duration
== DHCP6_DURATION_INFINITE
)
3428 /* reset the timer with the duration */
3429 timo
.tv_sec
= (long)binding
->duration
;
3431 dhcp6_set_timer(&timo
, binding
->timer
);
3433 return (binding
->timer
);
3436 static struct dhcp6_listval
*
3437 find_binding_ia(key
, binding
)
3438 struct dhcp6_listval
*key
;
3439 struct dhcp6_binding
*binding
;
3441 struct dhcp6_list
*ia_list
= &binding
->val_list
;
3443 switch (binding
->type
) {
3444 case DHCP6_BINDING_IA
:
3445 return (dhcp6_find_listval(ia_list
, key
->type
, &key
->uv
, 0));
3447 dprintf(LOG_ERR
, FNAME
, "unknown binding type %d",
3449 return (NULL
); /* XXX */
3455 struct dhcp6_binding
*binding
;
3457 static char strbuf
[LINE_MAX
]; /* XXX: thread unsafe */
3458 char *iatype
= NULL
;
3460 switch (binding
->type
) {
3461 case DHCP6_BINDING_IA
:
3462 switch (binding
->iatype
) {
3463 case DHCP6_LISTVAL_IAPD
:
3466 case DHCP6_LISTVAL_IANA
:
3471 snprintf(strbuf
, sizeof(strbuf
),
3472 "[IA: duid=%s, type=%s, iaid=%lu, duration=%lu]",
3473 duidstr(&binding
->clientid
), iatype
, (u_long
)binding
->iaid
,
3474 (u_long
)binding
->duration
);
3477 dprintf(LOG_ERR
, FNAME
, "unexpected binding type(%d)",
3486 process_auth(dh6
, len
, client_conf
, optinfo
, roptinfo
)
3489 struct host_conf
*client_conf
;
3490 struct dhcp6_optinfo
*optinfo
, *roptinfo
;
3492 u_int8_t msgtype
= dh6
->dh6_msgtype
;
3493 int authenticated
= 0;
3494 struct keyinfo
*key
;
3497 * if the client wanted DHCPv6 authentication, check if a secret
3498 * key is available for the client.
3500 switch (optinfo
->authproto
) {
3501 case DHCP6_AUTHPROTO_UNDEF
:
3503 * The client did not include authentication option. What if
3504 * we had sent authentication information? The specification
3505 * is not clear, but we should probably accept it, since the
3506 * client MAY ignore the information in advertise messages.
3509 case DHCP6_AUTHPROTO_DELAYED
:
3510 if (optinfo
->authalgorithm
!= DHCP6_AUTHALG_HMACMD5
) {
3511 dprintf(LOG_INFO
, FNAME
, "unknown authentication "
3512 "algorithm (%d) required by %s",
3513 optinfo
->authalgorithm
,
3514 clientstr(client_conf
, &optinfo
->clientID
));
3515 break; /* give up with this authentication */
3518 if (optinfo
->authrdm
!= DHCP6_AUTHRDM_MONOCOUNTER
) {
3519 dprintf(LOG_INFO
, FNAME
,
3520 "unknown RDM (%d) required by %s",
3522 clientstr(client_conf
, &optinfo
->clientID
));
3523 break; /* give up with this authentication */
3526 /* see if we have a key for the client */
3527 if (client_conf
== NULL
|| client_conf
->delayedkey
== NULL
) {
3528 dprintf(LOG_INFO
, FNAME
, "client %s wanted "
3529 "authentication, but no key found",
3530 clientstr(client_conf
, &optinfo
->clientID
));
3533 key
= client_conf
->delayedkey
;
3534 dprintf(LOG_DEBUG
, FNAME
, "found key %s for client %s",
3535 key
->name
, clientstr(client_conf
, &optinfo
->clientID
));
3537 if (msgtype
== DH6_SOLICIT
) {
3538 if (!(optinfo
->authflags
& DHCP6OPT_AUTHFLAG_NOINFO
)) {
3540 * A solicit message should not contain
3541 * authentication information.
3543 dprintf(LOG_INFO
, FNAME
,
3544 "authentication information "
3545 "provided in solicit from %s",
3546 clientstr(client_conf
,
3547 &optinfo
->clientID
));
3548 /* accept it anyway. (or discard?) */
3551 /* replay protection */
3552 if (!client_conf
->saw_previous_rd
) {
3553 dprintf(LOG_WARNING
, FNAME
,
3554 "previous RD value for %s is unknown "
3555 "(accept it)", clientstr(client_conf
,
3556 &optinfo
->clientID
));
3558 if (dhcp6_auth_replaycheck(optinfo
->authrdm
,
3559 client_conf
->previous_rd
,
3561 dprintf(LOG_INFO
, FNAME
,
3562 "possible replay attack detected "
3564 clientstr(client_conf
,
3565 &optinfo
->clientID
));
3570 if ((optinfo
->authflags
& DHCP6OPT_AUTHFLAG_NOINFO
)) {
3571 dprintf(LOG_INFO
, FNAME
,
3572 "client %s did not provide authentication "
3573 "information in %s",
3574 clientstr(client_conf
, &optinfo
->clientID
),
3575 dhcp6msgstr(msgtype
));
3580 * The client MUST use the same key used by the server
3581 * to generate the authentication information.
3582 * [RFC3315 Section 21.4.4.3]
3583 * The RFC does not say what the server should do if
3584 * the client breaks this rule, but it should be
3585 * natural to interpret this as authentication failure.
3587 if (optinfo
->delayedauth_keyid
!= key
->keyid
||
3588 optinfo
->delayedauth_realmlen
!= key
->realmlen
||
3589 memcmp(optinfo
->delayedauth_realmval
, key
->realm
,
3590 key
->realmlen
) != 0) {
3591 dprintf(LOG_INFO
, FNAME
, "authentication key "
3592 "mismatch with client %s",
3593 clientstr(client_conf
,
3594 &optinfo
->clientID
));
3598 /* check for the key lifetime */
3599 if (dhcp6_validate_key(key
)) {
3600 dprintf(LOG_INFO
, FNAME
, "key %s has expired",
3606 if (dhcp6_verify_mac((char *)dh6
, len
,
3607 optinfo
->authproto
, optinfo
->authalgorithm
,
3608 optinfo
->delayedauth_offset
+ sizeof(*dh6
), key
)
3610 dprintf(LOG_DEBUG
, FNAME
,
3611 "message authentication validated for "
3612 "client %s", clientstr(client_conf
,
3613 &optinfo
->clientID
));
3615 dprintf(LOG_INFO
, FNAME
, "invalid message "
3621 roptinfo
->authproto
= optinfo
->authproto
;
3622 roptinfo
->authalgorithm
= optinfo
->authalgorithm
;
3623 roptinfo
->authrdm
= optinfo
->authrdm
;
3625 if (get_rdvalue(roptinfo
->authrdm
, &roptinfo
->authrd
,
3626 sizeof(roptinfo
->authrd
))) {
3627 dprintf(LOG_ERR
, FNAME
, "failed to get a replay "
3628 "detection value for %s",
3629 clientstr(client_conf
, &optinfo
->clientID
));
3630 break; /* XXX: try to recover? */
3633 roptinfo
->delayedauth_keyid
= key
->keyid
;
3634 roptinfo
->delayedauth_realmlen
= key
->realmlen
;
3635 roptinfo
->delayedauth_realmval
=
3636 malloc(roptinfo
->delayedauth_realmlen
);
3637 if (roptinfo
->delayedauth_realmval
== NULL
) {
3638 dprintf(LOG_ERR
, FNAME
, "failed to allocate memory "
3639 "for authentication realm for %s",
3640 clientstr(client_conf
, &optinfo
->clientID
));
3643 memcpy(roptinfo
->delayedauth_realmval
, key
->realm
,
3644 roptinfo
->delayedauth_realmlen
);
3650 dprintf(LOG_INFO
, FNAME
, "client %s wanted authentication "
3651 "with unsupported protocol (%d)",
3652 clientstr(client_conf
, &optinfo
->clientID
),
3653 optinfo
->authproto
);
3654 return (-1); /* or simply ignore it? */
3657 if (authenticated
== 0) {
3658 if (msgtype
!= DH6_SOLICIT
) {
3660 * If the message fails to pass the validation test,
3661 * the server MUST discard the message.
3662 * [RFC3315 Section 21.4.5.2]
3667 /* Message authenticated. Update RD counter. */
3668 if (msgtype
!= DH6_SOLICIT
&& client_conf
!= NULL
) {
3669 client_conf
->previous_rd
= optinfo
->authrd
;
3670 client_conf
->saw_previous_rd
= 1;
3677 static inline char *
3678 clientstr(conf
, duid
)
3679 struct host_conf
*conf
;
3683 return (conf
->name
);
3685 return (duidstr(duid
));