fixes, fully translated tomato, with english dictionary and Polish translation
[tomato.git] / release / src / router / dhcpv6 / dhcp6s.c
blob494e571f46f8971e21342520ab16016104851e64
1 /* $KAME: dhcp6s.c,v 1.162 2005/10/04 11:53:32 suz 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.
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/ioctl.h>
34 #include <sys/queue.h>
35 #include <sys/uio.h>
36 #if TIME_WITH_SYS_TIME
37 # include <sys/time.h>
38 # include <time.h>
39 #else
40 # if HAVE_SYS_TIME_H
41 # include <sys/time.h>
42 # else
43 # include <time.h>
44 # endif
45 #endif
46 #include <errno.h>
48 #include <net/if.h>
49 #ifdef __FreeBSD__
50 #include <net/if_var.h>
51 #endif
53 #include <netinet/in.h>
54 #ifdef __KAME__
55 #include <netinet6/in6_var.h>
56 #endif
58 #include <arpa/inet.h>
59 #include <stdio.h>
60 #include <stdarg.h>
61 #include <syslog.h>
62 #include <stdlib.h>
63 #include <unistd.h>
64 #include <string.h>
65 #include <err.h>
66 #include <netdb.h>
67 #include <limits.h>
69 #include <dhcp6.h>
70 #include <config.h>
71 #include <common.h>
72 #include <timer.h>
73 #include <auth.h>
74 #include <base64.h>
75 #include <control.h>
76 #include <dhcp6_ctl.h>
77 #include <signal.h>
78 #include <lease.h>
80 #define DUID_FILE LOCALDBDIR "/dhcp6s_duid"
81 #define DHCP6S_CONF SYSCONFDIR "/dhcp6s.conf"
82 #define DEFAULT_KEYFILE SYSCONFDIR "/dhcp6sctlkey"
83 #define DHCP6S_PIDFILE "/var/run/dhcp6s.pid"
85 #define CTLSKEW 300
87 typedef enum { DHCP6_BINDING_IA } dhcp6_bindingtype_t;
89 struct dhcp6_binding {
90 TAILQ_ENTRY(dhcp6_binding) link;
92 dhcp6_bindingtype_t type;
94 /* identifier of the binding */
95 struct duid clientid;
96 /* additional identifiers for IA-based bindings */
97 int iatype;
98 u_int32_t iaid;
101 * configuration information of this binding,
102 * which is type-dependent.
104 union {
105 struct dhcp6_list uv_list;
106 } val;
107 #define val_list val.uv_list
109 u_int32_t duration;
110 time_t updatetime;
111 struct dhcp6_timer *timer;
113 static TAILQ_HEAD(, dhcp6_binding) dhcp6_binding_head;
115 struct relayinfo {
116 TAILQ_ENTRY(relayinfo) link;
118 u_int hcnt; /* hop count */
119 struct in6_addr linkaddr; /* link address */
120 struct in6_addr peeraddr; /* peer address */
121 struct dhcp6_vbuf relay_ifid; /* Interface ID (if provided) */
122 struct dhcp6_vbuf relay_msg; /* relay message */
124 TAILQ_HEAD(relayinfolist, relayinfo);
126 static int debug = 0;
127 static sig_atomic_t sig_flags = 0;
128 #define SIGF_TERM 0x1
130 const dhcp6_mode_t dhcp6_mode = DHCP6_MODE_SERVER;
131 char *device = NULL;
132 int ifidx;
133 int insock; /* inbound UDP port */
134 int outsock; /* outbound UDP port */
135 int ctlsock = -1; /* control TCP port */
136 char *ctladdr = DEFAULT_SERVER_CONTROL_ADDR;
137 char *ctlport = DEFAULT_SERVER_CONTROL_PORT;
139 static const struct sockaddr_in6 *sa6_any_downstream, *sa6_any_relay;
140 static struct msghdr rmh;
141 static char rdatabuf[BUFSIZ];
142 static int rmsgctllen;
143 static char *conffile = DHCP6S_CONF;
144 static char *rmsgctlbuf;
145 static struct duid server_duid;
146 static struct dhcp6_list arg_dnslist;
147 static char *ctlkeyfile = DEFAULT_KEYFILE;
148 static struct keyinfo *ctlkey = NULL;
149 static int ctldigestlen;
150 static char *pid_file = DHCP6S_PIDFILE;
152 static inline int get_val32 __P((char **, int *, u_int32_t *));
153 static inline int get_val __P((char **, int *, void *, size_t));
155 static void usage __P((void));
156 static void server6_init __P((void));
157 static void server6_mainloop __P((void));
158 static int server6_do_ctlcommand __P((char *, ssize_t));
159 static void server6_reload __P((void));
160 static void server6_stop __P((void));
161 static void server6_recv __P((int));
162 static void process_signals __P((void));
163 static void server6_signal __P((int));
164 static void free_relayinfo __P((struct relayinfo *));
165 static int process_relayforw __P((struct dhcp6 **, struct dhcp6opt **,
166 struct relayinfolist *, struct sockaddr *));
167 static int set_statelessinfo __P((int, struct dhcp6_optinfo *));
168 static int react_solicit __P((struct dhcp6_if *, struct dhcp6 *, ssize_t,
169 struct dhcp6_optinfo *, struct sockaddr *, int, struct relayinfolist *));
170 static int react_request __P((struct dhcp6_if *, struct in6_pktinfo *,
171 struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int,
172 struct relayinfolist *));
173 static int react_renew __P((struct dhcp6_if *, struct in6_pktinfo *,
174 struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int,
175 struct relayinfolist *));
176 static int react_rebind __P((struct dhcp6_if *, struct dhcp6 *, ssize_t,
177 struct dhcp6_optinfo *, struct sockaddr *, int, struct relayinfolist *));
178 static int react_release __P((struct dhcp6_if *, struct in6_pktinfo *,
179 struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int,
180 struct relayinfolist *));
181 static int react_decline __P((struct dhcp6_if *, struct in6_pktinfo *,
182 struct dhcp6 *, ssize_t, struct dhcp6_optinfo *, struct sockaddr *, int,
183 struct relayinfolist *));
184 static int react_confirm __P((struct dhcp6_if *, struct in6_pktinfo *,
185 struct dhcp6 *, ssize_t,
186 struct dhcp6_optinfo *, struct sockaddr *, int, struct relayinfolist *));
187 static int react_informreq __P((struct dhcp6_if *, struct dhcp6 *, ssize_t,
188 struct dhcp6_optinfo *, struct sockaddr *, int, struct relayinfolist *));
189 static int server6_send __P((int, struct dhcp6_if *, struct dhcp6 *,
190 struct dhcp6_optinfo *, struct sockaddr *, int, struct dhcp6_optinfo *,
191 struct relayinfolist *, struct host_conf *));
192 static int make_ia_stcode __P((int, u_int32_t, u_int16_t,
193 struct dhcp6_list *));
194 static int update_ia __P((int, struct dhcp6_listval *,
195 struct dhcp6_list *, struct dhcp6_optinfo *));
196 static int release_binding_ia __P((struct dhcp6_listval *, struct dhcp6_list *,
197 struct dhcp6_optinfo *));
198 static int decline_binding_ia __P((struct dhcp6_listval *, struct dhcp6_list *,
199 struct dhcp6_optinfo *));
200 static int make_ia __P((struct dhcp6_listval *, struct dhcp6_list *,
201 struct dhcp6_list *, struct host_conf *, int));
202 static int make_match_ia __P((struct dhcp6_listval *, struct dhcp6_list *,
203 struct dhcp6_list *));
204 static int make_iana_from_pool __P((struct dhcp6_poolspec *,
205 struct dhcp6_listval *, struct dhcp6_list *));
206 static void calc_ia_timo __P((struct dhcp6_ia *, struct dhcp6_list *,
207 struct host_conf *));
208 static void update_binding_duration __P((struct dhcp6_binding *));
209 static struct dhcp6_binding *add_binding __P((struct duid *,
210 dhcp6_bindingtype_t, int, u_int32_t, void *));
211 static struct dhcp6_binding *find_binding __P((struct duid *,
212 dhcp6_bindingtype_t, int, u_int32_t));
213 static void update_binding __P((struct dhcp6_binding *));
214 static void remove_binding __P((struct dhcp6_binding *));
215 static void free_binding __P((struct dhcp6_binding *));
216 static struct dhcp6_timer *binding_timo __P((void *));
217 static struct dhcp6_listval *find_binding_ia __P((struct dhcp6_listval *,
218 struct dhcp6_binding *));
219 static char *bindingstr __P((struct dhcp6_binding *));
220 static int process_auth __P((struct dhcp6 *, ssize_t, struct host_conf *,
221 struct dhcp6_optinfo *, struct dhcp6_optinfo *));
222 static inline char *clientstr __P((struct host_conf *, struct duid *));
225 main(argc, argv)
226 int argc;
227 char **argv;
229 int ch, pid;
230 struct in6_addr a;
231 struct dhcp6_listval *dlv;
232 char *progname;
233 FILE *pidfp;
235 if ((progname = strrchr(*argv, '/')) == NULL)
236 progname = *argv;
237 else
238 progname++;
240 TAILQ_INIT(&arg_dnslist);
241 TAILQ_INIT(&dnslist);
242 TAILQ_INIT(&dnsnamelist);
243 TAILQ_INIT(&siplist);
244 TAILQ_INIT(&sipnamelist);
245 TAILQ_INIT(&ntplist);
246 TAILQ_INIT(&nislist);
247 TAILQ_INIT(&nisnamelist);
248 TAILQ_INIT(&nisplist);
249 TAILQ_INIT(&nispnamelist);
250 TAILQ_INIT(&bcmcslist);
251 TAILQ_INIT(&bcmcsnamelist);
253 srandom(time(NULL) & getpid());
254 while ((ch = getopt(argc, argv, "c:dDfk:n:p:P:")) != -1) {
255 switch (ch) {
256 case 'c':
257 conffile = optarg;
258 break;
259 case 'd':
260 debug = 1;
261 break;
262 case 'D':
263 debug = 2;
264 break;
265 case 'f':
266 foreground++;
267 break;
268 case 'k':
269 ctlkeyfile = optarg;
270 break;
271 case 'n':
272 warnx("-n dnsserv option was obsoleted. "
273 "use configuration file.");
274 if (inet_pton(AF_INET6, optarg, &a) != 1) {
275 errx(1, "invalid DNS server %s", optarg);
276 /* NOTREACHED */
278 if ((dlv = malloc(sizeof *dlv)) == NULL) {
279 errx(1, "malloc failed for a DNS server");
280 /* NOTREACHED */
282 dlv->val_addr6 = a;
283 TAILQ_INSERT_TAIL(&arg_dnslist, dlv, link);
284 break;
285 case 'p':
286 ctlport = optarg;
287 break;
288 case 'P':
289 pid_file = optarg;
290 break;
291 default:
292 usage();
293 /* NOTREACHED */
296 argc -= optind;
297 argv += optind;
299 if (argc != 1) {
300 usage();
301 /* NOTREACHED */
303 device = argv[0];
305 if (foreground == 0)
306 openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON);
308 setloglevel(debug);
310 if (ifinit(device) == NULL)
311 exit(1);
313 if ((cfparse(conffile)) != 0) {
314 dprintf(LOG_ERR, FNAME, "failed to parse configuration file");
315 exit(1);
318 if (foreground == 0) {
319 int fd;
321 if (daemon(0, 0) < 0)
322 err(1, "daemon");
324 for (fd = 3; fd < 1024; fd++)
325 close(fd);
328 /* dump current PID */
329 pid = getpid();
330 if ((pidfp = fopen(pid_file, "w")) != NULL) {
331 fprintf(pidfp, "%d\n", pid);
332 fclose(pidfp);
335 /* prohibit a mixture of old and new style of DNS server config */
336 if (!TAILQ_EMPTY(&arg_dnslist)) {
337 if (!TAILQ_EMPTY(&dnslist)) {
338 dprintf(LOG_INFO, FNAME, "do not specify DNS servers "
339 "both by command line and by configuration file.");
340 exit(1);
342 dhcp6_move_list(&dnslist, &arg_dnslist);
343 TAILQ_INIT(&arg_dnslist);
346 server6_init();
348 server6_mainloop();
349 exit(0);
352 static void
353 usage()
355 fprintf(stderr,
356 "usage: dhcp6s [-c configfile] [-dDf] [-k ctlkeyfile] "
357 "[-p ctlport] [-P pidfile] intface\n");
358 exit(0);
361 /*------------------------------------------------------------*/
363 void
364 server6_init()
366 struct addrinfo hints;
367 struct addrinfo *res, *res2;
368 int error;
369 int on = 1;
370 struct ipv6_mreq mreq6;
371 static struct iovec iov;
372 static struct sockaddr_in6 sa6_any_downstream_storage;
373 static struct sockaddr_in6 sa6_any_relay_storage;
375 TAILQ_INIT(&dhcp6_binding_head);
376 if (lease_init() != 0) {
377 dprintf(LOG_ERR, FNAME, "failed to initialize the lease table");
378 exit(1);
381 ifidx = if_nametoindex(device);
382 if (ifidx == 0) {
383 dprintf(LOG_ERR, FNAME, "invalid interface %s", device);
384 exit(1);
387 /* get our DUID */
388 if (get_duid(DUID_FILE, &server_duid)) {
389 dprintf(LOG_ERR, FNAME, "failed to get a DUID");
390 exit(1);
393 if (dhcp6_ctl_authinit(ctlkeyfile, &ctlkey, &ctldigestlen) != 0) {
394 dprintf(LOG_NOTICE, FNAME,
395 "failed to initialize control message authentication");
396 /* run the server anyway */
399 /* initialize send/receive buffer */
400 iov.iov_base = (caddr_t)rdatabuf;
401 iov.iov_len = sizeof(rdatabuf);
402 rmh.msg_iov = &iov;
403 rmh.msg_iovlen = 1;
404 rmsgctllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
405 if ((rmsgctlbuf = (char *)malloc(rmsgctllen)) == NULL) {
406 dprintf(LOG_ERR, FNAME, "memory allocation failed");
407 exit(1);
410 /* initialize socket */
411 memset(&hints, 0, sizeof(hints));
412 hints.ai_family = AF_INET6;
413 hints.ai_socktype = SOCK_DGRAM;
414 hints.ai_protocol = IPPROTO_UDP;
415 hints.ai_flags = AI_PASSIVE;
416 error = getaddrinfo(NULL, DH6PORT_UPSTREAM, &hints, &res);
417 if (error) {
418 dprintf(LOG_ERR, FNAME, "getaddrinfo: %s",
419 gai_strerror(error));
420 exit(1);
422 insock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
423 if (insock < 0) {
424 dprintf(LOG_ERR, FNAME, "socket(insock): %s",
425 strerror(errno));
426 exit(1);
428 if (setsockopt(insock, SOL_SOCKET, SO_REUSEPORT, &on,
429 sizeof(on)) < 0) {
430 dprintf(LOG_ERR, FNAME, "setsockopt(insock, SO_REUSEPORT): %s",
431 strerror(errno));
432 exit(1);
434 if (setsockopt(insock, SOL_SOCKET, SO_REUSEADDR, &on,
435 sizeof(on)) < 0) {
436 dprintf(LOG_ERR, FNAME, "setsockopt(insock, SO_REUSEADDR): %s",
437 strerror(errno));
438 exit(1);
440 #ifdef IPV6_RECVPKTINFO
441 if (setsockopt(insock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
442 sizeof(on)) < 0) {
443 dprintf(LOG_ERR, FNAME,
444 "setsockopt(inbound, IPV6_RECVPKTINFO): %s",
445 strerror(errno));
446 exit(1);
448 #else
449 if (setsockopt(insock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
450 sizeof(on)) < 0) {
451 dprintf(LOG_ERR, FNAME,
452 "setsockopt(inbound, IPV6_PKTINFO): %s",
453 strerror(errno));
454 exit(1);
456 #endif
457 #ifdef IPV6_V6ONLY
458 if (setsockopt(insock, IPPROTO_IPV6, IPV6_V6ONLY,
459 &on, sizeof(on)) < 0) {
460 dprintf(LOG_ERR, FNAME,
461 "setsockopt(inbound, IPV6_V6ONLY): %s", strerror(errno));
462 exit(1);
464 #endif
465 if (bind(insock, res->ai_addr, res->ai_addrlen) < 0) {
466 dprintf(LOG_ERR, FNAME, "bind(insock): %s", strerror(errno));
467 exit(1);
469 freeaddrinfo(res);
471 hints.ai_flags = 0;
472 error = getaddrinfo(DH6ADDR_ALLAGENT, DH6PORT_UPSTREAM, &hints, &res2);
473 if (error) {
474 dprintf(LOG_ERR, FNAME, "getaddrinfo: %s",
475 gai_strerror(error));
476 exit(1);
478 memset(&mreq6, 0, sizeof(mreq6));
479 mreq6.ipv6mr_interface = ifidx;
480 memcpy(&mreq6.ipv6mr_multiaddr,
481 &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr,
482 sizeof(mreq6.ipv6mr_multiaddr));
483 if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
484 &mreq6, sizeof(mreq6))) {
485 dprintf(LOG_ERR, FNAME,
486 "setsockopt(insock, IPV6_JOIN_GROUP): %s",
487 strerror(errno));
488 exit(1);
490 freeaddrinfo(res2);
492 hints.ai_flags = 0;
493 error = getaddrinfo(DH6ADDR_ALLSERVER, DH6PORT_UPSTREAM,
494 &hints, &res2);
495 if (error) {
496 dprintf(LOG_ERR, FNAME, "getaddrinfo: %s",
497 gai_strerror(error));
498 exit(1);
500 memset(&mreq6, 0, sizeof(mreq6));
501 mreq6.ipv6mr_interface = ifidx;
502 memcpy(&mreq6.ipv6mr_multiaddr,
503 &((struct sockaddr_in6 *)res2->ai_addr)->sin6_addr,
504 sizeof(mreq6.ipv6mr_multiaddr));
505 if (setsockopt(insock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
506 &mreq6, sizeof(mreq6))) {
507 dprintf(LOG_ERR, FNAME,
508 "setsockopt(insock, IPV6_JOIN_GROUP): %s",
509 strerror(errno));
510 exit(1);
512 freeaddrinfo(res2);
514 hints.ai_flags = 0;
515 error = getaddrinfo(NULL, DH6PORT_DOWNSTREAM, &hints, &res);
516 if (error) {
517 dprintf(LOG_ERR, FNAME, "getaddrinfo: %s",
518 gai_strerror(error));
519 exit(1);
521 outsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
522 if (outsock < 0) {
523 dprintf(LOG_ERR, FNAME, "socket(outsock): %s",
524 strerror(errno));
525 exit(1);
527 /* set outgoing interface of multicast packets for DHCP reconfig */
528 if (setsockopt(outsock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
529 &ifidx, sizeof(ifidx)) < 0) {
530 dprintf(LOG_ERR, FNAME,
531 "setsockopt(outsock, IPV6_MULTICAST_IF): %s",
532 strerror(errno));
533 exit(1);
535 #if !defined(__linux__) && !defined(__sun__)
536 /* make the socket write-only */
537 if (shutdown(outsock, 0)) {
538 dprintf(LOG_ERR, FNAME, "shutdown(outbound, 0): %s",
539 strerror(errno));
540 exit(1);
542 #endif
543 freeaddrinfo(res);
545 memset(&hints, 0, sizeof(hints));
546 hints.ai_family = AF_INET6;
547 hints.ai_socktype = SOCK_DGRAM;
548 hints.ai_protocol = IPPROTO_UDP;
549 error = getaddrinfo("::", DH6PORT_DOWNSTREAM, &hints, &res);
550 if (error) {
551 dprintf(LOG_ERR, FNAME, "getaddrinfo: %s",
552 gai_strerror(error));
553 exit(1);
555 memcpy(&sa6_any_downstream_storage, res->ai_addr, res->ai_addrlen);
556 sa6_any_downstream =
557 (const struct sockaddr_in6*)&sa6_any_downstream_storage;
558 freeaddrinfo(res);
560 memset(&hints, 0, sizeof(hints));
561 hints.ai_family = AF_INET6;
562 hints.ai_socktype = SOCK_DGRAM;
563 hints.ai_protocol = IPPROTO_UDP;
564 error = getaddrinfo("::", DH6PORT_UPSTREAM, &hints, &res);
565 if (error) {
566 dprintf(LOG_ERR, FNAME, "getaddrinfo: %s",
567 gai_strerror(error));
568 exit(1);
570 memcpy(&sa6_any_relay_storage, res->ai_addr, res->ai_addrlen);
571 sa6_any_relay =
572 (const struct sockaddr_in6*)&sa6_any_relay_storage;
573 freeaddrinfo(res);
575 /* set up control socket */
576 if (ctlkey == NULL)
577 dprintf(LOG_NOTICE, FNAME, "skip opening control port");
578 else if (dhcp6_ctl_init(ctladdr, ctlport,
579 DHCP6CTL_DEF_COMMANDQUEUELEN, &ctlsock)) {
580 dprintf(LOG_ERR, FNAME,
581 "failed to initialize control channel");
582 exit(1);
585 if (signal(SIGTERM, server6_signal) == SIG_ERR) {
586 dprintf(LOG_WARNING, FNAME, "failed to set signal: %s",
587 strerror(errno));
588 exit(1);
590 return;
593 static void
594 process_signals()
596 if ((sig_flags & SIGF_TERM)) {
597 unlink(pid_file);
598 exit(0);
602 static void
603 server6_mainloop()
605 struct timeval *w;
606 int ret;
607 fd_set r;
608 int maxsock;
611 while (1) {
612 if (sig_flags)
613 process_signals();
615 w = dhcp6_check_timer();
617 FD_ZERO(&r);
618 FD_SET(insock, &r);
619 maxsock = insock;
620 if (ctlsock >= 0) {
621 FD_SET(ctlsock, &r);
622 maxsock = (insock > ctlsock) ? insock : ctlsock;
623 (void)dhcp6_ctl_setreadfds(&r, &maxsock);
626 ret = select(maxsock + 1, &r, NULL, NULL, w);
627 switch (ret) {
628 case -1:
629 if (errno != EINTR) {
630 dprintf(LOG_ERR, FNAME, "select: %s",
631 strerror(errno));
632 exit(1);
634 continue;
635 case 0: /* timeout */
636 break;
637 default:
638 break;
641 if (FD_ISSET(insock, &r))
642 server6_recv(insock);
643 if (ctlsock >= 0) {
644 if (FD_ISSET(ctlsock, &r)) {
645 (void)dhcp6_ctl_acceptcommand(ctlsock,
646 server6_do_ctlcommand);
648 (void)dhcp6_ctl_readcommand(&r);
653 static inline int
654 get_val32(bpp, lenp, valp)
655 char **bpp;
656 int *lenp;
657 u_int32_t *valp;
659 char *bp = *bpp;
660 int len = *lenp;
661 u_int32_t i32;
663 if (len < sizeof(*valp))
664 return (-1);
666 memcpy(&i32, bp, sizeof(i32));
667 *valp = ntohl(i32);
669 *bpp = bp + sizeof(*valp);
670 *lenp = len - sizeof(*valp);
672 return (0);
675 static inline int
676 get_val(bpp, lenp, valp, vallen)
677 char **bpp;
678 int *lenp;
679 void *valp;
680 size_t vallen;
682 char *bp = *bpp;
683 int len = *lenp;
685 if (len < vallen)
686 return (-1);
688 memcpy(valp, bp, vallen);
690 *bpp = bp + vallen;
691 *lenp = len - vallen;
693 return (0);
696 static int
697 server6_do_ctlcommand(buf, len)
698 char *buf;
699 ssize_t len;
701 struct dhcp6ctl *ctlhead;
702 struct dhcp6ctl_iaspec iaspec;
703 u_int16_t command, version;
704 u_int32_t p32, iaid, duidlen, ts, ts0;
705 struct duid duid;
706 struct dhcp6_binding *binding;
707 int commandlen;
708 char *bp;
709 time_t now;
711 ctlhead = (struct dhcp6ctl *)buf;
713 command = ntohs(ctlhead->command);
714 commandlen = (int)(ntohs(ctlhead->len));
715 version = ntohs(ctlhead->version);
716 if (len != sizeof(struct dhcp6ctl) + commandlen) {
717 dprintf(LOG_ERR, FNAME,
718 "assumption failure: command length mismatch");
719 return (DHCP6CTL_R_FAILURE);
722 /* replay protection and message authentication */
723 if ((now = time(NULL)) < 0) {
724 dprintf(LOG_ERR, FNAME, "failed to get current time: %s",
725 strerror(errno));
726 return (DHCP6CTL_R_FAILURE);
728 ts0 = (u_int32_t)now;
729 ts = ntohl(ctlhead->timestamp);
730 if (ts + CTLSKEW < ts0 || (ts - CTLSKEW) > ts0) {
731 dprintf(LOG_INFO, FNAME, "timestamp is out of range");
732 return (DHCP6CTL_R_FAILURE);
735 if (ctlkey == NULL) { /* should not happen!! */
736 dprintf(LOG_ERR, FNAME, "no secret key for control channel");
737 return (DHCP6CTL_R_FAILURE);
739 if (dhcp6_verify_mac(buf, len, DHCP6CTL_AUTHPROTO_UNDEF,
740 DHCP6CTL_AUTHALG_HMACMD5, sizeof(*ctlhead), ctlkey) != 0) {
741 dprintf(LOG_INFO, FNAME, "authentication failure");
742 return (DHCP6CTL_R_FAILURE);
745 bp = buf + sizeof(*ctlhead) + ctldigestlen;
746 commandlen -= ctldigestlen;
748 if (version > DHCP6CTL_VERSION) {
749 dprintf(LOG_INFO, FNAME, "unsupported version: %d", version);
750 return (DHCP6CTL_R_FAILURE);
753 switch (command) {
754 case DHCP6CTL_COMMAND_RELOAD:
755 if (commandlen != 0) {
756 dprintf(LOG_INFO, FNAME, "invalid command length "
757 "for reload: %d", commandlen);
758 return (DHCP6CTL_R_DONE);
760 server6_reload();
761 break;
762 case DHCP6CTL_COMMAND_STOP:
763 if (commandlen != 0) {
764 dprintf(LOG_INFO, FNAME, "invalid command length "
765 "for stop: %d", commandlen);
766 return (DHCP6CTL_R_DONE);
768 server6_stop();
769 break;
770 case DHCP6CTL_COMMAND_REMOVE:
771 if (get_val32(&bp, &commandlen, &p32))
772 return (DHCP6CTL_R_FAILURE);
773 if (p32 != DHCP6CTL_BINDING) {
774 dprintf(LOG_INFO, FNAME,
775 "unknown remove target: %ul", p32);
776 return (DHCP6CTL_R_FAILURE);
779 if (get_val32(&bp, &commandlen, &p32))
780 return (DHCP6CTL_R_FAILURE);
781 if (p32 != DHCP6CTL_BINDING_IA) {
782 dprintf(LOG_INFO, FNAME, "unknown binding type: %ul",
783 p32);
784 return (DHCP6CTL_R_FAILURE);
787 if (get_val(&bp, &commandlen, &iaspec, sizeof(iaspec)))
788 return (DHCP6CTL_R_FAILURE);
789 if (ntohl(iaspec.type) != DHCP6CTL_IA_PD &&
790 ntohl(iaspec.type) != DHCP6CTL_IA_NA) {
791 dprintf(LOG_INFO, FNAME, "unknown IA type: %ul",
792 ntohl(iaspec.type));
793 return (DHCP6CTL_R_FAILURE);
795 iaid = ntohl(iaspec.id);
796 duidlen = ntohl(iaspec.duidlen);
798 if (duidlen > commandlen) {
799 dprintf(LOG_INFO, FNAME, "DUID length mismatch");
800 return (DHCP6CTL_R_FAILURE);
803 duid.duid_len = (size_t)duidlen;
804 duid.duid_id = bp;
806 binding = find_binding(&duid, DHCP6_BINDING_IA,
807 DHCP6_LISTVAL_IAPD, iaid);
808 if (binding == NULL) {
809 binding = find_binding(&duid, DHCP6_BINDING_IA,
810 DHCP6_LISTVAL_IANA, iaid);
811 if (binding == NULL) {
812 dprintf(LOG_INFO, FNAME, "no such binding");
813 return (DHCP6CTL_R_FAILURE);
816 remove_binding(binding);
818 break;
819 default:
820 dprintf(LOG_INFO, FNAME,
821 "unknown control command: %d (len=%d)",
822 (int)command, commandlen);
823 return (DHCP6CTL_R_FAILURE);
826 return (DHCP6CTL_R_DONE);
829 static void
830 server6_reload()
832 /* reload the configuration file */
833 if (cfparse(conffile) != 0) {
834 dprintf(LOG_WARNING, FNAME,
835 "failed to reload configuration file");
836 return;
839 dprintf(LOG_NOTICE, FNAME, "server reloaded");
841 return;
844 static void
845 server6_stop()
847 /* Right now, we simply stop running */
849 dprintf(LOG_NOTICE, FNAME, "exiting");
851 exit (0);
854 static void
855 server6_recv(s)
856 int s;
858 ssize_t len;
859 struct sockaddr_storage from;
860 int fromlen;
861 struct msghdr mhdr;
862 struct iovec iov;
863 char cmsgbuf[BUFSIZ];
864 struct cmsghdr *cm;
865 struct in6_pktinfo *pi = NULL;
866 struct dhcp6_if *ifp;
867 struct dhcp6 *dh6;
868 struct dhcp6_optinfo optinfo;
869 struct dhcp6opt *optend;
870 struct relayinfolist relayinfohead;
871 struct relayinfo *relayinfo;
873 TAILQ_INIT(&relayinfohead);
875 memset(&iov, 0, sizeof(iov));
876 memset(&mhdr, 0, sizeof(mhdr));
878 iov.iov_base = rdatabuf;
879 iov.iov_len = sizeof(rdatabuf);
880 mhdr.msg_name = &from;
881 mhdr.msg_namelen = sizeof(from);
882 mhdr.msg_iov = &iov;
883 mhdr.msg_iovlen = 1;
884 mhdr.msg_control = (caddr_t)cmsgbuf;
885 mhdr.msg_controllen = sizeof(cmsgbuf);
887 if ((len = recvmsg(insock, &mhdr, 0)) < 0) {
888 dprintf(LOG_ERR, FNAME, "recvmsg: %s", strerror(errno));
889 return;
891 fromlen = mhdr.msg_namelen;
893 for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&mhdr); cm;
894 cm = (struct cmsghdr *)CMSG_NXTHDR(&mhdr, cm)) {
895 if (cm->cmsg_level == IPPROTO_IPV6 &&
896 cm->cmsg_type == IPV6_PKTINFO &&
897 cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
898 pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
901 if (pi == NULL) {
902 dprintf(LOG_NOTICE, FNAME, "failed to get packet info");
903 return;
906 * DHCPv6 server may receive a DHCPv6 packet from a non-listening
907 * interface, when a DHCPv6 relay agent is running on that interface.
908 * This check prevents such reception.
910 if (pi->ipi6_ifindex != ifidx)
911 return;
912 if ((ifp = find_ifconfbyid((unsigned int)pi->ipi6_ifindex)) == NULL) {
913 dprintf(LOG_INFO, FNAME, "unexpected interface (%d)",
914 (unsigned int)pi->ipi6_ifindex);
915 return;
918 dh6 = (struct dhcp6 *)rdatabuf;
920 if (len < sizeof(*dh6)) {
921 dprintf(LOG_INFO, FNAME, "short packet (%d bytes)", len);
922 return;
925 dprintf(LOG_DEBUG, FNAME, "received %s from %s",
926 dhcp6msgstr(dh6->dh6_msgtype),
927 addr2str((struct sockaddr *)&from));
930 * A server MUST discard any Solicit, Confirm, Rebind or
931 * Information-request messages it receives with a unicast
932 * destination address.
933 * [RFC3315 Section 15.]
935 if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) &&
936 (dh6->dh6_msgtype == DH6_SOLICIT ||
937 dh6->dh6_msgtype == DH6_CONFIRM ||
938 dh6->dh6_msgtype == DH6_REBIND ||
939 dh6->dh6_msgtype == DH6_INFORM_REQ)) {
940 dprintf(LOG_INFO, FNAME, "invalid unicast message");
941 return;
945 * A server never receives a relay reply message. Since relay
946 * replay messages will annoy option parser below, we explicitly
947 * reject them here.
949 if (dh6->dh6_msgtype == DH6_RELAY_REPLY) {
950 dprintf(LOG_INFO, FNAME, "relay reply message from %s",
951 addr2str((struct sockaddr *)&from));
952 return;
956 optend = (struct dhcp6opt *)(rdatabuf + len);
957 if (dh6->dh6_msgtype == DH6_RELAY_FORW) {
958 if (process_relayforw(&dh6, &optend, &relayinfohead,
959 (struct sockaddr *)&from)) {
960 goto end;
962 /* dh6 and optend should have been updated. */
963 len = (ssize_t)((char *)optend - (char *)dh6);
967 * parse and validate options in the message
969 dhcp6_init_options(&optinfo);
970 if (dhcp6_get_options((struct dhcp6opt *)(dh6 + 1),
971 optend, &optinfo) < 0) {
972 dprintf(LOG_INFO, FNAME, "failed to parse options");
973 goto end;
976 switch (dh6->dh6_msgtype) {
977 case DH6_SOLICIT:
978 (void)react_solicit(ifp, dh6, len, &optinfo,
979 (struct sockaddr *)&from, fromlen, &relayinfohead);
980 break;
981 case DH6_REQUEST:
982 (void)react_request(ifp, pi, dh6, len, &optinfo,
983 (struct sockaddr *)&from, fromlen, &relayinfohead);
984 break;
985 case DH6_RENEW:
986 (void)react_renew(ifp, pi, dh6, len, &optinfo,
987 (struct sockaddr *)&from, fromlen, &relayinfohead);
988 break;
989 case DH6_REBIND:
990 (void)react_rebind(ifp, dh6, len, &optinfo,
991 (struct sockaddr *)&from, fromlen, &relayinfohead);
992 break;
993 case DH6_RELEASE:
994 (void)react_release(ifp, pi, dh6, len, &optinfo,
995 (struct sockaddr *)&from, fromlen, &relayinfohead);
996 break;
997 case DH6_DECLINE:
998 (void)react_decline(ifp, pi, dh6, len, &optinfo,
999 (struct sockaddr *)&from, fromlen, &relayinfohead);
1000 break;
1001 case DH6_CONFIRM:
1002 (void)react_confirm(ifp, pi, dh6, len, &optinfo,
1003 (struct sockaddr *)&from, fromlen, &relayinfohead);
1004 break;
1005 case DH6_INFORM_REQ:
1006 (void)react_informreq(ifp, dh6, len, &optinfo,
1007 (struct sockaddr *)&from, fromlen, &relayinfohead);
1008 break;
1009 default:
1010 dprintf(LOG_INFO, FNAME, "unknown or unsupported msgtype (%s)",
1011 dhcp6msgstr(dh6->dh6_msgtype));
1012 break;
1015 dhcp6_clear_options(&optinfo);
1017 end:
1018 while ((relayinfo = TAILQ_FIRST(&relayinfohead)) != NULL) {
1019 TAILQ_REMOVE(&relayinfohead, relayinfo, link);
1020 free_relayinfo(relayinfo);
1023 return;
1026 static void
1027 free_relayinfo(relayinfo)
1028 struct relayinfo *relayinfo;
1030 if (relayinfo->relay_ifid.dv_buf)
1031 dhcp6_vbuf_free(&relayinfo->relay_ifid);
1033 if (relayinfo->relay_msg.dv_buf)
1034 dhcp6_vbuf_free(&relayinfo->relay_msg);
1036 free(relayinfo);
1039 static int
1040 process_relayforw(dh6p, optendp, relayinfohead, from)
1041 struct dhcp6 **dh6p;
1042 struct dhcp6opt **optendp;
1043 struct relayinfolist *relayinfohead;
1044 struct sockaddr *from;
1046 struct dhcp6_relay *dh6relay = (struct dhcp6_relay *)*dh6p;
1047 struct dhcp6opt *optend = *optendp;
1048 struct relayinfo *relayinfo;
1049 struct dhcp6_optinfo optinfo;
1050 int len;
1052 again:
1053 len = (void *)optend - (void *)dh6relay;
1054 if (len < sizeof (*dh6relay)) {
1055 dprintf(LOG_INFO, FNAME, "short relay message from %s",
1056 addr2str(from));
1057 return (-1);
1059 dprintf(LOG_DEBUG, FNAME,
1060 "dhcp6 relay: hop=%d, linkaddr=%s, peeraddr=%s",
1061 dh6relay->dh6relay_hcnt,
1062 in6addr2str(&dh6relay->dh6relay_linkaddr, 0),
1063 in6addr2str(&dh6relay->dh6relay_peeraddr, 0));
1066 * parse and validate options in the relay forward message.
1068 dhcp6_init_options(&optinfo);
1069 if (dhcp6_get_options((struct dhcp6opt *)(dh6relay + 1),
1070 optend, &optinfo) < 0) {
1071 dprintf(LOG_INFO, FNAME, "failed to parse options");
1072 return (-1);
1075 /* A relay forward message must include a relay message option */
1076 if (optinfo.relaymsg_msg == NULL) {
1077 dprintf(LOG_INFO, FNAME, "relay forward from %s "
1078 "without a relay message", addr2str(from));
1079 return (-1);
1082 /* relay message must contain a DHCPv6 message. */
1083 len = optinfo.relaymsg_len;
1084 if (len < sizeof (struct dhcp6)) {
1085 dprintf(LOG_INFO, FNAME,
1086 "short packet (%d bytes) in relay message", len);
1087 return (-1);
1090 if ((relayinfo = malloc(sizeof (*relayinfo))) == NULL) {
1091 dprintf(LOG_ERR, FNAME, "failed to allocate relay info");
1092 return (-1);
1094 memset(relayinfo, 0, sizeof (*relayinfo));
1096 relayinfo->hcnt = dh6relay->dh6relay_hcnt;
1097 memcpy(&relayinfo->linkaddr, &dh6relay->dh6relay_linkaddr,
1098 sizeof (relayinfo->linkaddr));
1099 memcpy(&relayinfo->peeraddr, &dh6relay->dh6relay_peeraddr,
1100 sizeof (relayinfo->peeraddr));
1102 if (dhcp6_vbuf_copy(&relayinfo->relay_msg, &optinfo.relay_msg))
1103 goto fail;
1104 if (optinfo.ifidopt_id &&
1105 dhcp6_vbuf_copy(&relayinfo->relay_ifid, &optinfo.ifidopt)) {
1106 goto fail;
1109 TAILQ_INSERT_HEAD(relayinfohead, relayinfo, link);
1111 dhcp6_clear_options(&optinfo);
1113 optend = (struct dhcp6opt *)(relayinfo->relay_msg.dv_buf + len);
1114 dh6relay = (struct dhcp6_relay *)relayinfo->relay_msg.dv_buf;
1116 if (dh6relay->dh6relay_msgtype != DH6_RELAY_FORW) {
1117 *dh6p = (struct dhcp6 *)dh6relay;
1118 *optendp = optend;
1119 return (0);
1122 goto again;
1124 fail:
1125 free_relayinfo(relayinfo);
1126 dhcp6_clear_options(&optinfo);
1128 return (-1);
1132 * Set stateless configuration information to a option structure.
1133 * It is the caller's responsibility to deal with error cases.
1135 static int
1136 set_statelessinfo(type, optinfo)
1137 int type;
1138 struct dhcp6_optinfo *optinfo;
1140 /* SIP domain name */
1141 if (dhcp6_copy_list(&optinfo->sipname_list, &sipnamelist)) {
1142 dprintf(LOG_ERR, FNAME,
1143 "failed to copy SIP domain list");
1144 return (-1);
1147 /* SIP server */
1148 if (dhcp6_copy_list(&optinfo->sip_list, &siplist)) {
1149 dprintf(LOG_ERR, FNAME, "failed to copy SIP servers");
1150 return (-1);
1153 /* DNS server */
1154 if (dhcp6_copy_list(&optinfo->dns_list, &dnslist)) {
1155 dprintf(LOG_ERR, FNAME, "failed to copy DNS servers");
1156 return (-1);
1159 /* DNS search list */
1160 if (dhcp6_copy_list(&optinfo->dnsname_list, &dnsnamelist)) {
1161 dprintf(LOG_ERR, FNAME, "failed to copy DNS search list");
1162 return (-1);
1165 /* NTP server */
1166 if (dhcp6_copy_list(&optinfo->ntp_list, &ntplist)) {
1167 dprintf(LOG_ERR, FNAME, "failed to copy NTP servers");
1168 return (-1);
1171 /* NIS domain name */
1172 if (dhcp6_copy_list(&optinfo->nisname_list, &nisnamelist)) {
1173 dprintf(LOG_ERR, FNAME,
1174 "failed to copy NIS domain list");
1175 return (-1);
1178 /* NIS server */
1179 if (dhcp6_copy_list(&optinfo->nis_list, &nislist)) {
1180 dprintf(LOG_ERR, FNAME, "failed to copy NIS servers");
1181 return (-1);
1184 /* NIS+ domain name */
1185 if (dhcp6_copy_list(&optinfo->nispname_list, &nispnamelist)) {
1186 dprintf(LOG_ERR, FNAME,
1187 "failed to copy NIS+ domain list");
1188 return (-1);
1191 /* NIS+ server */
1192 if (dhcp6_copy_list(&optinfo->nisp_list, &nisplist)) {
1193 dprintf(LOG_ERR, FNAME, "failed to copy NIS+ servers");
1194 return (-1);
1197 /* BCMCS domain name */
1198 if (dhcp6_copy_list(&optinfo->bcmcsname_list, &bcmcsnamelist)) {
1199 dprintf(LOG_ERR, FNAME,
1200 "failed to copy BCMCS domain list");
1201 return (-1);
1204 /* BCMCS server */
1205 if (dhcp6_copy_list(&optinfo->bcmcs_list, &bcmcslist)) {
1206 dprintf(LOG_ERR, FNAME, "failed to copy BCMCS servers");
1207 return (-1);
1211 * Information refresh time. Only include in a response to
1212 * an Information-request message.
1214 if (type == DH6_INFORM_REQ &&
1215 optrefreshtime != DH6OPT_REFRESHTIME_UNDEF) {
1216 optinfo->refreshtime = (int64_t)optrefreshtime;
1219 return (0);
1222 static int
1223 react_solicit(ifp, dh6, len, optinfo, from, fromlen, relayinfohead)
1224 struct dhcp6_if *ifp;
1225 struct dhcp6 *dh6;
1226 ssize_t len;
1227 struct dhcp6_optinfo *optinfo;
1228 struct sockaddr *from;
1229 int fromlen;
1230 struct relayinfolist *relayinfohead;
1232 struct dhcp6_optinfo roptinfo;
1233 struct host_conf *client_conf;
1234 int resptype, do_binding = 0, error;
1237 * Servers MUST discard any Solicit messages that do not include a
1238 * Client Identifier option.
1239 * [RFC3315 Section 15.2]
1241 if (optinfo->clientID.duid_len == 0) {
1242 dprintf(LOG_INFO, FNAME, "no client ID option");
1243 return (-1);
1244 } else {
1245 dprintf(LOG_DEBUG, FNAME, "client ID %s",
1246 duidstr(&optinfo->clientID));
1250 * Servers MUST discard any Solicit messages that do include a
1251 * Server Identifier option.
1252 * [RFC3315 Section 15.2]
1254 if (optinfo->serverID.duid_len) {
1255 dprintf(LOG_INFO, FNAME, "server ID option found");
1256 return (-1);
1259 /* get per-host configuration for the client, if any. */
1260 if ((client_conf = find_hostconf(&optinfo->clientID))) {
1261 dprintf(LOG_DEBUG, FNAME, "found a host configuration for %s",
1262 client_conf->name);
1266 * configure necessary options based on the options in solicit.
1268 dhcp6_init_options(&roptinfo);
1270 /* process authentication */
1271 if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) {
1272 dprintf(LOG_INFO, FNAME, "failed to process authentication "
1273 "information for %s",
1274 clientstr(client_conf, &optinfo->clientID));
1275 goto fail;
1278 /* server identifier option */
1279 if (duidcpy(&roptinfo.serverID, &server_duid)) {
1280 dprintf(LOG_ERR, FNAME, "failed to copy server ID");
1281 goto fail;
1284 /* copy client information back */
1285 if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) {
1286 dprintf(LOG_ERR, FNAME, "failed to copy client ID");
1287 goto fail;
1290 /* preference (if configured) */
1291 if (ifp->server_pref != DH6OPT_PREF_UNDEF)
1292 roptinfo.pref = ifp->server_pref;
1294 /* add other configuration information */
1295 if (set_statelessinfo(DH6_SOLICIT, &roptinfo)) {
1296 dprintf(LOG_ERR, FNAME,
1297 "failed to set other stateless information");
1298 goto fail;
1302 * see if we have information for requested options, and if so,
1303 * configure corresponding options.
1305 if (optinfo->rapidcommit && (ifp->allow_flags & DHCIFF_RAPID_COMMIT))
1306 do_binding = 1;
1309 * The delegating router MUST include an IA_PD option, identifying any
1310 * prefix(es) that the delegating router will delegate to the
1311 * requesting router. [RFC3633 Section 11.2]
1313 if (!TAILQ_EMPTY(&optinfo->iapd_list)) {
1314 int found = 0;
1315 struct dhcp6_list conflist;
1316 struct dhcp6_listval *iapd;
1318 TAILQ_INIT(&conflist);
1320 /* make a local copy of the configured prefixes */
1321 if (client_conf &&
1322 dhcp6_copy_list(&conflist, &client_conf->prefix_list)) {
1323 dprintf(LOG_NOTICE, FNAME,
1324 "failed to make local data");
1325 goto fail;
1328 for (iapd = TAILQ_FIRST(&optinfo->iapd_list); iapd;
1329 iapd = TAILQ_NEXT(iapd, link)) {
1331 * find an appropriate prefix for each IA_PD,
1332 * removing the adopted prefixes from the list.
1333 * (dhcp6s cannot create IAs without client config)
1335 if (client_conf &&
1336 make_ia(iapd, &conflist, &roptinfo.iapd_list,
1337 client_conf, do_binding) > 0)
1338 found = 1;
1341 dhcp6_clear_list(&conflist);
1343 if (!found) {
1345 * If the delegating router will not assign any
1346 * prefixes to any IA_PDs in a subsequent Request from
1347 * the requesting router, the delegating router MUST
1348 * send an Advertise message to the requesting router
1349 * that includes a Status Code option with code
1350 * NoPrefixAvail.
1351 * [dhcpv6-opt-prefix-delegation-01 Section 10.2]
1353 u_int16_t stcode = DH6OPT_STCODE_NOPREFIXAVAIL;
1355 if (dhcp6_add_listval(&roptinfo.stcode_list,
1356 DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL)
1357 goto fail;
1361 if (!TAILQ_EMPTY(&optinfo->iana_list)) {
1362 int found = 0;
1363 struct dhcp6_list conflist;
1364 struct dhcp6_listval *iana;
1366 if (client_conf == NULL && ifp->pool.name) {
1367 if ((client_conf = create_dynamic_hostconf(&optinfo->clientID,
1368 &ifp->pool)) == NULL)
1369 dprintf(LOG_NOTICE, FNAME,
1370 "failed to make host configuration");
1372 TAILQ_INIT(&conflist);
1374 /* make a local copy of the configured addresses */
1375 if (client_conf &&
1376 dhcp6_copy_list(&conflist, &client_conf->addr_list)) {
1377 dprintf(LOG_NOTICE, FNAME,
1378 "failed to make local data");
1379 goto fail;
1382 for (iana = TAILQ_FIRST(&optinfo->iana_list); iana;
1383 iana = TAILQ_NEXT(iana, link)) {
1385 * find an appropriate address for each IA_NA,
1386 * removing the adopted addresses from the list.
1387 * (dhcp6s cannot create IAs without client config)
1389 if (client_conf &&
1390 make_ia(iana, &conflist, &roptinfo.iana_list,
1391 client_conf, do_binding) > 0)
1392 found = 1;
1395 dhcp6_clear_list(&conflist);
1397 if (!found) {
1398 u_int16_t stcode = DH6OPT_STCODE_NOADDRSAVAIL;
1400 if (dhcp6_add_listval(&roptinfo.stcode_list,
1401 DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL)
1402 goto fail;
1406 if (optinfo->rapidcommit && (ifp->allow_flags & DHCIFF_RAPID_COMMIT)) {
1408 * If the client has included a Rapid Commit option and the
1409 * server has been configured to respond with committed address
1410 * assignments and other resources, responds to the Solicit
1411 * with a Reply message.
1412 * [RFC3315 Section 17.2.1]
1414 roptinfo.rapidcommit = 1;
1415 resptype = DH6_REPLY;
1416 } else
1417 resptype = DH6_ADVERTISE;
1419 error = server6_send(resptype, ifp, dh6, optinfo, from, fromlen,
1420 &roptinfo, relayinfohead, client_conf);
1421 dhcp6_clear_options(&roptinfo);
1422 return (error);
1424 fail:
1425 dhcp6_clear_options(&roptinfo);
1426 return (-1);
1429 static int
1430 react_request(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead)
1431 struct dhcp6_if *ifp;
1432 struct in6_pktinfo *pi;
1433 struct dhcp6 *dh6;
1434 ssize_t len;
1435 struct dhcp6_optinfo *optinfo;
1436 struct sockaddr *from;
1437 int fromlen;
1438 struct relayinfolist *relayinfohead;
1440 struct dhcp6_optinfo roptinfo;
1441 struct host_conf *client_conf;
1443 /* message validation according to Section 15.4 of RFC3315 */
1445 /* the message must include a Server Identifier option */
1446 if (optinfo->serverID.duid_len == 0) {
1447 dprintf(LOG_INFO, FNAME, "no server ID option");
1448 return (-1);
1450 /* the contents of the Server Identifier option must match ours */
1451 if (duidcmp(&optinfo->serverID, &server_duid)) {
1452 dprintf(LOG_INFO, FNAME, "server ID mismatch");
1453 return (-1);
1455 /* the message must include a Client Identifier option */
1456 if (optinfo->clientID.duid_len == 0) {
1457 dprintf(LOG_INFO, FNAME, "no client ID option");
1458 return (-1);
1462 * configure necessary options based on the options in request.
1464 dhcp6_init_options(&roptinfo);
1466 /* server identifier option */
1467 if (duidcpy(&roptinfo.serverID, &server_duid)) {
1468 dprintf(LOG_ERR, FNAME, "failed to copy server ID");
1469 goto fail;
1471 /* copy client information back */
1472 if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) {
1473 dprintf(LOG_ERR, FNAME, "failed to copy client ID");
1474 goto fail;
1477 /* get per-host configuration for the client, if any. */
1478 if ((client_conf = find_hostconf(&optinfo->clientID))) {
1479 dprintf(LOG_DEBUG, FNAME,
1480 "found a host configuration named %s", client_conf->name);
1483 /* process authentication */
1484 if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) {
1485 dprintf(LOG_INFO, FNAME, "failed to process authentication "
1486 "information for %s",
1487 clientstr(client_conf, &optinfo->clientID));
1488 goto fail;
1492 * When the server receives a Request message via unicast from a
1493 * client to which the server has not sent a unicast option, the server
1494 * discards the Request message and responds with a Reply message
1495 * containing a Status Code option with value UseMulticast, a Server
1496 * Identifier option containing the server's DUID, the Client
1497 * Identifier option from the client message and no other options.
1498 * [RFC3315 18.2.1]
1499 * (Our current implementation never sends a unicast option.)
1500 * Note: a request message encapsulated in a relay server option can be
1501 * unicasted.
1503 if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) &&
1504 TAILQ_EMPTY(relayinfohead)) {
1505 u_int16_t stcode = DH6OPT_STCODE_USEMULTICAST;
1507 dprintf(LOG_INFO, FNAME, "unexpected unicast message from %s",
1508 addr2str(from));
1509 if (dhcp6_add_listval(&roptinfo.stcode_list,
1510 DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) {
1511 dprintf(LOG_ERR, FNAME, "failed to add a status code");
1512 goto fail;
1514 server6_send(DH6_REPLY, ifp, dh6, optinfo, from,
1515 fromlen, &roptinfo, relayinfohead, client_conf);
1516 goto end;
1520 * See if we have to make a binding of some configuration information
1521 * for the client.
1525 * When a delegating router receives a Request message from a
1526 * requesting router that contains an IA_PD option, and the delegating
1527 * router is authorized to delegate prefix(es) to the requesting
1528 * router, the delegating router selects the prefix(es) to be delegated
1529 * to the requesting router.
1530 * [RFC3633 Section 12.2]
1532 if (!TAILQ_EMPTY(&optinfo->iapd_list)) {
1533 struct dhcp6_list conflist;
1534 struct dhcp6_listval *iapd;
1536 TAILQ_INIT(&conflist);
1538 /* make a local copy of the configured prefixes */
1539 if (client_conf &&
1540 dhcp6_copy_list(&conflist, &client_conf->prefix_list)) {
1541 dprintf(LOG_NOTICE, FNAME,
1542 "failed to make local data");
1543 goto fail;
1546 for (iapd = TAILQ_FIRST(&optinfo->iapd_list); iapd;
1547 iapd = TAILQ_NEXT(iapd, link)) {
1549 * Find an appropriate prefix for each IA_PD,
1550 * removing the adopted prefixes from the list.
1551 * The prefixes will be bound to the client.
1553 if (make_ia(iapd, &conflist, &roptinfo.iapd_list,
1554 client_conf, 1) == 0) {
1556 * We could not find any prefixes for the IA.
1557 * RFC3315 specifies to include NoAddrsAvail
1558 * for the IA in the address configuration
1559 * case (Section 18.2.1). We follow the same
1560 * logic for prefix delegation as well.
1562 if (make_ia_stcode(DHCP6_LISTVAL_IAPD,
1563 iapd->val_ia.iaid,
1564 DH6OPT_STCODE_NOPREFIXAVAIL,
1565 &roptinfo.iapd_list)) {
1566 dprintf(LOG_NOTICE, FNAME,
1567 "failed to make an option list");
1568 dhcp6_clear_list(&conflist);
1569 goto fail;
1574 dhcp6_clear_list(&conflist);
1577 if (!TAILQ_EMPTY(&optinfo->iana_list)) {
1578 struct dhcp6_list conflist;
1579 struct dhcp6_listval *iana;
1581 if (client_conf == NULL && ifp->pool.name) {
1582 if ((client_conf = create_dynamic_hostconf(&optinfo->clientID,
1583 &ifp->pool)) == NULL)
1584 dprintf(LOG_NOTICE, FNAME,
1585 "failed to make host configuration");
1587 TAILQ_INIT(&conflist);
1589 /* make a local copy of the configured prefixes */
1590 if (client_conf &&
1591 dhcp6_copy_list(&conflist, &client_conf->addr_list)) {
1592 dprintf(LOG_NOTICE, FNAME,
1593 "failed to make local data");
1594 goto fail;
1597 for (iana = TAILQ_FIRST(&optinfo->iana_list); iana;
1598 iana = TAILQ_NEXT(iana, link)) {
1600 * Find an appropriate address for each IA_NA,
1601 * removing the adopted addresses from the list.
1602 * The addresses will be bound to the client.
1604 if (make_ia(iana, &conflist, &roptinfo.iana_list,
1605 client_conf, 1) == 0) {
1606 if (make_ia_stcode(DHCP6_LISTVAL_IANA,
1607 iana->val_ia.iaid,
1608 DH6OPT_STCODE_NOADDRSAVAIL,
1609 &roptinfo.iana_list)) {
1610 dprintf(LOG_NOTICE, FNAME,
1611 "failed to make an option list");
1612 dhcp6_clear_list(&conflist);
1613 goto fail;
1618 dhcp6_clear_list(&conflist);
1622 * If the Request message contained an Option Request option, the
1623 * server MUST include options in the Reply message for any options in
1624 * the Option Request option the server is configured to return to the
1625 * client.
1626 * [RFC3315 18.2.1]
1627 * Note: our current implementation always includes all information
1628 * that we can provide. So we do not have to check the option request
1629 * options.
1631 #if 0
1632 for (opt = TAILQ_FIRST(&optinfo->reqopt_list); opt;
1633 opt = TAILQ_NEXT(opt, link)) {
1636 #endif
1639 * Add options to the Reply message for any other configuration
1640 * information to be assigned to the client.
1642 if (set_statelessinfo(DH6_REQUEST, &roptinfo)) {
1643 dprintf(LOG_ERR, FNAME,
1644 "failed to set other stateless information");
1645 goto fail;
1648 /* send a reply message. */
1649 (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen,
1650 &roptinfo, relayinfohead, client_conf);
1652 end:
1653 dhcp6_clear_options(&roptinfo);
1654 return (0);
1656 fail:
1657 dhcp6_clear_options(&roptinfo);
1658 return (-1);
1661 static int
1662 react_renew(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead)
1663 struct dhcp6_if *ifp;
1664 struct in6_pktinfo *pi;
1665 struct dhcp6 *dh6;
1666 ssize_t len;
1667 struct dhcp6_optinfo *optinfo;
1668 struct sockaddr *from;
1669 int fromlen;
1670 struct relayinfolist *relayinfohead;
1672 struct dhcp6_optinfo roptinfo;
1673 struct dhcp6_listval *ia;
1674 struct host_conf *client_conf;
1676 /* message validation according to Section 15.6 of RFC3315 */
1678 /* the message must include a Server Identifier option */
1679 if (optinfo->serverID.duid_len == 0) {
1680 dprintf(LOG_INFO, FNAME, "no server ID option");
1681 return (-1);
1683 /* the contents of the Server Identifier option must match ours */
1684 if (duidcmp(&optinfo->serverID, &server_duid)) {
1685 dprintf(LOG_INFO, FNAME, "server ID mismatch");
1686 return (-1);
1688 /* the message must include a Client Identifier option */
1689 if (optinfo->clientID.duid_len == 0) {
1690 dprintf(LOG_INFO, FNAME, "no client ID option");
1691 return (-1);
1695 * configure necessary options based on the options in request.
1697 dhcp6_init_options(&roptinfo);
1699 /* server identifier option */
1700 if (duidcpy(&roptinfo.serverID, &server_duid)) {
1701 dprintf(LOG_ERR, FNAME, "failed to copy server ID");
1702 goto fail;
1704 /* copy client information back */
1705 if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) {
1706 dprintf(LOG_ERR, FNAME, "failed to copy client ID");
1707 goto fail;
1710 /* get per-host configuration for the client, if any. */
1711 if ((client_conf = find_hostconf(&optinfo->clientID))) {
1712 dprintf(LOG_DEBUG, FNAME,
1713 "found a host configuration named %s", client_conf->name);
1716 /* process authentication */
1717 if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) {
1718 dprintf(LOG_INFO, FNAME, "failed to process authentication "
1719 "information for %s",
1720 clientstr(client_conf, &optinfo->clientID));
1721 goto fail;
1725 * When the server receives a Renew message via unicast from a
1726 * client to which the server has not sent a unicast option, the server
1727 * discards the Request message and responds with a Reply message
1728 * containing a status code option with value UseMulticast, a Server
1729 * Identifier option containing the server's DUID, the Client
1730 * Identifier option from the client message and no other options.
1731 * [RFC3315 18.2.3]
1732 * (Our current implementation never sends a unicast option.)
1734 if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) &&
1735 TAILQ_EMPTY(relayinfohead)) {
1736 u_int16_t stcode = DH6OPT_STCODE_USEMULTICAST;
1738 dprintf(LOG_INFO, FNAME, "unexpected unicast message from %s",
1739 addr2str(from));
1740 if (dhcp6_add_listval(&roptinfo.stcode_list,
1741 DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) {
1742 dprintf(LOG_ERR, FNAME, "failed to add a status code");
1743 goto fail;
1745 server6_send(DH6_REPLY, ifp, dh6, optinfo, from,
1746 fromlen, &roptinfo, relayinfohead, client_conf);
1747 goto end;
1751 * Locates the client's binding and verifies that the information
1752 * from the client matches the information stored for that client.
1754 for (ia = TAILQ_FIRST(&optinfo->iapd_list); ia;
1755 ia = TAILQ_NEXT(ia, link)) {
1756 if (update_ia(DH6_RENEW, ia, &roptinfo.iapd_list, optinfo))
1757 goto fail;
1759 for (ia = TAILQ_FIRST(&optinfo->iana_list); ia;
1760 ia = TAILQ_NEXT(ia, link)) {
1761 if (update_ia(DH6_RENEW, ia, &roptinfo.iana_list, optinfo))
1762 goto fail;
1765 /* add other configuration information */
1766 if (set_statelessinfo(DH6_RENEW, &roptinfo)) {
1767 dprintf(LOG_ERR, FNAME,
1768 "failed to set other stateless information");
1769 goto fail;
1772 (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen,
1773 &roptinfo, relayinfohead, client_conf);
1775 end:
1776 dhcp6_clear_options(&roptinfo);
1777 return (0);
1779 fail:
1780 dhcp6_clear_options(&roptinfo);
1781 return (-1);
1784 static int
1785 react_rebind(ifp, dh6, len, optinfo, from, fromlen, relayinfohead)
1786 struct dhcp6_if *ifp;
1787 struct dhcp6 *dh6;
1788 ssize_t len;
1789 struct dhcp6_optinfo *optinfo;
1790 struct sockaddr *from;
1791 int fromlen;
1792 struct relayinfolist *relayinfohead;
1794 struct dhcp6_optinfo roptinfo;
1795 struct dhcp6_listval *ia;
1796 struct host_conf *client_conf;
1798 /* message validation according to Section 15.7 of RFC3315 */
1800 /* the message must include a Client Identifier option */
1801 if (optinfo->clientID.duid_len == 0) {
1802 dprintf(LOG_INFO, FNAME, "no client ID option");
1803 return (-1);
1806 /* the message must not include a server Identifier option */
1807 if (optinfo->serverID.duid_len) {
1808 dprintf(LOG_INFO, FNAME, "server ID option is included in "
1809 "a rebind message");
1810 return (-1);
1814 * configure necessary options based on the options in request.
1816 dhcp6_init_options(&roptinfo);
1818 /* server identifier option */
1819 if (duidcpy(&roptinfo.serverID, &server_duid)) {
1820 dprintf(LOG_ERR, FNAME, "failed to copy server ID");
1821 goto fail;
1823 /* copy client information back */
1824 if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) {
1825 dprintf(LOG_ERR, FNAME, "failed to copy client ID");
1826 goto fail;
1829 /* get per-host configuration for the client, if any. */
1830 if ((client_conf = find_hostconf(&optinfo->clientID))) {
1831 dprintf(LOG_DEBUG, FNAME,
1832 "found a host configuration named %s", client_conf->name);
1835 /* process authentication */
1836 if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) {
1837 dprintf(LOG_INFO, FNAME, "failed to process authentication "
1838 "information for %s",
1839 clientstr(client_conf, &optinfo->clientID));
1840 goto fail;
1844 * Locates the client's binding and verifies that the information
1845 * from the client matches the information stored for that client.
1847 for (ia = TAILQ_FIRST(&optinfo->iapd_list); ia;
1848 ia = TAILQ_NEXT(ia, link)) {
1849 if (update_ia(DH6_REBIND, ia, &roptinfo.iapd_list, optinfo))
1850 goto fail;
1852 for (ia = TAILQ_FIRST(&optinfo->iana_list); ia;
1853 ia = TAILQ_NEXT(ia, link)) {
1854 if (update_ia(DH6_REBIND, ia, &roptinfo.iana_list, optinfo))
1855 goto fail;
1859 * If the returned iana/pd_list is empty, we do not have an explicit
1860 * knowledge about validity nor invalidity for any IA_NA/PD information
1861 * in the Rebind message. In this case, we should rather ignore the
1862 * message than to send a Reply with empty information back to the
1863 * client, which may annoy the recipient. However, if we have at least
1864 * one useful information, either positive or negative, based on some
1865 * explicit knowledge, we should reply with the responsible part.
1867 if (TAILQ_EMPTY(&roptinfo.iapd_list) &&
1868 TAILQ_EMPTY(&roptinfo.iana_list)) {
1869 dprintf(LOG_INFO, FNAME, "no useful information for a rebind");
1870 goto fail; /* discard the rebind */
1873 /* add other configuration information */
1874 if (set_statelessinfo(DH6_REBIND, &roptinfo)) {
1875 dprintf(LOG_ERR, FNAME,
1876 "failed to set other stateless information");
1877 goto fail;
1880 (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen,
1881 &roptinfo, relayinfohead, client_conf);
1883 dhcp6_clear_options(&roptinfo);
1884 return (0);
1886 fail:
1887 dhcp6_clear_options(&roptinfo);
1888 return (-1);
1891 static int
1892 react_release(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead)
1893 struct dhcp6_if *ifp;
1894 struct in6_pktinfo *pi;
1895 struct dhcp6 *dh6;
1896 ssize_t len;
1897 struct dhcp6_optinfo *optinfo;
1898 struct sockaddr *from;
1899 int fromlen;
1900 struct relayinfolist *relayinfohead;
1902 struct dhcp6_optinfo roptinfo;
1903 struct dhcp6_listval *ia;
1904 struct host_conf *client_conf;
1905 u_int16_t stcode;
1907 /* message validation according to Section 15.9 of RFC3315 */
1909 /* the message must include a Server Identifier option */
1910 if (optinfo->serverID.duid_len == 0) {
1911 dprintf(LOG_INFO, FNAME, "no server ID option");
1912 return (-1);
1914 /* the contents of the Server Identifier option must match ours */
1915 if (duidcmp(&optinfo->serverID, &server_duid)) {
1916 dprintf(LOG_INFO, FNAME, "server ID mismatch");
1917 return (-1);
1919 /* the message must include a Client Identifier option */
1920 if (optinfo->clientID.duid_len == 0) {
1921 dprintf(LOG_INFO, FNAME, "no client ID option");
1922 return (-1);
1926 * configure necessary options based on the options in request.
1928 dhcp6_init_options(&roptinfo);
1930 /* server identifier option */
1931 if (duidcpy(&roptinfo.serverID, &server_duid)) {
1932 dprintf(LOG_ERR, FNAME, "failed to copy server ID");
1933 goto fail;
1935 /* copy client information back */
1936 if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) {
1937 dprintf(LOG_ERR, FNAME, "failed to copy client ID");
1938 goto fail;
1941 /* get per-host configuration for the client, if any. */
1942 if ((client_conf = find_hostconf(&optinfo->clientID))) {
1943 dprintf(LOG_DEBUG, FNAME,
1944 "found a host configuration named %s", client_conf->name);
1947 /* process authentication */
1948 if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) {
1949 dprintf(LOG_INFO, FNAME, "failed to process authentication "
1950 "information for %s",
1951 clientstr(client_conf, &optinfo->clientID));
1952 goto fail;
1956 * When the server receives a Release message via unicast from a
1957 * client to which the server has not sent a unicast option, the server
1958 * discards the Release message and responds with a Reply message
1959 * containing a Status Code option with value UseMulticast, a Server
1960 * Identifier option containing the server's DUID, the Client
1961 * Identifier option from the client message and no other options.
1962 * [RFC3315 18.2.6]
1963 * (Our current implementation never sends a unicast option.)
1965 if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) &&
1966 TAILQ_EMPTY(relayinfohead)) {
1967 u_int16_t stcode = DH6OPT_STCODE_USEMULTICAST;
1969 dprintf(LOG_INFO, FNAME, "unexpected unicast message from %s",
1970 addr2str(from));
1971 if (dhcp6_add_listval(&roptinfo.stcode_list,
1972 DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) {
1973 dprintf(LOG_ERR, FNAME, "failed to add a status code");
1974 goto fail;
1976 server6_send(DH6_REPLY, ifp, dh6, optinfo, from,
1977 fromlen, &roptinfo, relayinfohead, client_conf);
1978 goto end;
1982 * Locates the client's binding and verifies that the information
1983 * from the client matches the information stored for that client.
1985 for (ia = TAILQ_FIRST(&optinfo->iapd_list); ia;
1986 ia = TAILQ_NEXT(ia, link)) {
1987 if (release_binding_ia(ia, &roptinfo.iapd_list, optinfo))
1988 goto fail;
1990 for (ia = TAILQ_FIRST(&optinfo->iana_list); ia;
1991 ia = TAILQ_NEXT(ia, link)) {
1992 if (release_binding_ia(ia, &roptinfo.iana_list, optinfo))
1993 goto fail;
1997 * After all the addresses have been processed, the server generates a
1998 * Reply message and includes a Status Code option with value Success.
1999 * [RFC3315 Section 18.2.6]
2001 stcode = DH6OPT_STCODE_SUCCESS;
2002 if (dhcp6_add_listval(&roptinfo.stcode_list,
2003 DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) {
2004 dprintf(LOG_NOTICE, FNAME, "failed to add a status code");
2005 goto fail;
2008 (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen,
2009 &roptinfo, relayinfohead, client_conf);
2011 end:
2012 dhcp6_clear_options(&roptinfo);
2013 return (0);
2015 fail:
2016 dhcp6_clear_options(&roptinfo);
2017 return (-1);
2020 static int
2021 react_decline(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead)
2022 struct dhcp6_if *ifp;
2023 struct in6_pktinfo *pi;
2024 struct dhcp6 *dh6;
2025 ssize_t len;
2026 struct dhcp6_optinfo *optinfo;
2027 struct sockaddr *from;
2028 int fromlen;
2029 struct relayinfolist *relayinfohead;
2031 struct dhcp6_optinfo roptinfo;
2032 struct dhcp6_listval *ia;
2033 struct host_conf *client_conf;
2034 u_int16_t stcode;
2036 /* message validation according to Section 15.8 of RFC3315 */
2038 /* the message must include a Server Identifier option */
2039 if (optinfo->serverID.duid_len == 0) {
2040 dprintf(LOG_INFO, FNAME, "no server ID option");
2041 return (-1);
2043 /* the contents of the Server Identifier option must match ours */
2044 if (duidcmp(&optinfo->serverID, &server_duid)) {
2045 dprintf(LOG_INFO, FNAME, "server ID mismatch");
2046 return (-1);
2048 /* the message must include a Client Identifier option */
2049 if (optinfo->clientID.duid_len == 0) {
2050 dprintf(LOG_INFO, FNAME, "no client ID option");
2051 return (-1);
2055 * configure necessary options based on the options in request.
2057 dhcp6_init_options(&roptinfo);
2059 /* server identifier option */
2060 if (duidcpy(&roptinfo.serverID, &server_duid)) {
2061 dprintf(LOG_ERR, FNAME, "failed to copy server ID");
2062 goto fail;
2064 /* copy client information back */
2065 if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) {
2066 dprintf(LOG_ERR, FNAME, "failed to copy client ID");
2067 goto fail;
2070 /* get per-host configuration for the client, if any. */
2071 if ((client_conf = find_hostconf(&optinfo->clientID))) {
2072 dprintf(LOG_DEBUG, FNAME,
2073 "found a host configuration named %s", client_conf->name);
2076 /* process authentication */
2077 if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) {
2078 dprintf(LOG_INFO, FNAME, "failed to process authentication "
2079 "information for %s",
2080 clientstr(client_conf, &optinfo->clientID));
2081 goto fail;
2085 * When the server receives a Decline message via unicast from a
2086 * client to which the server has not sent a unicast option, the server
2087 * discards the Decline message and responds with a Reply message
2088 * containing a Status Code option with value UseMulticast, a Server
2089 * Identifier option containing the server's DUID, the Client
2090 * Identifier option from the client message and no other options.
2091 * [RFC3315 18.2.6]
2092 * (Our current implementation never sends a unicast option.)
2094 if (!IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) &&
2095 TAILQ_EMPTY(relayinfohead)) {
2096 stcode = DH6OPT_STCODE_USEMULTICAST;
2098 dprintf(LOG_INFO, FNAME, "unexpected unicast message from %s",
2099 addr2str(from));
2100 if (dhcp6_add_listval(&roptinfo.stcode_list,
2101 DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) {
2102 dprintf(LOG_ERR, FNAME, "failed to add a status code");
2103 goto fail;
2105 server6_send(DH6_REPLY, ifp, dh6, optinfo, from,
2106 fromlen, &roptinfo, relayinfohead, client_conf);
2107 goto end;
2111 * Locates the client's binding on IA-NA and verifies that the
2112 * information from the client matches the information stored
2113 * for that client. (IA-PD is just ignored [RFC3633 12.1])
2115 for (ia = TAILQ_FIRST(&optinfo->iana_list); ia;
2116 ia = TAILQ_NEXT(ia, link)) {
2117 if (decline_binding_ia(ia, &roptinfo.iana_list, optinfo))
2118 goto fail;
2122 * After all the addresses have been processed, the server generates a
2123 * Reply message and includes a Status Code option with value Success.
2124 * [RFC3315 Section 18.2.7]
2126 stcode = DH6OPT_STCODE_SUCCESS;
2127 if (dhcp6_add_listval(&roptinfo.stcode_list,
2128 DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL) {
2129 dprintf(LOG_NOTICE, FNAME, "failed to add a status code");
2130 goto fail;
2133 (void)server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen,
2134 &roptinfo, relayinfohead, client_conf);
2136 end:
2137 dhcp6_clear_options(&roptinfo);
2138 return (0);
2140 fail:
2141 dhcp6_clear_options(&roptinfo);
2142 return (-1);
2145 static int
2146 react_confirm(ifp, pi, dh6, len, optinfo, from, fromlen, relayinfohead)
2147 struct dhcp6_if *ifp;
2148 struct in6_pktinfo *pi;
2149 struct dhcp6 *dh6;
2150 ssize_t len;
2151 struct dhcp6_optinfo *optinfo;
2152 struct sockaddr *from;
2153 int fromlen;
2154 struct relayinfolist *relayinfohead;
2156 struct dhcp6_optinfo roptinfo;
2157 struct dhcp6_list conflist;
2158 struct dhcp6_listval *iana, *iaaddr;
2159 struct host_conf *client_conf;
2160 u_int16_t stcode = DH6OPT_STCODE_SUCCESS;
2161 int error;
2163 /* message validation according to Section 15.5 of RFC3315 */
2165 /* the message may not include a Server Identifier option */
2166 if (optinfo->serverID.duid_len) {
2167 dprintf(LOG_INFO, FNAME, "server ID option found");
2168 return (-1);
2170 /* the message must include a Client Identifier option */
2171 if (optinfo->clientID.duid_len == 0) {
2172 dprintf(LOG_INFO, FNAME, "no client ID option");
2173 return (-1);
2176 dhcp6_init_options(&roptinfo);
2178 /* server identifier option */
2179 if (duidcpy(&roptinfo.serverID, &server_duid)) {
2180 dprintf(LOG_ERR, FNAME, "failed to copy server ID");
2181 goto fail;
2183 /* copy client information back */
2184 if (duidcpy(&roptinfo.clientID, &optinfo->clientID)) {
2185 dprintf(LOG_ERR, FNAME, "failed to copy client ID");
2186 goto fail;
2189 /* get per-host configuration for the client, if any. */
2190 if ((client_conf = find_hostconf(&optinfo->clientID))) {
2191 dprintf(LOG_DEBUG, FNAME,
2192 "found a host configuration named %s", client_conf->name);
2195 /* process authentication */
2196 if (process_auth(dh6, len, client_conf, optinfo, &roptinfo)) {
2197 dprintf(LOG_INFO, FNAME, "failed to process authentication "
2198 "information for %s",
2199 clientstr(client_conf, &optinfo->clientID));
2200 goto fail;
2203 if (client_conf == NULL && ifp->pool.name) {
2204 if ((client_conf = create_dynamic_hostconf(&optinfo->clientID,
2205 &ifp->pool)) == NULL) {
2206 dprintf(LOG_NOTICE, FNAME,
2207 "failed to make host configuration");
2208 goto fail;
2211 TAILQ_INIT(&conflist);
2212 /* make a local copy of the configured addresses */
2213 if (dhcp6_copy_list(&conflist, &client_conf->addr_list)) {
2214 dprintf(LOG_NOTICE, FNAME,
2215 "failed to make local data");
2216 goto fail;
2220 * the message must include an IPv6 address to be confirmed
2221 * [RFC3315 18.2]. (IA-PD is just ignored [RFC3633 12.1])
2223 if (TAILQ_EMPTY(&optinfo->iana_list)) {
2224 dprintf(LOG_INFO, FNAME, "no IA-NA option found");
2225 goto fail;
2227 for (iana = TAILQ_FIRST(&optinfo->iana_list); iana;
2228 iana = TAILQ_NEXT(iana, link)) {
2229 if (TAILQ_EMPTY(&iana->sublist)) {
2230 dprintf(LOG_INFO, FNAME,
2231 "no IA-ADDR option found in IA-NA %d",
2232 iana->val_ia.iaid);
2233 goto fail;
2237 * check whether the confirmed prefix matches
2238 * the prefix from where the message originates.
2239 * XXX: prefix length is assumed to be 64
2241 for (iaaddr = TAILQ_FIRST(&iana->sublist); iaaddr;
2242 iaaddr = TAILQ_NEXT(iaaddr, link)) {
2244 struct in6_addr *confaddr = &iaaddr->val_statefuladdr6.addr;
2245 struct in6_addr *linkaddr;
2246 struct sockaddr_in6 *src = (struct sockaddr_in6 *)from;
2248 if (!IN6_IS_ADDR_LINKLOCAL(&src->sin6_addr)) {
2249 /* CONFIRM is relayed via a DHCP-relay */
2250 struct relayinfo *relayinfo;
2252 if (relayinfohead == NULL) {
2253 dprintf(LOG_INFO, FNAME,
2254 "no link-addr found");
2255 goto fail;
2257 relayinfo = TAILQ_LAST(relayinfohead, relayinfolist);
2259 /* XXX: link-addr is supposed to be a global address */
2260 linkaddr = &relayinfo->linkaddr;
2261 } else {
2262 /* CONFIRM is directly arrived */
2263 linkaddr = &ifp->addr;
2266 if (memcmp(linkaddr, confaddr, 8) != 0) {
2267 dprintf(LOG_INFO, FNAME,
2268 "%s does not seem to belong to %s's link",
2269 in6addr2str(confaddr, 0),
2270 in6addr2str(linkaddr, 0));
2271 stcode = DH6OPT_STCODE_NOTONLINK;
2272 goto send_reply;
2278 * even when the given address seems to be on the appropriate link,
2279 * the confirm should be ignore if there's no corrensponding IA-NA
2280 * configuration.
2282 for (iana = TAILQ_FIRST(&optinfo->iana_list); iana;
2283 iana = TAILQ_NEXT(iana, link)) {
2284 if (make_ia(iana, &conflist, &roptinfo.iana_list,
2285 client_conf, 1) == 0) {
2286 dprintf(LOG_DEBUG, FNAME,
2287 "IA-NA configuration not found");
2288 goto fail;
2292 send_reply:
2293 if (dhcp6_add_listval(&roptinfo.stcode_list,
2294 DHCP6_LISTVAL_STCODE, &stcode, NULL) == NULL)
2295 goto fail;
2296 error = server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen,
2297 &roptinfo, relayinfohead, client_conf);
2299 dhcp6_clear_options(&roptinfo);
2300 dhcp6_clear_list(&conflist);
2302 return (error);
2304 fail:
2305 dhcp6_clear_options(&roptinfo);
2306 dhcp6_clear_list(&conflist);
2307 return (-1);
2310 static int
2311 react_informreq(ifp, dh6, len, optinfo, from, fromlen, relayinfohead)
2312 struct dhcp6_if *ifp;
2313 struct dhcp6 *dh6;
2314 ssize_t len;
2315 struct dhcp6_optinfo *optinfo;
2316 struct sockaddr *from;
2317 int fromlen;
2318 struct relayinfolist *relayinfohead;
2320 struct dhcp6_optinfo roptinfo;
2321 int error;
2324 * An IA option is not allowed to appear in an Information-request
2325 * message. Such a message SHOULD be discarded.
2326 * [RFC3315 Section 15]
2328 if (!TAILQ_EMPTY(&optinfo->iapd_list)) {
2329 dprintf(LOG_INFO, FNAME,
2330 "information request contains an IA_PD option");
2331 return (-1);
2333 if (!TAILQ_EMPTY(&optinfo->iana_list)) {
2334 dprintf(LOG_INFO, FNAME,
2335 "information request contains an IA_NA option");
2336 return (-1);
2339 /* if a server identifier is included, it must match ours. */
2340 if (optinfo->serverID.duid_len &&
2341 duidcmp(&optinfo->serverID, &server_duid)) {
2342 dprintf(LOG_INFO, FNAME, "server DUID mismatch");
2343 return (-1);
2347 * configure necessary options based on the options in request.
2349 dhcp6_init_options(&roptinfo);
2351 /* server identifier option */
2352 if (duidcpy(&roptinfo.serverID, &server_duid)) {
2353 dprintf(LOG_ERR, FNAME, "failed to copy server ID");
2354 goto fail;
2357 /* copy client information back (if provided) */
2358 if (optinfo->clientID.duid_id &&
2359 duidcpy(&roptinfo.clientID, &optinfo->clientID)) {
2360 dprintf(LOG_ERR, FNAME, "failed to copy client ID");
2361 goto fail;
2364 /* set stateless information */
2365 if (set_statelessinfo(DH6_INFORM_REQ, &roptinfo)) {
2366 dprintf(LOG_ERR, FNAME,
2367 "failed to set other stateless information");
2368 goto fail;
2371 error = server6_send(DH6_REPLY, ifp, dh6, optinfo, from, fromlen,
2372 &roptinfo, relayinfohead, NULL);
2374 dhcp6_clear_options(&roptinfo);
2375 return (error);
2377 fail:
2378 dhcp6_clear_options(&roptinfo);
2379 return (-1);
2382 static int
2383 update_ia(msgtype, iap, retlist, optinfo)
2384 int msgtype;
2385 struct dhcp6_listval *iap;
2386 struct dhcp6_list *retlist;
2387 struct dhcp6_optinfo *optinfo;
2389 struct dhcp6_binding *binding;
2390 struct host_conf *client_conf;
2392 /* get per-host configuration for the client, if any. */
2393 if ((client_conf = find_hostconf(&optinfo->clientID))) {
2394 dprintf(LOG_DEBUG, FNAME,
2395 "found a host configuration named %s", client_conf->name);
2398 if ((binding = find_binding(&optinfo->clientID, DHCP6_BINDING_IA,
2399 iap->type, iap->val_ia.iaid)) == NULL) {
2401 * Behavior in the case where the delegating router cannot
2402 * find a binding for the requesting router's IA_PD as
2403 * described in RFC3633 Section 12.2. It is derived from
2404 * Sections 18.2.3 and 18.2.4 of RFC3315, and the two sets
2405 * of behavior are identical.
2407 dprintf(LOG_INFO, FNAME, "no binding found for %s",
2408 duidstr(&optinfo->clientID));
2410 switch (msgtype) {
2411 case DH6_RENEW:
2413 * If the delegating router cannot find a binding for
2414 * the requesting router's IA_PD the delegating router
2415 * returns the IA_PD containing no prefixes with a
2416 * Status Code option set to NoBinding in the Reply
2417 * message.
2419 if (make_ia_stcode(iap->type, iap->val_ia.iaid,
2420 DH6OPT_STCODE_NOBINDING, retlist)) {
2421 dprintf(LOG_NOTICE, FNAME,
2422 "failed to make an option list");
2423 return (-1);
2425 break;
2426 case DH6_REBIND:
2428 * If it can be determined the prefixes are not
2429 * appropriate from the delegating router's explicit
2430 * configuration, it MAY send a Reply message to
2431 * the requesting router containing the IA_PD with the
2432 * lifetimes of the prefixes in the IA_PD set to zero.
2434 * If unable to determine, the Rebind message is
2435 * discarded.
2437 * XXX: it is not very clear what the explicit
2438 * configuration means. Thus, we always discard the
2439 * message.
2441 return (-1);
2442 default: /* XXX: should be a bug */
2443 dprintf(LOG_ERR, FNAME, "impossible message type %s",
2444 dhcp6msgstr(msgtype));
2445 return (-1);
2447 } else { /* we found a binding */
2448 struct dhcp6_list ialist;
2449 struct dhcp6_listval *lv;
2450 struct dhcp6_prefix prefix;
2451 struct dhcp6_statefuladdr saddr;
2452 struct dhcp6_ia ia;
2454 TAILQ_INIT(&ialist);
2455 update_binding(binding);
2457 /* see if each information to be renewed is still valid. */
2458 for (lv = TAILQ_FIRST(&iap->sublist); lv;
2459 lv = TAILQ_NEXT(lv, link)) {
2460 struct dhcp6_listval *blv;
2462 switch (iap->type) {
2463 case DHCP6_LISTVAL_IAPD:
2464 if (lv->type != DHCP6_LISTVAL_PREFIX6)
2465 continue;
2467 prefix = lv->val_prefix6;
2468 blv = dhcp6_find_listval(&binding->val_list,
2469 DHCP6_LISTVAL_PREFIX6, &prefix, 0);
2470 if (blv == NULL) {
2471 dprintf(LOG_DEBUG, FNAME,
2472 "%s/%d is not found in %s",
2473 in6addr2str(&prefix.addr, 0),
2474 prefix.plen, bindingstr(binding));
2475 prefix.pltime = 0;
2476 prefix.vltime = 0;
2477 } else {
2478 prefix.pltime =
2479 blv->val_prefix6.pltime;
2480 prefix.vltime =
2481 blv->val_prefix6.vltime;
2484 if (dhcp6_add_listval(&ialist,
2485 DHCP6_LISTVAL_PREFIX6, &prefix, NULL)
2486 == NULL) {
2487 dprintf(LOG_NOTICE, FNAME,
2488 "failed to copy binding info");
2489 dhcp6_clear_list(&ialist);
2490 return (-1);
2492 break;
2493 case DHCP6_LISTVAL_IANA:
2494 if (lv->type != DHCP6_LISTVAL_STATEFULADDR6)
2495 continue;
2497 saddr = lv->val_statefuladdr6;
2498 blv = dhcp6_find_listval(&binding->val_list,
2499 DHCP6_LISTVAL_STATEFULADDR6, &saddr, 0);
2500 if (blv == NULL) {
2501 dprintf(LOG_DEBUG, FNAME,
2502 "%s is not found in %s",
2503 in6addr2str(&saddr.addr, 0),
2504 bindingstr(binding));
2505 saddr.pltime = 0;
2506 saddr.vltime = 0;
2507 } else {
2508 saddr.pltime =
2509 blv->val_statefuladdr6.pltime;
2510 saddr.vltime =
2511 blv->val_statefuladdr6.vltime;
2514 if (dhcp6_add_listval(&ialist,
2515 DHCP6_LISTVAL_STATEFULADDR6, &saddr, NULL)
2516 == NULL) {
2517 dprintf(LOG_NOTICE, FNAME,
2518 "failed to copy binding info");
2519 dhcp6_clear_list(&ialist);
2520 return (-1);
2522 break;
2523 default:
2524 dprintf(LOG_ERR, FNAME, "unsupported IA type");
2525 return (-1); /* XXX */
2529 memset(&ia, 0, sizeof(ia));
2530 ia.iaid = binding->iaid;
2531 /* determine appropriate T1 and T2 */
2532 calc_ia_timo(&ia, &ialist, client_conf);
2534 if (dhcp6_add_listval(retlist, iap->type,
2535 &ia, &ialist) == NULL) {
2536 dhcp6_clear_list(&ialist);
2537 return (-1);
2539 dhcp6_clear_list(&ialist);
2542 return (0);
2545 static int
2546 release_binding_ia(iap, retlist, optinfo)
2547 struct dhcp6_listval *iap;
2548 struct dhcp6_list *retlist;
2549 struct dhcp6_optinfo *optinfo;
2551 struct dhcp6_binding *binding;
2553 if ((binding = find_binding(&optinfo->clientID, DHCP6_BINDING_IA,
2554 iap->type, iap->val_ia.iaid)) == NULL) {
2556 * For each IA in the Release message for which the server has
2557 * no binding information, the server adds an IA option using
2558 * the IAID from the Release message and includes a Status Code
2559 * option with the value NoBinding in the IA option.
2561 if (make_ia_stcode(iap->type, iap->val_ia.iaid,
2562 DH6OPT_STCODE_NOBINDING, retlist)) {
2563 dprintf(LOG_NOTICE, FNAME,
2564 "failed to make an option list");
2565 return (-1);
2567 } else {
2568 struct dhcp6_listval *lv, *lvia;
2571 * If the IAs in the message are in a binding for the client
2572 * and the addresses in the IAs have been assigned by the
2573 * server to those IAs, the server deletes the addresses from
2574 * the IAs and makes the addresses available for assignment to
2575 * other clients.
2576 * [RFC3315 Section 18.2.6]
2577 * RFC3633 is not very clear about the similar case for IA_PD,
2578 * but we apply the same logic.
2580 for (lv = TAILQ_FIRST(&iap->sublist); lv;
2581 lv = TAILQ_NEXT(lv, link)) {
2582 if ((lvia = find_binding_ia(lv, binding)) != NULL) {
2583 switch (binding->iatype) {
2584 case DHCP6_LISTVAL_IAPD:
2585 dprintf(LOG_DEBUG, FNAME,
2586 "bound prefix %s/%d "
2587 "has been released",
2588 in6addr2str(&lvia->val_prefix6.addr,
2590 lvia->val_prefix6.plen);
2591 break;
2592 case DHCP6_LISTVAL_IANA:
2593 release_address(&lvia->val_prefix6.addr);
2594 dprintf(LOG_DEBUG, FNAME,
2595 "bound address %s "
2596 "has been released",
2597 in6addr2str(&lvia->val_prefix6.addr,
2598 0));
2599 break;
2602 TAILQ_REMOVE(&binding->val_list, lvia, link);
2603 dhcp6_clear_listval(lvia);
2604 if (TAILQ_EMPTY(&binding->val_list)) {
2606 * if the binding has become empty,
2607 * stop procedure.
2609 remove_binding(binding);
2610 return (0);
2616 return (0);
2619 static int
2620 decline_binding_ia(iap, retlist, optinfo)
2621 struct dhcp6_listval *iap;
2622 struct dhcp6_list *retlist;
2623 struct dhcp6_optinfo *optinfo;
2625 struct dhcp6_binding *binding;
2626 struct dhcp6_listval *lv, *lvia;
2628 if ((binding = find_binding(&optinfo->clientID, DHCP6_BINDING_IA,
2629 iap->type, iap->val_ia.iaid)) == NULL) {
2631 * For each IA in the Decline message for which the server has
2632 * no binding information, the server adds an IA option using
2633 * the IAID from the Release message and includes a Status Code
2634 * option with the value NoBinding in the IA option.
2636 if (make_ia_stcode(iap->type, iap->val_ia.iaid,
2637 DH6OPT_STCODE_NOBINDING, retlist)) {
2638 dprintf(LOG_NOTICE, FNAME,
2639 "failed to make an option list");
2640 return (-1);
2643 return (0);
2647 * If the IAs in the message are in a binding for the client and the
2648 * addresses in the IAs have been assigned by the server to those IAs,
2649 * the server deletes the addresses from the IAs and makes the addresses
2650 * available for assignment to other clients. [RFC3315 Section 18.2.7]
2652 for (lv = TAILQ_FIRST(&iap->sublist); lv;
2653 lv = TAILQ_NEXT(lv, link)) {
2654 if (binding->iatype != DHCP6_LISTVAL_IANA) {
2655 /* should never reach here */
2656 continue;
2659 if ((lvia = find_binding_ia(lv, binding)) == NULL) {
2660 dprintf(LOG_DEBUG, FNAME, "no binding found "
2661 "for address %s",
2662 in6addr2str(&lv->val_statefuladdr6.addr, 0));
2663 continue;
2666 dprintf(LOG_DEBUG, FNAME,
2667 "bound address %s has been marked as declined",
2668 in6addr2str(&lvia->val_statefuladdr6.addr, 0));
2669 decline_address(&lvia->val_statefuladdr6.addr);
2671 TAILQ_REMOVE(&binding->val_list, lvia, link);
2672 dhcp6_clear_listval(lvia);
2673 if (TAILQ_EMPTY(&binding->val_list)) {
2675 * if the binding has become empty,
2676 * stop procedure.
2678 remove_binding(binding);
2679 return (0);
2683 return (0);
2686 static void
2687 server6_signal(sig)
2688 int sig;
2691 dprintf(LOG_INFO, FNAME, "received a signal (%d)", sig);
2693 switch (sig) {
2694 case SIGTERM:
2695 sig_flags |= SIGF_TERM;
2696 break;
2700 static int
2701 server6_send(type, ifp, origmsg, optinfo, from, fromlen,
2702 roptinfo, relayinfohead, client_conf)
2703 int type;
2704 struct dhcp6_if *ifp;
2705 struct dhcp6 *origmsg;
2706 struct dhcp6_optinfo *optinfo, *roptinfo;
2707 struct sockaddr *from;
2708 int fromlen;
2709 struct relayinfolist *relayinfohead;
2710 struct host_conf *client_conf;
2712 char replybuf[BUFSIZ];
2713 struct sockaddr_in6 dst;
2714 int len, optlen;
2715 int relayed = 0;
2716 struct dhcp6 *dh6;
2717 struct relayinfo *relayinfo;
2719 if (sizeof(struct dhcp6) > sizeof(replybuf)) {
2720 dprintf(LOG_ERR, FNAME, "buffer size assumption failed");
2721 return (-1);
2724 dh6 = (struct dhcp6 *)replybuf;
2725 len = sizeof(*dh6);
2726 memset(dh6, 0, sizeof(*dh6));
2727 dh6->dh6_msgtypexid = origmsg->dh6_msgtypexid;
2728 dh6->dh6_msgtype = (u_int8_t)type;
2730 /* set options in the reply message */
2731 if ((optlen = dhcp6_set_options(type, (struct dhcp6opt *)(dh6 + 1),
2732 (struct dhcp6opt *)(replybuf + sizeof(replybuf)), roptinfo)) < 0) {
2733 dprintf(LOG_INFO, FNAME, "failed to construct reply options");
2734 return (-1);
2736 len += optlen;
2738 /* calculate MAC if necessary, and put it to the message */
2739 switch (roptinfo->authproto) {
2740 case DHCP6_AUTHPROTO_DELAYED:
2741 if (client_conf == NULL || client_conf->delayedkey == NULL) {
2742 /* This case should have been caught earlier */
2743 dprintf(LOG_ERR, FNAME, "authentication required "
2744 "but not key provided");
2745 break;
2747 if (dhcp6_calc_mac((char *)dh6, len, roptinfo->authproto,
2748 roptinfo->authalgorithm,
2749 roptinfo->delayedauth_offset + sizeof(*dh6),
2750 client_conf->delayedkey)) {
2751 dprintf(LOG_WARNING, FNAME, "failed to calculate MAC");
2752 return (-1);
2754 break;
2755 default:
2756 break; /* do nothing */
2759 /* construct a relay chain, if necessary */
2760 for (relayinfo = TAILQ_FIRST(relayinfohead); relayinfo;
2761 relayinfo = TAILQ_NEXT(relayinfo, link)) {
2762 struct dhcp6_optinfo relayopt;
2763 struct dhcp6_vbuf relaymsgbuf;
2764 struct dhcp6_relay *dh6relay;
2766 relayed = 1;
2767 dhcp6_init_options(&relayopt);
2769 relaymsgbuf.dv_len = len;
2770 relaymsgbuf.dv_buf = replybuf;
2771 if (dhcp6_vbuf_copy(&relayopt.relay_msg, &relaymsgbuf))
2772 return (-1);
2773 if (relayinfo->relay_ifid.dv_buf &&
2774 dhcp6_vbuf_copy(&relayopt.ifidopt,
2775 &relayinfo->relay_ifid)) {
2776 dhcp6_vbuf_free(&relayopt.relay_msg);
2777 return (-1);
2780 /* we can safely reuse replybuf here */
2781 dh6relay = (struct dhcp6_relay *)replybuf;
2782 memset(dh6relay, 0, sizeof (*dh6relay));
2783 dh6relay->dh6relay_msgtype = DH6_RELAY_REPLY;
2784 dh6relay->dh6relay_hcnt = relayinfo->hcnt;
2785 memcpy(&dh6relay->dh6relay_linkaddr, &relayinfo->linkaddr,
2786 sizeof (dh6relay->dh6relay_linkaddr));
2787 memcpy(&dh6relay->dh6relay_peeraddr, &relayinfo->peeraddr,
2788 sizeof (dh6relay->dh6relay_peeraddr));
2790 len = sizeof(*dh6relay);
2791 if ((optlen = dhcp6_set_options(DH6_RELAY_REPLY,
2792 (struct dhcp6opt *)(dh6relay + 1),
2793 (struct dhcp6opt *)(replybuf + sizeof(replybuf)),
2794 &relayopt)) < 0) {
2795 dprintf(LOG_INFO, FNAME,
2796 "failed to construct relay message");
2797 dhcp6_clear_options(&relayopt);
2798 return (-1);
2800 len += optlen;
2802 dhcp6_clear_options(&relayopt);
2805 /* specify the destination and send the reply */
2806 dst = relayed ? *sa6_any_relay : *sa6_any_downstream;
2807 dst.sin6_addr = ((struct sockaddr_in6 *)from)->sin6_addr;
2808 dst.sin6_scope_id = ((struct sockaddr_in6 *)from)->sin6_scope_id;
2809 if (transmit_sa(outsock, (struct sockaddr *)&dst,
2810 replybuf, len) != 0) {
2811 dprintf(LOG_ERR, FNAME, "transmit %s to %s failed",
2812 dhcp6msgstr(type), addr2str((struct sockaddr *)&dst));
2813 return (-1);
2816 dprintf(LOG_DEBUG, FNAME, "transmit %s to %s",
2817 dhcp6msgstr(type), addr2str((struct sockaddr *)&dst));
2819 return (0);
2822 static int
2823 make_ia_stcode(iatype, iaid, stcode, retlist)
2824 int iatype;
2825 u_int16_t stcode;
2826 u_int32_t iaid;
2827 struct dhcp6_list *retlist;
2829 struct dhcp6_list stcode_list;
2830 struct dhcp6_ia ia_empty;
2832 memset(&ia_empty, 0, sizeof(ia_empty));
2833 ia_empty.iaid = iaid;
2835 TAILQ_INIT(&stcode_list);
2836 if (dhcp6_add_listval(&stcode_list, DHCP6_LISTVAL_STCODE,
2837 &stcode, NULL) == NULL) {
2838 dprintf(LOG_NOTICE, FNAME, "failed to make an option list");
2839 return (-1);
2842 if (dhcp6_add_listval(retlist, iatype,
2843 &ia_empty, &stcode_list) == NULL) {
2844 dprintf(LOG_NOTICE, FNAME, "failed to make an option list");
2845 dhcp6_clear_list(&stcode_list);
2846 return (-1);
2848 dhcp6_clear_list(&stcode_list);
2850 return (0);
2853 static int
2854 make_ia(spec, conflist, retlist, client_conf, do_binding)
2855 struct dhcp6_listval *spec;
2856 struct dhcp6_list *conflist, *retlist;
2857 struct host_conf *client_conf;
2858 int do_binding;
2860 struct dhcp6_binding *binding;
2861 struct dhcp6_list ialist;
2862 struct dhcp6_listval *specia;
2863 struct dhcp6_ia ia;
2864 int found = 0;
2867 * If we happen to have a binding already, update the binding and
2868 * return it. Perhaps the request is being retransmitted.
2870 if ((binding = find_binding(&client_conf->duid, DHCP6_BINDING_IA,
2871 spec->type, spec->val_ia.iaid)) != NULL) {
2872 struct dhcp6_list *blist = &binding->val_list;
2873 struct dhcp6_listval *bia, *v;
2875 dprintf(LOG_DEBUG, FNAME, "we have a binding already: %s",
2876 bindingstr(binding));
2878 update_binding(binding);
2880 memset(&ia, 0, sizeof(ia));
2881 ia.iaid = spec->val_ia.iaid;
2882 /* determine appropriate T1 and T2 */
2883 calc_ia_timo(&ia, blist, client_conf);
2884 if (dhcp6_add_listval(retlist, spec->type, &ia, blist)
2885 == NULL) {
2886 dprintf(LOG_NOTICE, FNAME,
2887 "failed to copy binding info");
2888 return (0);
2891 /* remove bound values from the configuration */
2892 for (bia = TAILQ_FIRST(blist); bia;
2893 bia = TAILQ_NEXT(bia, link)) {
2894 if ((v = dhcp6_find_listval(conflist,
2895 bia->type, &bia->uv, 0)) != NULL) {
2896 TAILQ_REMOVE(conflist, v, link);
2897 dhcp6_clear_listval(v);
2901 return (1);
2905 * trivial case:
2906 * if the configuration is empty, we cannot make any IA.
2908 if (TAILQ_EMPTY(conflist)) {
2909 if (spec->type != DHCP6_LISTVAL_IANA ||
2910 client_conf->pool.name == NULL) {
2911 return (0);
2915 TAILQ_INIT(&ialist);
2917 /* First, check if we can meet the client's requirement */
2918 for (specia = TAILQ_FIRST(&spec->sublist); specia;
2919 specia = TAILQ_NEXT(specia, link)) {
2920 /* try to find an IA that matches the spec best. */
2921 if (!TAILQ_EMPTY(conflist)) {
2922 if (make_match_ia(specia, conflist, &ialist))
2923 found++;
2924 } else if (spec->type == DHCP6_LISTVAL_IANA &&
2925 client_conf->pool.name != NULL) {
2926 if (make_iana_from_pool(&client_conf->pool, specia, &ialist))
2927 found++;
2930 if (found == 0) {
2931 if (!TAILQ_EMPTY(conflist)) {
2932 struct dhcp6_listval *v;
2934 /* use the first IA in the configuration list */
2935 for (v = TAILQ_FIRST(conflist); v; v = TAILQ_NEXT(v, link)) {
2936 if (spec->type != DHCP6_LISTVAL_IANA)
2937 break; /* always use the first IA for non-IANA */
2938 if (!is_leased(&v->val_statefuladdr6.addr))
2939 break;
2941 if (v && dhcp6_add_listval(&ialist, v->type, &v->uv, NULL)) {
2942 found = 1;
2943 TAILQ_REMOVE(conflist, v, link);
2944 dhcp6_clear_listval(v);
2946 } else if (spec->type == DHCP6_LISTVAL_IANA &&
2947 client_conf->pool.name != NULL) {
2948 if (make_iana_from_pool(&client_conf->pool, NULL, &ialist))
2949 found = 1;
2952 if (found) {
2953 memset(&ia, 0, sizeof(ia));
2954 ia.iaid = spec->val_ia.iaid;
2955 /* determine appropriate T1 and T2 */
2956 calc_ia_timo(&ia, &ialist, client_conf);
2958 /* make a binding for the set if necessary */
2959 if (do_binding) {
2960 if (add_binding(&client_conf->duid, DHCP6_BINDING_IA,
2961 spec->type, spec->val_ia.iaid, &ialist) == NULL) {
2962 dprintf(LOG_NOTICE, FNAME,
2963 "failed to make a binding");
2964 found = 0;
2967 if (found) {
2968 /* make an IA for the set */
2969 if (dhcp6_add_listval(retlist, spec->type,
2970 &ia, &ialist) == NULL)
2971 found = 0;
2973 dhcp6_clear_list(&ialist);
2976 return (found);
2979 static int
2980 make_match_ia(spec, conflist, retlist)
2981 struct dhcp6_listval *spec;
2982 struct dhcp6_list *conflist, *retlist;
2984 struct dhcp6_listval *match;
2985 int matched = 0;
2987 /* do we have the exact value specified? */
2988 match = dhcp6_find_listval(conflist, spec->type, &spec->uv, 0);
2990 /* if not, make further search specific to the IA type. */
2991 if (!match) {
2992 switch (spec->type) {
2993 case DHCP6_LISTVAL_PREFIX6:
2994 match = dhcp6_find_listval(conflist, spec->type,
2995 &spec->uv, MATCHLIST_PREFIXLEN);
2996 break;
2997 case DHCP6_LISTVAL_STATEFULADDR6:
2998 /* No "partial match" for addresses */
2999 if (is_leased(&spec->val_statefuladdr6.addr))
3000 match = 0;
3001 break;
3002 default:
3003 dprintf(LOG_ERR, FNAME, "unsupported IA type");
3004 return (0); /* XXX */
3009 * if found, remove the matched entry from the configuration list
3010 * and copy the value in the returned list.
3012 if (match) {
3013 if (dhcp6_add_listval(retlist, match->type,
3014 &match->uv, NULL)) {
3015 matched = 1;
3016 TAILQ_REMOVE(conflist, match, link);
3017 dhcp6_clear_listval(match);
3021 return (matched);
3024 /* making sublist of iana */
3025 static int
3026 make_iana_from_pool(poolspec, spec, retlist)
3027 struct dhcp6_poolspec *poolspec;
3028 struct dhcp6_listval *spec;
3029 struct dhcp6_list *retlist;
3031 struct dhcp6_statefuladdr saddr;
3032 struct pool_conf *pool;
3033 int found = 0;
3035 dprintf(LOG_DEBUG, FNAME, "called");
3037 if ((pool = find_pool(poolspec->name)) == NULL) {
3038 dprintf(LOG_ERR, FNAME, "pool '%s' not found", poolspec->name);
3039 return (0);
3042 if (spec) {
3043 memcpy(&saddr.addr, &spec->val_statefuladdr6.addr, sizeof(saddr.addr));
3044 if (is_available_in_pool(pool, &saddr.addr)) {
3045 found = 1;
3047 } else {
3048 if (get_free_address_from_pool(pool, &saddr.addr)) {
3049 found = 1;
3053 if (found) {
3054 saddr.pltime = poolspec->pltime;
3055 saddr.vltime = poolspec->vltime;
3057 if (!dhcp6_add_listval(retlist, DHCP6_LISTVAL_STATEFULADDR6,
3058 &saddr, NULL)) {
3059 return (0);
3063 dprintf(LOG_DEBUG, FNAME, "returns (found=%d)", found);
3065 return (found);
3068 static void
3069 calc_ia_timo(ia, ialist, client_conf)
3070 struct dhcp6_ia *ia;
3071 struct dhcp6_list *ialist; /* this should not be empty */
3072 struct host_conf *client_conf; /* unused yet */
3074 struct dhcp6_listval *iav;
3075 u_int32_t base = DHCP6_DURATION_INFINITE;
3076 int iatype;
3078 iatype = TAILQ_FIRST(ialist)->type;
3079 for (iav = TAILQ_FIRST(ialist); iav; iav = TAILQ_NEXT(iav, link)) {
3080 if (iav->type != iatype) {
3081 dprintf(LOG_ERR, FNAME,
3082 "assumption failure: IA list is not consistent");
3083 exit (1); /* XXX */
3085 switch (iatype) {
3086 case DHCP6_LISTVAL_PREFIX6:
3087 case DHCP6_LISTVAL_STATEFULADDR6:
3088 if (base == DHCP6_DURATION_INFINITE ||
3089 iav->val_prefix6.pltime < base)
3090 base = iav->val_prefix6.pltime;
3091 break;
3095 switch (iatype) {
3096 case DHCP6_LISTVAL_PREFIX6:
3097 case DHCP6_LISTVAL_STATEFULADDR6:
3099 * Configure the timeout parameters as recommended in
3100 * Section 22.4 of RFC3315 and Section 9 of RFC3633.
3101 * We could also set the parameters to 0 if we let the client
3102 * decide the renew timing (not implemented yet).
3104 if (base == DHCP6_DURATION_INFINITE) {
3105 ia->t1 = DHCP6_DURATION_INFINITE;
3106 ia->t2 = DHCP6_DURATION_INFINITE;
3107 } else {
3108 ia->t1 = base / 2;
3109 ia->t2 = (base * 4) / 5;
3111 break;
3115 static void
3116 update_binding_duration(binding)
3117 struct dhcp6_binding *binding;
3119 struct dhcp6_list *ia_list = &binding->val_list;
3120 struct dhcp6_listval *iav;
3121 int duration = DHCP6_DURATION_INFINITE;
3122 u_int32_t past, min_lifetime;
3123 time_t now = time(NULL);
3125 min_lifetime = 0;
3126 past = (u_int32_t)(now >= binding->updatetime ?
3127 now - binding->updatetime : 0);
3129 switch (binding->type) {
3130 case DHCP6_BINDING_IA:
3132 * Binding configuration is a list of IA parameters.
3133 * Determine the minimum valid lifetime.
3135 for (iav = TAILQ_FIRST(ia_list); iav;
3136 iav = TAILQ_NEXT(iav, link)) {
3137 u_int32_t lifetime;
3139 switch (binding->iatype) {
3140 case DHCP6_LISTVAL_IAPD:
3141 lifetime = iav->val_prefix6.vltime;
3142 break;
3143 case DHCP6_LISTVAL_IANA:
3144 lifetime = iav->val_statefuladdr6.vltime;
3145 break;
3146 default:
3147 dprintf(LOG_ERR, FNAME, "unsupported IA type");
3148 return; /* XXX */
3151 if (min_lifetime == 0 ||
3152 (lifetime != DHCP6_DURATION_INFINITE &&
3153 lifetime < min_lifetime))
3154 min_lifetime = lifetime;
3157 if (past < min_lifetime)
3158 duration = min_lifetime - past;
3159 else
3160 duration = 0;
3162 break;
3163 default:
3164 /* should be internal error. */
3165 dprintf(LOG_ERR, FNAME, "unknown binding type (%d)",
3166 binding->type);
3167 return;
3170 binding->duration = duration;
3173 static struct dhcp6_binding *
3174 add_binding(clientid, btype, iatype, iaid, val0)
3175 struct duid *clientid;
3176 dhcp6_bindingtype_t btype;
3177 int iatype;
3178 u_int32_t iaid;
3179 void *val0;
3181 struct dhcp6_binding *binding = NULL;
3182 u_int32_t duration = DHCP6_DURATION_INFINITE;
3184 if ((binding = malloc(sizeof(*binding))) == NULL) {
3185 dprintf(LOG_NOTICE, FNAME, "failed to allocate memory");
3186 return (NULL);
3188 memset(binding, 0, sizeof(*binding));
3189 binding->type = btype;
3190 if (duidcpy(&binding->clientid, clientid)) {
3191 dprintf(LOG_NOTICE, FNAME, "failed to copy DUID");
3192 goto fail;
3194 binding->iatype = iatype;
3195 binding->iaid = iaid;
3197 /* construct configuration information for this binding */
3198 switch (btype) {
3199 case DHCP6_BINDING_IA:
3200 TAILQ_INIT(&binding->val_list);
3201 if (dhcp6_copy_list(&binding->val_list,
3202 (struct dhcp6_list *)val0)) {
3203 dprintf(LOG_NOTICE, FNAME,
3204 "failed to copy binding data");
3205 goto fail;
3207 /* lease address */
3208 if (iatype == DHCP6_LISTVAL_IANA) {
3209 struct dhcp6_list *ia_list = &binding->val_list;
3210 struct dhcp6_listval *lv, *lv_next;
3212 for (lv = TAILQ_FIRST(ia_list); lv; lv = lv_next) {
3213 lv_next = TAILQ_NEXT(lv, link);
3215 if (lv->type != DHCP6_LISTVAL_STATEFULADDR6) {
3216 dprintf(LOG_ERR, FNAME,
3217 "unexpected binding value type(%d)", lv->type);
3218 continue;
3221 if (!lease_address(&lv->val_statefuladdr6.addr)) {
3222 dprintf(LOG_NOTICE, FNAME,
3223 "cannot lease address %s",
3224 in6addr2str(&lv->val_statefuladdr6.addr, 0));
3225 TAILQ_REMOVE(ia_list, lv, link);
3226 dhcp6_clear_listval(lv);
3229 if (TAILQ_EMPTY(ia_list)) {
3230 dprintf(LOG_NOTICE, FNAME, "cannot lease any address");
3231 goto fail;
3234 break;
3235 default:
3236 dprintf(LOG_ERR, FNAME, "unexpected binding type(%d)", btype);
3237 goto fail;
3240 /* calculate duration and start timer accordingly */
3241 binding->updatetime = time(NULL);
3242 update_binding_duration(binding);
3243 if (binding->duration != DHCP6_DURATION_INFINITE) {
3244 struct timeval timo;
3246 binding->timer = dhcp6_add_timer(binding_timo, binding);
3247 if (binding->timer == NULL) {
3248 dprintf(LOG_NOTICE, FNAME, "failed to add timer");
3249 goto fail;
3251 timo.tv_sec = (long)duration;
3252 timo.tv_usec = 0;
3253 dhcp6_set_timer(&timo, binding->timer);
3256 TAILQ_INSERT_TAIL(&dhcp6_binding_head, binding, link);
3258 dprintf(LOG_DEBUG, FNAME, "add a new binding %s", bindingstr(binding));
3260 return (binding);
3262 fail:
3263 if (binding)
3264 free_binding(binding);
3265 return (NULL);
3268 static struct dhcp6_binding *
3269 find_binding(clientid, btype, iatype, iaid)
3270 struct duid *clientid;
3271 dhcp6_bindingtype_t btype;
3272 int iatype;
3273 u_int32_t iaid;
3275 struct dhcp6_binding *bp;
3277 for (bp = TAILQ_FIRST(&dhcp6_binding_head); bp;
3278 bp = TAILQ_NEXT(bp, link)) {
3279 if (bp->type != btype || duidcmp(&bp->clientid, clientid))
3280 continue;
3282 if (btype == DHCP6_BINDING_IA &&
3283 (bp->iatype != iatype || bp->iaid != iaid))
3284 continue;
3286 return (bp);
3289 return (NULL);
3292 static void
3293 update_binding(binding)
3294 struct dhcp6_binding *binding;
3296 struct timeval timo;
3298 dprintf(LOG_DEBUG, FNAME, "update binding %s for %s",
3299 bindingstr(binding), duidstr(&binding->clientid));
3301 /* update timestamp and calculate new duration */
3302 binding->updatetime = time(NULL);
3303 update_binding_duration(binding);
3305 /* if the lease duration is infinite, there's nothing to do. */
3306 if (binding->duration == DHCP6_DURATION_INFINITE)
3307 return;
3309 /* reset the timer with the duration */
3310 timo.tv_sec = (long)binding->duration;
3311 timo.tv_usec = 0;
3312 dhcp6_set_timer(&timo, binding->timer);
3315 static void
3316 remove_binding(binding)
3317 struct dhcp6_binding *binding;
3319 dprintf(LOG_DEBUG, FNAME, "remove a binding %s",
3320 bindingstr(binding));
3322 if (binding->timer)
3323 dhcp6_remove_timer(&binding->timer);
3325 TAILQ_REMOVE(&dhcp6_binding_head, binding, link);
3327 free_binding(binding);
3330 static void
3331 free_binding(binding)
3332 struct dhcp6_binding *binding;
3334 duidfree(&binding->clientid);
3336 /* free configuration info in a type dependent manner. */
3337 switch (binding->type) {
3338 case DHCP6_BINDING_IA:
3339 /* releaes address */
3340 if (binding->iatype == DHCP6_LISTVAL_IANA) {
3341 struct dhcp6_list *ia_list = &binding->val_list;
3342 struct dhcp6_listval *lv;
3344 for (lv = TAILQ_FIRST(ia_list); lv; lv = TAILQ_NEXT(lv, link)) {
3345 if (lv->type != DHCP6_LISTVAL_STATEFULADDR6) {
3346 dprintf(LOG_ERR, FNAME,
3347 "unexpected binding value type(%d)", lv->type);
3348 continue;
3350 release_address(&lv->val_statefuladdr6.addr);
3353 dhcp6_clear_list(&binding->val_list);
3354 break;
3355 default:
3356 dprintf(LOG_ERR, FNAME, "unknown binding type %d",
3357 binding->type);
3358 break;
3361 free(binding);
3364 static struct dhcp6_timer *
3365 binding_timo(arg)
3366 void *arg;
3368 struct dhcp6_binding *binding = (struct dhcp6_binding *)arg;
3369 struct dhcp6_list *ia_list = &binding->val_list;
3370 struct dhcp6_listval *iav, *iav_next;
3371 time_t now = time(NULL);
3372 u_int32_t past, lifetime;
3373 struct timeval timo;
3375 past = (u_int32_t)(now >= binding->updatetime ?
3376 now - binding->updatetime : 0);
3378 switch (binding->type) {
3379 case DHCP6_BINDING_IA:
3380 for (iav = TAILQ_FIRST(ia_list); iav; iav = iav_next) {
3381 iav_next = TAILQ_NEXT(iav, link);
3383 switch (binding->iatype) {
3384 case DHCP6_LISTVAL_IAPD:
3385 case DHCP6_LISTVAL_IANA:
3386 lifetime = iav->val_prefix6.vltime;
3387 break;
3388 default:
3389 dprintf(LOG_ERR, FNAME, "internal error: "
3390 "unknown binding type (%d)",
3391 binding->iatype);
3392 return (NULL); /* XXX */
3395 if (lifetime != DHCP6_DURATION_INFINITE &&
3396 lifetime <= past) {
3397 dprintf(LOG_DEBUG, FNAME, "bound prefix %s/%d"
3398 " in %s has expired",
3399 in6addr2str(&iav->val_prefix6.addr, 0),
3400 iav->val_prefix6.plen,
3401 bindingstr(binding));
3402 if (binding->iatype == DHCP6_LISTVAL_IANA)
3403 release_address(&iav->val_prefix6.addr);
3404 TAILQ_REMOVE(ia_list, iav, link);
3405 dhcp6_clear_listval(iav);
3409 /* If all IA parameters have expired, remove the binding. */
3410 if (TAILQ_EMPTY(ia_list)) {
3411 remove_binding(binding);
3412 return (NULL);
3415 break;
3416 default:
3417 dprintf(LOG_ERR, FNAME, "unknown binding type %d",
3418 binding->type);
3419 return (NULL); /* XXX */
3422 update_binding_duration(binding);
3424 /* if the lease duration is infinite, there's nothing to do. */
3425 if (binding->duration == DHCP6_DURATION_INFINITE)
3426 return (NULL);
3428 /* reset the timer with the duration */
3429 timo.tv_sec = (long)binding->duration;
3430 timo.tv_usec = 0;
3431 dhcp6_set_timer(&timo, binding->timer);
3433 return (binding->timer);
3436 static struct dhcp6_listval *
3437 find_binding_ia(key, binding)
3438 struct dhcp6_listval *key;
3439 struct dhcp6_binding *binding;
3441 struct dhcp6_list *ia_list = &binding->val_list;
3443 switch (binding->type) {
3444 case DHCP6_BINDING_IA:
3445 return (dhcp6_find_listval(ia_list, key->type, &key->uv, 0));
3446 default:
3447 dprintf(LOG_ERR, FNAME, "unknown binding type %d",
3448 binding->type);
3449 return (NULL); /* XXX */
3453 static char *
3454 bindingstr(binding)
3455 struct dhcp6_binding *binding;
3457 static char strbuf[LINE_MAX]; /* XXX: thread unsafe */
3458 char *iatype = NULL;
3460 switch (binding->type) {
3461 case DHCP6_BINDING_IA:
3462 switch (binding->iatype) {
3463 case DHCP6_LISTVAL_IAPD:
3464 iatype = "PD";
3465 break;
3466 case DHCP6_LISTVAL_IANA:
3467 iatype = "NA";
3468 break;
3471 snprintf(strbuf, sizeof(strbuf),
3472 "[IA: duid=%s, type=%s, iaid=%lu, duration=%lu]",
3473 duidstr(&binding->clientid), iatype, (u_long)binding->iaid,
3474 (u_long)binding->duration);
3475 break;
3476 default:
3477 dprintf(LOG_ERR, FNAME, "unexpected binding type(%d)",
3478 binding->type);
3479 return ("???");
3482 return (strbuf);
3485 static int
3486 process_auth(dh6, len, client_conf, optinfo, roptinfo)
3487 struct dhcp6 *dh6;
3488 ssize_t len;
3489 struct host_conf *client_conf;
3490 struct dhcp6_optinfo *optinfo, *roptinfo;
3492 u_int8_t msgtype = dh6->dh6_msgtype;
3493 int authenticated = 0;
3494 struct keyinfo *key;
3497 * if the client wanted DHCPv6 authentication, check if a secret
3498 * key is available for the client.
3500 switch (optinfo->authproto) {
3501 case DHCP6_AUTHPROTO_UNDEF:
3503 * The client did not include authentication option. What if
3504 * we had sent authentication information? The specification
3505 * is not clear, but we should probably accept it, since the
3506 * client MAY ignore the information in advertise messages.
3508 return (0);
3509 case DHCP6_AUTHPROTO_DELAYED:
3510 if (optinfo->authalgorithm != DHCP6_AUTHALG_HMACMD5) {
3511 dprintf(LOG_INFO, FNAME, "unknown authentication "
3512 "algorithm (%d) required by %s",
3513 optinfo->authalgorithm,
3514 clientstr(client_conf, &optinfo->clientID));
3515 break; /* give up with this authentication */
3518 if (optinfo->authrdm != DHCP6_AUTHRDM_MONOCOUNTER) {
3519 dprintf(LOG_INFO, FNAME,
3520 "unknown RDM (%d) required by %s",
3521 optinfo->authrdm,
3522 clientstr(client_conf, &optinfo->clientID));
3523 break; /* give up with this authentication */
3526 /* see if we have a key for the client */
3527 if (client_conf == NULL || client_conf->delayedkey == NULL) {
3528 dprintf(LOG_INFO, FNAME, "client %s wanted "
3529 "authentication, but no key found",
3530 clientstr(client_conf, &optinfo->clientID));
3531 break;
3533 key = client_conf->delayedkey;
3534 dprintf(LOG_DEBUG, FNAME, "found key %s for client %s",
3535 key->name, clientstr(client_conf, &optinfo->clientID));
3537 if (msgtype == DH6_SOLICIT) {
3538 if (!(optinfo->authflags & DHCP6OPT_AUTHFLAG_NOINFO)) {
3540 * A solicit message should not contain
3541 * authentication information.
3543 dprintf(LOG_INFO, FNAME,
3544 "authentication information "
3545 "provided in solicit from %s",
3546 clientstr(client_conf,
3547 &optinfo->clientID));
3548 /* accept it anyway. (or discard?) */
3550 } else {
3551 /* replay protection */
3552 if (!client_conf->saw_previous_rd) {
3553 dprintf(LOG_WARNING, FNAME,
3554 "previous RD value for %s is unknown "
3555 "(accept it)", clientstr(client_conf,
3556 &optinfo->clientID));
3557 } else {
3558 if (dhcp6_auth_replaycheck(optinfo->authrdm,
3559 client_conf->previous_rd,
3560 optinfo->authrd)) {
3561 dprintf(LOG_INFO, FNAME,
3562 "possible replay attack detected "
3563 "for client %s",
3564 clientstr(client_conf,
3565 &optinfo->clientID));
3566 break;
3570 if ((optinfo->authflags & DHCP6OPT_AUTHFLAG_NOINFO)) {
3571 dprintf(LOG_INFO, FNAME,
3572 "client %s did not provide authentication "
3573 "information in %s",
3574 clientstr(client_conf, &optinfo->clientID),
3575 dhcp6msgstr(msgtype));
3576 break;
3580 * The client MUST use the same key used by the server
3581 * to generate the authentication information.
3582 * [RFC3315 Section 21.4.4.3]
3583 * The RFC does not say what the server should do if
3584 * the client breaks this rule, but it should be
3585 * natural to interpret this as authentication failure.
3587 if (optinfo->delayedauth_keyid != key->keyid ||
3588 optinfo->delayedauth_realmlen != key->realmlen ||
3589 memcmp(optinfo->delayedauth_realmval, key->realm,
3590 key->realmlen) != 0) {
3591 dprintf(LOG_INFO, FNAME, "authentication key "
3592 "mismatch with client %s",
3593 clientstr(client_conf,
3594 &optinfo->clientID));
3595 break;
3598 /* check for the key lifetime */
3599 if (dhcp6_validate_key(key)) {
3600 dprintf(LOG_INFO, FNAME, "key %s has expired",
3601 key->name);
3602 break;
3605 /* validate MAC */
3606 if (dhcp6_verify_mac((char *)dh6, len,
3607 optinfo->authproto, optinfo->authalgorithm,
3608 optinfo->delayedauth_offset + sizeof(*dh6), key)
3609 == 0) {
3610 dprintf(LOG_DEBUG, FNAME,
3611 "message authentication validated for "
3612 "client %s", clientstr(client_conf,
3613 &optinfo->clientID));
3614 } else {
3615 dprintf(LOG_INFO, FNAME, "invalid message "
3616 "authentication");
3617 break;
3621 roptinfo->authproto = optinfo->authproto;
3622 roptinfo->authalgorithm = optinfo->authalgorithm;
3623 roptinfo->authrdm = optinfo->authrdm;
3625 if (get_rdvalue(roptinfo->authrdm, &roptinfo->authrd,
3626 sizeof(roptinfo->authrd))) {
3627 dprintf(LOG_ERR, FNAME, "failed to get a replay "
3628 "detection value for %s",
3629 clientstr(client_conf, &optinfo->clientID));
3630 break; /* XXX: try to recover? */
3633 roptinfo->delayedauth_keyid = key->keyid;
3634 roptinfo->delayedauth_realmlen = key->realmlen;
3635 roptinfo->delayedauth_realmval =
3636 malloc(roptinfo->delayedauth_realmlen);
3637 if (roptinfo->delayedauth_realmval == NULL) {
3638 dprintf(LOG_ERR, FNAME, "failed to allocate memory "
3639 "for authentication realm for %s",
3640 clientstr(client_conf, &optinfo->clientID));
3641 break;
3643 memcpy(roptinfo->delayedauth_realmval, key->realm,
3644 roptinfo->delayedauth_realmlen);
3646 authenticated = 1;
3648 break;
3649 default:
3650 dprintf(LOG_INFO, FNAME, "client %s wanted authentication "
3651 "with unsupported protocol (%d)",
3652 clientstr(client_conf, &optinfo->clientID),
3653 optinfo->authproto);
3654 return (-1); /* or simply ignore it? */
3657 if (authenticated == 0) {
3658 if (msgtype != DH6_SOLICIT) {
3660 * If the message fails to pass the validation test,
3661 * the server MUST discard the message.
3662 * [RFC3315 Section 21.4.5.2]
3664 return (-1);
3666 } else {
3667 /* Message authenticated. Update RD counter. */
3668 if (msgtype != DH6_SOLICIT && client_conf != NULL) {
3669 client_conf->previous_rd = optinfo->authrd;
3670 client_conf->saw_previous_rd = 1;
3674 return (0);
3677 static inline char *
3678 clientstr(conf, duid)
3679 struct host_conf *conf;
3680 struct duid *duid;
3682 if (conf != NULL)
3683 return (conf->name);
3685 return (duidstr(duid));