1 /* $KAME: dhcp6c.c,v 1.164 2006/01/10 02:46:09 jinmei 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
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/socket.h>
34 #include <sys/queue.h>
37 #if TIME_WITH_SYS_TIME
38 # include <sys/time.h>
42 # include <sys/time.h>
49 #include <net/if_var.h>
52 #include <netinet/in.h>
54 #include <net/if_dl.h>
55 #include <netinet6/in6_var.h>
58 #include <arpa/inet.h>
78 #include <dhcp6_ctl.h>
80 #include <dhcp6c_ia.h>
81 #include <prefixconf.h>
85 static int exit_ok
= 0;
86 static sig_atomic_t sig_flags
= 0;
90 const dhcp6_mode_t dhcp6_mode
= DHCP6_MODE_CLIENT
;
92 int sock
; /* inbound/outbound udp port */
93 int rtsock
; /* routing socket */
96 int ctlsock
= -1; /* control TCP port */
97 char *ctladdr
= DEFAULT_CLIENT_CONTROL_ADDR
;
98 char *ctlport
= DEFAULT_CLIENT_CONTROL_PORT
;
100 #define DEFAULT_KEYFILE SYSCONFDIR "/dhcp6cctlkey"
101 #endif // USE_DHCP6CTL
105 static char *conffile
= DHCP6C_CONF
;
107 static const struct sockaddr_in6
*sa6_allagent
;
108 static struct duid client_duid
;
109 static char *pid_file
= DHCP6C_PIDFILE
;
112 static char *ctlkeyfile
= DEFAULT_KEYFILE
;
113 static struct keyinfo
*ctlkey
= NULL
;
114 static int ctldigestlen
;
117 static int infreq_mode
= 0;
119 static inline int get_val32
__P((char **, int *, u_int32_t
*));
120 static inline int get_ifname
__P((char **, int *, char *, int));
122 static void usage
__P((void));
123 static void client6_init
__P((void));
124 static void client6_startall
__P((int));
125 static void free_resources
__P((struct dhcp6_if
*));
126 static void client6_mainloop
__P((void));
128 static int client6_do_ctlcommand
__P((char *, ssize_t
));
129 static void client6_reload
__P((void));
130 static int client6_ifctl
__P((char *ifname
, u_int16_t
));
132 static void check_exit
__P((void));
133 static void process_signals
__P((void));
134 static struct dhcp6_serverinfo
*find_server
__P((struct dhcp6_event
*,
136 static struct dhcp6_serverinfo
*select_server
__P((struct dhcp6_event
*));
137 static void client6_recv
__P((void));
138 static int client6_recvadvert
__P((struct dhcp6_if
*, struct dhcp6
*,
139 ssize_t
, struct dhcp6_optinfo
*));
140 static int client6_recvreply
__P((struct dhcp6_if
*, struct dhcp6
*,
141 ssize_t
, struct dhcp6_optinfo
*));
142 static void client6_signal
__P((int));
143 static struct dhcp6_event
*find_event_withid
__P((struct dhcp6_if
*,
145 static int construct_confdata
__P((struct dhcp6_if
*, struct dhcp6_event
*));
146 static int construct_reqdata
__P((struct dhcp6_if
*, struct dhcp6_optinfo
*,
147 struct dhcp6_event
*));
148 static void destruct_iadata
__P((struct dhcp6_eventdata
*));
149 static void tv_sub
__P((struct timeval
*, struct timeval
*, struct timeval
*));
150 static struct dhcp6_timer
*client6_expire_refreshtime
__P((void *));
151 static int process_auth
__P((struct authparam
*, struct dhcp6
*dh6
, ssize_t
,
152 struct dhcp6_optinfo
*));
153 static int set_auth
__P((struct dhcp6_event
*, struct dhcp6_optinfo
*));
155 struct dhcp6_timer
*client6_timo
__P((void *));
156 int client6_start
__P((struct dhcp6_if
*));
157 static void info_printf
__P((const char *, ...));
159 extern int client6_script
__P((char *, int, struct dhcp6_optinfo
*));
161 #define MAX_ELAPSED_TIME 0xffff
171 struct dhcp6_if
*ifp
;
174 #ifndef HAVE_ARC4RANDOM
175 srandom(time(NULL
) & getpid());
178 if ((progname
= strrchr(*argv
, '/')) == NULL
)
183 while ((ch
= getopt(argc
, argv
, "c:dDT:fik:p:")) != -1) {
195 if (!strcasecmp(optarg
, "LL"))
197 else if (!strcasecmp(optarg
, "LLT"))
227 if (foreground
== 0) {
228 for (fd
= 3; fd
< 1024; fd
++)
231 openlog(progname
, LOG_NDELAY
|LOG_PID
, LOG_DAEMON
);
238 if ((ifp
= ifinit(argv
[0])) == NULL
) {
239 dprintf(LOG_ERR
, FNAME
, "failed to initialize %s",
246 if (infreq_mode
== 0 && (cfparse(conffile
)) != 0) {
247 dprintf(LOG_ERR
, FNAME
, "failed to parse configuration file");
251 if (foreground
== 0 && infreq_mode
== 0) {
252 if (daemon(0, 0) < 0)
256 /* dump current PID */
258 if ((pidfp
= fopen(pid_file
, "w")) != NULL
) {
259 fprintf(pidfp
, "%d\n", pid
);
272 fprintf(stderr
, "usage: dhcp6c [-c configfile] [-dDfi] "
273 "[-T LL|LLT] [-p pid-file] interface [interfaces...]\n");
276 /*------------------------------------------------------------*/
281 struct addrinfo hints
, *res
;
282 static struct sockaddr_in6 sa6_allagent_storage
;
286 if (get_duid(DUID_FILE
, &client_duid
)) {
287 dprintf(LOG_ERR
, FNAME
, "failed to get a DUID");
292 if (dhcp6_ctl_authinit(ctlkeyfile
, &ctlkey
, &ctldigestlen
) != 0) {
293 dprintf(LOG_NOTICE
, FNAME
,
294 "failed initialize control message authentication");
295 /* run the server anyway */
299 memset(&hints
, 0, sizeof(hints
));
300 hints
.ai_family
= PF_INET6
;
301 hints
.ai_socktype
= SOCK_DGRAM
;
302 hints
.ai_protocol
= IPPROTO_UDP
;
303 hints
.ai_flags
= AI_PASSIVE
;
304 error
= getaddrinfo(NULL
, DH6PORT_DOWNSTREAM
, &hints
, &res
);
306 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
307 gai_strerror(error
));
310 sock
= socket(res
->ai_family
, res
->ai_socktype
, res
->ai_protocol
);
312 dprintf(LOG_ERR
, FNAME
, "socket");
315 if (setsockopt(sock
, SOL_SOCKET
, SO_REUSEPORT
,
316 &on
, sizeof(on
)) < 0) {
317 dprintf(LOG_ERR
, FNAME
,
318 "setsockopt(SO_REUSEPORT): %s", strerror(errno
));
321 #ifdef IPV6_RECVPKTINFO
322 if (setsockopt(sock
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &on
,
324 dprintf(LOG_ERR
, FNAME
,
325 "setsockopt(IPV6_RECVPKTINFO): %s",
330 if (setsockopt(sock
, IPPROTO_IPV6
, IPV6_PKTINFO
, &on
,
332 dprintf(LOG_ERR
, FNAME
,
333 "setsockopt(IPV6_PKTINFO): %s",
338 if (setsockopt(sock
, IPPROTO_IPV6
, IPV6_MULTICAST_LOOP
, &on
,
340 dprintf(LOG_ERR
, FNAME
,
341 "setsockopt(sock, IPV6_MULTICAST_LOOP): %s",
346 if (setsockopt(sock
, IPPROTO_IPV6
, IPV6_V6ONLY
,
347 &on
, sizeof(on
)) < 0) {
348 dprintf(LOG_ERR
, FNAME
, "setsockopt(IPV6_V6ONLY): %s",
355 * According RFC3315 2.2, only the incoming port should be bound to UDP
356 * port 546. However, to have an interoperability with some servers,
357 * the outgoing port is also bound to the DH6PORT_DOWNSTREAM.
359 if (bind(sock
, res
->ai_addr
, res
->ai_addrlen
) < 0) {
360 dprintf(LOG_ERR
, FNAME
, "bind: %s", strerror(errno
));
365 /* open a routing socket to watch the routing table */
366 if ((rtsock
= socket(PF_ROUTE
, SOCK_RAW
, 0)) < 0) {
367 dprintf(LOG_ERR
, FNAME
, "open a routing socket: %s",
372 memset(&hints
, 0, sizeof(hints
));
373 hints
.ai_family
= PF_INET6
;
374 hints
.ai_socktype
= SOCK_DGRAM
;
375 hints
.ai_protocol
= IPPROTO_UDP
;
376 error
= getaddrinfo(DH6ADDR_ALLAGENT
, DH6PORT_UPSTREAM
, &hints
, &res
);
378 dprintf(LOG_ERR
, FNAME
, "getaddrinfo: %s",
379 gai_strerror(error
));
382 memcpy(&sa6_allagent_storage
, res
->ai_addr
, res
->ai_addrlen
);
383 sa6_allagent
= (const struct sockaddr_in6
*)&sa6_allagent_storage
;
387 /* set up control socket */
389 dprintf(LOG_NOTICE
, FNAME
, "skip opening control port");
390 else if (dhcp6_ctl_init(ctladdr
, ctlport
,
391 DHCP6CTL_DEF_COMMANDQUEUELEN
, &ctlsock
)) {
392 dprintf(LOG_ERR
, FNAME
,
393 "failed to initialize control channel");
398 if (signal(SIGHUP
, client6_signal
) == SIG_ERR
) {
399 dprintf(LOG_WARNING
, FNAME
, "failed to set signal: %s",
403 if (signal(SIGTERM
, client6_signal
) == SIG_ERR
) {
404 dprintf(LOG_WARNING
, FNAME
, "failed to set signal: %s",
412 struct dhcp6_if
*ifp
;
414 struct dhcp6_event
*ev
;
416 /* make sure that the interface does not have a timer */
417 if (ifp
->timer
!= NULL
) {
418 dprintf(LOG_DEBUG
, FNAME
,
419 "removed existing timer on %s", ifp
->ifname
);
420 dhcp6_remove_timer(&ifp
->timer
);
423 /* create an event for the initial delay */
424 if ((ev
= dhcp6_create_event(ifp
, DHCP6S_INIT
)) == NULL
) {
425 dprintf(LOG_NOTICE
, FNAME
, "failed to create an event");
428 TAILQ_INSERT_TAIL(&ifp
->event_list
, ev
, link
);
430 if ((ev
->authparam
= new_authparam(ifp
->authproto
,
431 ifp
->authalgorithm
, ifp
->authrdm
)) == NULL
) {
432 dprintf(LOG_WARNING
, FNAME
, "failed to allocate "
433 "authentication parameters");
434 dhcp6_remove_event(ev
);
438 if ((ev
->timer
= dhcp6_add_timer(client6_timo
, ev
)) == NULL
) {
439 dprintf(LOG_NOTICE
, FNAME
, "failed to add a timer for %s",
441 dhcp6_remove_event(ev
);
444 dhcp6_reset_timer(ev
);
450 client6_startall(isrestart
)
453 struct dhcp6_if
*ifp
;
455 for (ifp
= dhcp6_if
; ifp
; ifp
= ifp
->next
) {
456 if (isrestart
&&ifreset(ifp
)) {
457 dprintf(LOG_NOTICE
, FNAME
, "failed to reset %s",
459 continue; /* XXX: try to recover? */
461 if (client6_start(ifp
))
462 exit(1); /* initialization failure. we give up. */
467 free_resources(freeifp
)
468 struct dhcp6_if
*freeifp
;
470 struct dhcp6_if
*ifp
;
472 for (ifp
= dhcp6_if
; ifp
; ifp
= ifp
->next
) {
473 struct dhcp6_event
*ev
, *ev_next
;
475 if (freeifp
!= NULL
&& freeifp
!= ifp
)
478 /* release all IAs as well as send RELEASE message(s) */
482 * Cancel all outstanding events for each interface except
483 * ones being released.
485 for (ev
= TAILQ_FIRST(&ifp
->event_list
); ev
; ev
= ev_next
) {
486 ev_next
= TAILQ_NEXT(ev
, link
);
488 if (ev
->state
== DHCP6S_RELEASE
)
489 continue; /* keep it for now */
491 dhcp6_remove_event(ev
);
499 struct dhcp6_if
*ifp
;
504 for (ifp
= dhcp6_if
; ifp
; ifp
= ifp
->next
) {
506 * Check if we have an outstanding event. If we do, we cannot
509 if (!TAILQ_EMPTY(&ifp
->event_list
))
513 /* We have no existing event. Do exit. */
514 dprintf(LOG_INFO
, FNAME
, "exiting");
522 if ((sig_flags
& SIGF_TERM
)) {
524 free_resources(NULL
);
528 if ((sig_flags
& SIGF_HUP
)) {
529 dprintf(LOG_INFO
, FNAME
, "restarting");
530 free_resources(NULL
);
548 w
= dhcp6_check_timer();
556 maxsock
= (sock
> ctlsock
) ? sock
: ctlsock
;
557 (void)dhcp6_ctl_setreadfds(&r
, &maxsock
);
561 ret
= select(maxsock
+ 1, &r
, NULL
, NULL
, w
);
565 if (errno
!= EINTR
) {
566 dprintf(LOG_ERR
, FNAME
, "select: %s",
571 case 0: /* timeout */
572 break; /* dhcp6_check_timer() will treat the case */
576 if (FD_ISSET(sock
, &r
))
580 if (FD_ISSET(ctlsock
, &r
)) {
581 (void)dhcp6_ctl_acceptcommand(ctlsock
,
582 client6_do_ctlcommand
);
584 (void)dhcp6_ctl_readcommand(&r
);
591 get_val32(bpp
, lenp
, valp
)
600 if (len
< sizeof(*valp
))
603 memcpy(&i32
, bp
, sizeof(i32
));
606 *bpp
= bp
+ sizeof(*valp
);
607 *lenp
= len
- sizeof(*valp
);
613 get_ifname(bpp
, lenp
, ifbuf
, ifbuflen
)
620 int len
= *lenp
, ifnamelen
;
623 if (get_val32(bpp
, lenp
, &i32
))
625 ifnamelen
= (int)i32
;
627 if (*lenp
< ifnamelen
|| ifnamelen
> ifbuflen
)
630 memset(ifbuf
, 0, sizeof(ifbuf
));
631 memcpy(ifbuf
, *bpp
, ifnamelen
);
632 if (ifbuf
[ifbuflen
- 1] != '\0')
633 return (-1); /* not null terminated */
635 *bpp
= bp
+ sizeof(i32
) + ifnamelen
;
636 *lenp
= len
- (sizeof(i32
) + ifnamelen
);
643 client6_do_ctlcommand(buf
, len
)
647 struct dhcp6ctl
*ctlhead
;
648 u_int16_t command
, version
;
649 u_int32_t p32
, ts
, ts0
;
652 char ifname
[IFNAMSIZ
];
655 memset(ifname
, 0, sizeof(ifname
));
657 ctlhead
= (struct dhcp6ctl
*)buf
;
659 command
= ntohs(ctlhead
->command
);
660 commandlen
= (int)(ntohs(ctlhead
->len
));
661 version
= ntohs(ctlhead
->version
);
662 if (len
!= sizeof(struct dhcp6ctl
) + commandlen
) {
663 dprintf(LOG_ERR
, FNAME
,
664 "assumption failure: command length mismatch");
665 return (DHCP6CTL_R_FAILURE
);
668 /* replay protection and message authentication */
669 if ((now
= time(NULL
)) < 0) {
670 dprintf(LOG_ERR
, FNAME
, "failed to get current time: %s",
672 return (DHCP6CTL_R_FAILURE
);
674 ts0
= (u_int32_t
)now
;
675 ts
= ntohl(ctlhead
->timestamp
);
676 if (ts
+ CTLSKEW
< ts0
|| (ts
- CTLSKEW
) > ts0
) {
677 dprintf(LOG_INFO
, FNAME
, "timestamp is out of range");
678 return (DHCP6CTL_R_FAILURE
);
681 if (ctlkey
== NULL
) { /* should not happen!! */
682 dprintf(LOG_ERR
, FNAME
, "no secret key for control channel");
683 return (DHCP6CTL_R_FAILURE
);
685 if (dhcp6_verify_mac(buf
, len
, DHCP6CTL_AUTHPROTO_UNDEF
,
686 DHCP6CTL_AUTHALG_HMACMD5
, sizeof(*ctlhead
), ctlkey
) != 0) {
687 dprintf(LOG_INFO
, FNAME
, "authentication failure");
688 return (DHCP6CTL_R_FAILURE
);
691 bp
= buf
+ sizeof(*ctlhead
) + ctldigestlen
;
692 commandlen
-= ctldigestlen
;
694 if (version
> DHCP6CTL_VERSION
) {
695 dprintf(LOG_INFO
, FNAME
, "unsupported version: %d", version
);
696 return (DHCP6CTL_R_FAILURE
);
700 case DHCP6CTL_COMMAND_RELOAD
:
701 if (commandlen
!= 0) {
702 dprintf(LOG_INFO
, FNAME
, "invalid command length "
703 "for reload: %d", commandlen
);
704 return (DHCP6CTL_R_DONE
);
708 case DHCP6CTL_COMMAND_START
:
709 if (get_val32(&bp
, &commandlen
, &p32
))
710 return (DHCP6CTL_R_FAILURE
);
712 case DHCP6CTL_INTERFACE
:
713 if (get_ifname(&bp
, &commandlen
, ifname
,
715 return (DHCP6CTL_R_FAILURE
);
717 if (client6_ifctl(ifname
, DHCP6CTL_COMMAND_START
))
718 return (DHCP6CTL_R_FAILURE
);
721 dprintf(LOG_INFO
, FNAME
,
722 "unknown start target: %ul", p32
);
723 return (DHCP6CTL_R_FAILURE
);
726 case DHCP6CTL_COMMAND_STOP
:
727 if (commandlen
== 0) {
729 free_resources(NULL
);
733 if (get_val32(&bp
, &commandlen
, &p32
))
734 return (DHCP6CTL_R_FAILURE
);
737 case DHCP6CTL_INTERFACE
:
738 if (get_ifname(&bp
, &commandlen
, ifname
,
740 return (DHCP6CTL_R_FAILURE
);
742 if (client6_ifctl(ifname
,
743 DHCP6CTL_COMMAND_STOP
)) {
744 return (DHCP6CTL_R_FAILURE
);
748 dprintf(LOG_INFO
, FNAME
,
749 "unknown start target: %ul", p32
);
750 return (DHCP6CTL_R_FAILURE
);
755 dprintf(LOG_INFO
, FNAME
,
756 "unknown control command: %d (len=%d)",
757 (int)command
, commandlen
);
758 return (DHCP6CTL_R_FAILURE
);
761 return (DHCP6CTL_R_DONE
);
767 /* reload the configuration file */
768 if (cfparse(conffile
) != 0) {
769 dprintf(LOG_WARNING
, FNAME
,
770 "failed to reload configuration file");
774 dprintf(LOG_NOTICE
, FNAME
, "client reloaded");
780 client6_ifctl(ifname
, command
)
784 struct dhcp6_if
*ifp
;
786 if ((ifp
= find_ifconfbyname(ifname
)) == NULL
) {
787 dprintf(LOG_INFO
, FNAME
,
788 "failed to find interface configuration for %s",
793 dprintf(LOG_DEBUG
, FNAME
, "%s interface %s",
794 command
== DHCP6CTL_COMMAND_START
? "start" : "stop", ifname
);
797 case DHCP6CTL_COMMAND_START
:
799 * The ifid might have changed, so reset it before releasing the
803 dprintf(LOG_NOTICE
, FNAME
, "failed to reset %s",
808 if (client6_start(ifp
)) {
809 dprintf(LOG_NOTICE
, FNAME
, "failed to restart %s",
814 case DHCP6CTL_COMMAND_STOP
:
816 if (ifp
->timer
!= NULL
) {
817 dprintf(LOG_DEBUG
, FNAME
,
818 "removed existing timer on %s", ifp
->ifname
);
819 dhcp6_remove_timer(&ifp
->timer
);
822 default: /* impossible case, should be a bug */
823 dprintf(LOG_ERR
, FNAME
, "unknown command: %d", (int)command
);
829 #endif // USE_DHCP6CTL
831 static struct dhcp6_timer
*
832 client6_expire_refreshtime(arg
)
835 struct dhcp6_if
*ifp
= arg
;
837 dprintf(LOG_DEBUG
, FNAME
,
838 "information refresh time on %s expired", ifp
->ifname
);
840 dhcp6_remove_timer(&ifp
->timer
);
850 struct dhcp6_event
*ev
= (struct dhcp6_event
*)arg
;
851 struct dhcp6_if
*ifp
;
852 int state
= ev
->state
;
858 * Unless MRC is zero, the message exchange fails once the client has
859 * transmitted the message MRC times.
862 if (ev
->max_retrans_cnt
&& ev
->timeouts
>= ev
->max_retrans_cnt
) {
863 dprintf(LOG_INFO
, FNAME
, "no responses were received");
864 dhcp6_remove_event(ev
);
866 if (state
== DHCP6S_RELEASE
)
874 ev
->timeouts
= 0; /* indicate to generate a new XID. */
875 if ((ifp
->send_flags
& DHCIFF_INFO_ONLY
) || infreq_mode
)
876 ev
->state
= DHCP6S_INFOREQ
;
878 ev
->state
= DHCP6S_SOLICIT
;
879 if (construct_confdata(ifp
, ev
)) {
880 dprintf(LOG_ERR
, FNAME
, "can't send solicit");
884 dhcp6_set_timeoparam(ev
); /* XXX */
893 if (!TAILQ_EMPTY(&ev
->data_list
))
896 dprintf(LOG_INFO
, FNAME
,
897 "all information to be updated was canceled");
898 dhcp6_remove_event(ev
);
905 * Send a Request to the best server.
906 * Note that when we set Rapid-commit in Solicit,
907 * but a direct Reply has been delayed (very much),
908 * the transition to DHCP6S_REQUEST (and the change of
909 * transaction ID) will invalidate the reply even if it
912 ev
->current_server
= select_server(ev
);
913 if (ev
->current_server
== NULL
) {
914 /* this should not happen! */
915 dprintf(LOG_NOTICE
, FNAME
,
916 "can't find a server");
919 if (duidcpy(&ev
->serverid
,
920 &ev
->current_server
->optinfo
.serverID
)) {
921 dprintf(LOG_NOTICE
, FNAME
,
922 "can't copy server ID");
923 return (NULL
); /* XXX: better recovery? */
926 ev
->state
= DHCP6S_REQUEST
;
927 dhcp6_set_timeoparam(ev
);
929 if (ev
->authparam
!= NULL
)
931 ev
->authparam
= ev
->current_server
->authparam
;
932 ev
->current_server
->authparam
= NULL
;
934 if (construct_reqdata(ifp
,
935 &ev
->current_server
->optinfo
, ev
)) {
936 dprintf(LOG_NOTICE
, FNAME
,
937 "failed to construct request data");
945 dhcp6_reset_timer(ev
);
951 construct_confdata(ifp
, ev
)
952 struct dhcp6_if
*ifp
;
953 struct dhcp6_event
*ev
;
956 struct dhcp6_eventdata
*evd
= NULL
;
957 struct dhcp6_list
*ial
= NULL
, pl
;
958 struct dhcp6_ia iaparam
;
960 TAILQ_INIT(&pl
); /* for safety */
962 for (iac
= TAILQ_FIRST(&ifp
->iaconf_list
); iac
;
963 iac
= TAILQ_NEXT(iac
, link
)) {
964 /* ignore IA config currently used */
965 if (!TAILQ_EMPTY(&iac
->iadata
))
969 if ((evd
= malloc(sizeof(*evd
))) == NULL
) {
970 dprintf(LOG_NOTICE
, FNAME
,
971 "failed to create a new event data");
974 memset(evd
, 0, sizeof(evd
));
976 memset(&iaparam
, 0, sizeof(iaparam
));
977 iaparam
.iaid
= iac
->iaid
;
981 if ((ial
= malloc(sizeof(*ial
))) == NULL
)
987 &((struct iapd_conf
*)iac
)->iapd_prefix_list
);
988 if (dhcp6_add_listval(ial
, DHCP6_LISTVAL_IAPD
,
989 &iaparam
, &pl
) == NULL
) {
992 dhcp6_clear_list(&pl
);
994 evd
->type
= DHCP6_EVDATA_IAPD
;
997 evd
->destructor
= destruct_iadata
;
998 TAILQ_INSERT_TAIL(&ev
->data_list
, evd
, link
);
1002 if ((ial
= malloc(sizeof(*ial
))) == NULL
)
1007 dhcp6_copy_list(&pl
,
1008 &((struct iana_conf
*)iac
)->iana_address_list
);
1009 if (dhcp6_add_listval(ial
, DHCP6_LISTVAL_IANA
,
1010 &iaparam
, &pl
) == NULL
) {
1013 dhcp6_clear_list(&pl
);
1015 evd
->type
= DHCP6_EVDATA_IANA
;
1018 evd
->destructor
= destruct_iadata
;
1019 TAILQ_INSERT_TAIL(&ev
->data_list
, evd
, link
);
1022 dprintf(LOG_ERR
, FNAME
, "internal error");
1034 dhcp6_remove_event(ev
); /* XXX */
1040 construct_reqdata(ifp
, optinfo
, ev
)
1041 struct dhcp6_if
*ifp
;
1042 struct dhcp6_optinfo
*optinfo
;
1043 struct dhcp6_event
*ev
;
1045 struct ia_conf
*iac
;
1046 struct dhcp6_eventdata
*evd
= NULL
;
1047 struct dhcp6_list
*ial
= NULL
;
1048 struct dhcp6_ia iaparam
;
1050 /* discard previous event data */
1051 dhcp6_remove_evdata(ev
);
1053 if (optinfo
== NULL
)
1056 for (iac
= TAILQ_FIRST(&ifp
->iaconf_list
); iac
;
1057 iac
= TAILQ_NEXT(iac
, link
)) {
1058 struct dhcp6_listval
*v
;
1060 /* ignore IA config currently used */
1061 if (!TAILQ_EMPTY(&iac
->iadata
))
1064 memset(&iaparam
, 0, sizeof(iaparam
));
1065 iaparam
.iaid
= iac
->iaid
;
1070 switch (iac
->type
) {
1072 if ((v
= dhcp6_find_listval(&optinfo
->iapd_list
,
1073 DHCP6_LISTVAL_IAPD
, &iaparam
, 0)) == NULL
)
1076 if ((ial
= malloc(sizeof(*ial
))) == NULL
)
1080 if (dhcp6_add_listval(ial
, DHCP6_LISTVAL_IAPD
,
1081 &iaparam
, &v
->sublist
) == NULL
) {
1085 if ((evd
= malloc(sizeof(*evd
))) == NULL
)
1087 memset(evd
, 0, sizeof(*evd
));
1088 evd
->type
= DHCP6_EVDATA_IAPD
;
1091 evd
->destructor
= destruct_iadata
;
1092 TAILQ_INSERT_TAIL(&ev
->data_list
, evd
, link
);
1095 if ((v
= dhcp6_find_listval(&optinfo
->iana_list
,
1096 DHCP6_LISTVAL_IANA
, &iaparam
, 0)) == NULL
)
1099 if ((ial
= malloc(sizeof(*ial
))) == NULL
)
1103 if (dhcp6_add_listval(ial
, DHCP6_LISTVAL_IANA
,
1104 &iaparam
, &v
->sublist
) == NULL
) {
1108 if ((evd
= malloc(sizeof(*evd
))) == NULL
)
1110 memset(evd
, 0, sizeof(*evd
));
1111 evd
->type
= DHCP6_EVDATA_IANA
;
1114 evd
->destructor
= destruct_iadata
;
1115 TAILQ_INSERT_TAIL(&ev
->data_list
, evd
, link
);
1118 dprintf(LOG_ERR
, FNAME
, "internal error");
1130 dhcp6_remove_event(ev
); /* XXX */
1136 destruct_iadata(evd
)
1137 struct dhcp6_eventdata
*evd
;
1139 struct dhcp6_list
*ial
;
1141 if (evd
->type
!= DHCP6_EVDATA_IAPD
&& evd
->type
!= DHCP6_EVDATA_IANA
) {
1142 dprintf(LOG_ERR
, FNAME
, "assumption failure %d", evd
->type
);
1146 ial
= (struct dhcp6_list
*)evd
->data
;
1147 dhcp6_clear_list(ial
);
1151 static struct dhcp6_serverinfo
*
1153 struct dhcp6_event
*ev
;
1155 struct dhcp6_serverinfo
*s
;
1158 * pick the best server according to RFC3315 Section 17.1.3.
1159 * XXX: we currently just choose the one that is active and has the
1160 * highest preference.
1162 for (s
= ev
->servers
; s
; s
= s
->next
) {
1164 dprintf(LOG_DEBUG
, FNAME
, "picked a server (ID: %s)",
1165 duidstr(&s
->optinfo
.serverID
));
1180 sig_flags
|= SIGF_TERM
;
1183 sig_flags
|= SIGF_HUP
;
1190 struct dhcp6_event
*ev
;
1192 struct dhcp6_if
*ifp
;
1194 struct sockaddr_in6 dst
;
1196 struct dhcp6_optinfo optinfo
;
1197 ssize_t optlen
, len
;
1198 struct dhcp6_eventdata
*evd
;
1202 dh6
= (struct dhcp6
*)buf
;
1203 memset(dh6
, 0, sizeof(*dh6
));
1206 case DHCP6S_SOLICIT
:
1207 dh6
->dh6_msgtype
= DH6_SOLICIT
;
1209 case DHCP6S_REQUEST
:
1210 dh6
->dh6_msgtype
= DH6_REQUEST
;
1213 dh6
->dh6_msgtype
= DH6_RENEW
;
1216 dh6
->dh6_msgtype
= DH6_REBIND
;
1218 case DHCP6S_RELEASE
:
1219 dh6
->dh6_msgtype
= DH6_RELEASE
;
1221 case DHCP6S_INFOREQ
:
1222 dh6
->dh6_msgtype
= DH6_INFORM_REQ
;
1225 dprintf(LOG_ERR
, FNAME
, "unexpected state");
1229 if (ev
->timeouts
== 0) {
1231 * A client SHOULD generate a random number that cannot easily
1232 * be guessed or predicted to use as the transaction ID for
1233 * each new message it sends.
1235 * A client MUST leave the transaction-ID unchanged in
1236 * retransmissions of a message. [RFC3315 15.1]
1238 #ifdef HAVE_ARC4RANDOM
1239 ev
->xid
= arc4random() & DH6_XIDMASK
;
1241 ev
->xid
= random() & DH6_XIDMASK
;
1243 dprintf(LOG_DEBUG
, FNAME
, "a new XID (%x) is generated",
1246 dh6
->dh6_xid
&= ~ntohl(DH6_XIDMASK
);
1247 dh6
->dh6_xid
|= htonl(ev
->xid
);
1253 dhcp6_init_options(&optinfo
);
1256 switch (ev
->state
) {
1257 case DHCP6S_REQUEST
:
1259 case DHCP6S_RELEASE
:
1260 if (duidcpy(&optinfo
.serverID
, &ev
->serverid
)) {
1261 dprintf(LOG_ERR
, FNAME
, "failed to copy server ID");
1268 if (duidcpy(&optinfo
.clientID
, &client_duid
)) {
1269 dprintf(LOG_ERR
, FNAME
, "failed to copy client ID");
1273 /* rapid commit (in Solicit only) */
1274 if (ev
->state
== DHCP6S_SOLICIT
&&
1275 (ifp
->send_flags
& DHCIFF_RAPID_COMMIT
)) {
1276 optinfo
.rapidcommit
= 1;
1280 if (ev
->timeouts
== 0) {
1281 gettimeofday(&ev
->tv_start
, NULL
);
1282 optinfo
.elapsed_time
= 0;
1284 struct timeval now
, tv_diff
;
1287 gettimeofday(&now
, NULL
);
1288 tv_sub(&now
, &ev
->tv_start
, &tv_diff
);
1291 * The client uses the value 0xffff to represent any elapsed
1292 * time values greater than the largest time value that can be
1293 * represented in the Elapsed Time option.
1296 if (tv_diff
.tv_sec
>= (MAX_ELAPSED_TIME
/ 100) + 1) {
1298 * Perhaps we are nervous too much, but without this
1299 * additional check, we would see an overflow in 248
1300 * days (of no responses).
1302 et
= MAX_ELAPSED_TIME
;
1304 et
= tv_diff
.tv_sec
* 100 + tv_diff
.tv_usec
/ 10000;
1305 if (et
>= MAX_ELAPSED_TIME
)
1306 et
= MAX_ELAPSED_TIME
;
1308 optinfo
.elapsed_time
= (int32_t)et
;
1311 /* option request options */
1312 if (ev
->state
!= DHCP6S_RELEASE
&&
1313 dhcp6_copy_list(&optinfo
.reqopt_list
, &ifp
->reqopt_list
)) {
1314 dprintf(LOG_ERR
, FNAME
, "failed to copy requested options");
1318 /* configuration information specified as event data */
1319 for (evd
= TAILQ_FIRST(&ev
->data_list
); evd
;
1320 evd
= TAILQ_NEXT(evd
, link
)) {
1322 case DHCP6_EVDATA_IAPD
:
1323 if (dhcp6_copy_list(&optinfo
.iapd_list
,
1324 (struct dhcp6_list
*)evd
->data
)) {
1325 dprintf(LOG_NOTICE
, FNAME
,
1326 "failed to add an IAPD");
1330 case DHCP6_EVDATA_IANA
:
1331 if (dhcp6_copy_list(&optinfo
.iana_list
,
1332 (struct dhcp6_list
*)evd
->data
)) {
1333 dprintf(LOG_NOTICE
, FNAME
,
1334 "failed to add an IAPD");
1339 dprintf(LOG_ERR
, FNAME
, "unexpected event data (%d)",
1345 /* authentication information */
1346 if (set_auth(ev
, &optinfo
)) {
1347 dprintf(LOG_INFO
, FNAME
,
1348 "failed to set authentication option");
1352 /* set options in the message */
1353 if ((optlen
= dhcp6_set_options(dh6
->dh6_msgtype
,
1354 (struct dhcp6opt
*)(dh6
+ 1),
1355 (struct dhcp6opt
*)(buf
+ sizeof(buf
)), &optinfo
)) < 0) {
1356 dprintf(LOG_INFO
, FNAME
, "failed to construct options");
1361 /* calculate MAC if necessary, and put it to the message */
1362 if (ev
->authparam
!= NULL
) {
1363 switch (ev
->authparam
->authproto
) {
1364 case DHCP6_AUTHPROTO_DELAYED
:
1365 if (ev
->authparam
->key
== NULL
)
1368 if (dhcp6_calc_mac((char *)dh6
, len
,
1369 optinfo
.authproto
, optinfo
.authalgorithm
,
1370 optinfo
.delayedauth_offset
+ sizeof(*dh6
),
1371 ev
->authparam
->key
)) {
1372 dprintf(LOG_WARNING
, FNAME
,
1373 "failed to calculate MAC");
1378 break; /* do nothing */
1383 * Unless otherwise specified in this document or in a document that
1384 * describes how IPv6 is carried over a specific type of link (for link
1385 * types that do not support multicast), a client sends DHCP messages
1386 * to the All_DHCP_Relay_Agents_and_Servers.
1387 * [RFC3315 Section 13.]
1389 dst
= *sa6_allagent
;
1390 dst
.sin6_scope_id
= ifp
->linkid
;
1392 if (sendto(sock
, buf
, len
, 0, (struct sockaddr
*)&dst
,
1393 sysdep_sa_len((struct sockaddr
*)&dst
)) == -1) {
1394 dprintf(LOG_ERR
, FNAME
,
1395 "transmit failed: %s", strerror(errno
));
1399 dprintf(LOG_DEBUG
, FNAME
, "send %s to %s",
1400 dhcp6msgstr(dh6
->dh6_msgtype
), addr2str((struct sockaddr
*)&dst
));
1403 dhcp6_clear_options(&optinfo
);
1407 /* result will be a - b */
1409 tv_sub(a
, b
, result
)
1410 struct timeval
*a
, *b
, *result
;
1412 if (a
->tv_sec
< b
->tv_sec
||
1413 (a
->tv_sec
== b
->tv_sec
&& a
->tv_usec
< b
->tv_usec
)) {
1415 result
->tv_usec
= 0;
1420 result
->tv_sec
= a
->tv_sec
- b
->tv_sec
;
1421 if (a
->tv_usec
< b
->tv_usec
) {
1422 result
->tv_usec
= a
->tv_usec
+ 1000000 - b
->tv_usec
;
1423 result
->tv_sec
-= 1;
1425 result
->tv_usec
= a
->tv_usec
- b
->tv_usec
;
1433 char rbuf
[BUFSIZ
], cmsgbuf
[BUFSIZ
];
1436 struct sockaddr_storage from
;
1437 struct dhcp6_if
*ifp
;
1438 struct dhcp6opt
*p
, *ep
;
1439 struct dhcp6_optinfo optinfo
;
1443 struct in6_pktinfo
*pi
= NULL
;
1445 memset(&iov
, 0, sizeof(iov
));
1446 memset(&mhdr
, 0, sizeof(mhdr
));
1448 iov
.iov_base
= (caddr_t
)rbuf
;
1449 iov
.iov_len
= sizeof(rbuf
);
1450 mhdr
.msg_name
= (caddr_t
)&from
;
1451 mhdr
.msg_namelen
= sizeof(from
);
1452 mhdr
.msg_iov
= &iov
;
1453 mhdr
.msg_iovlen
= 1;
1454 mhdr
.msg_control
= (caddr_t
)cmsgbuf
;
1455 mhdr
.msg_controllen
= sizeof(cmsgbuf
);
1456 if ((len
= recvmsg(sock
, &mhdr
, 0)) < 0) {
1457 dprintf(LOG_ERR
, FNAME
, "recvmsg: %s", strerror(errno
));
1461 /* detect receiving interface */
1462 for (cm
= (struct cmsghdr
*)CMSG_FIRSTHDR(&mhdr
); cm
;
1463 cm
= (struct cmsghdr
*)CMSG_NXTHDR(&mhdr
, cm
)) {
1464 if (cm
->cmsg_level
== IPPROTO_IPV6
&&
1465 cm
->cmsg_type
== IPV6_PKTINFO
&&
1466 cm
->cmsg_len
== CMSG_LEN(sizeof(struct in6_pktinfo
))) {
1467 pi
= (struct in6_pktinfo
*)(CMSG_DATA(cm
));
1471 dprintf(LOG_NOTICE
, FNAME
, "failed to get packet info");
1475 if ((ifp
= find_ifconfbyid((unsigned int)pi
->ipi6_ifindex
)) == NULL
) {
1476 dprintf(LOG_INFO
, FNAME
, "unexpected interface (%d)",
1477 (unsigned int)pi
->ipi6_ifindex
);
1481 if (len
< sizeof(*dh6
)) {
1482 dprintf(LOG_INFO
, FNAME
, "short packet (%d bytes)", len
);
1486 dh6
= (struct dhcp6
*)rbuf
;
1488 dprintf(LOG_DEBUG
, FNAME
, "receive %s from %s on %s",
1489 dhcp6msgstr(dh6
->dh6_msgtype
),
1490 addr2str((struct sockaddr
*)&from
), ifp
->ifname
);
1493 dhcp6_init_options(&optinfo
);
1494 p
= (struct dhcp6opt
*)(dh6
+ 1);
1495 ep
= (struct dhcp6opt
*)((char *)dh6
+ len
);
1496 if (dhcp6_get_options(p
, ep
, &optinfo
) < 0) {
1497 dprintf(LOG_INFO
, FNAME
, "failed to parse options");
1501 switch(dh6
->dh6_msgtype
) {
1503 (void)client6_recvadvert(ifp
, dh6
, len
, &optinfo
);
1506 (void)client6_recvreply(ifp
, dh6
, len
, &optinfo
);
1509 dprintf(LOG_INFO
, FNAME
, "received an unexpected message (%s) "
1510 "from %s", dhcp6msgstr(dh6
->dh6_msgtype
),
1511 addr2str((struct sockaddr
*)&from
));
1515 dhcp6_clear_options(&optinfo
);
1520 client6_recvadvert(ifp
, dh6
, len
, optinfo
)
1521 struct dhcp6_if
*ifp
;
1524 struct dhcp6_optinfo
*optinfo
;
1526 struct dhcp6_serverinfo
*newserver
, **sp
;
1527 struct dhcp6_event
*ev
;
1528 struct dhcp6_eventdata
*evd
;
1529 struct authparam
*authparam
= NULL
, authparam0
;
1531 /* find the corresponding event based on the received xid */
1532 ev
= find_event_withid(ifp
, ntohl(dh6
->dh6_xid
) & DH6_XIDMASK
);
1534 dprintf(LOG_INFO
, FNAME
, "XID mismatch");
1538 /* packet validation based on Section 15.3 of RFC3315. */
1539 if (optinfo
->serverID
.duid_len
== 0) {
1540 dprintf(LOG_INFO
, FNAME
, "no server ID option");
1543 dprintf(LOG_DEBUG
, FNAME
, "server ID: %s, pref=%d",
1544 duidstr(&optinfo
->serverID
),
1547 if (optinfo
->clientID
.duid_len
== 0) {
1548 dprintf(LOG_INFO
, FNAME
, "no client ID option");
1551 if (duidcmp(&optinfo
->clientID
, &client_duid
)) {
1552 dprintf(LOG_INFO
, FNAME
, "client DUID mismatch");
1556 /* validate authentication */
1557 authparam0
= *ev
->authparam
;
1558 if (process_auth(&authparam0
, dh6
, len
, optinfo
)) {
1559 dprintf(LOG_INFO
, FNAME
, "failed to process authentication");
1564 * The requesting router MUST ignore any Advertise message that
1565 * includes a Status Code option containing the value NoPrefixAvail
1566 * [RFC3633 Section 11.1].
1567 * Likewise, the client MUST ignore any Advertise message that includes
1568 * a Status Code option containing the value NoAddrsAvail.
1569 * [RFC3315 Section 17.1.3].
1570 * We only apply this when we are going to request an address or
1573 for (evd
= TAILQ_FIRST(&ev
->data_list
); evd
;
1574 evd
= TAILQ_NEXT(evd
, link
)) {
1578 switch (evd
->type
) {
1579 case DHCP6_EVDATA_IAPD
:
1580 stcode
= DH6OPT_STCODE_NOPREFIXAVAIL
;
1581 stcodestr
= "NoPrefixAvail";
1583 case DHCP6_EVDATA_IANA
:
1584 stcode
= DH6OPT_STCODE_NOADDRSAVAIL
;
1585 stcodestr
= "NoAddrsAvail";
1590 if (dhcp6_find_listval(&optinfo
->stcode_list
,
1591 DHCP6_LISTVAL_STCODE
, &stcode
, 0)) {
1592 dprintf(LOG_INFO
, FNAME
,
1593 "advertise contains %s status", stcodestr
);
1598 if (ev
->state
!= DHCP6S_SOLICIT
||
1599 (ifp
->send_flags
& DHCIFF_RAPID_COMMIT
) || infreq_mode
) {
1601 * We expected a reply message, but do actually receive an
1602 * Advertise message. The server should be configured not to
1603 * allow the Rapid Commit option.
1604 * We process the message as if we expected the Advertise.
1605 * [RFC3315 Section 17.1.4]
1607 dprintf(LOG_INFO
, FNAME
, "unexpected advertise");
1608 /* proceed anyway */
1611 /* ignore the server if it is known */
1612 if (find_server(ev
, &optinfo
->serverID
)) {
1613 dprintf(LOG_INFO
, FNAME
, "duplicated server (ID: %s)",
1614 duidstr(&optinfo
->serverID
));
1618 /* keep the server */
1619 if ((newserver
= malloc(sizeof(*newserver
))) == NULL
) {
1620 dprintf(LOG_WARNING
, FNAME
,
1621 "memory allocation failed for server");
1624 memset(newserver
, 0, sizeof(*newserver
));
1626 /* remember authentication parameters */
1627 newserver
->authparam
= ev
->authparam
;
1628 newserver
->authparam
->flags
= authparam0
.flags
;
1629 newserver
->authparam
->prevrd
= authparam0
.prevrd
;
1630 newserver
->authparam
->key
= authparam0
.key
;
1632 /* allocate new authentication parameter for the soliciting event */
1633 if ((authparam
= new_authparam(ev
->authparam
->authproto
,
1634 ev
->authparam
->authalgorithm
, ev
->authparam
->authrdm
)) == NULL
) {
1635 dprintf(LOG_WARNING
, FNAME
, "memory allocation failed "
1636 "for authentication parameters");
1640 ev
->authparam
= authparam
;
1643 dhcp6_init_options(&newserver
->optinfo
);
1644 if (dhcp6_copy_options(&newserver
->optinfo
, optinfo
)) {
1645 dprintf(LOG_ERR
, FNAME
, "failed to copy options");
1646 if (newserver
->authparam
!= NULL
)
1647 free(newserver
->authparam
);
1651 if (optinfo
->pref
!= DH6OPT_PREF_UNDEF
)
1652 newserver
->pref
= optinfo
->pref
;
1653 newserver
->active
= 1;
1654 for (sp
= &ev
->servers
; *sp
; sp
= &(*sp
)->next
) {
1655 if ((*sp
)->pref
!= DH6OPT_PREF_MAX
&&
1656 (*sp
)->pref
< newserver
->pref
) {
1660 newserver
->next
= *sp
;
1663 if (newserver
->pref
== DH6OPT_PREF_MAX
) {
1665 * If the client receives an Advertise message that includes a
1666 * Preference option with a preference value of 255, the client
1667 * immediately begins a client-initiated message exchange.
1668 * [RFC3315 Section 17.1.2]
1670 ev
->current_server
= newserver
;
1671 if (duidcpy(&ev
->serverid
,
1672 &ev
->current_server
->optinfo
.serverID
)) {
1673 dprintf(LOG_NOTICE
, FNAME
, "can't copy server ID");
1674 return (-1); /* XXX: better recovery? */
1676 if (construct_reqdata(ifp
, &ev
->current_server
->optinfo
, ev
)) {
1677 dprintf(LOG_NOTICE
, FNAME
,
1678 "failed to construct request data");
1679 return (-1); /* XXX */
1683 ev
->state
= DHCP6S_REQUEST
;
1685 free(ev
->authparam
);
1686 ev
->authparam
= newserver
->authparam
;
1687 newserver
->authparam
= NULL
;
1691 dhcp6_set_timeoparam(ev
);
1692 dhcp6_reset_timer(ev
);
1693 } else if (ev
->servers
->next
== NULL
) {
1694 struct timeval
*rest
, elapsed
, tv_rt
, tv_irt
, timo
;
1697 * If this is the first advertise, adjust the timer so that
1698 * the client can collect other servers until IRT elapses.
1699 * XXX: we did not want to do such "low level" timer
1702 rest
= dhcp6_timer_rest(ev
->timer
);
1703 tv_rt
.tv_sec
= (ev
->retrans
* 1000) / 1000000;
1704 tv_rt
.tv_usec
= (ev
->retrans
* 1000) % 1000000;
1705 tv_irt
.tv_sec
= (ev
->init_retrans
* 1000) / 1000000;
1706 tv_irt
.tv_usec
= (ev
->init_retrans
* 1000) % 1000000;
1707 timeval_sub(&tv_rt
, rest
, &elapsed
);
1708 if (TIMEVAL_LEQ(elapsed
, tv_irt
))
1709 timeval_sub(&tv_irt
, &elapsed
, &timo
);
1711 timo
.tv_sec
= timo
.tv_usec
= 0;
1713 dprintf(LOG_DEBUG
, FNAME
, "reset timer for %s to %d.%06d",
1714 ifp
->ifname
, (int)timo
.tv_sec
, (int)timo
.tv_usec
);
1716 dhcp6_set_timer(&timo
, ev
->timer
);
1722 static struct dhcp6_serverinfo
*
1723 find_server(ev
, duid
)
1724 struct dhcp6_event
*ev
;
1727 struct dhcp6_serverinfo
*s
;
1729 for (s
= ev
->servers
; s
; s
= s
->next
) {
1730 if (duidcmp(&s
->optinfo
.serverID
, duid
) == 0)
1738 client6_recvreply(ifp
, dh6
, len
, optinfo
)
1739 struct dhcp6_if
*ifp
;
1742 struct dhcp6_optinfo
*optinfo
;
1744 struct dhcp6_listval
*lv
;
1745 struct dhcp6_event
*ev
;
1748 /* find the corresponding event based on the received xid */
1749 ev
= find_event_withid(ifp
, ntohl(dh6
->dh6_xid
) & DH6_XIDMASK
);
1751 dprintf(LOG_INFO
, FNAME
, "XID mismatch");
1756 if (state
!= DHCP6S_INFOREQ
&&
1757 state
!= DHCP6S_REQUEST
&&
1758 state
!= DHCP6S_RENEW
&&
1759 state
!= DHCP6S_REBIND
&&
1760 state
!= DHCP6S_RELEASE
&&
1761 (state
!= DHCP6S_SOLICIT
||
1762 !(ifp
->send_flags
& DHCIFF_RAPID_COMMIT
))) {
1763 dprintf(LOG_INFO
, FNAME
, "unexpected reply");
1767 /* A Reply message must contain a Server ID option */
1768 if (optinfo
->serverID
.duid_len
== 0) {
1769 dprintf(LOG_INFO
, FNAME
, "no server ID option");
1774 * DUID in the Client ID option (which must be contained for our
1775 * client implementation) must match ours.
1777 if (optinfo
->clientID
.duid_len
== 0) {
1778 dprintf(LOG_INFO
, FNAME
, "no client ID option");
1781 if (duidcmp(&optinfo
->clientID
, &client_duid
)) {
1782 dprintf(LOG_INFO
, FNAME
, "client DUID mismatch");
1786 /* validate authentication */
1787 if (process_auth(ev
->authparam
, dh6
, len
, optinfo
)) {
1788 dprintf(LOG_INFO
, FNAME
, "failed to process authentication");
1793 * If the client included a Rapid Commit option in the Solicit message,
1794 * the client discards any Reply messages it receives that do not
1795 * include a Rapid Commit option.
1796 * (should we keep the server otherwise?)
1797 * [RFC3315 Section 17.1.4]
1799 if (state
== DHCP6S_SOLICIT
&&
1800 (ifp
->send_flags
& DHCIFF_RAPID_COMMIT
) &&
1801 !optinfo
->rapidcommit
) {
1802 dprintf(LOG_INFO
, FNAME
, "no rapid commit");
1807 * The client MAY choose to report any status code or message from the
1808 * status code option in the Reply message.
1809 * [RFC3315 Section 18.1.8]
1811 for (lv
= TAILQ_FIRST(&optinfo
->stcode_list
); lv
;
1812 lv
= TAILQ_NEXT(lv
, link
)) {
1813 dprintf(LOG_INFO
, FNAME
, "status code: %s",
1814 dhcp6_stcodestr(lv
->val_num16
));
1817 if (!TAILQ_EMPTY(&optinfo
->dns_list
)) {
1818 struct dhcp6_listval
*d
;
1821 for (d
= TAILQ_FIRST(&optinfo
->dns_list
); d
;
1822 d
= TAILQ_NEXT(d
, link
), i
++) {
1823 info_printf("nameserver[%d] %s",
1824 i
, in6addr2str(&d
->val_addr6
, 0));
1828 if (!TAILQ_EMPTY(&optinfo
->dnsname_list
)) {
1829 struct dhcp6_listval
*d
;
1832 for (d
= TAILQ_FIRST(&optinfo
->dnsname_list
); d
;
1833 d
= TAILQ_NEXT(d
, link
), i
++) {
1834 info_printf("Domain search list[%d] %s",
1835 i
, d
->val_vbuf
.dv_buf
);
1839 if (!TAILQ_EMPTY(&optinfo
->ntp_list
)) {
1840 struct dhcp6_listval
*d
;
1843 for (d
= TAILQ_FIRST(&optinfo
->ntp_list
); d
;
1844 d
= TAILQ_NEXT(d
, link
), i
++) {
1845 info_printf("NTP server[%d] %s",
1846 i
, in6addr2str(&d
->val_addr6
, 0));
1850 if (!TAILQ_EMPTY(&optinfo
->sip_list
)) {
1851 struct dhcp6_listval
*d
;
1854 for (d
= TAILQ_FIRST(&optinfo
->sip_list
); d
;
1855 d
= TAILQ_NEXT(d
, link
), i
++) {
1856 info_printf("SIP server address[%d] %s",
1857 i
, in6addr2str(&d
->val_addr6
, 0));
1861 if (!TAILQ_EMPTY(&optinfo
->sipname_list
)) {
1862 struct dhcp6_listval
*d
;
1865 for (d
= TAILQ_FIRST(&optinfo
->sipname_list
); d
;
1866 d
= TAILQ_NEXT(d
, link
), i
++) {
1867 info_printf("SIP domain name[%d] %s",
1868 i
, d
->val_vbuf
.dv_buf
);
1873 * Set refresh timer for configuration information specified in
1874 * information-request. If the timer value is specified by the server
1875 * in an information refresh time option, use it; use the protocol
1876 * default otherwise.
1878 if (state
== DHCP6S_INFOREQ
) {
1879 int64_t refreshtime
= DHCP6_IRT_DEFAULT
;
1881 if (optinfo
->refreshtime
!= DH6OPT_REFRESHTIME_UNDEF
)
1882 refreshtime
= optinfo
->refreshtime
;
1884 ifp
->timer
= dhcp6_add_timer(client6_expire_refreshtime
, ifp
);
1885 if (ifp
->timer
== NULL
) {
1886 dprintf(LOG_WARNING
, FNAME
,
1887 "failed to add timer for refresh time");
1891 tv
.tv_sec
= (long)refreshtime
;
1894 if (tv
.tv_sec
< 0) {
1896 * XXX: tv_sec can overflow for an
1897 * unsigned 32bit value.
1899 dprintf(LOG_WARNING
, FNAME
,
1900 "refresh time is too large: %lu",
1901 (u_int32_t
)refreshtime
);
1902 tv
.tv_sec
= 0x7fffffff; /* XXX */
1905 dhcp6_set_timer(&tv
, ifp
->timer
);
1907 } else if (optinfo
->refreshtime
!= DH6OPT_REFRESHTIME_UNDEF
) {
1909 * draft-ietf-dhc-lifetime-02 clarifies that refresh time
1910 * is only used for information-request and reply exchanges.
1912 dprintf(LOG_INFO
, FNAME
,
1913 "unexpected information refresh time option (ignored)");
1916 /* update stateful configuration information */
1917 if (state
!= DHCP6S_RELEASE
) {
1918 update_ia(IATYPE_PD
, &optinfo
->iapd_list
, ifp
,
1919 &optinfo
->serverID
, ev
->authparam
);
1920 update_ia(IATYPE_NA
, &optinfo
->iana_list
, ifp
,
1921 &optinfo
->serverID
, ev
->authparam
);
1925 * Call the configuration script, if specified, to handle various
1926 * configuration parameters.
1928 if (ifp
->scriptpath
!= NULL
&& strlen(ifp
->scriptpath
) != 0) {
1929 dprintf(LOG_DEBUG
, FNAME
, "executes %s", ifp
->scriptpath
);
1930 client6_script(ifp
->scriptpath
, state
, optinfo
);
1933 dhcp6_remove_event(ev
);
1935 if (state
== DHCP6S_RELEASE
) {
1937 * When the client receives a valid Reply message in response
1938 * to a Release message, the client considers the Release event
1939 * completed, regardless of the Status Code option(s) returned
1941 * [RFC3315 Section 18.1.8]
1946 dprintf(LOG_DEBUG
, FNAME
, "got an expected reply, sleeping.");
1950 free_resources(NULL
);
1957 static struct dhcp6_event
*
1958 find_event_withid(ifp
, xid
)
1959 struct dhcp6_if
*ifp
;
1962 struct dhcp6_event
*ev
;
1964 for (ev
= TAILQ_FIRST(&ifp
->event_list
); ev
;
1965 ev
= TAILQ_NEXT(ev
, link
)) {
1974 process_auth(authparam
, dh6
, len
, optinfo
)
1975 struct authparam
*authparam
;
1978 struct dhcp6_optinfo
*optinfo
;
1980 struct keyinfo
*key
= NULL
;
1981 int authenticated
= 0;
1983 switch (optinfo
->authproto
) {
1984 case DHCP6_AUTHPROTO_UNDEF
:
1985 /* server did not provide authentication option */
1987 case DHCP6_AUTHPROTO_DELAYED
:
1988 if ((optinfo
->authflags
& DHCP6OPT_AUTHFLAG_NOINFO
)) {
1989 dprintf(LOG_INFO
, FNAME
, "server did not include "
1990 "authentication information");
1994 if (optinfo
->authalgorithm
!= DHCP6_AUTHALG_HMACMD5
) {
1995 dprintf(LOG_INFO
, FNAME
, "unknown authentication "
1996 "algorithm (%d)", optinfo
->authalgorithm
);
2000 if (optinfo
->authrdm
!= DHCP6_AUTHRDM_MONOCOUNTER
) {
2001 dprintf(LOG_INFO
, FNAME
,"unknown RDM (%d)",
2007 * Replay protection. If we do not know the previous RD value,
2008 * we accept the message anyway (XXX).
2010 if ((authparam
->flags
& AUTHPARAM_FLAGS_NOPREVRD
)) {
2011 dprintf(LOG_WARNING
, FNAME
, "previous RD value is "
2012 "unknown (accept it)");
2014 if (dhcp6_auth_replaycheck(optinfo
->authrdm
,
2015 authparam
->prevrd
, optinfo
->authrd
)) {
2016 dprintf(LOG_INFO
, FNAME
,
2017 "possible replay attack detected");
2022 /* identify the secret key */
2023 if ((key
= authparam
->key
) != NULL
) {
2025 * If we already know a key, its identification should
2026 * match that contained in the received option.
2027 * (from Section 21.4.5.1 of RFC3315)
2029 if (optinfo
->delayedauth_keyid
!= key
->keyid
||
2030 optinfo
->delayedauth_realmlen
!= key
->realmlen
||
2031 memcmp(optinfo
->delayedauth_realmval
, key
->realm
,
2032 key
->realmlen
) != 0) {
2033 dprintf(LOG_INFO
, FNAME
,
2034 "authentication key mismatch");
2038 key
= find_key(optinfo
->delayedauth_realmval
,
2039 optinfo
->delayedauth_realmlen
,
2040 optinfo
->delayedauth_keyid
);
2042 dprintf(LOG_INFO
, FNAME
, "failed to find key "
2043 "provided by the server (ID: %x)",
2044 optinfo
->delayedauth_keyid
);
2047 dprintf(LOG_DEBUG
, FNAME
, "found key for "
2048 "authentication: %s", key
->name
);
2050 authparam
->key
= key
;
2053 /* check for the key lifetime */
2054 if (dhcp6_validate_key(key
)) {
2055 dprintf(LOG_INFO
, FNAME
, "key %s has expired",
2061 if (dhcp6_verify_mac((char *)dh6
, len
, optinfo
->authproto
,
2062 optinfo
->authalgorithm
,
2063 optinfo
->delayedauth_offset
+ sizeof(*dh6
), key
) == 0) {
2064 dprintf(LOG_DEBUG
, FNAME
, "message authentication "
2068 dprintf(LOG_INFO
, FNAME
, "invalid message "
2074 dprintf(LOG_INFO
, FNAME
, "server sent unsupported "
2075 "authentication protocol (%d)", optinfo
->authproto
);
2079 if (authenticated
== 0) {
2080 if (authparam
->authproto
!= DHCP6_AUTHPROTO_UNDEF
) {
2081 dprintf(LOG_INFO
, FNAME
, "message not authenticated "
2082 "while authentication required");
2085 * Right now, we simply discard unauthenticated
2091 /* if authenticated, update the "previous" RD value */
2092 authparam
->prevrd
= optinfo
->authrd
;
2093 authparam
->flags
&= ~AUTHPARAM_FLAGS_NOPREVRD
;
2100 set_auth(ev
, optinfo
)
2101 struct dhcp6_event
*ev
;
2102 struct dhcp6_optinfo
*optinfo
;
2104 struct authparam
*authparam
= ev
->authparam
;
2106 if (authparam
== NULL
)
2109 optinfo
->authproto
= authparam
->authproto
;
2110 optinfo
->authalgorithm
= authparam
->authalgorithm
;
2111 optinfo
->authrdm
= authparam
->authrdm
;
2113 switch (authparam
->authproto
) {
2114 case DHCP6_AUTHPROTO_UNDEF
: /* we simply do not need authentication */
2116 case DHCP6_AUTHPROTO_DELAYED
:
2117 if (ev
->state
== DHCP6S_INFOREQ
) {
2119 * In the current implementation, delayed
2120 * authentication for Information-request and Reply
2121 * exchanges doesn't work. Specification is also
2122 * unclear on this usage.
2124 dprintf(LOG_WARNING
, FNAME
, "delayed authentication "
2125 "cannot be used for Information-request yet");
2129 if (ev
->state
== DHCP6S_SOLICIT
) {
2130 optinfo
->authflags
|= DHCP6OPT_AUTHFLAG_NOINFO
;
2131 return (0); /* no auth information is needed */
2134 if (authparam
->key
== NULL
) {
2135 dprintf(LOG_INFO
, FNAME
,
2136 "no authentication key for %s",
2137 dhcp6_event_statestr(ev
));
2141 if (dhcp6_validate_key(authparam
->key
)) {
2142 dprintf(LOG_INFO
, FNAME
, "key %s is invalid",
2143 authparam
->key
->name
);
2147 if (get_rdvalue(optinfo
->authrdm
, &optinfo
->authrd
,
2148 sizeof(optinfo
->authrd
))) {
2149 dprintf(LOG_ERR
, FNAME
, "failed to get a replay "
2154 optinfo
->delayedauth_keyid
= authparam
->key
->keyid
;
2155 optinfo
->delayedauth_realmlen
= authparam
->key
->realmlen
;
2156 optinfo
->delayedauth_realmval
=
2157 malloc(optinfo
->delayedauth_realmlen
);
2158 if (optinfo
->delayedauth_realmval
== NULL
) {
2159 dprintf(LOG_ERR
, FNAME
, "failed to allocate memory "
2160 "for authentication realm");
2163 memcpy(optinfo
->delayedauth_realmval
, authparam
->key
->realm
,
2164 optinfo
->delayedauth_realmlen
);
2168 dprintf(LOG_ERR
, FNAME
, "unsupported authentication protocol "
2169 "%d", authparam
->authproto
);
2177 info_printf(const char *fmt
, ...)
2180 char logbuf
[LINE_MAX
];
2183 vsnprintf(logbuf
, sizeof(logbuf
), fmt
, ap
);
2185 dprintf(LOG_DEBUG
, FNAME
, "%s", logbuf
);
2187 printf("%s\n", logbuf
);