minidlna support now Samsung TV C550/C650 (thx amir909)
[tomato.git] / release / src / router / dhcpv6 / common.c
blob9481dcca848be59a6a5a4862fbb19c7505c4737d
1 /* $KAME: common.c,v 1.129 2005/09/16 11:30:13 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/stat.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 <net/if.h>
47 #include <netinet/in.h>
48 #ifdef __KAME__
49 #include <net/if_types.h>
50 #ifdef __FreeBSD__
51 #include <net/if_var.h>
52 #endif
53 #include <net/if_dl.h>
54 #endif
55 #ifdef __linux__
56 #include <linux/if_packet.h>
57 #endif
58 #include <net/if_arp.h>
59 #ifdef __sun__
60 #include <sys/sockio.h>
61 #include <sys/dlpi.h>
62 #include <stropts.h>
63 #include <fcntl.h>
64 #include <libdevinfo.h>
65 #endif
67 #ifdef __KAME__
68 #include <netinet6/in6_var.h>
69 #endif
71 #include <ctype.h>
72 #include <errno.h>
73 #include <limits.h>
74 #include <stdio.h>
75 #include <stdarg.h>
76 #include <syslog.h>
77 #include <stdlib.h>
78 #include <unistd.h>
79 #include <string.h>
80 #include <err.h>
81 #include <netdb.h>
82 #include <ifaddrs.h>
84 #include <dhcp6.h>
85 #include <config.h>
86 #include <common.h>
87 #include <timer.h>
89 #ifdef __linux__
90 /* from /usr/include/linux/ipv6.h */
92 struct in6_ifreq {
93 struct in6_addr ifr6_addr;
94 u_int32_t ifr6_prefixlen;
95 unsigned int ifr6_ifindex;
97 #endif
99 #define MAXDNAME 255
101 int foreground;
102 int debug_thresh;
103 int duid_type = 1;
105 static int dhcp6_count_list __P((struct dhcp6_list *));
106 static int in6_matchflags __P((struct sockaddr *, char *, int));
107 static ssize_t dnsencode __P((const char *, char *, size_t));
108 static char *dnsdecode __P((u_char **, u_char *, char *, size_t));
109 static int copyout_option __P((char *, char *, struct dhcp6_listval *));
110 static int copyin_option __P((int, struct dhcp6opt *, struct dhcp6opt *,
111 struct dhcp6_list *));
112 static int copy_option __P((u_int16_t, u_int16_t, void *, struct dhcp6opt **,
113 struct dhcp6opt *, int *));
114 static ssize_t gethwid __P((char *, int, const char *, u_int16_t *));
115 static char *sprint_uint64 __P((char *, int, u_int64_t));
116 static char *sprint_auth __P((struct dhcp6_optinfo *));
119 dhcp6_copy_list(dst, src)
120 struct dhcp6_list *dst, *src;
122 struct dhcp6_listval *ent;
124 for (ent = TAILQ_FIRST(src); ent; ent = TAILQ_NEXT(ent, link)) {
125 if (dhcp6_add_listval(dst, ent->type,
126 &ent->uv, &ent->sublist) == NULL)
127 goto fail;
130 return (0);
132 fail:
133 dhcp6_clear_list(dst);
134 return (-1);
137 void
138 dhcp6_move_list(dst, src)
139 struct dhcp6_list *dst, *src;
141 struct dhcp6_listval *v;
143 while ((v = TAILQ_FIRST(src)) != NULL) {
144 TAILQ_REMOVE(src, v, link);
145 TAILQ_INSERT_TAIL(dst, v, link);
149 void
150 dhcp6_clear_list(head)
151 struct dhcp6_list *head;
153 struct dhcp6_listval *v;
155 while ((v = TAILQ_FIRST(head)) != NULL) {
156 TAILQ_REMOVE(head, v, link);
157 dhcp6_clear_listval(v);
160 return;
163 static int
164 dhcp6_count_list(head)
165 struct dhcp6_list *head;
167 struct dhcp6_listval *v;
168 int i;
170 for (i = 0, v = TAILQ_FIRST(head); v; v = TAILQ_NEXT(v, link))
171 i++;
173 return (i);
176 void
177 dhcp6_clear_listval(lv)
178 struct dhcp6_listval *lv;
180 dhcp6_clear_list(&lv->sublist);
181 switch (lv->type) {
182 case DHCP6_LISTVAL_VBUF:
183 dhcp6_vbuf_free(&lv->val_vbuf);
184 break;
185 default: /* nothing to do */
186 break;
188 free(lv);
192 * Note: this function only searches for the first entry that matches
193 * VAL. It also does not care about sublists.
195 struct dhcp6_listval *
196 dhcp6_find_listval(head, type, val, option)
197 struct dhcp6_list *head;
198 dhcp6_listval_type_t type;
199 void *val;
200 int option;
202 struct dhcp6_listval *lv;
204 for (lv = TAILQ_FIRST(head); lv; lv = TAILQ_NEXT(lv, link)) {
205 if (lv->type != type)
206 continue;
208 switch(type) {
209 case DHCP6_LISTVAL_NUM:
210 if (lv->val_num == *(int *)val)
211 return (lv);
212 break;
213 case DHCP6_LISTVAL_STCODE:
214 if (lv->val_num16 == *(u_int16_t *)val)
215 return (lv);
216 break;
217 case DHCP6_LISTVAL_ADDR6:
218 if (IN6_ARE_ADDR_EQUAL(&lv->val_addr6,
219 (struct in6_addr *)val)) {
220 return (lv);
222 break;
223 case DHCP6_LISTVAL_PREFIX6:
224 if ((option & MATCHLIST_PREFIXLEN) &&
225 lv->val_prefix6.plen ==
226 ((struct dhcp6_prefix *)val)->plen) {
227 return (lv);
228 } else if (IN6_ARE_ADDR_EQUAL(&lv->val_prefix6.addr,
229 &((struct dhcp6_prefix *)val)->addr) &&
230 lv->val_prefix6.plen ==
231 ((struct dhcp6_prefix *)val)->plen) {
232 return (lv);
234 break;
235 case DHCP6_LISTVAL_STATEFULADDR6:
236 if (IN6_ARE_ADDR_EQUAL(&lv->val_statefuladdr6.addr,
237 &((struct dhcp6_prefix *)val)->addr)) {
238 return (lv);
240 break;
241 case DHCP6_LISTVAL_IAPD:
242 case DHCP6_LISTVAL_IANA:
243 if (lv->val_ia.iaid ==
244 ((struct dhcp6_ia *)val)->iaid) {
245 return (lv);
247 break;
248 case DHCP6_LISTVAL_VBUF:
249 if (dhcp6_vbuf_cmp(&lv->val_vbuf,
250 (struct dhcp6_vbuf *)val) == 0) {
251 return (lv);
253 break;
257 return (NULL);
260 struct dhcp6_listval *
261 dhcp6_add_listval(head, type, val, sublist)
262 struct dhcp6_list *head, *sublist;
263 dhcp6_listval_type_t type;
264 void *val;
266 struct dhcp6_listval *lv = NULL;
268 if ((lv = malloc(sizeof(*lv))) == NULL) {
269 dprintf(LOG_ERR, FNAME,
270 "failed to allocate memory for list entry");
271 goto fail;
273 memset(lv, 0, sizeof(*lv));
274 lv->type = type;
275 TAILQ_INIT(&lv->sublist);
277 switch(type) {
278 case DHCP6_LISTVAL_NUM:
279 lv->val_num = *(int *)val;
280 break;
281 case DHCP6_LISTVAL_STCODE:
282 lv->val_num16 = *(u_int16_t *)val;
283 break;
284 case DHCP6_LISTVAL_ADDR6:
285 lv->val_addr6 = *(struct in6_addr *)val;
286 break;
287 case DHCP6_LISTVAL_PREFIX6:
288 lv->val_prefix6 = *(struct dhcp6_prefix *)val;
289 break;
290 case DHCP6_LISTVAL_STATEFULADDR6:
291 lv->val_statefuladdr6 = *(struct dhcp6_statefuladdr *)val;
292 break;
293 case DHCP6_LISTVAL_IAPD:
294 case DHCP6_LISTVAL_IANA:
295 lv->val_ia = *(struct dhcp6_ia *)val;
296 break;
297 case DHCP6_LISTVAL_VBUF:
298 if (dhcp6_vbuf_copy(&lv->val_vbuf, (struct dhcp6_vbuf *)val))
299 goto fail;
300 break;
301 default:
302 dprintf(LOG_ERR, FNAME,
303 "unexpected list value type (%d)", type);
304 goto fail;
307 if (sublist && dhcp6_copy_list(&lv->sublist, sublist))
308 goto fail;
310 TAILQ_INSERT_TAIL(head, lv, link);
312 return (lv);
314 fail:
315 if (lv)
316 free(lv);
318 return (NULL);
322 dhcp6_vbuf_copy(dst, src)
323 struct dhcp6_vbuf *dst, *src;
325 dst->dv_buf = malloc(src->dv_len);
326 if (dst->dv_buf == NULL)
327 return (-1);
329 dst->dv_len = src->dv_len;
330 memcpy(dst->dv_buf, src->dv_buf, dst->dv_len);
332 return (0);
335 void
336 dhcp6_vbuf_free(vbuf)
337 struct dhcp6_vbuf *vbuf;
339 free(vbuf->dv_buf);
341 vbuf->dv_len = 0;
342 vbuf->dv_buf = NULL;
346 dhcp6_vbuf_cmp(vb1, vb2)
347 struct dhcp6_vbuf *vb1, *vb2;
349 if (vb1->dv_len != vb2->dv_len)
350 return (vb1->dv_len - vb2->dv_len);
352 return (memcmp(vb1->dv_buf, vb2->dv_buf, vb1->dv_len));
355 static int
356 dhcp6_get_addr(optlen, cp, type, list)
357 int optlen;
358 void *cp;
359 dhcp6_listval_type_t type;
360 struct dhcp6_list *list;
362 void *val;
364 if (optlen % sizeof(struct in6_addr) || optlen == 0) {
365 dprintf(LOG_INFO, FNAME,
366 "malformed DHCP option: type %d, len %d", type, optlen);
367 return -1;
369 for (val = cp; val < cp + optlen; val += sizeof(struct in6_addr)) {
370 struct in6_addr valaddr;
372 memcpy(&valaddr, val, sizeof(valaddr));
373 if (dhcp6_find_listval(list,
374 DHCP6_LISTVAL_ADDR6, &valaddr, 0)) {
375 dprintf(LOG_INFO, FNAME, "duplicated %s address (%s)",
376 dhcp6optstr(type), in6addr2str(&valaddr, 0));
377 continue;
380 if (dhcp6_add_listval(list, DHCP6_LISTVAL_ADDR6,
381 &valaddr, NULL) == NULL) {
382 dprintf(LOG_ERR, FNAME,
383 "failed to copy %s address", dhcp6optstr(type));
384 return -1;
388 return 0;
391 static int
392 dhcp6_set_addr(type, list, p, optep, len)
393 dhcp6_listval_type_t type;
394 struct dhcp6_list *list;
395 struct dhcp6opt **p, *optep;
396 int *len;
398 struct in6_addr *in6;
399 char *tmpbuf;
400 struct dhcp6_listval *d;
401 int optlen;
403 if (TAILQ_EMPTY(list))
404 return 0;
406 tmpbuf = NULL;
407 optlen = dhcp6_count_list(list) * sizeof(struct in6_addr);
408 if ((tmpbuf = malloc(optlen)) == NULL) {
409 dprintf(LOG_ERR, FNAME,
410 "memory allocation failed for %s options",
411 dhcp6optstr(type));
412 return -1;
414 in6 = (struct in6_addr *)tmpbuf;
415 for (d = TAILQ_FIRST(list); d; d = TAILQ_NEXT(d, link), in6++)
416 memcpy(in6, &d->val_addr6, sizeof(*in6));
417 if (copy_option(type, optlen, tmpbuf, p, optep, len) != 0) {
418 free(tmpbuf);
419 return -1;
422 free(tmpbuf);
423 return 0;
426 static int
427 dhcp6_get_domain(optlen, cp, type, list)
428 int optlen;
429 void *cp;
430 dhcp6_listval_type_t type;
431 struct dhcp6_list *list;
433 void *val;
435 val = cp;
436 while (val < cp + optlen) {
437 struct dhcp6_vbuf vb;
438 char name[MAXDNAME + 1];
440 if (dnsdecode((u_char **)(void *)&val,
441 (u_char *)(cp + optlen), name, sizeof(name)) == NULL) {
442 dprintf(LOG_INFO, FNAME, "failed to "
443 "decode a %s domain name",
444 dhcp6optstr(type));
445 dprintf(LOG_INFO, FNAME,
446 "malformed DHCP option: type %d, len %d",
447 type, optlen);
448 return -1;
451 vb.dv_len = strlen(name) + 1;
452 vb.dv_buf = name;
454 if (dhcp6_add_listval(list,
455 DHCP6_LISTVAL_VBUF, &vb, NULL) == NULL) {
456 dprintf(LOG_ERR, FNAME, "failed to "
457 "copy a %s domain name", dhcp6optstr(type));
458 return -1;
462 return 0;
465 static int
466 dhcp6_set_domain(type, list, p, optep, len)
467 dhcp6_listval_type_t type;
468 struct dhcp6_list *list;
469 struct dhcp6opt **p, *optep;
470 int *len;
472 int optlen = 0;
473 struct dhcp6_listval *d;
474 char *tmpbuf;
475 char name[MAXDNAME], *cp, *ep;
477 if (TAILQ_EMPTY(list))
478 return 0;
480 for (d = TAILQ_FIRST(list); d; d = TAILQ_NEXT(d, link))
481 optlen += (d->val_vbuf.dv_len + 1);
483 if (optlen == 0) {
484 return 0;
487 tmpbuf = NULL;
488 if ((tmpbuf = malloc(optlen)) == NULL) {
489 dprintf(LOG_ERR, FNAME, "memory allocation failed for "
490 "%s domain options", dhcp6optstr(type));
491 return -1;
493 cp = tmpbuf;
494 ep = cp + optlen;
495 for (d = TAILQ_FIRST(list); d; d = TAILQ_NEXT(d, link)) {
496 int nlen;
498 nlen = dnsencode((const char *)d->val_vbuf.dv_buf,
499 name, sizeof (name));
500 if (nlen < 0) {
501 dprintf(LOG_ERR, FNAME,
502 "failed to encode a %s domain name",
503 dhcp6optstr(type));
504 free(tmpbuf);
505 return -1;
507 if (ep - cp < nlen) {
508 dprintf(LOG_ERR, FNAME,
509 "buffer length for %s domain name is too short",
510 dhcp6optstr(type));
511 free(tmpbuf);
512 return -1;
514 memcpy(cp, name, nlen);
515 cp += nlen;
517 if (copy_option(type, cp - tmpbuf, tmpbuf, p, optep, len) != 0) {
518 free(tmpbuf);
519 return -1;
521 free(tmpbuf);
523 return 0;
526 struct dhcp6_event *
527 dhcp6_create_event(ifp, state)
528 struct dhcp6_if *ifp;
529 int state;
531 struct dhcp6_event *ev;
533 if ((ev = malloc(sizeof(*ev))) == NULL) {
534 dprintf(LOG_ERR, FNAME,
535 "failed to allocate memory for an event");
536 return (NULL);
538 memset(ev, 0, sizeof(*ev));
539 ev->ifp = ifp;
540 ev->state = state;
541 TAILQ_INIT(&ev->data_list);
543 return (ev);
546 void
547 dhcp6_remove_event(ev)
548 struct dhcp6_event *ev;
550 struct dhcp6_serverinfo *sp, *sp_next;
552 dprintf(LOG_DEBUG, FNAME, "removing an event on %s, state=%s",
553 ev->ifp->ifname, dhcp6_event_statestr(ev));
555 dhcp6_remove_evdata(ev);
557 duidfree(&ev->serverid);
559 if (ev->timer)
560 dhcp6_remove_timer(&ev->timer);
561 TAILQ_REMOVE(&ev->ifp->event_list, ev, link);
563 for (sp = ev->servers; sp; sp = sp_next) {
564 sp_next = sp->next;
566 dprintf(LOG_DEBUG, FNAME, "removing server (ID: %s)",
567 duidstr(&sp->optinfo.serverID));
568 dhcp6_clear_options(&sp->optinfo);
569 if (sp->authparam != NULL)
570 free(sp->authparam);
571 free(sp);
574 if (ev->authparam != NULL)
575 free(ev->authparam);
577 free(ev);
580 void
581 dhcp6_remove_evdata(ev)
582 struct dhcp6_event *ev;
584 struct dhcp6_eventdata *evd;
586 while ((evd = TAILQ_FIRST(&ev->data_list)) != NULL) {
587 TAILQ_REMOVE(&ev->data_list, evd, link);
588 if (evd->destructor)
589 (*evd->destructor)(evd);
590 free(evd);
594 struct authparam *
595 new_authparam(proto, alg, rdm)
596 int proto, alg, rdm;
598 struct authparam *authparam;
600 if ((authparam = malloc(sizeof(*authparam))) == NULL)
601 return (NULL);
603 memset(authparam, 0, sizeof(*authparam));
605 authparam->authproto = proto;
606 authparam->authalgorithm = alg;
607 authparam->authrdm = rdm;
608 authparam->key = NULL;
609 authparam->flags |= AUTHPARAM_FLAGS_NOPREVRD;
610 authparam->prevrd = 0;
612 return (authparam);
615 struct authparam *
616 copy_authparam(authparam)
617 struct authparam *authparam;
619 struct authparam *dst;
621 if ((dst = malloc(sizeof(*dst))) == NULL)
622 return (NULL);
624 memcpy(dst, authparam, sizeof(*dst));
626 return (dst);
630 * Home-brew function of a 64-bit version of ntohl.
631 * XXX: is there any standard for this?
633 #if (BYTE_ORDER == LITTLE_ENDIAN)
634 static __inline u_int64_t
635 ntohq(u_int64_t x)
637 return (u_int64_t)ntohl((u_int32_t)(x >> 32)) |
638 (int64_t)ntohl((u_int32_t)(x & 0xffffffff)) << 32;
640 #else /* (BYTE_ORDER == LITTLE_ENDIAN) */
641 #define ntohq(x) (x)
642 #endif
645 dhcp6_auth_replaycheck(method, prev, current)
646 int method;
647 u_int64_t prev, current;
649 char bufprev[] = "ffff ffff ffff ffff";
650 char bufcurrent[] = "ffff ffff ffff ffff";
652 if (method != DHCP6_AUTHRDM_MONOCOUNTER) {
653 dprintf(LOG_ERR, FNAME, "unsupported replay detection "
654 "method (%d)", method);
655 return (-1);
658 (void)sprint_uint64(bufprev, sizeof(bufprev), prev);
659 (void)sprint_uint64(bufcurrent, sizeof(bufcurrent), current);
660 dprintf(LOG_DEBUG, FNAME, "previous: %s, current: %s",
661 bufprev, bufcurrent);
663 prev = ntohq(prev);
664 current = ntohq(current);
667 * we call the singular point guilty, since we cannot guess
668 * whether the serial number is increasing or not.
670 if (prev == (current ^ 0x8000000000000000ULL)) {
671 dprintf(LOG_INFO, FNAME, "detected a singular point");
672 return (1);
675 return (((int64_t)(current - prev) > 0) ? 0 : 1);
679 getifaddr(addr, ifnam, prefix, plen, strong, ignoreflags)
680 struct in6_addr *addr;
681 char *ifnam;
682 struct in6_addr *prefix;
683 int plen;
684 int strong; /* if strong host model is required or not */
685 int ignoreflags;
687 struct ifaddrs *ifap, *ifa;
688 struct sockaddr_in6 sin6;
689 int error = -1;
691 if (getifaddrs(&ifap) != 0) {
692 dprintf(LOG_WARNING, FNAME,
693 "getifaddrs failed: %s", strerror(errno));
694 return (-1);
697 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
698 int s1, s2;
700 if (strong && strcmp(ifnam, ifa->ifa_name) != 0)
701 continue;
703 /* in any case, ignore interfaces in different scope zones. */
704 if ((s1 = in6_addrscopebyif(prefix, ifnam)) < 0 ||
705 (s2 = in6_addrscopebyif(prefix, ifa->ifa_name)) < 0 ||
706 s1 != s2)
707 continue;
709 if (ifa->ifa_addr->sa_family != AF_INET6)
710 continue;
711 #ifdef HAVE_SA_LEN
712 if (ifa->ifa_addr->sa_len > sizeof(sin6))
713 continue;
714 #endif
716 if (in6_matchflags(ifa->ifa_addr, ifa->ifa_name, ignoreflags))
717 continue;
719 memcpy(&sin6, ifa->ifa_addr, sysdep_sa_len(ifa->ifa_addr));
720 #ifdef __KAME__
721 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) {
722 sin6.sin6_addr.s6_addr[2] = 0;
723 sin6.sin6_addr.s6_addr[3] = 0;
725 #endif
726 if (plen % 8 == 0) {
727 if (memcmp(&sin6.sin6_addr, prefix, plen / 8) != 0)
728 continue;
729 } else {
730 struct in6_addr a, m;
731 int i;
733 memcpy(&a, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
734 memset(&m, 0, sizeof(m));
735 memset(&m, 0xff, plen / 8);
736 m.s6_addr[plen / 8] = (0xff00 >> (plen % 8)) & 0xff;
737 for (i = 0; i < sizeof(a); i++)
738 a.s6_addr[i] &= m.s6_addr[i];
740 if (memcmp(&a, prefix, plen / 8) != 0 ||
741 a.s6_addr[plen / 8] !=
742 (prefix->s6_addr[plen / 8] & m.s6_addr[plen / 8]))
743 continue;
745 memcpy(addr, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
746 #ifdef __KAME__
747 if (IN6_IS_ADDR_LINKLOCAL(addr))
748 addr->s6_addr[2] = addr->s6_addr[3] = 0;
749 #endif
750 error = 0;
751 break;
754 freeifaddrs(ifap);
755 return (error);
759 getifidfromaddr(addr, ifidp)
760 struct in6_addr *addr;
761 unsigned int *ifidp;
763 struct ifaddrs *ifap, *ifa;
764 struct sockaddr_in6 *sa6;
765 unsigned int ifid;
766 int retval = -1;
768 if (getifaddrs(&ifap) != 0) {
769 dprintf(LOG_WARNING, FNAME,
770 "getifaddrs failed: %s", strerror(errno));
771 return (-1);
774 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
775 if (ifa->ifa_addr->sa_family != AF_INET6)
776 continue;
778 sa6 = (struct sockaddr_in6 *)ifa->ifa_addr;
779 if (IN6_ARE_ADDR_EQUAL(addr, &sa6->sin6_addr))
780 break;
783 if (ifa != NULL) {
784 if ((ifid = if_nametoindex(ifa->ifa_name)) == 0) {
785 dprintf(LOG_ERR, FNAME,
786 "if_nametoindex failed for %s", ifa->ifa_name);
787 goto end;
789 retval = 0;
790 *ifidp = ifid;
793 end:
794 freeifaddrs(ifap);
795 return (retval);
799 in6_addrscopebyif(addr, ifnam)
800 struct in6_addr *addr;
801 char *ifnam;
803 u_int ifindex;
805 if ((ifindex = if_nametoindex(ifnam)) == 0)
806 return (-1);
808 if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_MC_LINKLOCAL(addr))
809 return (ifindex);
811 if (IN6_IS_ADDR_SITELOCAL(addr) || IN6_IS_ADDR_MC_SITELOCAL(addr))
812 return (1); /* XXX */
814 if (IN6_IS_ADDR_MC_ORGLOCAL(addr))
815 return (1); /* XXX */
817 return (1); /* treat it as global */
821 transmit_sa(s, sa, buf, len)
822 int s;
823 struct sockaddr *sa;
824 char *buf;
825 size_t len;
827 int error;
829 error = sendto(s, buf, len, 0, sa, sysdep_sa_len(sa));
831 return (error != len) ? -1 : 0;
834 long
835 random_between(x, y)
836 long x;
837 long y;
839 long ratio;
841 ratio = 1 << 16;
842 while ((y - x) * ratio < (y - x))
843 ratio = ratio / 2;
844 return (x + ((y - x) * (ratio - 1) / random() & (ratio - 1)));
848 prefix6_mask(in6, plen)
849 struct in6_addr *in6;
850 int plen;
852 struct sockaddr_in6 mask6;
853 int i;
855 if (sa6_plen2mask(&mask6, plen))
856 return (-1);
858 for (i = 0; i < 16; i++)
859 in6->s6_addr[i] &= mask6.sin6_addr.s6_addr[i];
861 return (0);
865 sa6_plen2mask(sa6, plen)
866 struct sockaddr_in6 *sa6;
867 int plen;
869 u_char *cp;
871 if (plen < 0 || plen > 128)
872 return (-1);
874 memset(sa6, 0, sizeof(*sa6));
875 sa6->sin6_family = AF_INET6;
876 #ifdef HAVE_SA_LEN
877 sa6->sin6_len = sizeof(*sa6);
878 #endif
880 for (cp = (u_char *)&sa6->sin6_addr; plen > 7; plen -= 8)
881 *cp++ = 0xff;
882 *cp = 0xff << (8 - plen);
884 return (0);
887 char *
888 addr2str(sa)
889 struct sockaddr *sa;
891 static char addrbuf[8][NI_MAXHOST];
892 static int round = 0;
893 char *cp;
895 round = (round + 1) & 7;
896 cp = addrbuf[round];
898 getnameinfo(sa, sysdep_sa_len(sa), cp, NI_MAXHOST,
899 NULL, 0, NI_NUMERICHOST);
901 return (cp);
904 char *
905 in6addr2str(in6, scopeid)
906 struct in6_addr *in6;
907 int scopeid;
909 struct sockaddr_in6 sa6;
911 memset(&sa6, 0, sizeof(sa6));
912 sa6.sin6_family = AF_INET6;
913 #ifdef HAVE_SA_LEN
914 sa6.sin6_len = sizeof(sa6);
915 #endif
916 sa6.sin6_addr = *in6;
917 sa6.sin6_scope_id = scopeid;
919 return (addr2str((struct sockaddr *)&sa6));
922 /* return IPv6 address scope type. caller assumes that smaller is narrower. */
924 in6_scope(addr)
925 struct in6_addr *addr;
927 int scope;
929 if (addr->s6_addr[0] == 0xfe) {
930 scope = addr->s6_addr[1] & 0xc0;
932 switch (scope) {
933 case 0x80:
934 return (2); /* link-local */
935 break;
936 case 0xc0:
937 return (5); /* site-local */
938 break;
939 default:
940 return (14); /* global: just in case */
941 break;
945 /* multicast scope. just return the scope field */
946 if (addr->s6_addr[0] == 0xff)
947 return (addr->s6_addr[1] & 0x0f);
949 if (bcmp(&in6addr_loopback, addr, sizeof(addr) - 1) == 0) {
950 if (addr->s6_addr[15] == 1) /* loopback */
951 return (1);
952 if (addr->s6_addr[15] == 0) /* unspecified */
953 return (0); /* XXX: good value? */
956 return (14); /* global */
959 static int
960 in6_matchflags(addr, ifnam, flags)
961 struct sockaddr *addr;
962 char *ifnam;
963 int flags;
965 #ifdef __KAME__
966 int s;
967 struct in6_ifreq ifr6;
969 if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
970 warn("in6_matchflags: socket(DGRAM6)");
971 return (-1);
973 memset(&ifr6, 0, sizeof(ifr6));
974 strncpy(ifr6.ifr_name, ifnam, sizeof(ifr6.ifr_name));
975 ifr6.ifr_addr = *(struct sockaddr_in6 *)addr;
977 if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
978 warn("in6_matchflags: ioctl(SIOCGIFAFLAG_IN6, %s)",
979 addr2str(addr));
980 close(s);
981 return (-1);
984 close(s);
986 return (ifr6.ifr_ifru.ifru_flags6 & flags);
987 #else
988 return (0);
989 #endif
993 get_duid(idfile, duid)
994 char *idfile;
995 struct duid *duid;
997 FILE *fp = NULL;
998 u_int16_t len = 0, hwtype;
999 int hwlen = 0;
1000 char tmpbuf[256]; /* HWID should be no more than 256 bytes */
1002 if ((fp = fopen(idfile, "r")) == NULL && errno != ENOENT)
1003 dprintf(LOG_NOTICE, FNAME, "failed to open DUID file: %s",
1004 idfile);
1006 memset(duid, 0, sizeof(*duid));
1007 if (fp) {
1008 /* decode length */
1009 if (fread(&len, sizeof(len), 1, fp) != 1) {
1010 dprintf(LOG_ERR, FNAME, "DUID file corrupted");
1011 goto fail;
1013 duid->duid_len = len;
1014 } else {
1015 if ((hwlen = gethwid(tmpbuf, sizeof(tmpbuf), NULL, &hwtype)) < 0) {
1016 dprintf(LOG_INFO, FNAME,
1017 "failed to get a hardware address");
1018 goto fail;
1020 len = hwlen + sizeof(union dhcp6opt_duid_type);
1023 if ((duid->duid_id = (char *)malloc(len)) == NULL) {
1024 dprintf(LOG_ERR, FNAME, "failed to allocate memory");
1025 goto fail;
1028 /* copy (and fill) the ID */
1029 if (fp) {
1030 if (fread(duid->duid_id, len, 1, fp) != 1) {
1031 dprintf(LOG_ERR, FNAME, "DUID file corrupted");
1032 goto fail;
1035 dprintf(LOG_DEBUG, FNAME,
1036 "extracted an existing DUID from %s: %s",
1037 idfile, duidstr(duid));
1038 } else {
1039 /* we only support the types 1,3 DUID */
1040 switch (duid_type) {
1041 case 1: {
1042 u_int64_t t64;
1043 struct dhcp6opt_duid_type1 *dp;
1045 duid->duid_len = hwlen + sizeof(struct dhcp6opt_duid_type1);
1046 dp = (struct dhcp6opt_duid_type1 *)duid->duid_id;
1047 dp->dh6_duid1_type = htons(1); /* type 1 */
1048 dp->dh6_duid1_hwtype = htons(hwtype);
1049 /* time is Jan 1, 2000 (UTC), modulo 2^32 */
1050 t64 = (u_int64_t)(time(NULL) - 946684800);
1051 dp->dh6_duid1_time = htonl((u_long)(t64 & 0xffffffff));
1052 memcpy((void *)(dp + 1), tmpbuf, hwlen);
1053 break;
1055 case 3: {
1056 struct dhcp6opt_duid_type3 *dp;
1058 duid->duid_len = hwlen + sizeof(struct dhcp6opt_duid_type3);
1059 dp = (struct dhcp6opt_duid_type3 *)duid->duid_id;
1060 dp->dh6_duid3_type = htons(3); /* type 3 */
1061 dp->dh6_duid3_hwtype = htons(hwtype);
1062 memcpy((void *)(dp + 1), tmpbuf, hwlen);
1063 break;
1067 dprintf(LOG_DEBUG, FNAME, "generated a new DUID: %s",
1068 duidstr(duid));
1071 /* save the (new) ID to the file for next time */
1072 if (!fp) {
1073 if ((fp = fopen(idfile, "w+")) == NULL) {
1074 dprintf(LOG_ERR, FNAME,
1075 "failed to open DUID file for save");
1076 goto fail;
1078 len = duid->duid_len;
1079 if ((fwrite(&len, sizeof(len), 1, fp)) != 1) {
1080 dprintf(LOG_ERR, FNAME, "failed to save DUID");
1081 goto fail;
1083 if ((fwrite(duid->duid_id, len, 1, fp)) != 1) {
1084 dprintf(LOG_ERR, FNAME, "failed to save DUID");
1085 goto fail;
1088 dprintf(LOG_DEBUG, FNAME, "saved generated DUID to %s",
1089 idfile);
1092 if (fp)
1093 fclose(fp);
1094 return (0);
1096 fail:
1097 if (fp)
1098 fclose(fp);
1099 duidfree(duid);
1100 return (-1);
1103 #ifdef __sun__
1104 struct hwparms {
1105 char *buf;
1106 u_int16_t *hwtypep;
1107 ssize_t retval;
1110 static ssize_t
1111 getifhwaddr(const char *ifname, char *buf, u_int16_t *hwtypep, int ppa)
1113 int fd, flags;
1114 char fname[MAXPATHLEN], *cp;
1115 struct strbuf putctl;
1116 struct strbuf getctl;
1117 long getbuf[1024];
1118 dl_info_req_t dlir;
1119 dl_phys_addr_req_t dlpar;
1120 dl_phys_addr_ack_t *dlpaa;
1122 dprintf(LOG_DEBUG, FNAME, "trying %s ppa %d", ifname, ppa);
1124 if (ifname[0] == '\0')
1125 return (-1);
1126 if (ppa >= 0 && !isdigit(ifname[strlen(ifname) - 1]))
1127 (void) snprintf(fname, sizeof (fname), "/dev/%s%d", ifname,
1128 ppa);
1129 else
1130 (void) snprintf(fname, sizeof (fname), "/dev/%s", ifname);
1131 getctl.maxlen = sizeof (getbuf);
1132 getctl.buf = (char *)getbuf;
1133 if ((fd = open(fname, O_RDWR)) == -1) {
1134 dl_attach_req_t dlar;
1136 cp = fname + strlen(fname) - 1;
1137 if (!isdigit(*cp))
1138 return (-1);
1139 while (cp > fname) {
1140 if (!isdigit(*cp))
1141 break;
1142 cp--;
1144 if (cp == fname)
1145 return (-1);
1146 cp++;
1147 dlar.dl_ppa = atoi(cp);
1148 *cp = '\0';
1149 if ((fd = open(fname, O_RDWR)) == -1)
1150 return (-1);
1151 dlar.dl_primitive = DL_ATTACH_REQ;
1152 putctl.len = sizeof (dlar);
1153 putctl.buf = (char *)&dlar;
1154 if (putmsg(fd, &putctl, NULL, 0) == -1) {
1155 (void) close(fd);
1156 return (-1);
1158 flags = 0;
1159 if (getmsg(fd, &getctl, NULL, &flags) == -1) {
1160 (void) close(fd);
1161 return (-1);
1163 if (getbuf[0] != DL_OK_ACK) {
1164 (void) close(fd);
1165 return (-1);
1168 dlir.dl_primitive = DL_INFO_REQ;
1169 putctl.len = sizeof (dlir);
1170 putctl.buf = (char *)&dlir;
1171 if (putmsg(fd, &putctl, NULL, 0) == -1) {
1172 (void) close(fd);
1173 return (-1);
1175 flags = 0;
1176 if (getmsg(fd, &getctl, NULL, &flags) == -1) {
1177 (void) close(fd);
1178 return (-1);
1180 if (getbuf[0] != DL_INFO_ACK) {
1181 (void) close(fd);
1182 return (-1);
1184 switch (((dl_info_ack_t *)getbuf)->dl_mac_type) {
1185 case DL_CSMACD:
1186 case DL_ETHER:
1187 case DL_100VG:
1188 case DL_ETH_CSMA:
1189 case DL_100BT:
1190 *hwtypep = ARPHRD_ETHER;
1191 break;
1192 default:
1193 (void) close(fd);
1194 return (-1);
1196 dlpar.dl_primitive = DL_PHYS_ADDR_REQ;
1197 dlpar.dl_addr_type = DL_CURR_PHYS_ADDR;
1198 putctl.len = sizeof (dlpar);
1199 putctl.buf = (char *)&dlpar;
1200 if (putmsg(fd, &putctl, NULL, 0) == -1) {
1201 (void) close(fd);
1202 return (-1);
1204 flags = 0;
1205 if (getmsg(fd, &getctl, NULL, &flags) == -1) {
1206 (void) close(fd);
1207 return (-1);
1209 if (getbuf[0] != DL_PHYS_ADDR_ACK) {
1210 (void) close(fd);
1211 return (-1);
1213 dlpaa = (dl_phys_addr_ack_t *)getbuf;
1214 if (dlpaa->dl_addr_length != 6) {
1215 (void) close(fd);
1216 return (-1);
1218 (void) memcpy(buf, (char *)getbuf + dlpaa->dl_addr_offset,
1219 dlpaa->dl_addr_length);
1220 return (dlpaa->dl_addr_length);
1223 static int
1224 devfs_handler(di_node_t node, di_minor_t minor, void *arg)
1226 struct hwparms *parms = arg;
1228 parms->retval = getifhwaddr(di_minor_name(minor), parms->buf,
1229 parms->hwtypep, di_instance(node));
1230 return (parms->retval == -1 ? DI_WALK_CONTINUE : DI_WALK_TERMINATE);
1232 #endif
1234 static ssize_t
1235 gethwid(buf, len, ifname, hwtypep)
1236 char *buf;
1237 int len;
1238 const char *ifname;
1239 u_int16_t *hwtypep;
1241 struct ifaddrs *ifa, *ifap;
1242 #ifdef __KAME__
1243 struct sockaddr_dl *sdl;
1244 #endif
1245 #ifdef __linux__
1246 struct sockaddr_ll *sll;
1247 #endif
1248 ssize_t l;
1250 #ifdef __sun__
1251 if (ifname == NULL) {
1252 di_node_t root;
1253 struct hwparms parms;
1255 if ((root = di_init("/", DINFOSUBTREE | DINFOMINOR |
1256 DINFOPROP)) == DI_NODE_NIL) {
1257 dprintf(LOG_INFO, FNAME, "di_init failed");
1258 return (-1);
1260 parms.buf = buf;
1261 parms.hwtypep = hwtypep;
1262 parms.retval = -1;
1263 (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &parms,
1264 devfs_handler);
1265 di_fini(root);
1266 return (parms.retval);
1267 } else {
1268 return (getifhwaddr(ifname, buf, hwtypep, -1));
1270 #endif
1272 if (getifaddrs(&ifap) < 0)
1273 return (-1);
1275 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1276 if (ifname && strcmp(ifa->ifa_name, ifname) != 0)
1277 continue;
1278 if (ifa->ifa_addr == NULL)
1279 continue;
1280 #ifdef __KAME__
1281 if (ifa->ifa_addr->sa_family != AF_LINK)
1282 continue;
1284 sdl = (struct sockaddr_dl *)ifa->ifa_addr;
1285 if (len < 2 + sdl->sdl_alen)
1286 goto fail;
1288 * translate interface type to hardware type based on
1289 * http://www.iana.org/assignments/arp-parameters
1291 switch(sdl->sdl_type) {
1292 case IFT_ETHER:
1293 #ifdef IFT_IEEE80211
1294 case IFT_IEEE80211:
1295 #endif
1296 *hwtypep = ARPHRD_ETHER;
1297 break;
1298 default:
1299 continue; /* XXX */
1301 dprintf(LOG_DEBUG, FNAME, "found an interface %s for DUID",
1302 ifa->ifa_name);
1303 memcpy(buf, LLADDR(sdl), sdl->sdl_alen);
1304 l = sdl->sdl_alen; /* sdl will soon be freed */
1305 #endif
1306 #ifdef __linux__
1307 if (ifa->ifa_addr->sa_family != AF_PACKET)
1308 continue;
1310 sll = (struct sockaddr_ll *)ifa->ifa_addr;
1311 if (sll->sll_hatype != ARPHRD_ETHER)
1312 continue;
1313 *hwtypep = ARPHRD_ETHER;
1314 dprintf(LOG_DEBUG, FNAME, "found an interface %s for DUID",
1315 ifa->ifa_name);
1316 memcpy(buf, sll->sll_addr, sll->sll_halen);
1317 l = sll->sll_halen; /* sll will soon be freed */
1318 #endif
1319 freeifaddrs(ifap);
1320 return (l);
1323 #ifdef __KAME__
1324 fail:
1325 #endif
1326 freeifaddrs(ifap);
1327 return (-1);
1330 void
1331 dhcp6_init_options(optinfo)
1332 struct dhcp6_optinfo *optinfo;
1334 memset(optinfo, 0, sizeof(*optinfo));
1336 optinfo->pref = DH6OPT_PREF_UNDEF;
1337 optinfo->elapsed_time = DH6OPT_ELAPSED_TIME_UNDEF;
1338 optinfo->refreshtime = DH6OPT_REFRESHTIME_UNDEF;
1340 TAILQ_INIT(&optinfo->iapd_list);
1341 TAILQ_INIT(&optinfo->iana_list);
1342 TAILQ_INIT(&optinfo->reqopt_list);
1343 TAILQ_INIT(&optinfo->stcode_list);
1344 TAILQ_INIT(&optinfo->sip_list);
1345 TAILQ_INIT(&optinfo->sipname_list);
1346 TAILQ_INIT(&optinfo->dns_list);
1347 TAILQ_INIT(&optinfo->dnsname_list);
1348 TAILQ_INIT(&optinfo->ntp_list);
1349 TAILQ_INIT(&optinfo->prefix_list);
1350 TAILQ_INIT(&optinfo->nis_list);
1351 TAILQ_INIT(&optinfo->nisname_list);
1352 TAILQ_INIT(&optinfo->nisp_list);
1353 TAILQ_INIT(&optinfo->nispname_list);
1354 TAILQ_INIT(&optinfo->bcmcs_list);
1355 TAILQ_INIT(&optinfo->bcmcsname_list);
1357 optinfo->authproto = DHCP6_AUTHPROTO_UNDEF;
1358 optinfo->authalgorithm = DHCP6_AUTHALG_UNDEF;
1359 optinfo->authrdm = DHCP6_AUTHRDM_UNDEF;
1362 void
1363 dhcp6_clear_options(optinfo)
1364 struct dhcp6_optinfo *optinfo;
1366 switch (optinfo->authproto) {
1367 case DHCP6_AUTHPROTO_DELAYED:
1368 if (optinfo->delayedauth_realmval != NULL) {
1369 free(optinfo->delayedauth_realmval);
1371 break;
1374 duidfree(&optinfo->clientID);
1375 duidfree(&optinfo->serverID);
1377 dhcp6_clear_list(&optinfo->iapd_list);
1378 dhcp6_clear_list(&optinfo->iana_list);
1379 dhcp6_clear_list(&optinfo->reqopt_list);
1380 dhcp6_clear_list(&optinfo->stcode_list);
1381 dhcp6_clear_list(&optinfo->sip_list);
1382 dhcp6_clear_list(&optinfo->sipname_list);
1383 dhcp6_clear_list(&optinfo->dns_list);
1384 dhcp6_clear_list(&optinfo->dnsname_list);
1385 dhcp6_clear_list(&optinfo->ntp_list);
1386 dhcp6_clear_list(&optinfo->prefix_list);
1387 dhcp6_clear_list(&optinfo->nis_list);
1388 dhcp6_clear_list(&optinfo->nisname_list);
1389 dhcp6_clear_list(&optinfo->nisp_list);
1390 dhcp6_clear_list(&optinfo->nispname_list);
1391 dhcp6_clear_list(&optinfo->bcmcs_list);
1392 dhcp6_clear_list(&optinfo->bcmcsname_list);
1394 if (optinfo->relaymsg_msg != NULL)
1395 free(optinfo->relaymsg_msg);
1397 if (optinfo->ifidopt_id != NULL)
1398 free(optinfo->ifidopt_id);
1400 dhcp6_init_options(optinfo);
1404 dhcp6_copy_options(dst, src)
1405 struct dhcp6_optinfo *dst, *src;
1407 if (duidcpy(&dst->clientID, &src->clientID))
1408 goto fail;
1409 if (duidcpy(&dst->serverID, &src->serverID))
1410 goto fail;
1411 dst->rapidcommit = src->rapidcommit;
1413 if (dhcp6_copy_list(&dst->iapd_list, &src->iapd_list))
1414 goto fail;
1415 if (dhcp6_copy_list(&dst->iana_list, &src->iana_list))
1416 goto fail;
1417 if (dhcp6_copy_list(&dst->reqopt_list, &src->reqopt_list))
1418 goto fail;
1419 if (dhcp6_copy_list(&dst->stcode_list, &src->stcode_list))
1420 goto fail;
1421 if (dhcp6_copy_list(&dst->sip_list, &src->sip_list))
1422 goto fail;
1423 if (dhcp6_copy_list(&dst->sipname_list, &src->sipname_list))
1424 goto fail;
1425 if (dhcp6_copy_list(&dst->dns_list, &src->dns_list))
1426 goto fail;
1427 if (dhcp6_copy_list(&dst->dnsname_list, &src->dnsname_list))
1428 goto fail;
1429 if (dhcp6_copy_list(&dst->ntp_list, &src->ntp_list))
1430 goto fail;
1431 if (dhcp6_copy_list(&dst->prefix_list, &src->prefix_list))
1432 goto fail;
1433 if (dhcp6_copy_list(&dst->nis_list, &src->nis_list))
1434 goto fail;
1435 if (dhcp6_copy_list(&dst->nisname_list, &src->nisname_list))
1436 goto fail;
1437 if (dhcp6_copy_list(&dst->nisp_list, &src->nisp_list))
1438 goto fail;
1439 if (dhcp6_copy_list(&dst->nispname_list, &src->nispname_list))
1440 goto fail;
1441 if (dhcp6_copy_list(&dst->bcmcs_list, &src->bcmcs_list))
1442 goto fail;
1443 if (dhcp6_copy_list(&dst->bcmcsname_list, &src->bcmcsname_list))
1444 goto fail;
1445 dst->elapsed_time = src->elapsed_time;
1446 dst->refreshtime = src->refreshtime;
1447 dst->pref = src->pref;
1449 if (src->relaymsg_msg != NULL) {
1450 if ((dst->relaymsg_msg = malloc(src->relaymsg_len)) == NULL)
1451 goto fail;
1452 dst->relaymsg_len = src->relaymsg_len;
1453 memcpy(dst->relaymsg_msg, src->relaymsg_msg,
1454 src->relaymsg_len);
1457 if (src->ifidopt_id != NULL) {
1458 if ((dst->ifidopt_id = malloc(src->ifidopt_len)) == NULL)
1459 goto fail;
1460 dst->ifidopt_len = src->ifidopt_len;
1461 memcpy(dst->ifidopt_id, src->ifidopt_id, src->ifidopt_len);
1464 dst->authflags = src->authflags;
1465 dst->authproto = src->authproto;
1466 dst->authalgorithm = src->authalgorithm;
1467 dst->authrdm = src->authrdm;
1468 dst->authrd = src->authrd;
1470 switch (src->authproto) {
1471 case DHCP6_AUTHPROTO_DELAYED:
1472 dst->delayedauth_keyid = src->delayedauth_keyid;
1473 dst->delayedauth_offset = src->delayedauth_offset;
1474 dst->delayedauth_realmlen = src->delayedauth_realmlen;
1475 if (src->delayedauth_realmval != NULL) {
1476 if ((dst->delayedauth_realmval =
1477 malloc(src->delayedauth_realmlen)) == NULL) {
1478 goto fail;
1480 memcpy(dst->delayedauth_realmval,
1481 src->delayedauth_realmval,
1482 src->delayedauth_realmlen);
1484 break;
1485 case DHCP6_AUTHPROTO_RECONFIG:
1486 dst->reconfigauth_type = src->reconfigauth_type;
1487 dst->reconfigauth_offset = src->reconfigauth_offset;
1488 memcpy(dst->reconfigauth_val, src->reconfigauth_val,
1489 sizeof(dst->reconfigauth_val));
1490 break;
1493 return (0);
1495 fail:
1496 /* cleanup temporary resources */
1497 dhcp6_clear_options(dst);
1498 return (-1);
1502 dhcp6_get_options(p, ep, optinfo)
1503 struct dhcp6opt *p, *ep;
1504 struct dhcp6_optinfo *optinfo;
1506 struct dhcp6opt *np, opth;
1507 int i, opt, optlen, reqopts, num;
1508 u_int16_t num16;
1509 char *bp, *cp, *val;
1510 u_int16_t val16;
1511 u_int32_t val32;
1512 struct dhcp6opt_ia optia;
1513 struct dhcp6_ia ia;
1514 struct dhcp6_list sublist;
1515 int authinfolen;
1517 bp = (char *)p;
1518 for (; p + 1 <= ep; p = np) {
1519 struct duid duid0;
1522 * get the option header. XXX: since there is no guarantee
1523 * about the header alignment, we need to make a local copy.
1525 memcpy(&opth, p, sizeof(opth));
1526 optlen = ntohs(opth.dh6opt_len);
1527 opt = ntohs(opth.dh6opt_type);
1529 cp = (char *)(p + 1);
1530 np = (struct dhcp6opt *)(cp + optlen);
1532 dprintf(LOG_DEBUG, FNAME, "get DHCP option %s, len %d",
1533 dhcp6optstr(opt), optlen);
1535 /* option length field overrun */
1536 if (np > ep) {
1537 dprintf(LOG_INFO, FNAME, "malformed DHCP options");
1538 goto fail;
1541 switch (opt) {
1542 case DH6OPT_CLIENTID:
1543 if (optlen == 0)
1544 goto malformed;
1545 duid0.duid_len = optlen;
1546 duid0.duid_id = cp;
1547 dprintf(LOG_DEBUG, "",
1548 " DUID: %s", duidstr(&duid0));
1549 if (duidcpy(&optinfo->clientID, &duid0)) {
1550 dprintf(LOG_ERR, FNAME, "failed to copy DUID");
1551 goto fail;
1553 break;
1554 case DH6OPT_SERVERID:
1555 if (optlen == 0)
1556 goto malformed;
1557 duid0.duid_len = optlen;
1558 duid0.duid_id = cp;
1559 dprintf(LOG_DEBUG, "", " DUID: %s", duidstr(&duid0));
1560 if (duidcpy(&optinfo->serverID, &duid0)) {
1561 dprintf(LOG_ERR, FNAME, "failed to copy DUID");
1562 goto fail;
1564 break;
1565 case DH6OPT_STATUS_CODE:
1566 if (optlen < sizeof(u_int16_t))
1567 goto malformed;
1568 memcpy(&val16, cp, sizeof(val16));
1569 num16 = ntohs(val16);
1570 dprintf(LOG_DEBUG, "", " status code: %s",
1571 dhcp6_stcodestr(num16));
1573 /* need to check duplication? */
1575 if (dhcp6_add_listval(&optinfo->stcode_list,
1576 DHCP6_LISTVAL_STCODE, &num16, NULL) == NULL) {
1577 dprintf(LOG_ERR, FNAME, "failed to copy "
1578 "status code");
1579 goto fail;
1582 break;
1583 case DH6OPT_ORO:
1584 if ((optlen % 2) != 0 || optlen == 0)
1585 goto malformed;
1586 reqopts = optlen / 2;
1587 for (i = 0, val = cp; i < reqopts;
1588 i++, val += sizeof(u_int16_t)) {
1589 u_int16_t opttype;
1591 memcpy(&opttype, val, sizeof(u_int16_t));
1592 num = (int)ntohs(opttype);
1594 dprintf(LOG_DEBUG, "",
1595 " requested option: %s",
1596 dhcp6optstr(num));
1598 if (dhcp6_find_listval(&optinfo->reqopt_list,
1599 DHCP6_LISTVAL_NUM, &num, 0)) {
1600 dprintf(LOG_INFO, FNAME, "duplicated "
1601 "option type (%s)",
1602 dhcp6optstr(opttype));
1603 goto nextoption;
1606 if (dhcp6_add_listval(&optinfo->reqopt_list,
1607 DHCP6_LISTVAL_NUM, &num, NULL) == NULL) {
1608 dprintf(LOG_ERR, FNAME,
1609 "failed to copy requested option");
1610 goto fail;
1612 nextoption:
1615 break;
1616 case DH6OPT_PREFERENCE:
1617 if (optlen != 1)
1618 goto malformed;
1619 dprintf(LOG_DEBUG, "", " preference: %d",
1620 (int)*(u_char *)cp);
1621 if (optinfo->pref != DH6OPT_PREF_UNDEF) {
1622 dprintf(LOG_INFO, FNAME,
1623 "duplicated preference option");
1624 } else
1625 optinfo->pref = (int)*(u_char *)cp;
1626 break;
1627 case DH6OPT_ELAPSED_TIME:
1628 if (optlen != 2)
1629 goto malformed;
1630 memcpy(&val16, cp, sizeof(val16));
1631 val16 = ntohs(val16);
1632 dprintf(LOG_DEBUG, "", " elapsed time: %lu",
1633 (u_int32_t)val16);
1634 if (optinfo->elapsed_time !=
1635 DH6OPT_ELAPSED_TIME_UNDEF) {
1636 dprintf(LOG_INFO, FNAME,
1637 "duplicated elapsed time option");
1638 } else
1639 optinfo->elapsed_time = val16;
1640 break;
1641 case DH6OPT_RELAY_MSG:
1642 if ((optinfo->relaymsg_msg = malloc(optlen)) == NULL)
1643 goto fail;
1644 memcpy(optinfo->relaymsg_msg, cp, optlen);
1645 optinfo->relaymsg_len = optlen;
1646 break;
1647 case DH6OPT_AUTH:
1648 if (optlen < sizeof(struct dhcp6opt_auth) - 4)
1649 goto malformed;
1652 * Any DHCP message that includes more than one
1653 * authentication option MUST be discarded.
1654 * [RFC3315 Section 21.4.2]
1656 if (optinfo->authproto != DHCP6_AUTHPROTO_UNDEF) {
1657 dprintf(LOG_INFO, FNAME, "found more than one "
1658 "authentication option");
1659 goto fail;
1662 optinfo->authproto = *cp++;
1663 optinfo->authalgorithm = *cp++;
1664 optinfo->authrdm = *cp++;
1665 memcpy(&optinfo->authrd, cp, sizeof(optinfo->authrd));
1666 cp += sizeof(optinfo->authrd);
1668 dprintf(LOG_DEBUG, "", " %s", sprint_auth(optinfo));
1670 authinfolen =
1671 optlen - (sizeof(struct dhcp6opt_auth) - 4);
1672 switch (optinfo->authproto) {
1673 case DHCP6_AUTHPROTO_DELAYED:
1674 if (authinfolen == 0) {
1675 optinfo->authflags |=
1676 DHCP6OPT_AUTHFLAG_NOINFO;
1677 break;
1679 /* XXX: should we reject an empty realm? */
1680 if (authinfolen <
1681 sizeof(optinfo->delayedauth_keyid) + 16) {
1682 goto malformed;
1685 optinfo->delayedauth_realmlen = authinfolen -
1686 (sizeof(optinfo->delayedauth_keyid) + 16);
1687 optinfo->delayedauth_realmval =
1688 malloc(optinfo->delayedauth_realmlen);
1689 if (optinfo->delayedauth_realmval == NULL) {
1690 dprintf(LOG_WARNING, FNAME, "failed "
1691 "allocate memory for auth realm");
1692 goto fail;
1694 memcpy(optinfo->delayedauth_realmval, cp,
1695 optinfo->delayedauth_realmlen);
1696 cp += optinfo->delayedauth_realmlen;
1698 memcpy(&optinfo->delayedauth_keyid, cp,
1699 sizeof(optinfo->delayedauth_keyid));
1700 optinfo->delayedauth_keyid =
1701 ntohl(optinfo->delayedauth_keyid);
1702 cp += sizeof(optinfo->delayedauth_keyid);
1704 optinfo->delayedauth_offset = cp - bp;
1705 cp += 16;
1707 dprintf(LOG_DEBUG, "", " auth key ID: %x, "
1708 "offset=%d, realmlen=%d",
1709 optinfo->delayedauth_keyid,
1710 optinfo->delayedauth_offset,
1711 optinfo->delayedauth_realmlen);
1712 break;
1713 #ifdef notyet
1714 case DHCP6_AUTHPROTO_RECONFIG:
1715 break;
1716 #endif
1717 default:
1718 dprintf(LOG_INFO, FNAME,
1719 "unsupported authentication protocol: %d",
1720 *cp);
1721 goto fail;
1723 break;
1724 case DH6OPT_RAPID_COMMIT:
1725 if (optlen != 0)
1726 goto malformed;
1727 optinfo->rapidcommit = 1;
1728 break;
1729 case DH6OPT_INTERFACE_ID:
1730 if ((optinfo->ifidopt_id = malloc(optlen)) == NULL)
1731 goto fail;
1732 memcpy(optinfo->ifidopt_id, cp, optlen);
1733 optinfo->ifidopt_len = optlen;
1734 break;
1735 case DH6OPT_SIP_SERVER_D:
1736 if (dhcp6_get_domain(optlen, cp, opt,
1737 &optinfo->sipname_list) == -1)
1738 goto fail;
1739 break;
1740 case DH6OPT_DNSNAME:
1741 if (dhcp6_get_domain(optlen, cp, opt,
1742 &optinfo->dnsname_list) == -1)
1743 goto fail;
1744 break;
1745 case DH6OPT_NIS_DOMAIN_NAME:
1746 if (dhcp6_get_domain(optlen, cp, opt,
1747 &optinfo->nisname_list) == -1)
1748 goto fail;
1749 break;
1750 case DH6OPT_NISP_DOMAIN_NAME:
1751 if (dhcp6_get_domain(optlen, cp, opt,
1752 &optinfo->nispname_list) == -1)
1753 goto fail;
1754 break;
1755 case DH6OPT_BCMCS_SERVER_D:
1756 if (dhcp6_get_domain(optlen, cp, opt,
1757 &optinfo->bcmcsname_list) == -1)
1758 goto fail;
1759 break;
1760 case DH6OPT_SIP_SERVER_A:
1761 if (dhcp6_get_addr(optlen, cp, opt,
1762 &optinfo->sip_list) == -1)
1763 goto fail;
1764 break;
1765 case DH6OPT_DNS:
1766 if (dhcp6_get_addr(optlen, cp, opt,
1767 &optinfo->dns_list) == -1)
1768 goto fail;
1769 break;
1770 case DH6OPT_NIS_SERVERS:
1771 if (dhcp6_get_addr(optlen, cp, opt,
1772 &optinfo->nis_list) == -1)
1773 goto fail;
1774 break;
1775 case DH6OPT_NISP_SERVERS:
1776 if (dhcp6_get_addr(optlen, cp, opt,
1777 &optinfo->nisp_list) == -1)
1778 goto fail;
1779 break;
1780 case DH6OPT_BCMCS_SERVER_A:
1781 if (dhcp6_get_addr(optlen, cp, opt,
1782 &optinfo->bcmcs_list) == -1)
1783 goto fail;
1784 break;
1785 case DH6OPT_NTP:
1786 if (dhcp6_get_addr(optlen, cp, opt,
1787 &optinfo->ntp_list) == -1)
1788 goto fail;
1789 break;
1790 case DH6OPT_IA_PD:
1791 if (optlen + sizeof(struct dhcp6opt) <
1792 sizeof(optia))
1793 goto malformed;
1794 memcpy(&optia, p, sizeof(optia));
1795 ia.iaid = ntohl(optia.dh6_ia_iaid);
1796 ia.t1 = ntohl(optia.dh6_ia_t1);
1797 ia.t2 = ntohl(optia.dh6_ia_t2);
1799 dprintf(LOG_DEBUG, "",
1800 " IA_PD: ID=%lu, T1=%lu, T2=%lu",
1801 ia.iaid, ia.t1, ia.t2);
1803 /* duplication check */
1804 if (dhcp6_find_listval(&optinfo->iapd_list,
1805 DHCP6_LISTVAL_IAPD, &ia, 0)) {
1806 dprintf(LOG_INFO, FNAME,
1807 "duplicated IA_PD %lu", ia.iaid);
1808 break; /* ignore this IA_PD */
1811 /* take care of sub-options */
1812 TAILQ_INIT(&sublist);
1813 if (copyin_option(opt,
1814 (struct dhcp6opt *)((char *)p + sizeof(optia)),
1815 (struct dhcp6opt *)(cp + optlen), &sublist)) {
1816 goto fail;
1819 /* link this option set */
1820 if (dhcp6_add_listval(&optinfo->iapd_list,
1821 DHCP6_LISTVAL_IAPD, &ia, &sublist) == NULL) {
1822 dhcp6_clear_list(&sublist);
1823 goto fail;
1825 dhcp6_clear_list(&sublist);
1827 break;
1828 case DH6OPT_REFRESHTIME:
1829 if (optlen != 4)
1830 goto malformed;
1831 memcpy(&val32, cp, sizeof(val32));
1832 val32 = ntohl(val32);
1833 dprintf(LOG_DEBUG, "",
1834 " information refresh time: %lu", val32);
1835 if (val32 < DHCP6_IRT_MINIMUM) {
1837 * A client MUST use the refresh time
1838 * IRT_MINIMUM if it receives the option with a
1839 * value less than IRT_MINIMUM.
1840 * [draft-ietf-dhc-lifetime-02.txt,
1841 * Section 3.2]
1843 dprintf(LOG_INFO, FNAME,
1844 "refresh time is too small (%d), adjusted",
1845 val32);
1846 val32 = DHCP6_IRT_MINIMUM;
1848 if (optinfo->refreshtime != DH6OPT_REFRESHTIME_UNDEF) {
1849 dprintf(LOG_INFO, FNAME,
1850 "duplicated refresh time option");
1851 } else
1852 optinfo->refreshtime = (int64_t)val32;
1853 break;
1854 case DH6OPT_IA_NA:
1855 if (optlen + sizeof(struct dhcp6opt) <
1856 sizeof(optia))
1857 goto malformed;
1858 memcpy(&optia, p, sizeof(optia));
1859 ia.iaid = ntohl(optia.dh6_ia_iaid);
1860 ia.t1 = ntohl(optia.dh6_ia_t1);
1861 ia.t2 = ntohl(optia.dh6_ia_t2);
1863 dprintf(LOG_DEBUG, "",
1864 " IA_NA: ID=%lu, T1=%lu, T2=%lu",
1865 ia.iaid, ia.t1, ia.t2);
1867 /* duplication check */
1868 if (dhcp6_find_listval(&optinfo->iana_list,
1869 DHCP6_LISTVAL_IANA, &ia, 0)) {
1870 dprintf(LOG_INFO, FNAME,
1871 "duplicated IA_NA %lu", ia.iaid);
1872 break; /* ignore this IA_NA */
1875 /* take care of sub-options */
1876 TAILQ_INIT(&sublist);
1877 if (copyin_option(opt,
1878 (struct dhcp6opt *)((char *)p + sizeof(optia)),
1879 (struct dhcp6opt *)(cp + optlen), &sublist)) {
1880 goto fail;
1883 /* link this option set */
1884 if (dhcp6_add_listval(&optinfo->iana_list,
1885 DHCP6_LISTVAL_IANA, &ia, &sublist) == NULL) {
1886 dhcp6_clear_list(&sublist);
1887 goto fail;
1889 dhcp6_clear_list(&sublist);
1891 break;
1892 default:
1893 /* no option specific behavior */
1894 dprintf(LOG_INFO, FNAME,
1895 "unknown or unexpected DHCP6 option %s, len %d",
1896 dhcp6optstr(opt), optlen);
1897 break;
1901 return (0);
1903 malformed:
1904 dprintf(LOG_INFO, FNAME, "malformed DHCP option: type %d, len %d",
1905 opt, optlen);
1906 fail:
1907 dhcp6_clear_options(optinfo);
1908 return (-1);
1911 static char *
1912 dnsdecode(sp, ep, buf, bufsiz)
1913 u_char **sp;
1914 u_char *ep;
1915 char *buf;
1916 size_t bufsiz;
1918 int i, l;
1919 u_char *cp;
1920 char tmpbuf[MAXDNAME + 1];
1922 cp = *sp;
1923 *buf = '\0';
1924 i = 0; /* XXX: appease gcc */
1926 if (cp >= ep)
1927 return (NULL);
1928 while (cp < ep) {
1929 i = *cp;
1930 if (i == 0 || cp != *sp) {
1931 if (strlcat((char *)buf, ".", bufsiz) >= bufsiz)
1932 return (NULL); /* result overrun */
1934 if (i == 0)
1935 break;
1936 cp++;
1938 if (i > 0x3f)
1939 return (NULL); /* invalid label */
1941 if (i > ep - cp)
1942 return (NULL); /* source overrun */
1943 while (i-- > 0 && cp < ep) {
1944 if (!isprint(*cp)) /* we don't accept non-printables */
1945 return (NULL);
1946 l = snprintf(tmpbuf, sizeof(tmpbuf), "%c" , *cp);
1947 if (l >= sizeof(tmpbuf) || l < 0)
1948 return (NULL);
1949 if (strlcat(buf, tmpbuf, bufsiz) >= bufsiz)
1950 return (NULL); /* result overrun */
1951 cp++;
1954 if (i != 0)
1955 return (NULL); /* not terminated */
1956 cp++;
1957 *sp = cp;
1958 return (buf);
1961 static int
1962 copyin_option(type, p, ep, list)
1963 int type;
1964 struct dhcp6opt *p, *ep;
1965 struct dhcp6_list *list;
1967 int opt, optlen;
1968 char *cp;
1969 struct dhcp6opt *np, opth;
1970 struct dhcp6opt_stcode opt_stcode;
1971 struct dhcp6opt_ia_pd_prefix opt_iapd_prefix;
1972 struct dhcp6_prefix iapd_prefix;
1973 struct dhcp6opt_ia_addr opt_ia_addr;
1974 struct dhcp6_prefix ia_addr;
1975 struct dhcp6_list sublist;
1977 TAILQ_INIT(&sublist);
1979 for (; p + 1 <= ep; p = np) {
1980 memcpy(&opth, p, sizeof(opth));
1981 optlen = ntohs(opth.dh6opt_len);
1982 opt = ntohs(opth.dh6opt_type);
1984 cp = (char *)(p + 1);
1985 np = (struct dhcp6opt *)(cp + optlen);
1987 dprintf(LOG_DEBUG, FNAME, "get DHCP option %s, len %d",
1988 dhcp6optstr(opt), optlen);
1990 if (np > ep) {
1991 dprintf(LOG_INFO, FNAME, "malformed DHCP option");
1992 goto fail;
1995 switch (opt) {
1996 case DH6OPT_IA_PD_PREFIX:
1997 /* check option context */
1998 if (type != DH6OPT_IA_PD) {
1999 dprintf(LOG_INFO, FNAME,
2000 "%s is an invalid position for %s",
2001 dhcp6optstr(type), dhcp6optstr(opt));
2002 goto fail;
2004 /* check option length */
2005 if (optlen + sizeof(opth) < sizeof(opt_iapd_prefix))
2006 goto malformed;
2008 /* copy and convert option values */
2009 memcpy(&opt_iapd_prefix, p, sizeof(opt_iapd_prefix));
2010 if (opt_iapd_prefix.dh6_iapd_prefix_prefix_len > 128) {
2011 dprintf(LOG_INFO, FNAME,
2012 "invalid prefix length (%d)",
2013 opt_iapd_prefix.dh6_iapd_prefix_prefix_len);
2014 goto malformed;
2016 iapd_prefix.pltime = ntohl(opt_iapd_prefix.dh6_iapd_prefix_preferred_time);
2017 iapd_prefix.vltime = ntohl(opt_iapd_prefix.dh6_iapd_prefix_valid_time);
2018 iapd_prefix.plen =
2019 opt_iapd_prefix.dh6_iapd_prefix_prefix_len;
2020 memcpy(&iapd_prefix.addr,
2021 &opt_iapd_prefix.dh6_iapd_prefix_prefix_addr,
2022 sizeof(iapd_prefix.addr));
2023 /* clear padding bits in the prefix address */
2024 prefix6_mask(&iapd_prefix.addr, iapd_prefix.plen);
2026 dprintf(LOG_DEBUG, FNAME, " IA_PD prefix: "
2027 "%s/%d pltime=%lu vltime=%lu",
2028 in6addr2str(&iapd_prefix.addr, 0),
2029 iapd_prefix.plen,
2030 iapd_prefix.pltime, iapd_prefix.vltime);
2032 if (dhcp6_find_listval(list, DHCP6_LISTVAL_PREFIX6,
2033 &iapd_prefix, 0)) {
2034 dprintf(LOG_INFO, FNAME,
2035 "duplicated IA_PD prefix "
2036 "%s/%d pltime=%lu vltime=%lu",
2037 in6addr2str(&iapd_prefix.addr, 0),
2038 iapd_prefix.plen,
2039 iapd_prefix.pltime, iapd_prefix.vltime);
2040 goto nextoption;
2043 /* take care of sub-options */
2044 TAILQ_INIT(&sublist);
2045 if (copyin_option(opt,
2046 (struct dhcp6opt *)((char *)p +
2047 sizeof(opt_iapd_prefix)), np, &sublist)) {
2048 goto fail;
2051 if (dhcp6_add_listval(list, DHCP6_LISTVAL_PREFIX6,
2052 &iapd_prefix, &sublist) == NULL) {
2053 dhcp6_clear_list(&sublist);
2054 goto fail;
2056 dhcp6_clear_list(&sublist);
2057 break;
2058 case DH6OPT_IAADDR:
2059 /* check option context */
2060 if (type != DH6OPT_IA_NA) {
2061 dprintf(LOG_INFO, FNAME,
2062 "%s is an invalid position for %s",
2063 dhcp6optstr(type), dhcp6optstr(opt));
2064 goto fail;
2066 /* check option length */
2067 if (optlen + sizeof(opth) < sizeof(opt_ia_addr))
2068 goto malformed;
2070 /* copy and convert option values */
2071 memcpy(&opt_ia_addr, p, sizeof(opt_ia_addr));
2072 ia_addr.pltime = ntohl(opt_ia_addr.dh6_ia_addr_preferred_time);
2073 ia_addr.vltime = ntohl(opt_ia_addr.dh6_ia_addr_valid_time);
2074 memcpy(&ia_addr.addr, &opt_ia_addr.dh6_ia_addr_addr,
2075 sizeof(ia_addr.addr));
2077 dprintf(LOG_DEBUG, FNAME, " IA_NA address: "
2078 "%s pltime=%lu vltime=%lu",
2079 in6addr2str(&ia_addr.addr, 0),
2080 ia_addr.pltime, ia_addr.vltime);
2082 if (dhcp6_find_listval(list,
2083 DHCP6_LISTVAL_STATEFULADDR6, &ia_addr, 0)) {
2084 dprintf(LOG_INFO, FNAME,
2085 "duplicated IA_NA address"
2086 "%s pltime=%lu vltime=%lu",
2087 in6addr2str(&ia_addr.addr, 0),
2088 ia_addr.pltime, ia_addr.vltime);
2089 goto nextoption;
2092 /* take care of sub-options */
2093 TAILQ_INIT(&sublist);
2094 if (copyin_option(opt,
2095 (struct dhcp6opt *)((char *)p +
2096 sizeof(opt_ia_addr)), np, &sublist)) {
2097 goto fail;
2100 if (dhcp6_add_listval(list, DHCP6_LISTVAL_STATEFULADDR6,
2101 &ia_addr, &sublist) == NULL) {
2102 dhcp6_clear_list(&sublist);
2103 goto fail;
2105 dhcp6_clear_list(&sublist);
2106 break;
2107 case DH6OPT_STATUS_CODE:
2108 /* check option context */
2109 if (type != DH6OPT_IA_PD &&
2110 type != DH6OPT_IA_PD_PREFIX &&
2111 type != DH6OPT_IA_NA &&
2112 type != DH6OPT_IAADDR) {
2113 dprintf(LOG_INFO, FNAME,
2114 "%s is an invalid position for %s",
2115 dhcp6optstr(type), dhcp6optstr(opt));
2116 goto nextoption; /* or discard the message? */
2118 /* check option length */
2119 if (optlen + sizeof(opth) < sizeof(opt_stcode))
2120 goto malformed;
2122 /* copy and convert option values */
2123 memcpy(&opt_stcode, p, sizeof(opt_stcode));
2124 opt_stcode.dh6_stcode_code =
2125 ntohs(opt_stcode.dh6_stcode_code);
2127 dprintf(LOG_DEBUG, "", " status code: %s",
2128 dhcp6_stcodestr(opt_stcode.dh6_stcode_code));
2130 /* duplication check */
2131 if (dhcp6_find_listval(list, DHCP6_LISTVAL_STCODE,
2132 &opt_stcode.dh6_stcode_code, 0)) {
2133 dprintf(LOG_INFO, FNAME,
2134 "duplicated status code (%d)",
2135 opt_stcode.dh6_stcode_code);
2136 goto nextoption;
2139 /* copy-in the code value */
2140 if (dhcp6_add_listval(list, DHCP6_LISTVAL_STCODE,
2141 &opt_stcode.dh6_stcode_code, NULL) == NULL)
2142 goto fail;
2144 break;
2146 nextoption:
2150 return (0);
2152 malformed:
2153 dprintf(LOG_INFO, "", " malformed DHCP option: type %d", opt);
2155 fail:
2156 dhcp6_clear_list(&sublist);
2157 return (-1);
2160 static char *
2161 sprint_uint64(buf, buflen, i64)
2162 char *buf;
2163 int buflen;
2164 u_int64_t i64;
2166 u_int16_t rd0, rd1, rd2, rd3;
2167 u_int16_t *ptr = (u_int16_t *)(void *)&i64;
2169 rd0 = ntohs(*ptr++);
2170 rd1 = ntohs(*ptr++);
2171 rd2 = ntohs(*ptr++);
2172 rd3 = ntohs(*ptr);
2174 snprintf(buf, buflen, "%04x %04x %04x %04x", rd0, rd1, rd2, rd3);
2176 return (buf);
2179 static char *
2180 sprint_auth(optinfo)
2181 struct dhcp6_optinfo *optinfo;
2183 static char ret[1024]; /* XXX: thread unsafe */
2184 char *proto, proto0[] = "unknown(255)";
2185 char *alg, alg0[] = "unknown(255)";
2186 char *rdm, rdm0[] = "unknown(255)";
2187 char rd[] = "ffff ffff ffff ffff";
2189 switch (optinfo->authproto) {
2190 case DHCP6_AUTHPROTO_DELAYED:
2191 proto = "delayed";
2192 break;
2193 case DHCP6_AUTHPROTO_RECONFIG:
2194 proto = "reconfig";
2195 break;
2196 default:
2197 snprintf(proto0, sizeof(proto0), "unknown(%d)",
2198 optinfo->authproto & 0xff);
2199 proto = proto0;
2200 break;
2203 switch (optinfo->authalgorithm) {
2204 case DHCP6_AUTHALG_HMACMD5:
2205 alg = "HMAC-MD5";
2206 break;
2207 default:
2208 snprintf(alg0, sizeof(alg0), "unknown(%d)",
2209 optinfo->authalgorithm & 0xff);
2210 alg = alg0;
2211 break;
2214 switch (optinfo->authrdm) {
2215 case DHCP6_AUTHRDM_MONOCOUNTER:
2216 rdm = "mono counter";
2217 break;
2218 default:
2219 snprintf(rdm0, sizeof(rdm0), "unknown(%d)", optinfo->authrdm);
2220 rdm = rdm0;
2223 (void)sprint_uint64(rd, sizeof(rd), optinfo->authrd);
2225 snprintf(ret, sizeof(ret), "proto: %s, alg: %s, RDM: %s, RD: %s",
2226 proto, alg, rdm, rd);
2228 return (ret);
2231 static int
2232 copy_option(type, len, val, optp, ep, totallenp)
2233 u_int16_t type, len;
2234 void *val;
2235 struct dhcp6opt **optp, *ep;
2236 int *totallenp;
2238 struct dhcp6opt *opt = *optp, opth;
2240 if ((void *)ep - (void *)optp < len + sizeof(struct dhcp6opt)) {
2241 dprintf(LOG_INFO, FNAME,
2242 "option buffer short for %s", dhcp6optstr(type));
2243 return (-1);
2245 opth.dh6opt_type = htons(type);
2246 opth.dh6opt_len = htons(len);
2247 memcpy(opt, &opth, sizeof(opth));
2248 if (len != 0)
2249 memcpy(opt + 1, val, len);
2251 *optp = (struct dhcp6opt *)((char *)(opt + 1) + len);
2252 *totallenp += sizeof(struct dhcp6opt) + len;
2253 dprintf(LOG_DEBUG, FNAME, "set %s (len %d)", dhcp6optstr(type), len);
2255 return (0);
2259 dhcp6_set_options(type, optbp, optep, optinfo)
2260 int type;
2261 struct dhcp6opt *optbp, *optep;
2262 struct dhcp6_optinfo *optinfo;
2264 struct dhcp6opt *p = optbp;
2265 struct dhcp6_listval *stcode, *op;
2266 int len = 0, optlen;
2267 char *tmpbuf = NULL;
2269 if (optinfo->clientID.duid_len) {
2270 if (copy_option(DH6OPT_CLIENTID, optinfo->clientID.duid_len,
2271 optinfo->clientID.duid_id, &p, optep, &len) != 0) {
2272 goto fail;
2276 if (optinfo->serverID.duid_len) {
2277 if (copy_option(DH6OPT_SERVERID, optinfo->serverID.duid_len,
2278 optinfo->serverID.duid_id, &p, optep, &len) != 0) {
2279 goto fail;
2283 for (op = TAILQ_FIRST(&optinfo->iana_list); op;
2284 op = TAILQ_NEXT(op, link)) {
2285 int optlen;
2287 tmpbuf = NULL;
2288 if ((optlen = copyout_option(NULL, NULL, op)) < 0) {
2289 dprintf(LOG_INFO, FNAME,
2290 "failed to count option length");
2291 goto fail;
2293 if ((void *)optep - (void *)p < optlen) {
2294 dprintf(LOG_INFO, FNAME, "short buffer");
2295 goto fail;
2297 if ((tmpbuf = malloc(optlen)) == NULL) {
2298 dprintf(LOG_NOTICE, FNAME,
2299 "memory allocation failed for IA_NA options");
2300 goto fail;
2302 if (copyout_option(tmpbuf, tmpbuf + optlen, op) < 0) {
2303 dprintf(LOG_ERR, FNAME,
2304 "failed to construct an IA_NA option");
2305 goto fail;
2307 memcpy(p, tmpbuf, optlen);
2308 free(tmpbuf);
2309 tmpbuf = NULL;
2310 p = (struct dhcp6opt *)((char *)p + optlen);
2311 len += optlen;
2314 if (optinfo->rapidcommit) {
2315 if (copy_option(DH6OPT_RAPID_COMMIT, 0, NULL, &p,
2316 optep, &len) != 0) {
2317 goto fail;
2321 if (optinfo->pref != DH6OPT_PREF_UNDEF) {
2322 u_int8_t p8 = (u_int8_t)optinfo->pref;
2324 if (copy_option(DH6OPT_PREFERENCE, sizeof(p8), &p8, &p,
2325 optep, &len) != 0) {
2326 goto fail;
2330 if (optinfo->elapsed_time != DH6OPT_ELAPSED_TIME_UNDEF) {
2331 u_int16_t p16 = (u_int16_t)optinfo->elapsed_time;
2333 p16 = htons(p16);
2334 if (copy_option(DH6OPT_ELAPSED_TIME, sizeof(p16), &p16, &p,
2335 optep, &len) != 0) {
2336 goto fail;
2340 for (stcode = TAILQ_FIRST(&optinfo->stcode_list); stcode;
2341 stcode = TAILQ_NEXT(stcode, link)) {
2342 u_int16_t code;
2344 code = htons(stcode->val_num16);
2345 if (copy_option(DH6OPT_STATUS_CODE, sizeof(code), &code, &p,
2346 optep, &len) != 0) {
2347 goto fail;
2351 if (!TAILQ_EMPTY(&optinfo->reqopt_list)) {
2352 struct dhcp6_listval *opt;
2353 u_int16_t *valp;
2354 int buflen;
2356 tmpbuf = NULL;
2357 buflen = dhcp6_count_list(&optinfo->reqopt_list) *
2358 sizeof(u_int16_t);
2359 if ((tmpbuf = malloc(buflen)) == NULL) {
2360 dprintf(LOG_ERR, FNAME,
2361 "memory allocation failed for options");
2362 goto fail;
2364 optlen = 0;
2365 valp = (u_int16_t *)tmpbuf;
2366 for (opt = TAILQ_FIRST(&optinfo->reqopt_list); opt;
2367 opt = TAILQ_NEXT(opt, link)) {
2369 * Information request option can only be specified
2370 * in information-request messages.
2371 * [draft-ietf-dhc-lifetime-02.txt, Section 3.2]
2373 if (opt->val_num == DH6OPT_REFRESHTIME &&
2374 type != DH6_INFORM_REQ) {
2375 dprintf(LOG_DEBUG, FNAME,
2376 "refresh time option is not requested "
2377 "for %s", dhcp6msgstr(type));
2380 *valp = htons((u_int16_t)opt->val_num);
2381 valp++;
2382 optlen += sizeof(u_int16_t);
2384 if (optlen > 0 &&
2385 copy_option(DH6OPT_ORO, optlen, tmpbuf, &p,
2386 optep, &len) != 0) {
2387 goto fail;
2389 free(tmpbuf);
2390 tmpbuf = NULL;
2393 if (dhcp6_set_domain(DH6OPT_SIP_SERVER_D, &optinfo->sipname_list,
2394 &p, optep, &len) != 0)
2395 goto fail;
2397 if (dhcp6_set_addr(DH6OPT_SIP_SERVER_A, &optinfo->sip_list,
2398 &p, optep, &len) != 0)
2399 goto fail;
2401 if (dhcp6_set_addr(DH6OPT_DNS, &optinfo->dns_list,
2402 &p, optep, &len) != 0)
2403 goto fail;
2405 if (dhcp6_set_domain(DH6OPT_DNSNAME, &optinfo->dnsname_list,
2406 &p, optep, &len) != 0)
2407 goto fail;
2409 if (dhcp6_set_addr(DH6OPT_NIS_SERVERS, &optinfo->nis_list,
2410 &p, optep, &len) != 0)
2411 goto fail;
2413 if (dhcp6_set_addr(DH6OPT_NISP_SERVERS, &optinfo->nisp_list,
2414 &p, optep, &len) != 0)
2415 goto fail;
2417 if (dhcp6_set_domain(DH6OPT_NIS_DOMAIN_NAME, &optinfo->nisname_list,
2418 &p, optep, &len) != 0)
2419 goto fail;
2421 if (dhcp6_set_domain(DH6OPT_NISP_DOMAIN_NAME, &optinfo->nispname_list,
2422 &p, optep, &len) != 0)
2423 goto fail;
2425 if (dhcp6_set_addr(DH6OPT_NTP, &optinfo->ntp_list,
2426 &p, optep, &len) != 0)
2427 goto fail;
2429 if (dhcp6_set_domain(DH6OPT_BCMCS_SERVER_D, &optinfo->bcmcsname_list,
2430 &p, optep, &len) != 0)
2431 goto fail;
2433 if (dhcp6_set_addr(DH6OPT_BCMCS_SERVER_A, &optinfo->bcmcs_list,
2434 &p, optep, &len) != 0)
2435 goto fail;
2437 for (op = TAILQ_FIRST(&optinfo->iapd_list); op;
2438 op = TAILQ_NEXT(op, link)) {
2439 int optlen;
2441 tmpbuf = NULL;
2442 if ((optlen = copyout_option(NULL, NULL, op)) < 0) {
2443 dprintf(LOG_INFO, FNAME,
2444 "failed to count option length");
2445 goto fail;
2447 if ((void *)optep - (void *)p < optlen) {
2448 dprintf(LOG_INFO, FNAME, "short buffer");
2449 goto fail;
2451 if ((tmpbuf = malloc(optlen)) == NULL) {
2452 dprintf(LOG_NOTICE, FNAME,
2453 "memory allocation failed for IA_PD options");
2454 goto fail;
2456 if (copyout_option(tmpbuf, tmpbuf + optlen, op) < 0) {
2457 dprintf(LOG_ERR, FNAME,
2458 "failed to construct an IA_PD option");
2459 goto fail;
2461 memcpy(p, tmpbuf, optlen);
2462 free(tmpbuf);
2463 tmpbuf = NULL;
2464 p = (struct dhcp6opt *)((char *)p + optlen);
2465 len += optlen;
2468 if (optinfo->relaymsg_len) {
2469 if (copy_option(DH6OPT_RELAY_MSG, optinfo->relaymsg_len,
2470 optinfo->relaymsg_msg, &p, optep, &len) != 0) {
2471 goto fail;
2475 if (optinfo->ifidopt_id) {
2476 if (copy_option(DH6OPT_INTERFACE_ID, optinfo->ifidopt_len,
2477 optinfo->ifidopt_id, &p, optep, &len) != 0) {
2478 goto fail;
2482 if (optinfo->refreshtime != DH6OPT_REFRESHTIME_UNDEF) {
2483 u_int32_t p32 = (u_int32_t)optinfo->refreshtime;
2485 p32 = htonl(p32);
2486 if (copy_option(DH6OPT_REFRESHTIME, sizeof(p32), &p32, &p,
2487 optep, &len) != 0) {
2488 goto fail;
2492 if (optinfo->authproto != DHCP6_AUTHPROTO_UNDEF) {
2493 struct dhcp6opt_auth *auth;
2494 int authlen;
2495 char *authinfo;
2497 authlen = sizeof(*auth);
2498 if (!(optinfo->authflags & DHCP6OPT_AUTHFLAG_NOINFO)) {
2499 switch (optinfo->authproto) {
2500 case DHCP6_AUTHPROTO_DELAYED:
2501 /* Realm + key ID + HMAC-MD5 */
2502 authlen += optinfo->delayedauth_realmlen +
2503 sizeof(optinfo->delayedauth_keyid) + 16;
2504 break;
2505 #ifdef notyet
2506 case DHCP6_AUTHPROTO_RECONFIG:
2507 /* type + key-or-HAMC */
2508 authlen += 17;
2509 break;
2510 #endif
2511 default:
2512 dprintf(LOG_ERR, FNAME,
2513 "unexpected authentication protocol");
2514 goto fail;
2517 if ((auth = malloc(authlen)) == NULL) {
2518 dprintf(LOG_WARNING, FNAME, "failed to allocate "
2519 "memory for authentication information");
2520 goto fail;
2523 memset(auth, 0, authlen);
2524 /* copy_option will take care of type and len later */
2525 auth->dh6_auth_proto = (u_int8_t)optinfo->authproto;
2526 auth->dh6_auth_alg = (u_int8_t)optinfo->authalgorithm;
2527 auth->dh6_auth_rdm = (u_int8_t)optinfo->authrdm;
2528 memcpy(auth->dh6_auth_rdinfo, &optinfo->authrd,
2529 sizeof(auth->dh6_auth_rdinfo));
2531 if (!(optinfo->authflags & DHCP6OPT_AUTHFLAG_NOINFO)) {
2532 u_int32_t p32;
2534 switch (optinfo->authproto) {
2535 case DHCP6_AUTHPROTO_DELAYED:
2536 authinfo = (char *)(auth + 1);
2538 /* copy realm */
2539 memcpy(authinfo, optinfo->delayedauth_realmval,
2540 optinfo->delayedauth_realmlen);
2541 authinfo += optinfo->delayedauth_realmlen;
2543 /* copy key ID (need memcpy for alignment) */
2544 p32 = htonl(optinfo->delayedauth_keyid);
2545 memcpy(authinfo, &p32, sizeof(p32));
2548 * Set the offset so that the caller can
2549 * calculate the HMAC.
2551 optinfo->delayedauth_offset =
2552 ((char *)p - (char *)optbp) + authlen - 16;
2554 dprintf(LOG_DEBUG, FNAME,
2555 "key ID %x, offset %d",
2556 optinfo->delayedauth_keyid,
2557 optinfo->delayedauth_offset);
2558 break;
2559 #ifdef notyet
2560 case DHCP6_AUTHPROTO_RECONFIG:
2561 #endif
2562 default:
2563 dprintf(LOG_ERR, FNAME,
2564 "unexpected authentication protocol");
2565 free(auth);
2566 goto fail;
2570 if (copy_option(DH6OPT_AUTH, authlen - 4,
2571 &auth->dh6_auth_proto, &p, optep, &len) != 0) {
2572 goto fail;
2574 free(auth);
2577 return (len);
2579 fail:
2580 if (tmpbuf)
2581 free(tmpbuf);
2582 return (-1);
2584 #undef COPY_OPTION
2586 static ssize_t
2587 dnsencode(name, buf, buflen)
2588 const char *name;
2589 char *buf;
2590 size_t buflen;
2592 char *cp, *ep;
2593 const char *p, *q;
2594 int i;
2595 int namelen = strlen(name);
2597 cp = buf;
2598 ep = cp + buflen;
2600 /* if not certain about my name, return an empty buffer */
2601 if (namelen == 0)
2602 return (0);
2604 p = name;
2605 while (cp < ep && p < name + namelen) {
2606 i = 0;
2607 for (q = p; q < name + namelen && *q && *q != '.'; q++)
2608 i++;
2609 /* result does not fit into buf */
2610 if (cp + i + 1 >= ep)
2611 goto fail;
2613 * DNS label length restriction, RFC1035 page 8.
2614 * "i == 0" case is included here to avoid returning
2615 * 0-length label on "foo..bar".
2617 if (i <= 0 || i >= 64)
2618 goto fail;
2619 *cp++ = i;
2620 if (!isalpha(p[0]) || !isalnum(p[i - 1]))
2621 goto fail;
2622 while (i > 0) {
2623 if (!isalnum(*p) && *p != '-')
2624 goto fail;
2625 if (isupper(*p))
2626 *cp++ = tolower(*p++);
2627 else
2628 *cp++ = *p++;
2629 i--;
2631 p = q;
2632 if (p < name + namelen && *p == '.')
2633 p++;
2635 /* termination */
2636 if (cp + 1 >= ep)
2637 goto fail;
2638 *cp++ = '\0';
2639 return (cp - buf);
2641 fail:
2642 return (-1);
2646 * Construct a DHCPv6 option along with sub-options in the wire format.
2647 * If the packet buffer is NULL, just calculate the length of the option
2648 * (and sub-options) so that the caller can allocate a buffer to store the
2649 * option(s).
2650 * This function basically assumes that the caller prepares enough buffer to
2651 * store all the options. However, it also takes the buffer end and checks
2652 * the possibility of overrun for safety.
2654 static int
2655 copyout_option(p, ep, optval)
2656 char *p, *ep;
2657 struct dhcp6_listval *optval;
2659 struct dhcp6opt *opt;
2660 struct dhcp6opt_stcode stcodeopt;
2661 struct dhcp6opt_ia ia;
2662 struct dhcp6opt_ia_pd_prefix pd_prefix;
2663 struct dhcp6opt_ia_addr ia_addr;
2664 char *subp;
2665 struct dhcp6_listval *subov;
2666 int optlen, headlen, sublen, opttype;
2668 /* check invariant for safety */
2669 if (p && ep <= p)
2670 return (-1);
2672 /* first, detect the length of the option head */
2673 switch(optval->type) {
2674 case DHCP6_LISTVAL_IAPD:
2675 memset(&ia, 0, sizeof(ia));
2676 headlen = sizeof(ia);
2677 opttype = DH6OPT_IA_PD;
2678 opt = (struct dhcp6opt *)(void *)&ia;
2679 break;
2680 case DHCP6_LISTVAL_IANA:
2681 memset(&ia, 0, sizeof(ia));
2682 headlen = sizeof(ia);
2683 opttype = DH6OPT_IA_NA;
2684 opt = (struct dhcp6opt *)(void *)&ia;
2685 break;
2686 case DHCP6_LISTVAL_ADDR6:
2687 memset(&pd_prefix, 0, sizeof(pd_prefix));
2688 headlen = sizeof(pd_prefix);
2689 opttype = DH6OPT_IA_PD_PREFIX;
2690 opt = (struct dhcp6opt *)&pd_prefix;
2691 break;
2692 case DHCP6_LISTVAL_PREFIX6:
2693 memset(&pd_prefix, 0, sizeof(pd_prefix));
2694 headlen = sizeof(pd_prefix);
2695 opttype = DH6OPT_IA_PD_PREFIX;
2696 opt = (struct dhcp6opt *)&pd_prefix;
2697 break;
2698 case DHCP6_LISTVAL_STATEFULADDR6:
2699 memset(&ia_addr, 0, sizeof(ia_addr));
2700 headlen = sizeof(ia_addr);
2701 opttype = DH6OPT_IAADDR;
2702 opt = (struct dhcp6opt *)&ia_addr;
2703 break;
2704 case DHCP6_LISTVAL_STCODE:
2705 memset(&stcodeopt, 0, sizeof(stcodeopt));
2706 headlen = sizeof(stcodeopt);
2707 opttype = DH6OPT_STATUS_CODE;
2708 opt = (struct dhcp6opt *)(void *)&stcodeopt;
2709 break;
2710 default:
2712 * we encounter an unknown option. this should be an internal
2713 * error.
2715 dprintf(LOG_ERR, FNAME, "unknown option: code %d",
2716 optval->type);
2717 return (-1);
2720 /* then, calculate the length of and/or fill in the sub-options */
2721 subp = NULL;
2722 sublen = 0;
2723 if (p)
2724 subp = p + headlen;
2725 for (subov = TAILQ_FIRST(&optval->sublist); subov;
2726 subov = TAILQ_NEXT(subov, link)) {
2727 int s;
2729 if ((s = copyout_option(subp, ep, subov)) < 0)
2730 return (-1);
2731 if (p)
2732 subp += s;
2733 sublen += s;
2736 /* finally, deal with the head part again */
2737 optlen = headlen + sublen;
2738 if (!p)
2739 return(optlen);
2741 dprintf(LOG_DEBUG, FNAME, "set %s", dhcp6optstr(opttype));
2742 if (ep - p < headlen) /* check it just in case */
2743 return (-1);
2745 /* fill in the common part */
2746 opt->dh6opt_type = htons(opttype);
2747 opt->dh6opt_len = htons(optlen - sizeof(struct dhcp6opt));
2749 /* fill in type specific fields */
2750 switch(optval->type) {
2751 case DHCP6_LISTVAL_IAPD:
2752 ia.dh6_ia_iaid = htonl(optval->val_ia.iaid);
2753 ia.dh6_ia_t1 = htonl(optval->val_ia.t1);
2754 ia.dh6_ia_t2 = htonl(optval->val_ia.t2);
2755 break;
2756 case DHCP6_LISTVAL_IANA:
2757 ia.dh6_ia_iaid = htonl(optval->val_ia.iaid);
2758 ia.dh6_ia_t1 = htonl(optval->val_ia.t1);
2759 ia.dh6_ia_t2 = htonl(optval->val_ia.t2);
2760 break;
2761 case DHCP6_LISTVAL_PREFIX6:
2762 pd_prefix.dh6_iapd_prefix_preferred_time =
2763 htonl(optval->val_prefix6.pltime);
2764 pd_prefix.dh6_iapd_prefix_valid_time =
2765 htonl(optval->val_prefix6.vltime);
2766 pd_prefix.dh6_iapd_prefix_prefix_len =
2767 optval->val_prefix6.plen;
2768 /* XXX: prefix_addr is badly aligned, so we need memcpy */
2769 memcpy(&pd_prefix.dh6_iapd_prefix_prefix_addr,
2770 &optval->val_prefix6.addr, sizeof(struct in6_addr));
2771 break;
2772 case DHCP6_LISTVAL_STATEFULADDR6:
2773 ia_addr.dh6_ia_addr_preferred_time =
2774 htonl(optval->val_statefuladdr6.pltime);
2775 ia_addr.dh6_ia_addr_valid_time =
2776 htonl(optval->val_statefuladdr6.vltime);
2777 ia_addr.dh6_ia_addr_addr = optval->val_statefuladdr6.addr;
2778 break;
2779 case DHCP6_LISTVAL_STCODE:
2780 stcodeopt.dh6_stcode_code = htons(optval->val_num16);
2781 break;
2782 default:
2784 * XXX: this case should be rejected at the beginning of this
2785 * function.
2787 return (-1);
2790 /* copyout the data (p must be non NULL at this point) */
2791 memcpy(p, opt, headlen);
2792 return (optlen);
2795 void
2796 dhcp6_set_timeoparam(ev)
2797 struct dhcp6_event *ev;
2799 ev->retrans = 0;
2800 ev->init_retrans = 0;
2801 ev->max_retrans_cnt = 0;
2802 ev->max_retrans_dur = 0;
2803 ev->max_retrans_time = 0;
2805 switch(ev->state) {
2806 case DHCP6S_SOLICIT:
2807 ev->init_retrans = SOL_TIMEOUT;
2808 ev->max_retrans_time = SOL_MAX_RT;
2809 break;
2810 case DHCP6S_INFOREQ:
2811 ev->init_retrans = INF_TIMEOUT;
2812 ev->max_retrans_time = INF_MAX_RT;
2813 break;
2814 case DHCP6S_REQUEST:
2815 ev->init_retrans = REQ_TIMEOUT;
2816 ev->max_retrans_time = REQ_MAX_RT;
2817 ev->max_retrans_cnt = REQ_MAX_RC;
2818 break;
2819 case DHCP6S_RENEW:
2820 ev->init_retrans = REN_TIMEOUT;
2821 ev->max_retrans_time = REN_MAX_RT;
2822 break;
2823 case DHCP6S_REBIND:
2824 ev->init_retrans = REB_TIMEOUT;
2825 ev->max_retrans_time = REB_MAX_RT;
2826 break;
2827 case DHCP6S_RELEASE:
2828 ev->init_retrans = REL_TIMEOUT;
2829 ev->max_retrans_cnt = REL_MAX_RC;
2830 break;
2831 default:
2832 dprintf(LOG_ERR, FNAME, "unexpected event state %d on %s",
2833 ev->state, ev->ifp->ifname);
2834 exit(1);
2838 void
2839 dhcp6_reset_timer(ev)
2840 struct dhcp6_event *ev;
2842 double n, r;
2843 char *statestr;
2844 struct timeval interval;
2846 switch(ev->state) {
2847 case DHCP6S_INIT:
2849 * The first Solicit message from the client on the interface
2850 * MUST be delayed by a random amount of time between
2851 * 0 and SOL_MAX_DELAY.
2852 * [RFC3315 17.1.2]
2853 * XXX: a random delay is also necessary before the first
2854 * information-request message. Fortunately, the parameters
2855 * and the algorithm for these two cases are the same.
2856 * [RFC3315 18.1.5]
2858 ev->retrans = (random() % (SOL_MAX_DELAY));
2859 break;
2860 default:
2861 if (ev->state == DHCP6S_SOLICIT && ev->timeouts == 0) {
2863 * The first RT MUST be selected to be strictly
2864 * greater than IRT by choosing RAND to be strictly
2865 * greater than 0.
2866 * [RFC3315 17.1.2]
2868 r = (double)((random() % 1000) + 1) / 10000;
2869 n = ev->init_retrans + r * ev->init_retrans;
2870 } else {
2871 r = (double)((random() % 2000) - 1000) / 10000;
2873 if (ev->timeouts == 0) {
2874 n = ev->init_retrans + r * ev->init_retrans;
2875 } else
2876 n = 2 * ev->retrans + r * ev->retrans;
2878 if (ev->max_retrans_time && n > ev->max_retrans_time)
2879 n = ev->max_retrans_time + r * ev->max_retrans_time;
2880 ev->retrans = (long)n;
2881 break;
2884 interval.tv_sec = (ev->retrans * 1000) / 1000000;
2885 interval.tv_usec = (ev->retrans * 1000) % 1000000;
2886 dhcp6_set_timer(&interval, ev->timer);
2888 statestr = dhcp6_event_statestr(ev);
2890 dprintf(LOG_DEBUG, FNAME, "reset a timer on %s, "
2891 "state=%s, timeo=%d, retrans=%d",
2892 ev->ifp->ifname, statestr, ev->timeouts, ev->retrans);
2896 duidcpy(dd, ds)
2897 struct duid *dd, *ds;
2899 dd->duid_len = ds->duid_len;
2900 if ((dd->duid_id = malloc(dd->duid_len)) == NULL) {
2901 dprintf(LOG_ERR, FNAME, "memory allocation failed");
2902 return (-1);
2904 memcpy(dd->duid_id, ds->duid_id, dd->duid_len);
2906 return (0);
2910 duidcmp(d1, d2)
2911 struct duid *d1, *d2;
2913 if (d1->duid_len == d2->duid_len) {
2914 return (memcmp(d1->duid_id, d2->duid_id, d1->duid_len));
2915 } else
2916 return (-1);
2919 void
2920 duidfree(duid)
2921 struct duid *duid;
2923 if (duid->duid_id)
2924 free(duid->duid_id);
2925 duid->duid_id = NULL;
2926 duid->duid_len = 0;
2930 * Provide an NTP-format timestamp as a replay detection counter
2931 * as mentioned in RFC3315.
2933 #define JAN_1970 2208988800UL /* 1970 - 1900 in seconds */
2935 get_rdvalue(rdm, rdvalue, rdsize)
2936 int rdm;
2937 void *rdvalue;
2938 size_t rdsize;
2940 #if defined(HAVE_CLOCK_GETTIME)
2941 struct timespec tp;
2942 double nsec;
2943 #else
2944 struct timeval tv;
2945 #endif
2946 u_int32_t u32, l32;
2948 if (rdm != DHCP6_AUTHRDM_MONOCOUNTER) {
2949 dprintf(LOG_INFO, FNAME, "unsupported RDM (%d)", rdm);
2950 return (-1);
2952 if (rdsize != sizeof(u_int64_t)) {
2953 dprintf(LOG_INFO, FNAME, "unsupported RD size (%d)", rdsize);
2954 return (-1);
2957 #if defined(HAVE_CLOCK_GETTIME)
2958 if (clock_gettime(CLOCK_REALTIME, &tp)) {
2959 dprintf(LOG_WARNING, FNAME, "clock_gettime failed: %s",
2960 strerror(errno));
2961 return (-1);
2964 u32 = (u_int32_t)tp.tv_sec;
2965 u32 += JAN_1970;
2967 nsec = (double)tp.tv_nsec / 1e9 * 0x100000000ULL;
2968 /* nsec should be smaller than 2^32 */
2969 l32 = (u_int32_t)nsec;
2970 #else
2971 if (gettimeofday(&tv, NULL) != 0) {
2972 dprintf(LOG_WARNING, FNAME, "gettimeofday failed: %s",
2973 strerror(errno));
2974 return (-1);
2976 u32 = (u_int32_t)tv.tv_sec;
2977 u32 += JAN_1970;
2978 l32 = (u_int32_t)tv.tv_usec;
2979 #endif /* HAVE_CLOCK_GETTIME */
2981 u32 = htonl(u32);
2982 l32 = htonl(l32);
2984 memcpy(rdvalue, &u32, sizeof(u32));
2985 memcpy((char *)rdvalue + sizeof(u32), &l32, sizeof(l32));
2987 return (0);
2990 char *
2991 dhcp6optstr(type)
2992 int type;
2994 static char genstr[sizeof("opt_65535") + 1]; /* XXX thread unsafe */
2996 if (type > 65535)
2997 return ("INVALID option");
2999 switch(type) {
3000 case DH6OPT_CLIENTID:
3001 return ("client ID");
3002 case DH6OPT_SERVERID:
3003 return ("server ID");
3004 case DH6OPT_IA_NA:
3005 return ("identity association");
3006 case DH6OPT_IA_TA:
3007 return ("IA for temporary");
3008 case DH6OPT_IAADDR:
3009 return ("IA address");
3010 case DH6OPT_ORO:
3011 return ("option request");
3012 case DH6OPT_PREFERENCE:
3013 return ("preference");
3014 case DH6OPT_ELAPSED_TIME:
3015 return ("elapsed time");
3016 case DH6OPT_RELAY_MSG:
3017 return ("relay message");
3018 case DH6OPT_AUTH:
3019 return ("authentication");
3020 case DH6OPT_UNICAST:
3021 return ("server unicast");
3022 case DH6OPT_STATUS_CODE:
3023 return ("status code");
3024 case DH6OPT_RAPID_COMMIT:
3025 return ("rapid commit");
3026 case DH6OPT_USER_CLASS:
3027 return ("user class");
3028 case DH6OPT_VENDOR_CLASS:
3029 return ("vendor class");
3030 case DH6OPT_VENDOR_OPTS:
3031 return ("vendor specific info");
3032 case DH6OPT_INTERFACE_ID:
3033 return ("interface ID");
3034 case DH6OPT_RECONF_MSG:
3035 return ("reconfigure message");
3036 case DH6OPT_SIP_SERVER_D:
3037 return ("SIP domain name");
3038 case DH6OPT_SIP_SERVER_A:
3039 return ("SIP server address");
3040 case DH6OPT_DNS:
3041 return ("DNS");
3042 case DH6OPT_DNSNAME:
3043 return ("domain search list");
3044 case DH6OPT_NTP:
3045 return ("NTP server");
3046 case DH6OPT_IA_PD:
3047 return ("IA_PD");
3048 case DH6OPT_IA_PD_PREFIX:
3049 return ("IA_PD prefix");
3050 case DH6OPT_REFRESHTIME:
3051 return ("information refresh time");
3052 case DH6OPT_NIS_SERVERS:
3053 return ("NIS servers");
3054 case DH6OPT_NISP_SERVERS:
3055 return ("NIS+ servers");
3056 case DH6OPT_NIS_DOMAIN_NAME:
3057 return ("NIS domain name");
3058 case DH6OPT_NISP_DOMAIN_NAME:
3059 return ("NIS+ domain name");
3060 case DH6OPT_BCMCS_SERVER_D:
3061 return ("BCMCS domain name");
3062 case DH6OPT_BCMCS_SERVER_A:
3063 return ("BCMCS server address");
3064 case DH6OPT_GEOCONF_CIVIC:
3065 return ("Geoconf Civic");
3066 case DH6OPT_REMOTE_ID:
3067 return ("remote ID");
3068 case DH6OPT_SUBSCRIBER_ID:
3069 return ("subscriber ID");
3070 case DH6OPT_CLIENT_FQDN:
3071 return ("client FQDN");
3072 default:
3073 snprintf(genstr, sizeof(genstr), "opt_%d", type);
3074 return (genstr);
3078 char *
3079 dhcp6msgstr(type)
3080 int type;
3082 static char genstr[sizeof("msg255") + 1]; /* XXX thread unsafe */
3084 if (type > 255)
3085 return ("INVALID msg");
3087 switch(type) {
3088 case DH6_SOLICIT:
3089 return ("solicit");
3090 case DH6_ADVERTISE:
3091 return ("advertise");
3092 case DH6_REQUEST:
3093 return ("request");
3094 case DH6_CONFIRM:
3095 return ("confirm");
3096 case DH6_RENEW:
3097 return ("renew");
3098 case DH6_REBIND:
3099 return ("rebind");
3100 case DH6_REPLY:
3101 return ("reply");
3102 case DH6_RELEASE:
3103 return ("release");
3104 case DH6_DECLINE:
3105 return ("decline");
3106 case DH6_RECONFIGURE:
3107 return ("reconfigure");
3108 case DH6_INFORM_REQ:
3109 return ("information request");
3110 case DH6_RELAY_FORW:
3111 return ("relay-forward");
3112 case DH6_RELAY_REPLY:
3113 return ("relay-reply");
3114 default:
3115 snprintf(genstr, sizeof(genstr), "msg%d", type);
3116 return (genstr);
3120 char *
3121 dhcp6_stcodestr(code)
3122 u_int16_t code;
3124 static char genstr[sizeof("code255") + 1]; /* XXX thread unsafe */
3126 if (code > 255)
3127 return ("INVALID code");
3129 switch(code) {
3130 case DH6OPT_STCODE_SUCCESS:
3131 return ("success");
3132 case DH6OPT_STCODE_UNSPECFAIL:
3133 return ("unspec failure");
3134 case DH6OPT_STCODE_NOADDRSAVAIL:
3135 return ("no addresses");
3136 case DH6OPT_STCODE_NOBINDING:
3137 return ("no binding");
3138 case DH6OPT_STCODE_NOTONLINK:
3139 return ("not on-link");
3140 case DH6OPT_STCODE_USEMULTICAST:
3141 return ("use multicast");
3142 case DH6OPT_STCODE_NOPREFIXAVAIL:
3143 return ("no prefixes");
3144 default:
3145 snprintf(genstr, sizeof(genstr), "code%d", code);
3146 return (genstr);
3150 char *
3151 duidstr(duid)
3152 struct duid *duid;
3154 int i, n;
3155 char *cp, *ep;
3156 static char duidstr[sizeof("xx:") * 128 + sizeof("...")];
3158 cp = duidstr;
3159 ep = duidstr + sizeof(duidstr);
3160 for (i = 0; i < duid->duid_len && i <= 128; i++) {
3161 n = snprintf(cp, ep - cp, "%s%02x", i == 0 ? "" : ":",
3162 duid->duid_id[i] & 0xff);
3163 if (n < 0)
3164 return NULL;
3165 cp += n;
3167 if (i < duid->duid_len)
3168 snprintf(cp, ep - cp, "%s", "...");
3170 return (duidstr);
3173 char *dhcp6_event_statestr(ev)
3174 struct dhcp6_event *ev;
3176 switch(ev->state) {
3177 case DHCP6S_INIT:
3178 return ("INIT");
3179 case DHCP6S_SOLICIT:
3180 return ("SOLICIT");
3181 case DHCP6S_INFOREQ:
3182 return ("INFOREQ");
3183 case DHCP6S_REQUEST:
3184 return ("REQUEST");
3185 case DHCP6S_RENEW:
3186 return ("RENEW");
3187 case DHCP6S_REBIND:
3188 return ("REBIND");
3189 case DHCP6S_RELEASE:
3190 return ("RELEASE");
3191 case DHCP6S_IDLE:
3192 return ("IDLE");
3193 default:
3194 return ("???"); /* XXX */
3198 void
3199 setloglevel(debuglevel)
3200 int debuglevel;
3202 if (foreground) {
3203 switch(debuglevel) {
3204 case 0:
3205 debug_thresh = LOG_ERR;
3206 break;
3207 case 1:
3208 debug_thresh = LOG_INFO;
3209 break;
3210 default:
3211 debug_thresh = LOG_DEBUG;
3212 break;
3214 } else {
3215 switch(debuglevel) {
3216 case 0:
3217 setlogmask(LOG_UPTO(LOG_ERR));
3218 break;
3219 case 1:
3220 setlogmask(LOG_UPTO(LOG_INFO));
3221 break;
3226 void
3227 my_dprintf(int level, const char *fname, const char *fmt, ...)
3229 va_list ap;
3230 char logbuf[LINE_MAX];
3231 int printfname = 1;
3233 va_start(ap, fmt);
3234 vsnprintf(logbuf, sizeof(logbuf), fmt, ap);
3236 if (*fname == '\0')
3237 printfname = 0;
3239 if (foreground && debug_thresh >= level) {
3240 time_t now;
3241 struct tm *tm_now;
3242 const char *month[] = {
3243 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
3244 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
3247 if ((now = time(NULL)) < 0)
3248 exit(1); /* XXX */
3249 tm_now = localtime(&now);
3250 fprintf(stderr, "%3s/%02d/%04d %02d:%02d:%02d: %s%s%s\n",
3251 month[tm_now->tm_mon], tm_now->tm_mday,
3252 tm_now->tm_year + 1900,
3253 tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
3254 fname, printfname ? ": " : "",
3255 logbuf);
3256 } else
3257 syslog(level, "%s%s%s", fname, printfname ? ": " : "", logbuf);
3261 ifaddrconf(cmd, ifname, addr, plen, pltime, vltime)
3262 ifaddrconf_cmd_t cmd;
3263 char *ifname;
3264 struct sockaddr_in6 *addr;
3265 int plen;
3266 int pltime;
3267 int vltime;
3269 #ifdef __KAME__
3270 struct in6_aliasreq req;
3271 #endif
3272 #ifdef __linux__
3273 struct in6_ifreq req;
3274 struct ifreq ifr;
3275 #endif
3276 #ifdef __sun__
3277 struct lifreq req;
3278 #endif
3279 unsigned long ioctl_cmd;
3280 char *cmdstr;
3281 int s; /* XXX overhead */
3283 switch(cmd) {
3284 case IFADDRCONF_ADD:
3285 cmdstr = "add";
3286 #ifdef __KAME__
3287 ioctl_cmd = SIOCAIFADDR_IN6;
3288 #endif
3289 #ifdef __linux__
3290 ioctl_cmd = SIOCSIFADDR;
3291 #endif
3292 #ifdef __sun__
3293 ioctl_cmd = SIOCLIFADDIF;
3294 #endif
3295 break;
3296 case IFADDRCONF_REMOVE:
3297 cmdstr = "remove";
3298 #ifdef __KAME__
3299 ioctl_cmd = SIOCDIFADDR_IN6;
3300 #endif
3301 #ifdef __linux__
3302 ioctl_cmd = SIOCDIFADDR;
3303 #endif
3304 #ifdef __sun__
3305 ioctl_cmd = SIOCLIFREMOVEIF;
3306 #endif
3307 break;
3308 default:
3309 return (-1);
3312 if ((s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
3313 dprintf(LOG_ERR, FNAME, "can't open a temporary socket: %s",
3314 strerror(errno));
3315 return (-1);
3318 memset(&req, 0, sizeof(req));
3319 #ifdef __KAME__
3320 req.ifra_addr = *addr;
3321 memcpy(req.ifra_name, ifname, sizeof(req.ifra_name));
3322 (void)sa6_plen2mask(&req.ifra_prefixmask, plen);
3323 /* XXX: should lifetimes be calculated based on the lease duration? */
3324 req.ifra_lifetime.ia6t_vltime = vltime;
3325 req.ifra_lifetime.ia6t_pltime = pltime;
3326 #endif
3327 #ifdef __linux__
3328 memset(&ifr, 0, sizeof(ifr));
3329 strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
3330 if (ioctl(s, SIOGIFINDEX, &ifr) < 0) {
3331 dprintf(LOG_NOTICE, FNAME, "failed to get the index of %s: %s",
3332 ifname, strerror(errno));
3333 close(s);
3334 return (-1);
3336 memcpy(&req.ifr6_addr, &addr->sin6_addr, sizeof(struct in6_addr));
3337 req.ifr6_prefixlen = plen;
3338 req.ifr6_ifindex = ifr.ifr_ifindex;
3339 #endif
3340 #ifdef __sun__
3341 strncpy(req.lifr_name, ifname, sizeof (req.lifr_name));
3342 #endif
3344 if (ioctl(s, ioctl_cmd, &req)) {
3345 dprintf(LOG_NOTICE, FNAME, "failed to %s an address on %s: %s",
3346 cmdstr, ifname, strerror(errno));
3347 close(s);
3348 return (-1);
3351 #ifdef __sun__
3352 memcpy(&req.lifr_addr, addr, sizeof (*addr));
3353 if (ioctl(s, SIOCSLIFADDR, &req) == -1) {
3354 dprintf(LOG_NOTICE, FNAME, "failed to %s new address on %s: %s",
3355 cmdstr, ifname, strerror(errno));
3356 close(s);
3357 return (-1);
3359 #endif
3361 dprintf(LOG_DEBUG, FNAME, "%s an address %s/%d on %s", cmdstr,
3362 addr2str((struct sockaddr *)addr), plen, ifname);
3364 close(s);
3365 return (0);
3369 safefile(path)
3370 const char *path;
3372 struct stat s;
3373 uid_t myuid;
3375 /* no setuid */
3376 if (getuid() != geteuid()) {
3377 dprintf(LOG_NOTICE, FNAME,
3378 "setuid'ed execution not allowed");
3379 return (-1);
3382 if (lstat(path, &s) != 0) {
3383 dprintf(LOG_NOTICE, FNAME, "lstat failed: %s",
3384 strerror(errno));
3385 return (-1);
3388 /* the file must be owned by the running uid */
3389 myuid = getuid();
3390 if (s.st_uid != myuid) {
3391 dprintf(LOG_NOTICE, FNAME, "%s has invalid owner uid", path);
3392 return (-1);
3395 switch (s.st_mode & S_IFMT) {
3396 case S_IFREG:
3397 case S_IFLNK:
3398 break;
3399 default:
3400 dprintf(LOG_NOTICE, FNAME, "%s is an invalid file type 0x%o",
3401 path, (s.st_mode & S_IFMT));
3402 return (-1);
3405 return (0);