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