xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dhcpv6.c (revision 484ad3ba6a529a2471a98577d59d8ed49c7dd2c7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Dynamic Host Configuration Protocol version 6, for IPv6.  Supports
29  * RFCs 3315, 3319, 3646, 3898, 4075, 4242, 4280, 4580, 4649, and 4704.
30  */
31 
32 #include <ctype.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <time.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <netinet/dhcp6.h>
41 #include <arpa/inet.h>
42 #include <dhcp_impl.h>
43 #include <dhcp_inittab.h>
44 
45 #include "snoop.h"
46 
47 static const char *mtype_to_str(uint8_t);
48 static const char *option_to_str(uint8_t);
49 static const char *duidtype_to_str(uint16_t);
50 static const char *status_to_str(uint16_t);
51 static const char *entr_to_str(uint32_t);
52 static const char *reconf_to_str(uint8_t);
53 static const char *authproto_to_str(uint8_t);
54 static const char *authalg_to_str(uint8_t, uint8_t);
55 static const char *authrdm_to_str(uint8_t);
56 static const char *cwhat_to_str(uint8_t);
57 static const char *catype_to_str(uint8_t);
58 static void show_hex(const uint8_t *, int, const char *);
59 static void show_ascii(const uint8_t *, int, const char *);
60 static void show_address(const char *, const void *);
61 static void show_options(const uint8_t *, int);
62 
63 int
64 interpret_dhcpv6(int flags, const uint8_t *data, int len)
65 {
66 	int olen = len;
67 	char *line, *lstart;
68 	dhcpv6_relay_t d6r;
69 	dhcpv6_message_t d6m;
70 	uint_t optlen;
71 	uint16_t statuscode;
72 
73 	if (len <= 0) {
74 		(void) strlcpy(get_sum_line(), "DHCPv6?", MAXLINE);
75 		return (0);
76 	}
77 	if (flags & F_SUM) {
78 		uint_t ias;
79 		dhcpv6_option_t *d6o;
80 		in6_addr_t link, peer;
81 		char linkstr[INET6_ADDRSTRLEN];
82 		char peerstr[INET6_ADDRSTRLEN];
83 
84 		line = lstart = get_sum_line();
85 		line += snprintf(line, MAXLINE, "DHCPv6 %s",
86 		    mtype_to_str(data[0]));
87 		if (data[0] == DHCPV6_MSG_RELAY_FORW ||
88 		    data[0] == DHCPV6_MSG_RELAY_REPL) {
89 			if (len < sizeof (d6r)) {
90 				(void) strlcpy(line, "?",
91 				    MAXLINE - (line - lstart));
92 				return (olen);
93 			}
94 			/* Not much in DHCPv6 is aligned. */
95 			(void) memcpy(&d6r, data, sizeof (d6r));
96 			(void) memcpy(&link, d6r.d6r_linkaddr, sizeof (link));
97 			(void) memcpy(&peer, d6r.d6r_peeraddr, sizeof (peer));
98 			line += snprintf(line, MAXLINE - (line - lstart),
99 			    " HC=%d link=%s peer=%s", d6r.d6r_hop_count,
100 			    inet_ntop(AF_INET6, &link, linkstr,
101 			    sizeof (linkstr)),
102 			    inet_ntop(AF_INET6, &peer, peerstr,
103 			    sizeof (peerstr)));
104 			data += sizeof (d6r);
105 			len -= sizeof (d6r);
106 		} else {
107 			if (len < sizeof (d6m)) {
108 				(void) strlcpy(line, "?",
109 				    MAXLINE - (line - lstart));
110 				return (olen);
111 			}
112 			(void) memcpy(&d6m, data, sizeof (d6m));
113 			line += snprintf(line, MAXLINE - (line - lstart),
114 			    " xid=%x", DHCPV6_GET_TRANSID(&d6m));
115 			data += sizeof (d6m);
116 			len -= sizeof (d6m);
117 		}
118 		ias = 0;
119 		d6o = NULL;
120 		while ((d6o = dhcpv6_find_option(data, len, d6o,
121 		    DHCPV6_OPT_IA_NA, NULL)) != NULL)
122 			ias++;
123 		if (ias > 0)
124 			line += snprintf(line, MAXLINE - (line - lstart),
125 			    " IAs=%u", ias);
126 		d6o = dhcpv6_find_option(data, len, NULL,
127 		    DHCPV6_OPT_STATUS_CODE, &optlen);
128 		optlen -= sizeof (*d6o);
129 		if (d6o != NULL && optlen >= sizeof (statuscode)) {
130 			(void) memcpy(&statuscode, d6o + 1,
131 			    sizeof (statuscode));
132 			line += snprintf(line, MAXLINE - (line - lstart),
133 			    " status=%u", ntohs(statuscode));
134 			optlen -= sizeof (statuscode);
135 			if (optlen > 0) {
136 				line += snprintf(line,
137 				    MAXLINE - (line - lstart), " \"%.*s\"",
138 				    optlen, (char *)(d6o + 1) + 2);
139 			}
140 		}
141 		d6o = dhcpv6_find_option(data, len, NULL,
142 		    DHCPV6_OPT_RELAY_MSG, &optlen);
143 		optlen -= sizeof (*d6o);
144 		if (d6o != NULL && optlen >= 1) {
145 			line += snprintf(line, MAXLINE - (line - lstart),
146 			    " relay=%s", mtype_to_str(*(uint8_t *)(d6o + 1)));
147 		}
148 	} else if (flags & F_DTAIL) {
149 		show_header("DHCPv6: ",
150 		    "Dynamic Host Configuration Protocol Version 6", len);
151 		show_space();
152 		(void) snprintf(get_line(0, 0), get_line_remain(),
153 		    "Message type (msg-type) = %u (%s)", data[0],
154 		    mtype_to_str(data[0]));
155 		if (data[0] == DHCPV6_MSG_RELAY_FORW ||
156 		    data[0] == DHCPV6_MSG_RELAY_REPL) {
157 			if (len < sizeof (d6r)) {
158 				(void) strlcpy(get_line(0, 0), "Truncated",
159 				    get_line_remain());
160 				return (olen);
161 			}
162 			(void) memcpy(&d6r, data, sizeof (d6r));
163 			(void) snprintf(get_line(0, 0), get_line_remain(),
164 			    "Hop count = %u", d6r.d6r_hop_count);
165 			show_address("Link address", d6r.d6r_linkaddr);
166 			show_address("Peer address", d6r.d6r_peeraddr);
167 			data += sizeof (d6r);
168 			len -= sizeof (d6r);
169 		} else {
170 			if (len < sizeof (d6m)) {
171 				(void) strlcpy(get_line(0, 0), "Truncated",
172 				    get_line_remain());
173 				return (olen);
174 			}
175 			(void) memcpy(&d6m, data, sizeof (d6m));
176 			(void) snprintf(get_line(0, 0), get_line_remain(),
177 			    "Transaction ID = %x", DHCPV6_GET_TRANSID(&d6m));
178 			data += sizeof (d6m);
179 			len -= sizeof (d6m);
180 		}
181 		show_space();
182 		show_options(data, len);
183 		show_space();
184 	}
185 	return (olen);
186 }
187 
188 static const char *
189 mtype_to_str(uint8_t mtype)
190 {
191 	switch (mtype) {
192 	case DHCPV6_MSG_SOLICIT:
193 		return ("Solicit");
194 	case DHCPV6_MSG_ADVERTISE:
195 		return ("Advertise");
196 	case DHCPV6_MSG_REQUEST:
197 		return ("Request");
198 	case DHCPV6_MSG_CONFIRM:
199 		return ("Confirm");
200 	case DHCPV6_MSG_RENEW:
201 		return ("Renew");
202 	case DHCPV6_MSG_REBIND:
203 		return ("Rebind");
204 	case DHCPV6_MSG_REPLY:
205 		return ("Reply");
206 	case DHCPV6_MSG_RELEASE:
207 		return ("Release");
208 	case DHCPV6_MSG_DECLINE:
209 		return ("Decline");
210 	case DHCPV6_MSG_RECONFIGURE:
211 		return ("Reconfigure");
212 	case DHCPV6_MSG_INFO_REQ:
213 		return ("Information-Request");
214 	case DHCPV6_MSG_RELAY_FORW:
215 		return ("Relay-Forward");
216 	case DHCPV6_MSG_RELAY_REPL:
217 		return ("Relay-Reply");
218 	default:
219 		return ("Unknown");
220 	}
221 }
222 
223 static const char *
224 option_to_str(uint8_t mtype)
225 {
226 	switch (mtype) {
227 	case DHCPV6_OPT_CLIENTID:
228 		return ("Client Identifier");
229 	case DHCPV6_OPT_SERVERID:
230 		return ("Server Identifier");
231 	case DHCPV6_OPT_IA_NA:
232 		return ("Identity Association for Non-temporary Addresses");
233 	case DHCPV6_OPT_IA_TA:
234 		return ("Identity Association for Temporary Addresses");
235 	case DHCPV6_OPT_IAADDR:
236 		return ("IA Address");
237 	case DHCPV6_OPT_ORO:
238 		return ("Option Request");
239 	case DHCPV6_OPT_PREFERENCE:
240 		return ("Preference");
241 	case DHCPV6_OPT_ELAPSED_TIME:
242 		return ("Elapsed Time");
243 	case DHCPV6_OPT_RELAY_MSG:
244 		return ("Relay Message");
245 	case DHCPV6_OPT_AUTH:
246 		return ("Authentication");
247 	case DHCPV6_OPT_UNICAST:
248 		return ("Server Unicast");
249 	case DHCPV6_OPT_STATUS_CODE:
250 		return ("Status Code");
251 	case DHCPV6_OPT_RAPID_COMMIT:
252 		return ("Rapid Commit");
253 	case DHCPV6_OPT_USER_CLASS:
254 		return ("User Class");
255 	case DHCPV6_OPT_VENDOR_CLASS:
256 		return ("Vendor Class");
257 	case DHCPV6_OPT_VENDOR_OPT:
258 		return ("Vendor-specific Information");
259 	case DHCPV6_OPT_INTERFACE_ID:
260 		return ("Interface-Id");
261 	case DHCPV6_OPT_RECONF_MSG:
262 		return ("Reconfigure Message");
263 	case DHCPV6_OPT_RECONF_ACC:
264 		return ("Reconfigure Accept");
265 	case DHCPV6_OPT_SIP_NAMES:
266 		return ("SIP Servers Domain Name List");
267 	case DHCPV6_OPT_SIP_ADDR:
268 		return ("SIP Servers IPv6 Address List");
269 	case DHCPV6_OPT_DNS_ADDR:
270 		return ("DNS Recursive Name Server");
271 	case DHCPV6_OPT_DNS_SEARCH:
272 		return ("Domain Search List");
273 	case DHCPV6_OPT_IA_PD:
274 		return ("Identity Association for Prefix Delegation");
275 	case DHCPV6_OPT_IAPREFIX:
276 		return ("IA_PD Prefix");
277 	case DHCPV6_OPT_NIS_SERVERS:
278 		return ("Network Information Service Servers");
279 	case DHCPV6_OPT_NIS_DOMAIN:
280 		return ("Network Information Service Domain Name");
281 	case DHCPV6_OPT_SNTP_SERVERS:
282 		return ("Simple Network Time Protocol Servers");
283 	case DHCPV6_OPT_INFO_REFTIME:
284 		return ("Information Refresh Time");
285 	case DHCPV6_OPT_BCMCS_SRV_D:
286 		return ("BCMCS Controller Domain Name List");
287 	case DHCPV6_OPT_BCMCS_SRV_A:
288 		return ("BCMCS Controller IPv6 Address");
289 	case DHCPV6_OPT_GEOCONF_CVC:
290 		return ("Civic Location");
291 	case DHCPV6_OPT_REMOTE_ID:
292 		return ("Relay Agent Remote-ID");
293 	case DHCPV6_OPT_SUBSCRIBER:
294 		return ("Relay Agent Subscriber-ID");
295 	case DHCPV6_OPT_CLIENT_FQDN:
296 		return ("Client FQDN");
297 	default:
298 		return ("Unknown");
299 	}
300 }
301 
302 static const char *
303 duidtype_to_str(uint16_t dtype)
304 {
305 	switch (dtype) {
306 	case DHCPV6_DUID_LLT:
307 		return ("Link-layer Address Plus Time");
308 	case DHCPV6_DUID_EN:
309 		return ("Enterprise Number");
310 	case DHCPV6_DUID_LL:
311 		return ("Link-layer Address");
312 	default:
313 		return ("Unknown");
314 	}
315 }
316 
317 static const char *
318 status_to_str(uint16_t status)
319 {
320 	switch (status) {
321 	case DHCPV6_STAT_SUCCESS:
322 		return ("Success");
323 	case DHCPV6_STAT_UNSPECFAIL:
324 		return ("Failure, reason unspecified");
325 	case DHCPV6_STAT_NOADDRS:
326 		return ("No addresses for IAs");
327 	case DHCPV6_STAT_NOBINDING:
328 		return ("Client binding unavailable");
329 	case DHCPV6_STAT_NOTONLINK:
330 		return ("Prefix not on link");
331 	case DHCPV6_STAT_USEMCAST:
332 		return ("Use multicast");
333 	case DHCPV6_STAT_NOPREFIX:
334 		return ("No prefix available");
335 	default:
336 		return ("Unknown");
337 	}
338 }
339 
340 static const char *
341 entr_to_str(uint32_t entr)
342 {
343 	switch (entr) {
344 	case DHCPV6_SUN_ENT:
345 		return ("Sun Microsystems");
346 	default:
347 		return ("Unknown");
348 	}
349 }
350 
351 static const char *
352 reconf_to_str(uint8_t msgtype)
353 {
354 	switch (msgtype) {
355 	case DHCPV6_RECONF_RENEW:
356 		return ("Renew");
357 	case DHCPV6_RECONF_INFO:
358 		return ("Information-request");
359 	default:
360 		return ("Unknown");
361 	}
362 }
363 
364 static const char *
365 authproto_to_str(uint8_t aproto)
366 {
367 	switch (aproto) {
368 	case DHCPV6_PROTO_DELAYED:
369 		return ("Delayed");
370 	case DHCPV6_PROTO_RECONFIG:
371 		return ("Reconfigure Key");
372 	default:
373 		return ("Unknown");
374 	}
375 }
376 
377 static const char *
378 authalg_to_str(uint8_t aproto, uint8_t aalg)
379 {
380 	switch (aproto) {
381 	case DHCPV6_PROTO_DELAYED:
382 	case DHCPV6_PROTO_RECONFIG:
383 		switch (aalg) {
384 		case DHCPV6_ALG_HMAC_MD5:
385 			return ("HMAC-MD5 Signature");
386 		default:
387 			return ("Unknown");
388 		}
389 		break;
390 	default:
391 		return ("Unknown");
392 	}
393 }
394 
395 static const char *
396 authrdm_to_str(uint8_t ardm)
397 {
398 	switch (ardm) {
399 	case DHCPV6_RDM_MONOCNT:
400 		return ("Monotonic Counter");
401 	default:
402 		return ("Unknown");
403 	}
404 }
405 
406 static const char *
407 cwhat_to_str(uint8_t what)
408 {
409 	switch (what) {
410 	case DHCPV6_CWHAT_SERVER:
411 		return ("Server");
412 	case DHCPV6_CWHAT_NETWORK:
413 		return ("Network");
414 	case DHCPV6_CWHAT_CLIENT:
415 		return ("Client");
416 	default:
417 		return ("Unknown");
418 	}
419 }
420 
421 static const char *
422 catype_to_str(uint8_t catype)
423 {
424 	switch (catype) {
425 	case CIVICADDR_LANG:
426 		return ("Language; RFC 2277");
427 	case CIVICADDR_A1:
428 		return ("National division (state)");
429 	case CIVICADDR_A2:
430 		return ("County");
431 	case CIVICADDR_A3:
432 		return ("City");
433 	case CIVICADDR_A4:
434 		return ("City division");
435 	case CIVICADDR_A5:
436 		return ("Neighborhood");
437 	case CIVICADDR_A6:
438 		return ("Street group");
439 	case CIVICADDR_PRD:
440 		return ("Leading street direction");
441 	case CIVICADDR_POD:
442 		return ("Trailing street suffix");
443 	case CIVICADDR_STS:
444 		return ("Street suffix or type");
445 	case CIVICADDR_HNO:
446 		return ("House number");
447 	case CIVICADDR_HNS:
448 		return ("House number suffix");
449 	case CIVICADDR_LMK:
450 		return ("Landmark");
451 	case CIVICADDR_LOC:
452 		return ("Additional location information");
453 	case CIVICADDR_NAM:
454 		return ("Name/occupant");
455 	case CIVICADDR_PC:
456 		return ("Postal Code/ZIP");
457 	case CIVICADDR_BLD:
458 		return ("Building");
459 	case CIVICADDR_UNIT:
460 		return ("Unit/apt/suite");
461 	case CIVICADDR_FLR:
462 		return ("Floor");
463 	case CIVICADDR_ROOM:
464 		return ("Room number");
465 	case CIVICADDR_TYPE:
466 		return ("Place type");
467 	case CIVICADDR_PCN:
468 		return ("Postal community name");
469 	case CIVICADDR_POBOX:
470 		return ("Post office box");
471 	case CIVICADDR_ADDL:
472 		return ("Additional code");
473 	case CIVICADDR_SEAT:
474 		return ("Seat/desk");
475 	case CIVICADDR_ROAD:
476 		return ("Primary road or street");
477 	case CIVICADDR_RSEC:
478 		return ("Road section");
479 	case CIVICADDR_RBRA:
480 		return ("Road branch");
481 	case CIVICADDR_RSBR:
482 		return ("Road sub-branch");
483 	case CIVICADDR_SPRE:
484 		return ("Street name pre-modifier");
485 	case CIVICADDR_SPOST:
486 		return ("Street name post-modifier");
487 	case CIVICADDR_SCRIPT:
488 		return ("Script");
489 	default:
490 		return ("Unknown");
491 	}
492 }
493 
494 static void
495 show_hex(const uint8_t *data, int len, const char *name)
496 {
497 	char buffer[16 * 3 + 1];
498 	int nlen;
499 	int i;
500 	char sep;
501 
502 	nlen = strlen(name);
503 	sep = '=';
504 	while (len > 0) {
505 		for (i = 0; i < 16 && i < len; i++)
506 			(void) snprintf(buffer + 3 * i, 4, " %02x", *data++);
507 		(void) snprintf(get_line(0, 0), get_line_remain(), "%*s %c%s",
508 		    nlen, name, sep, buffer);
509 		name = "";
510 		sep = ' ';
511 		len -= i;
512 	}
513 }
514 
515 static void
516 show_ascii(const uint8_t *data, int len, const char *name)
517 {
518 	char buffer[64], *bp;
519 	int nlen;
520 	int i;
521 	char sep;
522 
523 	nlen = strlen(name);
524 	sep = '=';
525 	while (len > 0) {
526 		bp = buffer;
527 		for (i = 0; i < sizeof (buffer) - 4 && len > 0; len--) {
528 			if (!isascii(*data) || !isprint(*data))
529 				bp += snprintf(bp, 5, "\\%03o", *data++);
530 			else
531 				*bp++;
532 		}
533 		*bp = '\0';
534 		(void) snprintf(get_line(0, 0), get_line_remain(),
535 		    "%*s %c \"%s\"", nlen, name, sep, buffer);
536 		sep = ' ';
537 		name = "";
538 	}
539 }
540 
541 static void
542 show_address(const char *addrname, const void *aptr)
543 {
544 	char *hname;
545 	char addrstr[INET6_ADDRSTRLEN];
546 	in6_addr_t addr;
547 
548 	(void) memcpy(&addr, aptr, sizeof (in6_addr_t));
549 	(void) inet_ntop(AF_INET6, &addr, addrstr, sizeof (addrstr));
550 	hname = addrtoname(AF_INET6, &addr);
551 	if (strcmp(hname, addrstr) == 0) {
552 		(void) snprintf(get_line(0, 0), get_line_remain(), "%s = %s",
553 		    addrname, addrstr);
554 	} else {
555 		(void) snprintf(get_line(0, 0), get_line_remain(),
556 		    "%s = %s (%s)", addrname, addrstr, hname);
557 	}
558 }
559 
560 static void
561 nest_options(const uint8_t *data, uint_t olen, char *prefix, char *title)
562 {
563 	char *str, *oldnest, *oldprefix;
564 
565 	if (olen <= 0)
566 		return;
567 	oldprefix = prot_prefix;
568 	oldnest = prot_nest_prefix;
569 	str = malloc(strlen(prot_nest_prefix) + strlen(prot_prefix) + 1);
570 	if (str == NULL) {
571 		prot_nest_prefix = prot_prefix;
572 	} else {
573 		(void) sprintf(str, "%s%s", prot_nest_prefix, prot_prefix);
574 		prot_nest_prefix = str;
575 	}
576 	show_header(prefix, title, 0);
577 	show_options(data, olen);
578 	free(str);
579 	prot_prefix = oldprefix;
580 	prot_nest_prefix = oldnest;
581 }
582 
583 static void
584 show_options(const uint8_t *data, int len)
585 {
586 	dhcpv6_option_t d6o;
587 	uint_t olen, retlen;
588 	uint16_t val16;
589 	uint16_t type;
590 	uint32_t val32;
591 	const uint8_t *ostart;
592 	char *str, *sp;
593 	char *oldnest;
594 
595 	/*
596 	 * Be very careful with negative numbers; ANSI signed/unsigned
597 	 * comparison doesn't work as expected.
598 	 */
599 	while (len >= (signed)sizeof (d6o)) {
600 		(void) memcpy(&d6o, data, sizeof (d6o));
601 		d6o.d6o_code = ntohs(d6o.d6o_code);
602 		d6o.d6o_len = olen = ntohs(d6o.d6o_len);
603 		(void) snprintf(get_line(0, 0), get_line_remain(),
604 		    "Option Code = %u (%s)", d6o.d6o_code,
605 		    option_to_str(d6o.d6o_code));
606 		ostart = data += sizeof (d6o);
607 		len -= sizeof (d6o);
608 		if (olen > len) {
609 			(void) strlcpy(get_line(0, 0), "Option truncated",
610 			    get_line_remain());
611 			olen = len;
612 		}
613 		switch (d6o.d6o_code) {
614 		case DHCPV6_OPT_CLIENTID:
615 		case DHCPV6_OPT_SERVERID:
616 			if (olen < sizeof (val16))
617 				break;
618 			(void) memcpy(&val16, data, sizeof (val16));
619 			data += sizeof (val16);
620 			olen -= sizeof (val16);
621 			type = ntohs(val16);
622 			(void) snprintf(get_line(0, 0), get_line_remain(),
623 			    "  DUID Type = %u (%s)", type,
624 			    duidtype_to_str(type));
625 			if (type == DHCPV6_DUID_LLT || type == DHCPV6_DUID_LL) {
626 				if (olen < sizeof (val16))
627 					break;
628 				(void) memcpy(&val16, data, sizeof (val16));
629 				data += sizeof (val16);
630 				olen -= sizeof (val16);
631 				val16 = ntohs(val16);
632 				(void) snprintf(get_line(0, 0),
633 				    get_line_remain(),
634 				    "  Hardware Type = %u (%s)", val16,
635 				    arp_htype(val16));
636 			}
637 			if (type == DHCPV6_DUID_LLT) {
638 				time_t timevalue;
639 
640 				if (olen < sizeof (val32))
641 					break;
642 				(void) memcpy(&val32, data, sizeof (val32));
643 				data += sizeof (val32);
644 				olen -= sizeof (val32);
645 				timevalue = ntohl(val32) + DUID_TIME_BASE;
646 				(void) snprintf(get_line(0, 0),
647 				    get_line_remain(),
648 				    "  Time = %lu (%.24s)", ntohl(val32),
649 				    ctime(&timevalue));
650 			}
651 			if (type == DHCPV6_DUID_EN) {
652 				if (olen < sizeof (val32))
653 					break;
654 				(void) memcpy(&val32, data, sizeof (val32));
655 				data += sizeof (val32);
656 				olen -= sizeof (val32);
657 				val32 = ntohl(val32);
658 				(void) snprintf(get_line(0, 0),
659 				    get_line_remain(),
660 				    "  Enterprise Number = %lu (%s)", val32,
661 				    entr_to_str(val32));
662 			}
663 			if (olen == 0)
664 				break;
665 			if ((str = malloc(olen * 3)) == NULL)
666 				pr_err("interpret_dhcpv6: no mem");
667 			sp = str + snprintf(str, 3, "%02x", *data++);
668 			while (--olen > 0) {
669 				*sp++ = (type == DHCPV6_DUID_LLT ||
670 				    type == DHCPV6_DUID_LL) ? ':' : ' ';
671 				sp = sp + snprintf(sp, 3, "%02x", *data++);
672 			}
673 			(void) snprintf(get_line(0, 0), get_line_remain(),
674 			    (type == DHCPV6_DUID_LLT ||
675 			    type == DHCPV6_DUID_LL) ?
676 			    "  Link Layer Address = %s" :
677 			    "  Identifier = %s", str);
678 			free(str);
679 			break;
680 		case DHCPV6_OPT_IA_NA:
681 		case DHCPV6_OPT_IA_PD: {
682 			dhcpv6_ia_na_t d6in;
683 
684 			if (olen < sizeof (d6in) - sizeof (d6o))
685 				break;
686 			(void) memcpy(&d6in, data - sizeof (d6o),
687 			    sizeof (d6in));
688 			data += sizeof (d6in) - sizeof (d6o);
689 			olen -= sizeof (d6in) - sizeof (d6o);
690 			(void) snprintf(get_line(0, 0), get_line_remain(),
691 			    "  IAID = %u", ntohl(d6in.d6in_iaid));
692 			(void) snprintf(get_line(0, 0), get_line_remain(),
693 			    "  T1 (renew) = %u seconds", ntohl(d6in.d6in_t1));
694 			(void) snprintf(get_line(0, 0), get_line_remain(),
695 			    "  T2 (rebind) = %u seconds", ntohl(d6in.d6in_t2));
696 			nest_options(data, olen, "IA: ",
697 			    "Identity Association");
698 			break;
699 		}
700 		case DHCPV6_OPT_IA_TA: {
701 			dhcpv6_ia_ta_t d6it;
702 
703 			if (olen < sizeof (d6it) - sizeof (d6o))
704 				break;
705 			(void) memcpy(&d6it, data - sizeof (d6o),
706 			    sizeof (d6it));
707 			data += sizeof (d6it) - sizeof (d6o);
708 			olen -= sizeof (d6it) - sizeof (d6o);
709 			(void) snprintf(get_line(0, 0), get_line_remain(),
710 			    "  IAID = %u", ntohl(d6it.d6it_iaid));
711 			nest_options(data, olen, "IA: ",
712 			    "Identity Association");
713 			break;
714 		}
715 		case DHCPV6_OPT_IAADDR: {
716 			dhcpv6_iaaddr_t d6ia;
717 
718 			if (olen < sizeof (d6ia) - sizeof (d6o))
719 				break;
720 			(void) memcpy(&d6ia, data - sizeof (d6o),
721 			    sizeof (d6ia));
722 			data += sizeof (d6ia) - sizeof (d6o);
723 			olen -= sizeof (d6ia) - sizeof (d6o);
724 			show_address("  Address", &d6ia.d6ia_addr);
725 			(void) snprintf(get_line(0, 0), get_line_remain(),
726 			    "  Preferred lifetime = %u seconds",
727 			    ntohl(d6ia.d6ia_preflife));
728 			(void) snprintf(get_line(0, 0), get_line_remain(),
729 			    "  Valid lifetime = %u seconds",
730 			    ntohl(d6ia.d6ia_vallife));
731 			nest_options(data, olen, "ADDR: ", "Address");
732 			break;
733 		}
734 		case DHCPV6_OPT_ORO:
735 			while (olen >= sizeof (val16)) {
736 				(void) memcpy(&val16, data, sizeof (val16));
737 				val16 = ntohs(val16);
738 				(void) snprintf(get_line(0, 0),
739 				    get_line_remain(),
740 				    "  Requested Option Code = %u (%s)", val16,
741 				    option_to_str(val16));
742 				data += sizeof (val16);
743 				olen -= sizeof (val16);
744 			}
745 			break;
746 		case DHCPV6_OPT_PREFERENCE:
747 			if (olen > 0) {
748 				(void) snprintf(get_line(0, 0),
749 				    get_line_remain(),
750 				    *data == 255 ?
751 				    "  Preference = %u (immediate)" :
752 				    "  Preference = %u", *data);
753 			}
754 			break;
755 		case DHCPV6_OPT_ELAPSED_TIME:
756 			if (olen == sizeof (val16)) {
757 				(void) memcpy(&val16, data, sizeof (val16));
758 				val16 = ntohs(val16);
759 				(void) snprintf(get_line(0, 0),
760 				    get_line_remain(),
761 				    "  Elapsed Time = %u.%02u seconds",
762 				    val16 / 100, val16 % 100);
763 			}
764 			break;
765 		case DHCPV6_OPT_RELAY_MSG:
766 			if (olen > 0) {
767 				oldnest = prot_nest_prefix;
768 				prot_nest_prefix = prot_prefix;
769 				retlen = interpret_dhcpv6(F_DTAIL, data, olen);
770 				prot_prefix = prot_nest_prefix;
771 				prot_nest_prefix = oldnest;
772 			}
773 			break;
774 		case DHCPV6_OPT_AUTH: {
775 			dhcpv6_auth_t d6a;
776 
777 			if (olen < DHCPV6_AUTH_SIZE - sizeof (d6o))
778 				break;
779 			(void) memcpy(&d6a, data - sizeof (d6o),
780 			    DHCPV6_AUTH_SIZE);
781 			data += DHCPV6_AUTH_SIZE - sizeof (d6o);
782 			olen += DHCPV6_AUTH_SIZE - sizeof (d6o);
783 			(void) snprintf(get_line(0, 0), get_line_remain(),
784 			    "  Protocol = %u (%s)", d6a.d6a_proto,
785 			    authproto_to_str(d6a.d6a_proto));
786 			(void) snprintf(get_line(0, 0), get_line_remain(),
787 			    "  Algorithm = %u (%s)", d6a.d6a_alg,
788 			    authalg_to_str(d6a.d6a_proto, d6a.d6a_alg));
789 			(void) snprintf(get_line(0, 0), get_line_remain(),
790 			    "  Replay Detection Method = %u (%s)", d6a.d6a_rdm,
791 			    authrdm_to_str(d6a.d6a_rdm));
792 			show_hex(d6a.d6a_replay, sizeof (d6a.d6a_replay),
793 			    "  RDM Data");
794 			if (olen > 0)
795 				show_hex(data, olen, "  Auth Info");
796 			break;
797 		}
798 		case DHCPV6_OPT_UNICAST:
799 			if (olen >= sizeof (in6_addr_t))
800 				show_address("  Server Address", data);
801 			break;
802 		case DHCPV6_OPT_STATUS_CODE:
803 			if (olen < sizeof (val16))
804 				break;
805 			(void) memcpy(&val16, data, sizeof (val16));
806 			val16 = ntohs(val16);
807 			(void) snprintf(get_line(0, 0), get_line_remain(),
808 			    "  Status Code = %u (%s)", val16,
809 			    status_to_str(val16));
810 			data += sizeof (val16);
811 			olen -= sizeof (val16);
812 			if (olen > 0)
813 				(void) snprintf(get_line(0, 0),
814 				    get_line_remain(), "  Text = \"%.*s\"",
815 				    olen, data);
816 			break;
817 		case DHCPV6_OPT_VENDOR_CLASS:
818 			if (olen < sizeof (val32))
819 				break;
820 			(void) memcpy(&val32, data, sizeof (val32));
821 			data += sizeof (val32);
822 			olen -= sizeof (val32);
823 			val32 = ntohl(val32);
824 			(void) snprintf(get_line(0, 0), get_line_remain(),
825 			    "  Enterprise Number = %lu (%s)", val32,
826 			    entr_to_str(val32));
827 			/* FALLTHROUGH */
828 		case DHCPV6_OPT_USER_CLASS:
829 			while (olen >= sizeof (val16)) {
830 				(void) memcpy(&val16, data, sizeof (val16));
831 				data += sizeof (val16);
832 				olen -= sizeof (val16);
833 				val16 = ntohs(val16);
834 				if (val16 > olen) {
835 					(void) strlcpy(get_line(0, 0),
836 					    "  Truncated class",
837 					    get_line_remain());
838 					val16 = olen;
839 				}
840 				show_hex(data, olen, "  Class");
841 				data += val16;
842 				olen -= val16;
843 			}
844 			break;
845 		case DHCPV6_OPT_VENDOR_OPT: {
846 			dhcpv6_option_t sd6o;
847 
848 			if (olen < sizeof (val32))
849 				break;
850 			(void) memcpy(&val32, data, sizeof (val32));
851 			data += sizeof (val32);
852 			olen -= sizeof (val32);
853 			val32 = ntohl(val32);
854 			(void) snprintf(get_line(0, 0), get_line_remain(),
855 			    "  Enterprise Number = %lu (%s)", val32,
856 			    entr_to_str(val32));
857 			while (olen >= sizeof (sd6o)) {
858 				(void) memcpy(&sd6o, data, sizeof (sd6o));
859 				sd6o.d6o_code = ntohs(sd6o.d6o_code);
860 				sd6o.d6o_len = ntohs(sd6o.d6o_len);
861 				(void) snprintf(get_line(0, 0),
862 				    get_line_remain(),
863 				    "  Vendor Option Code = %u", d6o.d6o_code);
864 				data += sizeof (d6o);
865 				olen -= sizeof (d6o);
866 				if (sd6o.d6o_len > olen) {
867 					(void) strlcpy(get_line(0, 0),
868 					    "  Vendor Option truncated",
869 					    get_line_remain());
870 					sd6o.d6o_len = olen;
871 				}
872 				if (sd6o.d6o_len > 0) {
873 					show_hex(data, sd6o.d6o_len,
874 					    "    Data");
875 					data += sd6o.d6o_len;
876 					olen -= sd6o.d6o_len;
877 				}
878 			}
879 			break;
880 		}
881 		case DHCPV6_OPT_REMOTE_ID:
882 			if (olen < sizeof (val32))
883 				break;
884 			(void) memcpy(&val32, data, sizeof (val32));
885 			data += sizeof (val32);
886 			olen -= sizeof (val32);
887 			val32 = ntohl(val32);
888 			(void) snprintf(get_line(0, 0), get_line_remain(),
889 			    "  Enterprise Number = %lu (%s)", val32,
890 			    entr_to_str(val32));
891 			/* FALLTHROUGH */
892 		case DHCPV6_OPT_INTERFACE_ID:
893 		case DHCPV6_OPT_SUBSCRIBER:
894 			if (olen > 0)
895 				show_hex(data, olen, "  ID");
896 			break;
897 		case DHCPV6_OPT_RECONF_MSG:
898 			if (olen > 0) {
899 				(void) snprintf(get_line(0, 0),
900 				    get_line_remain(),
901 				    "  Message Type = %u (%s)", *data,
902 				    reconf_to_str(*data));
903 			}
904 			break;
905 		case DHCPV6_OPT_SIP_NAMES:
906 		case DHCPV6_OPT_DNS_SEARCH:
907 		case DHCPV6_OPT_NIS_DOMAIN:
908 		case DHCPV6_OPT_BCMCS_SRV_D: {
909 			dhcp_symbol_t *symp;
910 			char *sp2;
911 
912 			symp = inittab_getbycode(
913 			    ITAB_CAT_STANDARD | ITAB_CAT_V6, ITAB_CONS_SNOOP,
914 			    d6o.d6o_code);
915 			if (symp != NULL) {
916 				str = inittab_decode(symp, data, olen, B_TRUE);
917 				if (str != NULL) {
918 					sp = str;
919 					do {
920 						sp2 = strchr(sp, ' ');
921 						if (sp2 != NULL)
922 							*sp2++ = '\0';
923 						(void) snprintf(get_line(0, 0),
924 						    get_line_remain(),
925 						    "  Name = %s", sp);
926 					} while ((sp = sp2) != NULL);
927 					free(str);
928 				}
929 				free(symp);
930 			}
931 			break;
932 		}
933 		case DHCPV6_OPT_SIP_ADDR:
934 		case DHCPV6_OPT_DNS_ADDR:
935 		case DHCPV6_OPT_NIS_SERVERS:
936 		case DHCPV6_OPT_SNTP_SERVERS:
937 		case DHCPV6_OPT_BCMCS_SRV_A:
938 			while (olen >= sizeof (in6_addr_t)) {
939 				show_address("  Address", data);
940 				data += sizeof (in6_addr_t);
941 				olen -= sizeof (in6_addr_t);
942 			}
943 			break;
944 		case DHCPV6_OPT_IAPREFIX: {
945 			dhcpv6_iaprefix_t d6ip;
946 
947 			if (olen < DHCPV6_IAPREFIX_SIZE - sizeof (d6o))
948 				break;
949 			(void) memcpy(&d6ip, data - sizeof (d6o),
950 			    DHCPV6_IAPREFIX_SIZE);
951 			data += DHCPV6_IAPREFIX_SIZE - sizeof (d6o);
952 			olen -= DHCPV6_IAPREFIX_SIZE - sizeof (d6o);
953 			show_address("  Prefix", d6ip.d6ip_addr);
954 			(void) snprintf(get_line(0, 0), get_line_remain(),
955 			    "  Preferred lifetime = %u seconds",
956 			    ntohl(d6ip.d6ip_preflife));
957 			(void) snprintf(get_line(0, 0), get_line_remain(),
958 			    "  Valid lifetime = %u seconds",
959 			    ntohl(d6ip.d6ip_vallife));
960 			(void) snprintf(get_line(0, 0), get_line_remain(),
961 			    "  Prefix length = %u", d6ip.d6ip_preflen);
962 			nest_options(data, olen, "ADDR: ", "Address");
963 			break;
964 		}
965 		case DHCPV6_OPT_INFO_REFTIME:
966 			if (olen < sizeof (val32))
967 				break;
968 			(void) memcpy(&val32, data, sizeof (val32));
969 			(void) snprintf(get_line(0, 0), get_line_remain(),
970 			    "  Refresh Time = %lu seconds", ntohl(val32));
971 			break;
972 		case DHCPV6_OPT_GEOCONF_CVC: {
973 			dhcpv6_civic_t d6c;
974 			int solen;
975 
976 			if (olen < DHCPV6_CIVIC_SIZE - sizeof (d6o))
977 				break;
978 			(void) memcpy(&d6c, data - sizeof (d6o),
979 			    DHCPV6_CIVIC_SIZE);
980 			data += DHCPV6_CIVIC_SIZE - sizeof (d6o);
981 			olen -= DHCPV6_CIVIC_SIZE - sizeof (d6o);
982 			(void) snprintf(get_line(0, 0), get_line_remain(),
983 			    "  What Location = %u (%s)", d6c.d6c_what,
984 			    cwhat_to_str(d6c.d6c_what));
985 			(void) snprintf(get_line(0, 0), get_line_remain(),
986 			    "  Country Code = %.*s", sizeof (d6c.d6c_cc),
987 			    d6c.d6c_cc);
988 			while (olen >= 2) {
989 				(void) snprintf(get_line(0, 0),
990 				    get_line_remain(),
991 				    "  CA Element = %u (%s)", *data,
992 				    catype_to_str(*data));
993 				solen = data[1];
994 				data += 2;
995 				olen -= 2;
996 				if (solen > olen) {
997 					(void) strlcpy(get_line(0, 0),
998 					    "  CA Element truncated",
999 					    get_line_remain());
1000 					solen = olen;
1001 				}
1002 				if (solen > 0) {
1003 					show_ascii(data, solen, "  CA Data");
1004 					data += solen;
1005 					olen -= solen;
1006 				}
1007 			}
1008 			break;
1009 		}
1010 		case DHCPV6_OPT_CLIENT_FQDN: {
1011 			dhcp_symbol_t *symp;
1012 
1013 			if (olen == 0)
1014 				break;
1015 			(void) snprintf(get_line(0, 0), get_line_remain(),
1016 			    "  Flags = %02x", *data);
1017 			(void) snprintf(get_line(0, 0), get_line_remain(),
1018 			    "        %s", getflag(*data, DHCPV6_FQDNF_S,
1019 			    "Perform AAAA RR updates", "No AAAA RR updates"));
1020 			(void) snprintf(get_line(0, 0), get_line_remain(),
1021 			    "        %s", getflag(*data, DHCPV6_FQDNF_O,
1022 			    "Server override updates",
1023 			    "No server override updates"));
1024 			(void) snprintf(get_line(0, 0), get_line_remain(),
1025 			    "        %s", getflag(*data, DHCPV6_FQDNF_N,
1026 			    "Server performs no updates",
1027 			    "Server performs updates"));
1028 			symp = inittab_getbycode(
1029 			    ITAB_CAT_STANDARD | ITAB_CAT_V6, ITAB_CONS_SNOOP,
1030 			    d6o.d6o_code);
1031 			if (symp != NULL) {
1032 				str = inittab_decode(symp, data, olen, B_TRUE);
1033 				if (str != NULL) {
1034 					(void) snprintf(get_line(0, 0),
1035 					    get_line_remain(),
1036 					    "  FQDN = %s", str);
1037 					free(str);
1038 				}
1039 				free(symp);
1040 			}
1041 			break;
1042 		}
1043 		}
1044 		data = ostart + d6o.d6o_len;
1045 		len -= d6o.d6o_len;
1046 	}
1047 	if (len != 0) {
1048 		(void) strlcpy(get_line(0, 0), "Option entry truncated",
1049 		    get_line_remain());
1050 	}
1051 }
1052