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