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