Correct PPTP server firewall rules chain.
[tomato/davidwu.git] / release / src / router / dhcpv6 / dhcp6c.c
blob129257aeb49c8c73cba79ed8a539ceeeb4e02f62
1 /* $KAME: dhcp6c.c,v 1.164 2006/01/10 02:46:09 jinmei Exp $ */
2 /*
3 * Copyright (C) 1998 and 1999 WIDE Project.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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
28 * SUCH DAMAGE.
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/socket.h>
33 #include <sys/uio.h>
34 #include <sys/queue.h>
35 #include <errno.h>
36 #include <limits.h>
37 #if TIME_WITH_SYS_TIME
38 # include <sys/time.h>
39 # include <time.h>
40 #else
41 # if HAVE_SYS_TIME_H
42 # include <sys/time.h>
43 # else
44 # include <time.h>
45 # endif
46 #endif
47 #include <net/if.h>
48 #ifdef __FreeBSD__
49 #include <net/if_var.h>
50 #endif
52 #include <netinet/in.h>
53 #ifdef __KAME__
54 #include <net/if_dl.h>
55 #include <netinet6/in6_var.h>
56 #endif
58 #include <arpa/inet.h>
59 #include <netdb.h>
61 #include <signal.h>
62 #include <stdio.h>
63 #include <stdarg.h>
64 #include <syslog.h>
65 #include <stdlib.h>
66 #include <unistd.h>
67 #include <string.h>
68 #include <err.h>
69 #include <ifaddrs.h>
71 #include <dhcp6.h>
72 #include <config.h>
73 #include <common.h>
74 #include <timer.h>
75 #include <dhcp6c.h>
76 #ifdef USE_DHCP6CTL
77 #include <control.h>
78 #include <dhcp6_ctl.h>
79 #endif
80 #include <dhcp6c_ia.h>
81 #include <prefixconf.h>
82 #include <auth.h>
84 static int debug = 0;
85 static int exit_ok = 0;
86 static sig_atomic_t sig_flags = 0;
87 #define SIGF_TERM 0x1
88 #define SIGF_HUP 0x2
90 const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_CLIENT;
92 int sock; /* inbound/outbound udp port */
93 int rtsock; /* routing socket */
95 #ifdef USE_DHCP6CTL
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
103 #define CTLSKEW 300
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;
111 #ifdef USE_DHCP6CTL
112 static char *ctlkeyfile = DEFAULT_KEYFILE;
113 static struct keyinfo *ctlkey = NULL;
114 static int ctldigestlen;
115 #endif
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));
127 #ifdef USE_DHCP6CTL
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));
131 #endif
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 *,
135 struct duid *));
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 *,
144 u_int32_t));
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
164 main(argc, argv)
165 int argc;
166 char **argv;
168 int ch, pid;
169 char *progname;
170 FILE *pidfp;
171 struct dhcp6_if *ifp;
172 int fd;
174 #ifndef HAVE_ARC4RANDOM
175 srandom(time(NULL) & getpid());
176 #endif
178 if ((progname = strrchr(*argv, '/')) == NULL)
179 progname = *argv;
180 else
181 progname++;
183 while ((ch = getopt(argc, argv, "c:dDT:fik:p:")) != -1) {
184 switch (ch) {
185 case 'c':
186 conffile = optarg;
187 break;
188 case 'd':
189 debug = 1;
190 break;
191 case 'D':
192 debug = 2;
193 break;
194 case 'T':
195 if (!strcasecmp(optarg, "LL"))
196 duid_type = 3;
197 else if (!strcasecmp(optarg, "LLT"))
198 duid_type = 1;
199 break;
200 case 'f':
201 foreground++;
202 break;
203 case 'i':
204 infreq_mode = 1;
205 break;
206 #ifdef USE_DHCP6CTL
207 case 'k':
208 ctlkeyfile = optarg;
209 break;
210 #endif
211 case 'p':
212 pid_file = optarg;
213 break;
214 default:
215 usage();
216 exit(0);
219 argc -= optind;
220 argv += optind;
222 if (argc == 0) {
223 usage();
224 exit(0);
227 if (foreground == 0) {
228 for (fd = 3; fd < 1024; fd++)
229 close(fd);
231 openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON);
234 setloglevel(debug);
236 client6_init();
237 while (argc-- > 0) {
238 if ((ifp = ifinit(argv[0])) == NULL) {
239 dprintf(LOG_ERR, FNAME, "failed to initialize %s",
240 argv[0]);
241 exit(1);
243 argv++;
246 if (infreq_mode == 0 && (cfparse(conffile)) != 0) {
247 dprintf(LOG_ERR, FNAME, "failed to parse configuration file");
248 exit(1);
251 if (foreground == 0 && infreq_mode == 0) {
252 if (daemon(0, 0) < 0)
253 err(1, "daemon");
256 /* dump current PID */
257 pid = getpid();
258 if ((pidfp = fopen(pid_file, "w")) != NULL) {
259 fprintf(pidfp, "%d\n", pid);
260 fclose(pidfp);
263 client6_startall(0);
264 client6_mainloop();
265 exit(0);
268 static void
269 usage()
272 fprintf(stderr, "usage: dhcp6c [-c configfile] [-dDfi] "
273 "[-T LL|LLT] [-p pid-file] interface [interfaces...]\n");
276 /*------------------------------------------------------------*/
278 void
279 client6_init()
281 struct addrinfo hints, *res;
282 static struct sockaddr_in6 sa6_allagent_storage;
283 int error, on = 1;
285 /* get our DUID */
286 if (get_duid(DUID_FILE, &client_duid)) {
287 dprintf(LOG_ERR, FNAME, "failed to get a DUID");
288 exit(1);
291 #ifdef USE_DHCP6CTL
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 */
297 #endif
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);
305 if (error) {
306 dprintf(LOG_ERR, FNAME, "getaddrinfo: %s",
307 gai_strerror(error));
308 exit(1);
310 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
311 if (sock < 0) {
312 dprintf(LOG_ERR, FNAME, "socket");
313 exit(1);
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));
319 exit(1);
321 #ifdef IPV6_RECVPKTINFO
322 if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
323 sizeof(on)) < 0) {
324 dprintf(LOG_ERR, FNAME,
325 "setsockopt(IPV6_RECVPKTINFO): %s",
326 strerror(errno));
327 exit(1);
329 #else
330 if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
331 sizeof(on)) < 0) {
332 dprintf(LOG_ERR, FNAME,
333 "setsockopt(IPV6_PKTINFO): %s",
334 strerror(errno));
335 exit(1);
337 #endif
338 if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on,
339 sizeof(on)) < 0) {
340 dprintf(LOG_ERR, FNAME,
341 "setsockopt(sock, IPV6_MULTICAST_LOOP): %s",
342 strerror(errno));
343 exit(1);
345 #ifdef IPV6_V6ONLY
346 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
347 &on, sizeof(on)) < 0) {
348 dprintf(LOG_ERR, FNAME, "setsockopt(IPV6_V6ONLY): %s",
349 strerror(errno));
350 exit(1);
352 #endif
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));
361 exit(1);
363 freeaddrinfo(res);
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",
368 strerror(errno));
369 exit(1);
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);
377 if (error) {
378 dprintf(LOG_ERR, FNAME, "getaddrinfo: %s",
379 gai_strerror(error));
380 exit(1);
382 memcpy(&sa6_allagent_storage, res->ai_addr, res->ai_addrlen);
383 sa6_allagent = (const struct sockaddr_in6 *)&sa6_allagent_storage;
384 freeaddrinfo(res);
386 #ifdef USE_DHCP6CTL
387 /* set up control socket */
388 if (ctlkey == NULL)
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");
394 exit(1);
396 #endif
398 if (signal(SIGHUP, client6_signal) == SIG_ERR) {
399 dprintf(LOG_WARNING, FNAME, "failed to set signal: %s",
400 strerror(errno));
401 exit(1);
403 if (signal(SIGTERM, client6_signal) == SIG_ERR) {
404 dprintf(LOG_WARNING, FNAME, "failed to set signal: %s",
405 strerror(errno));
406 exit(1);
411 client6_start(ifp)
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");
426 return (-1);
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);
435 return (-1);
438 if ((ev->timer = dhcp6_add_timer(client6_timo, ev)) == NULL) {
439 dprintf(LOG_NOTICE, FNAME, "failed to add a timer for %s",
440 ifp->ifname);
441 dhcp6_remove_event(ev);
442 return (-1);
444 dhcp6_reset_timer(ev);
446 return (0);
449 static void
450 client6_startall(isrestart)
451 int 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",
458 ifp->ifname);
459 continue; /* XXX: try to recover? */
461 if (client6_start(ifp))
462 exit(1); /* initialization failure. we give up. */
466 static void
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)
476 continue;
478 /* release all IAs as well as send RELEASE message(s) */
479 release_all_ia(ifp);
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);
496 static void
497 check_exit()
499 struct dhcp6_if *ifp;
501 if (!exit_ok)
502 return;
504 for (ifp = dhcp6_if; ifp; ifp = ifp->next) {
506 * Check if we have an outstanding event. If we do, we cannot
507 * exit for now.
509 if (!TAILQ_EMPTY(&ifp->event_list))
510 return;
513 /* We have no existing event. Do exit. */
514 dprintf(LOG_INFO, FNAME, "exiting");
516 exit(0);
519 static void
520 process_signals()
522 if ((sig_flags & SIGF_TERM)) {
523 exit_ok = 1;
524 free_resources(NULL);
525 unlink(pid_file);
526 check_exit();
528 if ((sig_flags & SIGF_HUP)) {
529 dprintf(LOG_INFO, FNAME, "restarting");
530 free_resources(NULL);
531 client6_startall(1);
534 sig_flags = 0;
537 static void
538 client6_mainloop()
540 struct timeval *w;
541 int ret, maxsock;
542 fd_set r;
544 while(1) {
545 if (sig_flags)
546 process_signals();
548 w = dhcp6_check_timer();
550 FD_ZERO(&r);
551 FD_SET(sock, &r);
552 maxsock = sock;
553 #ifdef USE_DHCP6CTL
554 if (ctlsock >= 0) {
555 FD_SET(ctlsock, &r);
556 maxsock = (sock > ctlsock) ? sock : ctlsock;
557 (void)dhcp6_ctl_setreadfds(&r, &maxsock);
559 #endif
561 ret = select(maxsock + 1, &r, NULL, NULL, w);
563 switch (ret) {
564 case -1:
565 if (errno != EINTR) {
566 dprintf(LOG_ERR, FNAME, "select: %s",
567 strerror(errno));
568 exit(1);
570 continue;
571 case 0: /* timeout */
572 break; /* dhcp6_check_timer() will treat the case */
573 default:
574 break;
576 if (FD_ISSET(sock, &r))
577 client6_recv();
578 #ifdef USE_DHCP6CTL
579 if (ctlsock >= 0) {
580 if (FD_ISSET(ctlsock, &r)) {
581 (void)dhcp6_ctl_acceptcommand(ctlsock,
582 client6_do_ctlcommand);
584 (void)dhcp6_ctl_readcommand(&r);
586 #endif
590 static inline int
591 get_val32(bpp, lenp, valp)
592 char **bpp;
593 int *lenp;
594 u_int32_t *valp;
596 char *bp = *bpp;
597 int len = *lenp;
598 u_int32_t i32;
600 if (len < sizeof(*valp))
601 return (-1);
603 memcpy(&i32, bp, sizeof(i32));
604 *valp = ntohl(i32);
606 *bpp = bp + sizeof(*valp);
607 *lenp = len - sizeof(*valp);
609 return (0);
612 static inline int
613 get_ifname(bpp, lenp, ifbuf, ifbuflen)
614 char **bpp;
615 int *lenp;
616 char *ifbuf;
617 int ifbuflen;
619 char *bp = *bpp;
620 int len = *lenp, ifnamelen;
621 u_int32_t i32;
623 if (get_val32(bpp, lenp, &i32))
624 return (-1);
625 ifnamelen = (int)i32;
627 if (*lenp < ifnamelen || ifnamelen > ifbuflen)
628 return (-1);
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);
638 return (0);
641 #ifdef USE_DHCP6CTL
642 static int
643 client6_do_ctlcommand(buf, len)
644 char *buf;
645 ssize_t len;
647 struct dhcp6ctl *ctlhead;
648 u_int16_t command, version;
649 u_int32_t p32, ts, ts0;
650 int commandlen;
651 char *bp;
652 char ifname[IFNAMSIZ];
653 time_t now;
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",
671 strerror(errno));
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);
699 switch (command) {
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);
706 client6_reload();
707 break;
708 case DHCP6CTL_COMMAND_START:
709 if (get_val32(&bp, &commandlen, &p32))
710 return (DHCP6CTL_R_FAILURE);
711 switch (p32) {
712 case DHCP6CTL_INTERFACE:
713 if (get_ifname(&bp, &commandlen, ifname,
714 sizeof(ifname))) {
715 return (DHCP6CTL_R_FAILURE);
717 if (client6_ifctl(ifname, DHCP6CTL_COMMAND_START))
718 return (DHCP6CTL_R_FAILURE);
719 break;
720 default:
721 dprintf(LOG_INFO, FNAME,
722 "unknown start target: %ul", p32);
723 return (DHCP6CTL_R_FAILURE);
725 break;
726 case DHCP6CTL_COMMAND_STOP:
727 if (commandlen == 0) {
728 exit_ok = 1;
729 free_resources(NULL);
730 unlink(pid_file);
731 check_exit();
732 } else {
733 if (get_val32(&bp, &commandlen, &p32))
734 return (DHCP6CTL_R_FAILURE);
736 switch (p32) {
737 case DHCP6CTL_INTERFACE:
738 if (get_ifname(&bp, &commandlen, ifname,
739 sizeof(ifname))) {
740 return (DHCP6CTL_R_FAILURE);
742 if (client6_ifctl(ifname,
743 DHCP6CTL_COMMAND_STOP)) {
744 return (DHCP6CTL_R_FAILURE);
746 break;
747 default:
748 dprintf(LOG_INFO, FNAME,
749 "unknown start target: %ul", p32);
750 return (DHCP6CTL_R_FAILURE);
753 break;
754 default:
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);
764 static void
765 client6_reload()
767 /* reload the configuration file */
768 if (cfparse(conffile) != 0) {
769 dprintf(LOG_WARNING, FNAME,
770 "failed to reload configuration file");
771 return;
774 dprintf(LOG_NOTICE, FNAME, "client reloaded");
776 return;
779 static int
780 client6_ifctl(ifname, command)
781 char *ifname;
782 u_int16_t 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",
789 ifname);
790 return (-1);
793 dprintf(LOG_DEBUG, FNAME, "%s interface %s",
794 command == DHCP6CTL_COMMAND_START ? "start" : "stop", ifname);
796 switch(command) {
797 case DHCP6CTL_COMMAND_START:
799 * The ifid might have changed, so reset it before releasing the
800 * lease.
802 if (ifreset(ifp)) {
803 dprintf(LOG_NOTICE, FNAME, "failed to reset %s",
804 ifname);
805 return (-1);
807 free_resources(ifp);
808 if (client6_start(ifp)) {
809 dprintf(LOG_NOTICE, FNAME, "failed to restart %s",
810 ifname);
811 return (-1);
813 break;
814 case DHCP6CTL_COMMAND_STOP:
815 free_resources(ifp);
816 if (ifp->timer != NULL) {
817 dprintf(LOG_DEBUG, FNAME,
818 "removed existing timer on %s", ifp->ifname);
819 dhcp6_remove_timer(&ifp->timer);
821 break;
822 default: /* impossible case, should be a bug */
823 dprintf(LOG_ERR, FNAME, "unknown command: %d", (int)command);
824 break;
827 return (0);
829 #endif // USE_DHCP6CTL
831 static struct dhcp6_timer *
832 client6_expire_refreshtime(arg)
833 void *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);
841 client6_start(ifp);
843 return (NULL);
846 struct dhcp6_timer *
847 client6_timo(arg)
848 void *arg;
850 struct dhcp6_event *ev = (struct dhcp6_event *)arg;
851 struct dhcp6_if *ifp;
852 int state = ev->state;
854 ifp = ev->ifp;
855 ev->timeouts++;
858 * Unless MRC is zero, the message exchange fails once the client has
859 * transmitted the message MRC times.
860 * [RFC3315 14.]
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)
867 check_exit();
869 return (NULL);
872 switch(ev->state) {
873 case DHCP6S_INIT:
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;
877 else {
878 ev->state = DHCP6S_SOLICIT;
879 if (construct_confdata(ifp, ev)) {
880 dprintf(LOG_ERR, FNAME, "can't send solicit");
881 exit(1); /* XXX */
884 dhcp6_set_timeoparam(ev); /* XXX */
885 /* fall through */
886 case DHCP6S_REQUEST:
887 case DHCP6S_RELEASE:
888 case DHCP6S_INFOREQ:
889 client6_send(ev);
890 break;
891 case DHCP6S_RENEW:
892 case DHCP6S_REBIND:
893 if (!TAILQ_EMPTY(&ev->data_list))
894 client6_send(ev);
895 else {
896 dprintf(LOG_INFO, FNAME,
897 "all information to be updated was canceled");
898 dhcp6_remove_event(ev);
899 return (NULL);
901 break;
902 case DHCP6S_SOLICIT:
903 if (ev->servers) {
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
910 * ever arrives.
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");
917 exit(1); /* XXX */
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? */
925 ev->timeouts = 0;
926 ev->state = DHCP6S_REQUEST;
927 dhcp6_set_timeoparam(ev);
929 if (ev->authparam != NULL)
930 free(ev->authparam);
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");
938 break;
941 client6_send(ev);
942 break;
945 dhcp6_reset_timer(ev);
947 return (ev->timer);
950 static int
951 construct_confdata(ifp, ev)
952 struct dhcp6_if *ifp;
953 struct dhcp6_event *ev;
955 struct ia_conf *iac;
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))
966 continue;
968 evd = NULL;
969 if ((evd = malloc(sizeof(*evd))) == NULL) {
970 dprintf(LOG_NOTICE, FNAME,
971 "failed to create a new event data");
972 goto fail;
974 memset(evd, 0, sizeof(evd));
976 memset(&iaparam, 0, sizeof(iaparam));
977 iaparam.iaid = iac->iaid;
978 switch (iac->type) {
979 case IATYPE_PD:
980 ial = NULL;
981 if ((ial = malloc(sizeof(*ial))) == NULL)
982 goto fail;
983 TAILQ_INIT(ial);
985 TAILQ_INIT(&pl);
986 dhcp6_copy_list(&pl,
987 &((struct iapd_conf *)iac)->iapd_prefix_list);
988 if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IAPD,
989 &iaparam, &pl) == NULL) {
990 goto fail;
992 dhcp6_clear_list(&pl);
994 evd->type = DHCP6_EVDATA_IAPD;
995 evd->data = ial;
996 evd->event = ev;
997 evd->destructor = destruct_iadata;
998 TAILQ_INSERT_TAIL(&ev->data_list, evd, link);
999 break;
1000 case IATYPE_NA:
1001 ial = NULL;
1002 if ((ial = malloc(sizeof(*ial))) == NULL)
1003 goto fail;
1004 TAILQ_INIT(ial);
1006 TAILQ_INIT(&pl);
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) {
1011 goto fail;
1013 dhcp6_clear_list(&pl);
1015 evd->type = DHCP6_EVDATA_IANA;
1016 evd->data = ial;
1017 evd->event = ev;
1018 evd->destructor = destruct_iadata;
1019 TAILQ_INSERT_TAIL(&ev->data_list, evd, link);
1020 break;
1021 default:
1022 dprintf(LOG_ERR, FNAME, "internal error");
1023 exit(1);
1027 return (0);
1029 fail:
1030 if (evd)
1031 free(evd);
1032 if (ial)
1033 free(ial);
1034 dhcp6_remove_event(ev); /* XXX */
1036 return (-1);
1039 static int
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)
1054 return (0);
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))
1062 continue;
1064 memset(&iaparam, 0, sizeof(iaparam));
1065 iaparam.iaid = iac->iaid;
1067 ial = NULL;
1068 evd = NULL;
1070 switch (iac->type) {
1071 case IATYPE_PD:
1072 if ((v = dhcp6_find_listval(&optinfo->iapd_list,
1073 DHCP6_LISTVAL_IAPD, &iaparam, 0)) == NULL)
1074 continue;
1076 if ((ial = malloc(sizeof(*ial))) == NULL)
1077 goto fail;
1079 TAILQ_INIT(ial);
1080 if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IAPD,
1081 &iaparam, &v->sublist) == NULL) {
1082 goto fail;
1085 if ((evd = malloc(sizeof(*evd))) == NULL)
1086 goto fail;
1087 memset(evd, 0, sizeof(*evd));
1088 evd->type = DHCP6_EVDATA_IAPD;
1089 evd->data = ial;
1090 evd->event = ev;
1091 evd->destructor = destruct_iadata;
1092 TAILQ_INSERT_TAIL(&ev->data_list, evd, link);
1093 break;
1094 case IATYPE_NA:
1095 if ((v = dhcp6_find_listval(&optinfo->iana_list,
1096 DHCP6_LISTVAL_IANA, &iaparam, 0)) == NULL)
1097 continue;
1099 if ((ial = malloc(sizeof(*ial))) == NULL)
1100 goto fail;
1102 TAILQ_INIT(ial);
1103 if (dhcp6_add_listval(ial, DHCP6_LISTVAL_IANA,
1104 &iaparam, &v->sublist) == NULL) {
1105 goto fail;
1108 if ((evd = malloc(sizeof(*evd))) == NULL)
1109 goto fail;
1110 memset(evd, 0, sizeof(*evd));
1111 evd->type = DHCP6_EVDATA_IANA;
1112 evd->data = ial;
1113 evd->event = ev;
1114 evd->destructor = destruct_iadata;
1115 TAILQ_INSERT_TAIL(&ev->data_list, evd, link);
1116 break;
1117 default:
1118 dprintf(LOG_ERR, FNAME, "internal error");
1119 exit(1);
1123 return (0);
1125 fail:
1126 if (evd)
1127 free(evd);
1128 if (ial)
1129 free(ial);
1130 dhcp6_remove_event(ev); /* XXX */
1132 return (-1);
1135 static void
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);
1143 exit(1);
1146 ial = (struct dhcp6_list *)evd->data;
1147 dhcp6_clear_list(ial);
1148 free(ial);
1151 static struct dhcp6_serverinfo *
1152 select_server(ev)
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) {
1163 if (s->active) {
1164 dprintf(LOG_DEBUG, FNAME, "picked a server (ID: %s)",
1165 duidstr(&s->optinfo.serverID));
1166 return (s);
1170 return (NULL);
1173 static void
1174 client6_signal(sig)
1175 int sig;
1178 switch (sig) {
1179 case SIGTERM:
1180 sig_flags |= SIGF_TERM;
1181 break;
1182 case SIGHUP:
1183 sig_flags |= SIGF_HUP;
1184 break;
1188 void
1189 client6_send(ev)
1190 struct dhcp6_event *ev;
1192 struct dhcp6_if *ifp;
1193 char buf[BUFSIZ];
1194 struct sockaddr_in6 dst;
1195 struct dhcp6 *dh6;
1196 struct dhcp6_optinfo optinfo;
1197 ssize_t optlen, len;
1198 struct dhcp6_eventdata *evd;
1200 ifp = ev->ifp;
1202 dh6 = (struct dhcp6 *)buf;
1203 memset(dh6, 0, sizeof(*dh6));
1205 switch(ev->state) {
1206 case DHCP6S_SOLICIT:
1207 dh6->dh6_msgtype = DH6_SOLICIT;
1208 break;
1209 case DHCP6S_REQUEST:
1210 dh6->dh6_msgtype = DH6_REQUEST;
1211 break;
1212 case DHCP6S_RENEW:
1213 dh6->dh6_msgtype = DH6_RENEW;
1214 break;
1215 case DHCP6S_REBIND:
1216 dh6->dh6_msgtype = DH6_REBIND;
1217 break;
1218 case DHCP6S_RELEASE:
1219 dh6->dh6_msgtype = DH6_RELEASE;
1220 break;
1221 case DHCP6S_INFOREQ:
1222 dh6->dh6_msgtype = DH6_INFORM_REQ;
1223 break;
1224 default:
1225 dprintf(LOG_ERR, FNAME, "unexpected state");
1226 exit(1); /* XXX */
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;
1240 #else
1241 ev->xid = random() & DH6_XIDMASK;
1242 #endif
1243 dprintf(LOG_DEBUG, FNAME, "a new XID (%x) is generated",
1244 ev->xid);
1246 dh6->dh6_xid &= ~ntohl(DH6_XIDMASK);
1247 dh6->dh6_xid |= htonl(ev->xid);
1248 len = sizeof(*dh6);
1251 * construct options
1253 dhcp6_init_options(&optinfo);
1255 /* server ID */
1256 switch (ev->state) {
1257 case DHCP6S_REQUEST:
1258 case DHCP6S_RENEW:
1259 case DHCP6S_RELEASE:
1260 if (duidcpy(&optinfo.serverID, &ev->serverid)) {
1261 dprintf(LOG_ERR, FNAME, "failed to copy server ID");
1262 goto end;
1264 break;
1267 /* client ID */
1268 if (duidcpy(&optinfo.clientID, &client_duid)) {
1269 dprintf(LOG_ERR, FNAME, "failed to copy client ID");
1270 goto end;
1273 /* rapid commit (in Solicit only) */
1274 if (ev->state == DHCP6S_SOLICIT &&
1275 (ifp->send_flags & DHCIFF_RAPID_COMMIT)) {
1276 optinfo.rapidcommit = 1;
1279 /* elapsed time */
1280 if (ev->timeouts == 0) {
1281 gettimeofday(&ev->tv_start, NULL);
1282 optinfo.elapsed_time = 0;
1283 } else {
1284 struct timeval now, tv_diff;
1285 long et;
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.
1294 * [RFC3315 22.9.]
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;
1303 } else {
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");
1315 goto end;
1318 /* configuration information specified as event data */
1319 for (evd = TAILQ_FIRST(&ev->data_list); evd;
1320 evd = TAILQ_NEXT(evd, link)) {
1321 switch(evd->type) {
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");
1327 goto end;
1329 break;
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");
1335 goto end;
1337 break;
1338 default:
1339 dprintf(LOG_ERR, FNAME, "unexpected event data (%d)",
1340 evd->type);
1341 exit(1);
1345 /* authentication information */
1346 if (set_auth(ev, &optinfo)) {
1347 dprintf(LOG_INFO, FNAME,
1348 "failed to set authentication option");
1349 goto end;
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");
1357 goto end;
1359 len += optlen;
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)
1366 break;
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");
1374 goto end;
1376 break;
1377 default:
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));
1396 goto end;
1399 dprintf(LOG_DEBUG, FNAME, "send %s to %s",
1400 dhcp6msgstr(dh6->dh6_msgtype), addr2str((struct sockaddr *)&dst));
1402 end:
1403 dhcp6_clear_options(&optinfo);
1404 return;
1407 /* result will be a - b */
1408 static void
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)) {
1414 result->tv_sec = 0;
1415 result->tv_usec = 0;
1417 return;
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;
1424 } else
1425 result->tv_usec = a->tv_usec - b->tv_usec;
1427 return;
1430 static void
1431 client6_recv()
1433 char rbuf[BUFSIZ], cmsgbuf[BUFSIZ];
1434 struct msghdr mhdr;
1435 struct iovec iov;
1436 struct sockaddr_storage from;
1437 struct dhcp6_if *ifp;
1438 struct dhcp6opt *p, *ep;
1439 struct dhcp6_optinfo optinfo;
1440 ssize_t len;
1441 struct dhcp6 *dh6;
1442 struct cmsghdr *cm;
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));
1458 return;
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));
1470 if (pi == NULL) {
1471 dprintf(LOG_NOTICE, FNAME, "failed to get packet info");
1472 return;
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);
1478 return;
1481 if (len < sizeof(*dh6)) {
1482 dprintf(LOG_INFO, FNAME, "short packet (%d bytes)", len);
1483 return;
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);
1492 /* get options */
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");
1498 return;
1501 switch(dh6->dh6_msgtype) {
1502 case DH6_ADVERTISE:
1503 (void)client6_recvadvert(ifp, dh6, len, &optinfo);
1504 break;
1505 case DH6_REPLY:
1506 (void)client6_recvreply(ifp, dh6, len, &optinfo);
1507 break;
1508 default:
1509 dprintf(LOG_INFO, FNAME, "received an unexpected message (%s) "
1510 "from %s", dhcp6msgstr(dh6->dh6_msgtype),
1511 addr2str((struct sockaddr *)&from));
1512 break;
1515 dhcp6_clear_options(&optinfo);
1516 return;
1519 static int
1520 client6_recvadvert(ifp, dh6, len, optinfo)
1521 struct dhcp6_if *ifp;
1522 struct dhcp6 *dh6;
1523 ssize_t len;
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);
1533 if (ev == NULL) {
1534 dprintf(LOG_INFO, FNAME, "XID mismatch");
1535 return (-1);
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");
1541 return (-1);
1542 } else {
1543 dprintf(LOG_DEBUG, FNAME, "server ID: %s, pref=%d",
1544 duidstr(&optinfo->serverID),
1545 optinfo->pref);
1547 if (optinfo->clientID.duid_len == 0) {
1548 dprintf(LOG_INFO, FNAME, "no client ID option");
1549 return (-1);
1551 if (duidcmp(&optinfo->clientID, &client_duid)) {
1552 dprintf(LOG_INFO, FNAME, "client DUID mismatch");
1553 return (-1);
1556 /* validate authentication */
1557 authparam0 = *ev->authparam;
1558 if (process_auth(&authparam0, dh6, len, optinfo)) {
1559 dprintf(LOG_INFO, FNAME, "failed to process authentication");
1560 return (-1);
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
1571 * a prefix.
1573 for (evd = TAILQ_FIRST(&ev->data_list); evd;
1574 evd = TAILQ_NEXT(evd, link)) {
1575 u_int16_t stcode;
1576 char *stcodestr;
1578 switch (evd->type) {
1579 case DHCP6_EVDATA_IAPD:
1580 stcode = DH6OPT_STCODE_NOPREFIXAVAIL;
1581 stcodestr = "NoPrefixAvail";
1582 break;
1583 case DHCP6_EVDATA_IANA:
1584 stcode = DH6OPT_STCODE_NOADDRSAVAIL;
1585 stcodestr = "NoAddrsAvail";
1586 break;
1587 default:
1588 continue;
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);
1594 return (-1);
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));
1615 return (-1);
1618 /* keep the server */
1619 if ((newserver = malloc(sizeof(*newserver))) == NULL) {
1620 dprintf(LOG_WARNING, FNAME,
1621 "memory allocation failed for server");
1622 return (-1);
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");
1637 free(newserver);
1638 return (-1);
1640 ev->authparam = authparam;
1642 /* copy options */
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);
1648 free(newserver);
1649 return (-1);
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) {
1657 break;
1660 newserver->next = *sp;
1661 *sp = newserver;
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 */
1682 ev->timeouts = 0;
1683 ev->state = DHCP6S_REQUEST;
1685 free(ev->authparam);
1686 ev->authparam = newserver->authparam;
1687 newserver->authparam = NULL;
1689 client6_send(ev);
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
1700 * calculation here.
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);
1710 else
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);
1719 return (0);
1722 static struct dhcp6_serverinfo *
1723 find_server(ev, duid)
1724 struct dhcp6_event *ev;
1725 struct duid *duid;
1727 struct dhcp6_serverinfo *s;
1729 for (s = ev->servers; s; s = s->next) {
1730 if (duidcmp(&s->optinfo.serverID, duid) == 0)
1731 return (s);
1734 return (NULL);
1737 static int
1738 client6_recvreply(ifp, dh6, len, optinfo)
1739 struct dhcp6_if *ifp;
1740 struct dhcp6 *dh6;
1741 ssize_t len;
1742 struct dhcp6_optinfo *optinfo;
1744 struct dhcp6_listval *lv;
1745 struct dhcp6_event *ev;
1746 int state;
1748 /* find the corresponding event based on the received xid */
1749 ev = find_event_withid(ifp, ntohl(dh6->dh6_xid) & DH6_XIDMASK);
1750 if (ev == NULL) {
1751 dprintf(LOG_INFO, FNAME, "XID mismatch");
1752 return (-1);
1755 state = ev->state;
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");
1764 return (-1);
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");
1770 return (-1);
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");
1779 return (-1);
1781 if (duidcmp(&optinfo->clientID, &client_duid)) {
1782 dprintf(LOG_INFO, FNAME, "client DUID mismatch");
1783 return (-1);
1786 /* validate authentication */
1787 if (process_auth(ev->authparam, dh6, len, optinfo)) {
1788 dprintf(LOG_INFO, FNAME, "failed to process authentication");
1789 return (-1);
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");
1803 return (-1);
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;
1819 int i = 0;
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;
1830 int i = 0;
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;
1841 int i = 0;
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;
1852 int i = 0;
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;
1863 int i = 0;
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");
1888 } else {
1889 struct timeval tv;
1891 tv.tv_sec = (long)refreshtime;
1892 tv.tv_usec = 0;
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
1940 * by the server.
1941 * [RFC3315 Section 18.1.8]
1943 check_exit();
1946 dprintf(LOG_DEBUG, FNAME, "got an expected reply, sleeping.");
1948 if (infreq_mode) {
1949 exit_ok = 1;
1950 free_resources(NULL);
1951 unlink(pid_file);
1952 check_exit();
1954 return (0);
1957 static struct dhcp6_event *
1958 find_event_withid(ifp, xid)
1959 struct dhcp6_if *ifp;
1960 u_int32_t xid;
1962 struct dhcp6_event *ev;
1964 for (ev = TAILQ_FIRST(&ifp->event_list); ev;
1965 ev = TAILQ_NEXT(ev, link)) {
1966 if (ev->xid == xid)
1967 return (ev);
1970 return (NULL);
1973 static int
1974 process_auth(authparam, dh6, len, optinfo)
1975 struct authparam *authparam;
1976 struct dhcp6 *dh6;
1977 ssize_t len;
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 */
1986 break;
1987 case DHCP6_AUTHPROTO_DELAYED:
1988 if ((optinfo->authflags & DHCP6OPT_AUTHFLAG_NOINFO)) {
1989 dprintf(LOG_INFO, FNAME, "server did not include "
1990 "authentication information");
1991 break;
1994 if (optinfo->authalgorithm != DHCP6_AUTHALG_HMACMD5) {
1995 dprintf(LOG_INFO, FNAME, "unknown authentication "
1996 "algorithm (%d)", optinfo->authalgorithm);
1997 break;
2000 if (optinfo->authrdm != DHCP6_AUTHRDM_MONOCOUNTER) {
2001 dprintf(LOG_INFO, FNAME,"unknown RDM (%d)",
2002 optinfo->authrdm);
2003 break;
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)");
2013 } else {
2014 if (dhcp6_auth_replaycheck(optinfo->authrdm,
2015 authparam->prevrd, optinfo->authrd)) {
2016 dprintf(LOG_INFO, FNAME,
2017 "possible replay attack detected");
2018 break;
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");
2035 break;
2037 } else {
2038 key = find_key(optinfo->delayedauth_realmval,
2039 optinfo->delayedauth_realmlen,
2040 optinfo->delayedauth_keyid);
2041 if (key == NULL) {
2042 dprintf(LOG_INFO, FNAME, "failed to find key "
2043 "provided by the server (ID: %x)",
2044 optinfo->delayedauth_keyid);
2045 break;
2046 } else {
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",
2056 key->name);
2057 break;
2060 /* validate MAC */
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 "
2065 "validated");
2066 authenticated = 1;
2067 } else {
2068 dprintf(LOG_INFO, FNAME, "invalid message "
2069 "authentication");
2072 break;
2073 default:
2074 dprintf(LOG_INFO, FNAME, "server sent unsupported "
2075 "authentication protocol (%d)", optinfo->authproto);
2076 break;
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
2086 * messages.
2088 return (-1);
2090 } else {
2091 /* if authenticated, update the "previous" RD value */
2092 authparam->prevrd = optinfo->authrd;
2093 authparam->flags &= ~AUTHPARAM_FLAGS_NOPREVRD;
2096 return (0);
2099 static int
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)
2107 return (0);
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 */
2115 return (0);
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");
2126 return (-1);
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));
2138 return (-1);
2141 if (dhcp6_validate_key(authparam->key)) {
2142 dprintf(LOG_INFO, FNAME, "key %s is invalid",
2143 authparam->key->name);
2144 return (-1);
2147 if (get_rdvalue(optinfo->authrdm, &optinfo->authrd,
2148 sizeof(optinfo->authrd))) {
2149 dprintf(LOG_ERR, FNAME, "failed to get a replay "
2150 "detection value");
2151 return (-1);
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");
2161 return (-1);
2163 memcpy(optinfo->delayedauth_realmval, authparam->key->realm,
2164 optinfo->delayedauth_realmlen);
2166 break;
2167 default:
2168 dprintf(LOG_ERR, FNAME, "unsupported authentication protocol "
2169 "%d", authparam->authproto);
2170 return (-1);
2173 return (0);
2176 static void
2177 info_printf(const char *fmt, ...)
2179 va_list ap;
2180 char logbuf[LINE_MAX];
2182 va_start(ap, fmt);
2183 vsnprintf(logbuf, sizeof(logbuf), fmt, ap);
2185 dprintf(LOG_DEBUG, FNAME, "%s", logbuf);
2186 if (infreq_mode)
2187 printf("%s\n", logbuf);
2189 return;