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 2017 Gary Mills
24 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28/*
29 * Dynamic Host Configuration Protocol version 6, for IPv6.  Supports
30 * RFCs 3315, 3319, 3646, 3898, 4075, 4242, 4280, 4580, 4649, and 4704.
31 */
32
33#include <ctype.h>
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
48static const char *mtype_to_str(uint8_t);
49static const char *option_to_str(uint8_t);
50static const char *duidtype_to_str(uint16_t);
51static const char *status_to_str(uint16_t);
52static const char *entr_to_str(uint32_t);
53static const char *reconf_to_str(uint8_t);
54static const char *authproto_to_str(uint8_t);
55static const char *authalg_to_str(uint8_t, uint8_t);
56static const char *authrdm_to_str(uint8_t);
57static const char *cwhat_to_str(uint8_t);
58static const char *catype_to_str(uint8_t);
59static void show_hex(const uint8_t *, int, const char *);
60static void show_ascii(const uint8_t *, int, const char *);
61static void show_address(const char *, const void *);
62static void show_options(const uint8_t *, int);
63
64int
65interpret_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
189static const char *
190mtype_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
224static const char *
225option_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_NIS_DOMAIN:
281		return ("Network Information Service Domain Name");
282	case DHCPV6_OPT_SNTP_SERVERS:
283		return ("Simple Network Time Protocol Servers");
284	case DHCPV6_OPT_INFO_REFTIME:
285		return ("Information Refresh Time");
286	case DHCPV6_OPT_BCMCS_SRV_D:
287		return ("BCMCS Controller Domain Name List");
288	case DHCPV6_OPT_BCMCS_SRV_A:
289		return ("BCMCS Controller IPv6 Address");
290	case DHCPV6_OPT_GEOCONF_CVC:
291		return ("Civic Location");
292	case DHCPV6_OPT_REMOTE_ID:
293		return ("Relay Agent Remote-ID");
294	case DHCPV6_OPT_SUBSCRIBER:
295		return ("Relay Agent Subscriber-ID");
296	case DHCPV6_OPT_CLIENT_FQDN:
297		return ("Client FQDN");
298	default:
299		return ("Unknown");
300	}
301}
302
303static const char *
304duidtype_to_str(uint16_t dtype)
305{
306	switch (dtype) {
307	case DHCPV6_DUID_LLT:
308		return ("Link-layer Address Plus Time");
309	case DHCPV6_DUID_EN:
310		return ("Enterprise Number");
311	case DHCPV6_DUID_LL:
312		return ("Link-layer Address");
313	default:
314		return ("Unknown");
315	}
316}
317
318static const char *
319status_to_str(uint16_t status)
320{
321	switch (status) {
322	case DHCPV6_STAT_SUCCESS:
323		return ("Success");
324	case DHCPV6_STAT_UNSPECFAIL:
325		return ("Failure, reason unspecified");
326	case DHCPV6_STAT_NOADDRS:
327		return ("No addresses for IAs");
328	case DHCPV6_STAT_NOBINDING:
329		return ("Client binding unavailable");
330	case DHCPV6_STAT_NOTONLINK:
331		return ("Prefix not on link");
332	case DHCPV6_STAT_USEMCAST:
333		return ("Use multicast");
334	case DHCPV6_STAT_NOPREFIX:
335		return ("No prefix available");
336	default:
337		return ("Unknown");
338	}
339}
340
341static const char *
342entr_to_str(uint32_t entr)
343{
344	switch (entr) {
345	case DHCPV6_SUN_ENT:
346		return ("Sun Microsystems");
347	default:
348		return ("Unknown");
349	}
350}
351
352static const char *
353reconf_to_str(uint8_t msgtype)
354{
355	switch (msgtype) {
356	case DHCPV6_RECONF_RENEW:
357		return ("Renew");
358	case DHCPV6_RECONF_INFO:
359		return ("Information-request");
360	default:
361		return ("Unknown");
362	}
363}
364
365static const char *
366authproto_to_str(uint8_t aproto)
367{
368	switch (aproto) {
369	case DHCPV6_PROTO_DELAYED:
370		return ("Delayed");
371	case DHCPV6_PROTO_RECONFIG:
372		return ("Reconfigure Key");
373	default:
374		return ("Unknown");
375	}
376}
377
378static const char *
379authalg_to_str(uint8_t aproto, uint8_t aalg)
380{
381	switch (aproto) {
382	case DHCPV6_PROTO_DELAYED:
383	case DHCPV6_PROTO_RECONFIG:
384		switch (aalg) {
385		case DHCPV6_ALG_HMAC_MD5:
386			return ("HMAC-MD5 Signature");
387		default:
388			return ("Unknown");
389		}
390		break;
391	default:
392		return ("Unknown");
393	}
394}
395
396static const char *
397authrdm_to_str(uint8_t ardm)
398{
399	switch (ardm) {
400	case DHCPV6_RDM_MONOCNT:
401		return ("Monotonic Counter");
402	default:
403		return ("Unknown");
404	}
405}
406
407static const char *
408cwhat_to_str(uint8_t what)
409{
410	switch (what) {
411	case DHCPV6_CWHAT_SERVER:
412		return ("Server");
413	case DHCPV6_CWHAT_NETWORK:
414		return ("Network");
415	case DHCPV6_CWHAT_CLIENT:
416		return ("Client");
417	default:
418		return ("Unknown");
419	}
420}
421
422static const char *
423catype_to_str(uint8_t catype)
424{
425	switch (catype) {
426	case CIVICADDR_LANG:
427		return ("Language; RFC 2277");
428	case CIVICADDR_A1:
429		return ("National division (state)");
430	case CIVICADDR_A2:
431		return ("County");
432	case CIVICADDR_A3:
433		return ("City");
434	case CIVICADDR_A4:
435		return ("City division");
436	case CIVICADDR_A5:
437		return ("Neighborhood");
438	case CIVICADDR_A6:
439		return ("Street group");
440	case CIVICADDR_PRD:
441		return ("Leading street direction");
442	case CIVICADDR_POD:
443		return ("Trailing street suffix");
444	case CIVICADDR_STS:
445		return ("Street suffix or type");
446	case CIVICADDR_HNO:
447		return ("House number");
448	case CIVICADDR_HNS:
449		return ("House number suffix");
450	case CIVICADDR_LMK:
451		return ("Landmark");
452	case CIVICADDR_LOC:
453		return ("Additional location information");
454	case CIVICADDR_NAM:
455		return ("Name/occupant");
456	case CIVICADDR_PC:
457		return ("Postal Code/ZIP");
458	case CIVICADDR_BLD:
459		return ("Building");
460	case CIVICADDR_UNIT:
461		return ("Unit/apt/suite");
462	case CIVICADDR_FLR:
463		return ("Floor");
464	case CIVICADDR_ROOM:
465		return ("Room number");
466	case CIVICADDR_TYPE:
467		return ("Place type");
468	case CIVICADDR_PCN:
469		return ("Postal community name");
470	case CIVICADDR_POBOX:
471		return ("Post office box");
472	case CIVICADDR_ADDL:
473		return ("Additional code");
474	case CIVICADDR_SEAT:
475		return ("Seat/desk");
476	case CIVICADDR_ROAD:
477		return ("Primary road or street");
478	case CIVICADDR_RSEC:
479		return ("Road section");
480	case CIVICADDR_RBRA:
481		return ("Road branch");
482	case CIVICADDR_RSBR:
483		return ("Road sub-branch");
484	case CIVICADDR_SPRE:
485		return ("Street name pre-modifier");
486	case CIVICADDR_SPOST:
487		return ("Street name post-modifier");
488	case CIVICADDR_SCRIPT:
489		return ("Script");
490	default:
491		return ("Unknown");
492	}
493}
494
495static void
496show_hex(const uint8_t *data, int len, const char *name)
497{
498	char buffer[16 * 3 + 1];
499	int nlen;
500	int i;
501	char sep;
502
503	nlen = strlen(name);
504	sep = '=';
505	while (len > 0) {
506		for (i = 0; i < 16 && i < len; i++)
507			(void) snprintf(buffer + 3 * i, 4, " %02x", *data++);
508		(void) snprintf(get_line(0, 0), get_line_remain(), "%*s %c%s",
509		    nlen, name, sep, buffer);
510		name = "";
511		sep = ' ';
512		len -= i;
513	}
514}
515
516static void
517show_ascii(const uint8_t *data, int len, const char *name)
518{
519	char buffer[64], *bp;
520	int nlen;
521	int i;
522	char sep;
523
524	nlen = strlen(name);
525	sep = '=';
526	while (len > 0) {
527		bp = buffer;
528		for (i = 0; i < sizeof (buffer) - 4 && len > 0; len--) {
529			if (!isascii(*data) || !isprint(*data))
530				bp += snprintf(bp, 5, "\\%03o", *data++);
531			else
532				*bp++;
533		}
534		*bp = '\0';
535		(void) snprintf(get_line(0, 0), get_line_remain(),
536		    "%*s %c \"%s\"", nlen, name, sep, buffer);
537		sep = ' ';
538		name = "";
539	}
540}
541
542static void
543show_address(const char *addrname, const void *aptr)
544{
545	char *hname;
546	char addrstr[INET6_ADDRSTRLEN];
547	in6_addr_t addr;
548
549	(void) memcpy(&addr, aptr, sizeof (in6_addr_t));
550	(void) inet_ntop(AF_INET6, &addr, addrstr, sizeof (addrstr));
551	hname = addrtoname(AF_INET6, &addr);
552	if (strcmp(hname, addrstr) == 0) {
553		(void) snprintf(get_line(0, 0), get_line_remain(), "%s = %s",
554		    addrname, addrstr);
555	} else {
556		(void) snprintf(get_line(0, 0), get_line_remain(),
557		    "%s = %s (%s)", addrname, addrstr, hname);
558	}
559}
560
561static void
562nest_options(const uint8_t *data, uint_t olen, char *prefix, char *title)
563{
564	char *str, *oldnest, *oldprefix;
565
566	if (olen <= 0)
567		return;
568	oldprefix = prot_prefix;
569	oldnest = prot_nest_prefix;
570	str = malloc(strlen(prot_nest_prefix) + strlen(prot_prefix) + 1);
571	if (str == NULL) {
572		prot_nest_prefix = prot_prefix;
573	} else {
574		(void) sprintf(str, "%s%s", prot_nest_prefix, prot_prefix);
575		prot_nest_prefix = str;
576	}
577	show_header(prefix, title, 0);
578	show_options(data, olen);
579	free(str);
580	prot_prefix = oldprefix;
581	prot_nest_prefix = oldnest;
582}
583
584static void
585show_options(const uint8_t *data, int len)
586{
587	dhcpv6_option_t d6o;
588	uint_t olen;
589	uint16_t val16;
590	uint16_t type;
591	uint32_t val32;
592	const uint8_t *ostart;
593	char *str, *sp;
594	char *oldnest;
595
596	/*
597	 * Be very careful with negative numbers; ANSI signed/unsigned
598	 * comparison doesn't work as expected.
599	 */
600	while (len >= (signed)sizeof (d6o)) {
601		(void) memcpy(&d6o, data, sizeof (d6o));
602		d6o.d6o_code = ntohs(d6o.d6o_code);
603		d6o.d6o_len = olen = ntohs(d6o.d6o_len);
604		(void) snprintf(get_line(0, 0), get_line_remain(),
605		    "Option Code = %u (%s)", d6o.d6o_code,
606		    option_to_str(d6o.d6o_code));
607		ostart = data += sizeof (d6o);
608		len -= sizeof (d6o);
609		if (olen > len) {
610			(void) strlcpy(get_line(0, 0), "Option truncated",
611			    get_line_remain());
612			olen = len;
613		}
614		switch (d6o.d6o_code) {
615		case DHCPV6_OPT_CLIENTID:
616		case DHCPV6_OPT_SERVERID:
617			if (olen < sizeof (val16))
618				break;
619			(void) memcpy(&val16, data, sizeof (val16));
620			data += sizeof (val16);
621			olen -= sizeof (val16);
622			type = ntohs(val16);
623			(void) snprintf(get_line(0, 0), get_line_remain(),
624			    "  DUID Type = %u (%s)", type,
625			    duidtype_to_str(type));
626			if (type == DHCPV6_DUID_LLT || type == DHCPV6_DUID_LL) {
627				if (olen < sizeof (val16))
628					break;
629				(void) memcpy(&val16, data, sizeof (val16));
630				data += sizeof (val16);
631				olen -= sizeof (val16);
632				val16 = ntohs(val16);
633				(void) snprintf(get_line(0, 0),
634				    get_line_remain(),
635				    "  Hardware Type = %u (%s)", val16,
636				    arp_htype(val16));
637			}
638			if (type == DHCPV6_DUID_LLT) {
639				time_t timevalue;
640
641				if (olen < sizeof (val32))
642					break;
643				(void) memcpy(&val32, data, sizeof (val32));
644				data += sizeof (val32);
645				olen -= sizeof (val32);
646				timevalue = ntohl(val32) + DUID_TIME_BASE;
647				(void) snprintf(get_line(0, 0),
648				    get_line_remain(),
649				    "  Time = %lu (%.24s)", ntohl(val32),
650				    ctime(&timevalue));
651			}
652			if (type == DHCPV6_DUID_EN) {
653				if (olen < sizeof (val32))
654					break;
655				(void) memcpy(&val32, data, sizeof (val32));
656				data += sizeof (val32);
657				olen -= sizeof (val32);
658				val32 = ntohl(val32);
659				(void) snprintf(get_line(0, 0),
660				    get_line_remain(),
661				    "  Enterprise Number = %lu (%s)", val32,
662				    entr_to_str(val32));
663			}
664			if (olen == 0)
665				break;
666			if ((str = malloc(olen * 3)) == NULL)
667				pr_err("interpret_dhcpv6: no mem");
668			sp = str + snprintf(str, 3, "%02x", *data++);
669			while (--olen > 0) {
670				*sp++ = (type == DHCPV6_DUID_LLT ||
671				    type == DHCPV6_DUID_LL) ? ':' : ' ';
672				sp = sp + snprintf(sp, 3, "%02x", *data++);
673			}
674			(void) snprintf(get_line(0, 0), get_line_remain(),
675			    (type == DHCPV6_DUID_LLT ||
676			    type == DHCPV6_DUID_LL) ?
677			    "  Link Layer Address = %s" :
678			    "  Identifier = %s", str);
679			free(str);
680			break;
681		case DHCPV6_OPT_IA_NA:
682		case DHCPV6_OPT_IA_PD: {
683			dhcpv6_ia_na_t d6in;
684
685			if (olen < sizeof (d6in) - sizeof (d6o))
686				break;
687			(void) memcpy(&d6in, data - sizeof (d6o),
688			    sizeof (d6in));
689			data += sizeof (d6in) - sizeof (d6o);
690			olen -= sizeof (d6in) - sizeof (d6o);
691			(void) snprintf(get_line(0, 0), get_line_remain(),
692			    "  IAID = %u", ntohl(d6in.d6in_iaid));
693			(void) snprintf(get_line(0, 0), get_line_remain(),
694			    "  T1 (renew) = %u seconds", ntohl(d6in.d6in_t1));
695			(void) snprintf(get_line(0, 0), get_line_remain(),
696			    "  T2 (rebind) = %u seconds", ntohl(d6in.d6in_t2));
697			nest_options(data, olen, "IA: ",
698			    "Identity Association");
699			break;
700		}
701		case DHCPV6_OPT_IA_TA: {
702			dhcpv6_ia_ta_t d6it;
703
704			if (olen < sizeof (d6it) - sizeof (d6o))
705				break;
706			(void) memcpy(&d6it, data - sizeof (d6o),
707			    sizeof (d6it));
708			data += sizeof (d6it) - sizeof (d6o);
709			olen -= sizeof (d6it) - sizeof (d6o);
710			(void) snprintf(get_line(0, 0), get_line_remain(),
711			    "  IAID = %u", ntohl(d6it.d6it_iaid));
712			nest_options(data, olen, "IA: ",
713			    "Identity Association");
714			break;
715		}
716		case DHCPV6_OPT_IAADDR: {
717			dhcpv6_iaaddr_t d6ia;
718
719			if (olen < sizeof (d6ia) - sizeof (d6o))
720				break;
721			(void) memcpy(&d6ia, data - sizeof (d6o),
722			    sizeof (d6ia));
723			data += sizeof (d6ia) - sizeof (d6o);
724			olen -= sizeof (d6ia) - sizeof (d6o);
725			show_address("  Address", &d6ia.d6ia_addr);
726			(void) snprintf(get_line(0, 0), get_line_remain(),
727			    "  Preferred lifetime = %u seconds",
728			    ntohl(d6ia.d6ia_preflife));
729			(void) snprintf(get_line(0, 0), get_line_remain(),
730			    "  Valid lifetime = %u seconds",
731			    ntohl(d6ia.d6ia_vallife));
732			nest_options(data, olen, "ADDR: ", "Address");
733			break;
734		}
735		case DHCPV6_OPT_ORO:
736			while (olen >= sizeof (val16)) {
737				(void) memcpy(&val16, data, sizeof (val16));
738				val16 = ntohs(val16);
739				(void) snprintf(get_line(0, 0),
740				    get_line_remain(),
741				    "  Requested Option Code = %u (%s)", val16,
742				    option_to_str(val16));
743				data += sizeof (val16);
744				olen -= sizeof (val16);
745			}
746			break;
747		case DHCPV6_OPT_PREFERENCE:
748			if (olen > 0) {
749				(void) snprintf(get_line(0, 0),
750				    get_line_remain(),
751				    *data == 255 ?
752				    "  Preference = %u (immediate)" :
753				    "  Preference = %u", *data);
754			}
755			break;
756		case DHCPV6_OPT_ELAPSED_TIME:
757			if (olen == sizeof (val16)) {
758				(void) memcpy(&val16, data, sizeof (val16));
759				val16 = ntohs(val16);
760				(void) snprintf(get_line(0, 0),
761				    get_line_remain(),
762				    "  Elapsed Time = %u.%02u seconds",
763				    val16 / 100, val16 % 100);
764			}
765			break;
766		case DHCPV6_OPT_RELAY_MSG:
767			if (olen > 0) {
768				oldnest = prot_nest_prefix;
769				prot_nest_prefix = prot_prefix;
770				(void) interpret_dhcpv6(F_DTAIL, data, olen);
771				prot_prefix = prot_nest_prefix;
772				prot_nest_prefix = oldnest;
773			}
774			break;
775		case DHCPV6_OPT_AUTH: {
776			dhcpv6_auth_t d6a;
777
778			if (olen < DHCPV6_AUTH_SIZE - sizeof (d6o))
779				break;
780			(void) memcpy(&d6a, data - sizeof (d6o),
781			    DHCPV6_AUTH_SIZE);
782			data += DHCPV6_AUTH_SIZE - sizeof (d6o);
783			olen += DHCPV6_AUTH_SIZE - sizeof (d6o);
784			(void) snprintf(get_line(0, 0), get_line_remain(),
785			    "  Protocol = %u (%s)", d6a.d6a_proto,
786			    authproto_to_str(d6a.d6a_proto));
787			(void) snprintf(get_line(0, 0), get_line_remain(),
788			    "  Algorithm = %u (%s)", d6a.d6a_alg,
789			    authalg_to_str(d6a.d6a_proto, d6a.d6a_alg));
790			(void) snprintf(get_line(0, 0), get_line_remain(),
791			    "  Replay Detection Method = %u (%s)", d6a.d6a_rdm,
792			    authrdm_to_str(d6a.d6a_rdm));
793			show_hex(d6a.d6a_replay, sizeof (d6a.d6a_replay),
794			    "  RDM Data");
795			if (olen > 0)
796				show_hex(data, olen, "  Auth Info");
797			break;
798		}
799		case DHCPV6_OPT_UNICAST:
800			if (olen >= sizeof (in6_addr_t))
801				show_address("  Server Address", data);
802			break;
803		case DHCPV6_OPT_STATUS_CODE:
804			if (olen < sizeof (val16))
805				break;
806			(void) memcpy(&val16, data, sizeof (val16));
807			val16 = ntohs(val16);
808			(void) snprintf(get_line(0, 0), get_line_remain(),
809			    "  Status Code = %u (%s)", val16,
810			    status_to_str(val16));
811			data += sizeof (val16);
812			olen -= sizeof (val16);
813			if (olen > 0)
814				(void) snprintf(get_line(0, 0),
815				    get_line_remain(), "  Text = \"%.*s\"",
816				    olen, data);
817			break;
818		case DHCPV6_OPT_VENDOR_CLASS:
819			if (olen < sizeof (val32))
820				break;
821			(void) memcpy(&val32, data, sizeof (val32));
822			data += sizeof (val32);
823			olen -= sizeof (val32);
824			val32 = ntohl(val32);
825			(void) snprintf(get_line(0, 0), get_line_remain(),
826			    "  Enterprise Number = %lu (%s)", val32,
827			    entr_to_str(val32));
828			/* FALLTHROUGH */
829		case DHCPV6_OPT_USER_CLASS:
830			while (olen >= sizeof (val16)) {
831				(void) memcpy(&val16, data, sizeof (val16));
832				data += sizeof (val16);
833				olen -= sizeof (val16);
834				val16 = ntohs(val16);
835				if (val16 > olen) {
836					(void) strlcpy(get_line(0, 0),
837					    "  Truncated class",
838					    get_line_remain());
839					val16 = olen;
840				}
841				show_hex(data, olen, "  Class");
842				data += val16;
843				olen -= val16;
844			}
845			break;
846		case DHCPV6_OPT_VENDOR_OPT: {
847			dhcpv6_option_t sd6o;
848
849			if (olen < sizeof (val32))
850				break;
851			(void) memcpy(&val32, data, sizeof (val32));
852			data += sizeof (val32);
853			olen -= sizeof (val32);
854			val32 = ntohl(val32);
855			(void) snprintf(get_line(0, 0), get_line_remain(),
856			    "  Enterprise Number = %lu (%s)", val32,
857			    entr_to_str(val32));
858			while (olen >= sizeof (sd6o)) {
859				(void) memcpy(&sd6o, data, sizeof (sd6o));
860				sd6o.d6o_code = ntohs(sd6o.d6o_code);
861				sd6o.d6o_len = ntohs(sd6o.d6o_len);
862				(void) snprintf(get_line(0, 0),
863				    get_line_remain(),
864				    "  Vendor Option Code = %u", d6o.d6o_code);
865				data += sizeof (d6o);
866				olen -= sizeof (d6o);
867				if (sd6o.d6o_len > olen) {
868					(void) strlcpy(get_line(0, 0),
869					    "  Vendor Option truncated",
870					    get_line_remain());
871					sd6o.d6o_len = olen;
872				}
873				if (sd6o.d6o_len > 0) {
874					show_hex(data, sd6o.d6o_len,
875					    "    Data");
876					data += sd6o.d6o_len;
877					olen -= sd6o.d6o_len;
878				}
879			}
880			break;
881		}
882		case DHCPV6_OPT_REMOTE_ID:
883			if (olen < sizeof (val32))
884				break;
885			(void) memcpy(&val32, data, sizeof (val32));
886			data += sizeof (val32);
887			olen -= sizeof (val32);
888			val32 = ntohl(val32);
889			(void) snprintf(get_line(0, 0), get_line_remain(),
890			    "  Enterprise Number = %lu (%s)", val32,
891			    entr_to_str(val32));
892			/* FALLTHROUGH */
893		case DHCPV6_OPT_INTERFACE_ID:
894		case DHCPV6_OPT_SUBSCRIBER:
895			if (olen > 0)
896				show_hex(data, olen, "  ID");
897			break;
898		case DHCPV6_OPT_RECONF_MSG:
899			if (olen > 0) {
900				(void) snprintf(get_line(0, 0),
901				    get_line_remain(),
902				    "  Message Type = %u (%s)", *data,
903				    reconf_to_str(*data));
904			}
905			break;
906		case DHCPV6_OPT_SIP_NAMES:
907		case DHCPV6_OPT_DNS_SEARCH:
908		case DHCPV6_OPT_NIS_DOMAIN:
909		case DHCPV6_OPT_BCMCS_SRV_D: {
910			dhcp_symbol_t *symp;
911			char *sp2;
912
913			symp = inittab_getbycode(
914			    ITAB_CAT_STANDARD | ITAB_CAT_V6, ITAB_CONS_SNOOP,
915			    d6o.d6o_code);
916			if (symp != NULL) {
917				str = inittab_decode(symp, data, olen, B_TRUE);
918				if (str != NULL) {
919					sp = str;
920					do {
921						sp2 = strchr(sp, ' ');
922						if (sp2 != NULL)
923							*sp2++ = '\0';
924						(void) snprintf(get_line(0, 0),
925						    get_line_remain(),
926						    "  Name = %s", sp);
927					} while ((sp = sp2) != NULL);
928					free(str);
929				}
930				free(symp);
931			}
932			break;
933		}
934		case DHCPV6_OPT_SIP_ADDR:
935		case DHCPV6_OPT_DNS_ADDR:
936		case DHCPV6_OPT_NIS_SERVERS:
937		case DHCPV6_OPT_SNTP_SERVERS:
938		case DHCPV6_OPT_BCMCS_SRV_A:
939			while (olen >= sizeof (in6_addr_t)) {
940				show_address("  Address", data);
941				data += sizeof (in6_addr_t);
942				olen -= sizeof (in6_addr_t);
943			}
944			break;
945		case DHCPV6_OPT_IAPREFIX: {
946			dhcpv6_iaprefix_t d6ip;
947
948			if (olen < DHCPV6_IAPREFIX_SIZE - sizeof (d6o))
949				break;
950			(void) memcpy(&d6ip, data - sizeof (d6o),
951			    DHCPV6_IAPREFIX_SIZE);
952			data += DHCPV6_IAPREFIX_SIZE - sizeof (d6o);
953			olen -= DHCPV6_IAPREFIX_SIZE - sizeof (d6o);
954			show_address("  Prefix", d6ip.d6ip_addr);
955			(void) snprintf(get_line(0, 0), get_line_remain(),
956			    "  Preferred lifetime = %u seconds",
957			    ntohl(d6ip.d6ip_preflife));
958			(void) snprintf(get_line(0, 0), get_line_remain(),
959			    "  Valid lifetime = %u seconds",
960			    ntohl(d6ip.d6ip_vallife));
961			(void) snprintf(get_line(0, 0), get_line_remain(),
962			    "  Prefix length = %u", d6ip.d6ip_preflen);
963			nest_options(data, olen, "ADDR: ", "Address");
964			break;
965		}
966		case DHCPV6_OPT_INFO_REFTIME:
967			if (olen < sizeof (val32))
968				break;
969			(void) memcpy(&val32, data, sizeof (val32));
970			(void) snprintf(get_line(0, 0), get_line_remain(),
971			    "  Refresh Time = %lu seconds", ntohl(val32));
972			break;
973		case DHCPV6_OPT_GEOCONF_CVC: {
974			dhcpv6_civic_t d6c;
975			int solen;
976
977			if (olen < DHCPV6_CIVIC_SIZE - sizeof (d6o))
978				break;
979			(void) memcpy(&d6c, data - sizeof (d6o),
980			    DHCPV6_CIVIC_SIZE);
981			data += DHCPV6_CIVIC_SIZE - sizeof (d6o);
982			olen -= DHCPV6_CIVIC_SIZE - sizeof (d6o);
983			(void) snprintf(get_line(0, 0), get_line_remain(),
984			    "  What Location = %u (%s)", d6c.d6c_what,
985			    cwhat_to_str(d6c.d6c_what));
986			(void) snprintf(get_line(0, 0), get_line_remain(),
987			    "  Country Code = %.*s", sizeof (d6c.d6c_cc),
988			    d6c.d6c_cc);
989			while (olen >= 2) {
990				(void) snprintf(get_line(0, 0),
991				    get_line_remain(),
992				    "  CA Element = %u (%s)", *data,
993				    catype_to_str(*data));
994				solen = data[1];
995				data += 2;
996				olen -= 2;
997				if (solen > olen) {
998					(void) strlcpy(get_line(0, 0),
999					    "  CA Element truncated",
1000					    get_line_remain());
1001					solen = olen;
1002				}
1003				if (solen > 0) {
1004					show_ascii(data, solen, "  CA Data");
1005					data += solen;
1006					olen -= solen;
1007				}
1008			}
1009			break;
1010		}
1011		case DHCPV6_OPT_CLIENT_FQDN: {
1012			dhcp_symbol_t *symp;
1013
1014			if (olen == 0)
1015				break;
1016			(void) snprintf(get_line(0, 0), get_line_remain(),
1017			    "  Flags = %02x", *data);
1018			(void) snprintf(get_line(0, 0), get_line_remain(),
1019			    "        %s", getflag(*data, DHCPV6_FQDNF_S,
1020			    "Perform AAAA RR updates", "No AAAA RR updates"));
1021			(void) snprintf(get_line(0, 0), get_line_remain(),
1022			    "        %s", getflag(*data, DHCPV6_FQDNF_O,
1023			    "Server override updates",
1024			    "No server override updates"));
1025			(void) snprintf(get_line(0, 0), get_line_remain(),
1026			    "        %s", getflag(*data, DHCPV6_FQDNF_N,
1027			    "Server performs no updates",
1028			    "Server performs updates"));
1029			symp = inittab_getbycode(
1030			    ITAB_CAT_STANDARD | ITAB_CAT_V6, ITAB_CONS_SNOOP,
1031			    d6o.d6o_code);
1032			if (symp != NULL) {
1033				str = inittab_decode(symp, data, olen, B_TRUE);
1034				if (str != NULL) {
1035					(void) snprintf(get_line(0, 0),
1036					    get_line_remain(),
1037					    "  FQDN = %s", str);
1038					free(str);
1039				}
1040				free(symp);
1041			}
1042			break;
1043		}
1044		}
1045		data = ostart + d6o.d6o_len;
1046		len -= d6o.d6o_len;
1047	}
1048	if (len != 0) {
1049		(void) strlcpy(get_line(0, 0), "Option entry truncated",
1050		    get_line_remain());
1051	}
1052}
1053